(reprezentace, generování, úpravy) IB113 Radek Pelánek 2019 2/75 3/75 Účel prednášky • procvičení základních konstrukcí z jiného pohledu • propojení programování a matematiky • téma „reprezentace dat" • procvičení „čtení kódu" • podklad pro zajímavé cvičení 4/75 Poznámka k efektivitě, obrázkům ukázky programů v přednášce: • snaha o čitelnost programů • neefektivní (pomalé): • algoritmy o technická realizace (např. „putpixel" vs „load + pixel access object") nízká / rozličná kvalita obrázků - čistě pragmatické důvody (nepříliš velké PDF), žádná skrytá pointa )alšŕ zdroje, náměty obrázky, zvuk, video: • kniha Introduction to Computing and Programming in Python, A Mutlimedia Approach, M. J. Guzdial, B. Ericson. • http://coweb.cc.gatech.edu/mediaComp-teach Reprezentace obrázků Bitmapová grafika Vektorová grafika • více barevných modelů (aditivní, subtraktivní) • budeme používat aditivní model RGB - red, green, blue • každá složka = hodnota 0-255 (8 bitů, 1 byte) • barva = trojice, např. (15, 255, 100) hovna Image • knihovna pro práci s bitmapovými obrázky • velmi bohatá funkcionalita • použijeme jen základní operace: • new - vytvoření obrázku • open, convert - otevření obrázku, konverze na mód • getpixel - zjištění barvy bodu • putpixel - změna barvy bodu • size - velikost obrázku • show, save - zobrazení, uložení Knihovna Image - technické poznámky Python Imaging Library (PIL): jen pro Python 2 http://www.pythonware.com/products/pil/ implementace Pillow (i pro Python 3): https://pypi.python.org/pypi/Pillow/2.1.0 from PIL import Image 10/75 • reprezentace souřadnic a barev pomocí n-tic (tuple) • podobné jako seznamy, ale neměnitelné; zápis pomocí kulatých závorek • u obrázků typicky: • souřadnice: (x, y) • barva: (r, g, b) 11/75 Koncepty v ukázkách čemu věnovat pozornost v ukázkách: • objekty - třída Image, objekty pro dílčí obrázky • n-tice, seznamy • parametry funkcí, závorky • vnořené cykly, podmínky (použití v jiném kontextu než doposud) 12/75 Image demo def demo () : im = Image.new("RGB", (20, 20), (255, 255, 255)) im.putpixel((10, 10), (0, 0, 0)) im.putpixel((8, 7), (255, 0, 0)) im.putpixel((5, 13), (100, 255, 105)) im.show() im.save(Mdemo.png") # model, velikost, barva pozadi 13/75 Geometrické útvary Napište programy pro generovaní následujících útvarů čtverec trojúhelník kruh elipsa spirála 14/75 def disc(a=150, r=50): im = Image.new("RGB", (a, a), (255, 255, 255)) for x in range(a): for y in range(a): if XXX: im.putpixel((x, y), (0, 0, 0)) im.show() 15/75 def disc(a=150, r=50): im = Image.new("RGB", (a, a), (255, 255, 255)) for x in range(a): for y in range(a): if (x-a/2)**2 + (y-a/2)**2 < r**2: im.putpixel((x, y), (0, 0, 0)) im.show() 16/75 17/75 Barvu „namícháme" podle vzdálenosti od středu kruhu d = math.sqrt((x-a/2)**2 + (y-a/2)**2) if d < r: c = int(255*d/r) im.putpixel((x, y), (c, 0, 255-c)) Pridaní náhodného kruhu do obrázku def add_random_disc(im): (width, height) = im.size r = random randint(8, min(width, height) // 6) sx = random.randint(r+1, width-r-1) sy = random.randint(r+1, height-r-1) color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) for x in range(width): for y in range(height): if (x-sx)**2 + (y-sy)**2 < r**2: im.putpixel((x, y), color) 20/75 •<□► 4 ^ t < -Ž ► 4 S ► 22/75 - Základní princip • potřebujeme plynulý přechod mezi bílou a černou • jakou matematickou funkci využijeme? •<□► 4 ^ t < -ž ► < -š ► 23/75 - Základní princip • potřebujeme plynulý přechod mezi bílou a černou • jakou matematickou funkci využijeme? • sinus - hodnoty mezi -1 a 1, perioda 2tt • potřebujeme - hodnoty mezi 0 a 255, perioda (např.) 20 23/75 def strips(size=150, count=5): im = Image.new("RGB", (size, size)) for x in range(size): for y in range(size): z = math.sin(count * 2*math.pi * x/size) shade = int(255 * (z+l)/2) im.putpixel((x, y), (shade, shade, shade)) im.show() 24/75 šachovnice a kruhy mřížka a kruh vlny a čtverec •<□► 4 ^ t < -ž ► < -E ► 25/75 26/75 Mandelbrotova množina • zi = 0, c = x + yi je konstanta (komplexní číslo) • definujeme posloupnost z„+i = z2n + c • c patří do Mandelbrotovy množiny <^> tato posloupnost je omezená andelbrotova množina - detail Zdroj: Wikipedia Video zoom: http://www.youtube.com/watch?v=gEw8xpblaRA 28/75 = ( 255, lambda V ,B,c :c and Y(V*V+B,B, c -l)if(abs(V)<6)else ( 2+c-4*abs(V)**-0.4)/i ) ;v, x=1500,1000;C=range(v*x );import struct;P=struct.pack;M,\ j =1 4 = > 32/75 Úprava barev - obecnější řešen def transform_colors(filename, f_trans): im = Image.open(filename) im = im.convert("RGB") width, height = im.size for x in range(width): for y in range(height): (r, g, b) = im.getpixel((x, y)) im.putpixel((x, y), f_trans(r, g, b)) im.show() def inversion(r, g, b): return (255-r, 255-g, 255-b) transform_colors("les.jpg", inversion) transform_colors("les.jpg", lambda r, g, b: (255-r, b, g)) 33/75 - Zrcadlový obraz •<□► 4 ^ t < -ž ► < -E ► 34/75 rcadlový obraz pro každý pixel v levé polovině: • zjisti jeho barvu (getpixel) • ulož barvu na příslušnou pozici v pravé polovině (putpixel) •<□► 4 ^ t < -ž ► < -š ► 34/75 Zrcadlový obraz - kód for x in range(width / 2): for y in range(height): im.putpixel((width-l-x, y), im.getpixel((x, y))) 35/75 prohazování symetrických bodů 36/75 Preklopení - kód V předchozím kódu (zrcadlový obraz) změníme tělo f or cyklu tmp = im.getpixel((width-l-x, y)) im.putpixel((width-l-x, y), im.getpixel((x, y))) im.putpixel((x, y), tmp) 37/75 •<□► 4 ^ t < -Ž ► 4 S ► 38/75 vytvoř nový obrázek a naplň jej pixely podle originálu - vhodně pozměněné souřadnice 38/75 def rotation(filename): im = Image.open(filename) im = im.convert("RGB") width, height = im.size new_im = Image.new("RGB", (height, width)) for x in range(width): for y in range(height): new.im.putpixel((XXX, YYY), im.getpixel((x, y))) new_im. showQ •<□► 4 ^ t < -z ► 4 S ► 39/75 40/75 41/75 goniometrické funkce, lineární transformace, matice - aplikace (procvičení) pojmů z matematiky 41/75 •<□► 4 ^ t < -Ž ► 4 S ► 42/75 colors = {'black1: (0, 0, 0), 'white': (255, 255, 255), 'red': (255, 0,0), 'yellow': (255, 255, 0), 'pink': (255, 192, 203)} names = sorted(colors.keys(), key=lambda c: sum(colors[c])) print(", ".join(names)) 43/75 Kahoot: otazka 7 def make_imagel(a=150): im = Image.new("RGB", (a, a), (255, 255, 255)) for x in range(a): for y in range(a): if y*10 > (x-a/2)**2: im.putpixel((x, y), (0, 0, 0)) im.show() make_image1() def make_image2(a=160): im = Image.new("RGB", (a, a), (255, 255, 255)) for x in range(a): for y in range(a): if (x // 20 + y // 20) °/0 2 == 0: im.putpixel((x, y), (0, 0, 0)) im.show() make_image2() 45/75 def transform(filename): im = Image.open(filename) im = im.convert("RGB") width, height = im.size for x in range(width): for y in range(height): (r, g, b) = im.getpixel((x, y)) g, b = int(g*0.8), int(b*0.8) r = min(int(r*1.4), 255) im.putpixel((x, y), (r, g, b)) im.show() < □ ► 4 ^ t < *■ 4 S ► 46/75 47/75 48/75 Histogram variace na téma „frekvenční analýza" "■ les.jpg-1.0 (RGB, 1 vrstva] 3&9xZ92 - CIMP Soubor Upravit Vybrat Zobrazení Obrázek Vrstva Barvy Nástro m.......\w.......\m iii"........ • an ..... U px : 100% * Pozadí (1,3 MB) Histogram Histogram Pozadí Kanál: 3/Zelená 0 Str. hodnota: 121,5 Směr, odch.: 44.5 Median: 122.0 Pixelů: 1135BB Počet: 113566 Procento: 100.0 49/75 Histogram - textový výpis 0 - 19: 0.3 % 20 - 39: 3.5 % 40 - 59: 6.3 % 60 - 79: 8.3 % 80-99: 12.7 % 100 - 119: 17.1 % 120 - 139: 18.5 % 140 - 159: 15.2 % 160 - 179: 9.0 % 180 - 199: 4.0 % 200 - 219: 1.8 % 220 - 239: 1.1 °/„ 240 - 259: 2.2 °/„ (implementace - doporučené cvičení) Další náměty na úpravy • změna velikosti obrázku • převod do stupňů šedi • rozmazání (blur), detekce hran • ... další věci co umí váš grafický program •<□► 4 ^ t < -ž ► 4 > 51/75 Pořádek v umění http://www.ted.com/talks/ursus_wehrli_tidies_up_art.html 52/75 Pořádek (nejen) v umění 53/75 Pořádek v umění - pixel po pixelu 54/75 Razení pixelů podle barvy • vytvoříme seznam všech použitých barev - seznam trojic [(0, 150, 20), (255,255,255), (0, 0, 255), ...] • seznam seřadíme • barvy umístíme do obrázku 55/75 def tidy_up(filename): im = Image.open(filename) im = im.convert("RGB") width, height = im.size pixels = [] for x in range(width): for y in range(height): pixels.append(im.getpixel((x, y))) pixels.sort() new_im = Image.new("RGB", (width, height)) for y in range(height): for x in range(width): new_im.putpixel((x, y), pixels [y*width+x]) new_im. showQ 56/75 Razení pixel • pixely je seznam trojic (r, g, b) • sort() používá „lexikografické" řazení • pokud chceme „řazení dle součtu" (intenzity) nahradíme pixels.sort () za: pixels = sorted(pixels, key=lambda c: -(c[0]+c [1]+c [2])) 58/75 Pořádek v umění - námět Zkuste další způsoby řazení: • po řádcích / sloupcích • po „čtverečcích" • podle jiného kritéria • „gradient" po úhlopříčce 59/75 Scalable Vector Graphics (SVG) • vektorový formát založený na XML • snadný způsob vytváření obrázků v jakémkoliv jazyce (generujeme prostý text) • prohlížení: např. webový prohlížeč • ruční editování: např. Inkscape • převod na bitmapu: např. convert (ImageMagick) •<□► 4 ^ t < -ž ► < -š ► 60/75 61/75 62/75 def star(n=10, length=100): svg_header() center_x = length * 1.5 center_y = length * 1.5 step = length / n for i in range(n + 1): svg_line(center_x + i*step, center_y, center_x, center_y + (n-i)*step) svg_line(center_x - i*step, center_y, center_x3 center_y + (n-i)*step) svg_line(center_x + i*step, center_y3 center_x, center_y - (n-i)*step) svg_line(center_x - i*step, center_y, center_x3 center_y - (n-i)*step) svg_f inishO < is? ► < i ► < = > i o^o 63/75 Kompaktnější zápis def star(n=10, length=100): svg_header() center_x = length * 1.5 center_y = length * 1.5 step = length / n for i in range(n + 1): for dx, dy in [(-1, -1), (-1, 1), (1, -1), (1, 1)]: svg_line(center_x + dx*i*step, center_y, center_x, center_y + dy*(n-i)*step) svg_finish() 64/75 •<□► 4 ^ t < -Ž ► 4 S ► 65/75 Vlastní knihovna pro želví grafiku 9 želví grafika - používána knihovna turtle • vytvořme vlastní „knihovnu" s vykreslováním do SVG • jen základní příkazy: • forward(length) • left(angle), right(angle) • save(filename) 66/75 rincip implementace 9 stav želvy: souřadnice x, y a aktuální natočení heading • vykreslený obrazec: seznam souřadnic 67/75 x = 50 y = 50 heading = 0 lines = [] def left(angle): global heading heading -= angle def right(angle): global heading heading += angle 68/75 def forward(d): global x global y nx = x + d * math.cos(heading * math.pi / 180) nY = y + d * math.sin(heading * math.pi / 180) lines.append((x, y, nx, ny)) x, y = nx, ny mplementace III def save(filename): f = open(filename, MwM) f.write(M") s = 'M) f. closeO 70/75 jde o názornou ukázku principů, nikoliv dobrou knihovnu: • príliš malá funkcionalita • chybí dokumentace nevhodné použití globálních proměnných - lepší přes objektovou reprezentaci 71/75 class Turtle: def __init__(self): self.x = 50 self.y = 50 self.heading = 0 self .lines = [] def left(self, angle): self.heading -= angle def right(self, angle): self.heading += angle def forward(self, d): nx = self.x + d * math.cos(self.heading * math.pi / 180) ny = self.y + d * math.sin(self.heading * math.pi / 180) self.lines.append((self.x, self.y, nx, ny)) self.x, self.y = nx, ny 72/75 Absolutní vs. relativní vykreslování (souřadnice vs. želva) 73/75 Jak vykreslíte tyto obrázky? • Jaký je rozdíl mezi bitmapovou a vektorovou grafikou? • Co znamená RGB? Jakým barvám odpovídají trojice (0, 0, 0), (0, 255, 0), (200, 0, 180)? • Jakou datovou strukturu použijeme v Pythonu pro reprezentaci barev? Proč? • Jakým způsobem vytvoříme v Pythonu bitmapový obrázek, který je celý bílý a uprostřed má červenou tečku? • Jakým způsobem můžeme v Pythonu otestovat, že obrázek obsahuje pouze bílou, modrou a černou barvu? • Co je to SVG? Jakým způsobem můžeme vytvořit obrázek v tomto formátu? •<□► 4 ^ t 4 E k 4 S ► 75/75 • ukázka elementární práce s grafikou • bitmapová - Image, putpixel, getpixel • vektorová - SVG, line • využití základních konstrukcí (vesměs vnořené for cykly), trocha matematiky 76/75