(reprezentace, generování, úpravy) IB113 Radek Pelánek 2022 2/71 3/71 Úč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/71 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 m Vektorová grafika (5,27) ^ (25,27) ^5,15) , x ^\(25,3) (5,3) • 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 9 show, save - zobrazení, uložení Knihovna Image - technické poznámky Není součástí standardní distribuce, nutno doinstalovat. • Python Imaging Library (PIL): jen pro Python 2 http://www.pythonware.com/products/pil/ • implementace Pillow (i pro Python 3): https://pypi.org/proj ect/Pillow/ • from PIL import Image 10/71 • 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/71 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 • např. putpixel má 2 parametry: souřadnice (=dvojice), barva (=trojice) • vnořené cykly, podmínky • použití v jiném kontextu než doposud 12/71 def demoO : im = Image.new("RGB", (20, 20), (255, 255, 255)) # model, velikost, barva pozadz 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("demo.png") Geometrické útvary Napište programy pro generovaní následujících útvarů čtverec trojúhelník kruh elipsa spirála 14/71 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/71 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/71 17/71 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/71 •<□► 4 ^ t < -Ž ► 4 S ► 22/71 - Základní princip • potřebujeme plynulý přechod mezi bílou a černou • jakou matematickou funkci využijeme? •<□► 4 ^ t < -ž ► < -š ► 23/71 - 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/71 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/71 šachovnice a kruhy mřížka a kruh itinimiiiiiniw vlny a čtverec ........ .....♦ • • m f f ...... 25/71 26/71 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/71 andelbrotova množina - kód = c 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 =' ► < -Ž ► 4 ± > 43/71 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 45/71 Pořádek v umění http://www.ted.com/talks/ursus_wehrli_tidies_up_art < □ ► 4 ^ t < -ž ► < ► 46/71 Pořádek (nejen) v umění Pořádek v umění - pixel po pixelu 48/71 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 49/71 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 50/71 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])) 52/71 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 53/71 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) 9 prohlížení: např. webový prohlížeč • ruční editování: např. Inkscape • převod na bitmapu: např. convert (ImageMagick) •<□► 4 ^ t < -ž ► 4 > 54/71 SVG příklad < □ ► 4 ^ t 4 *■ 4 S ► 55/71 56/71 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_x, center_y + (n-i)*step) svg_line(center_x + i*step, center_y, center_x, center_y - (n-i)*step) svg_line(center_x - i*step, center_y, center_x, center_y - (n-i)*step) svg_finish() 57/71 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() 58/71 59/71 Vlastní knihovna pro želví grafiku • ž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) 60/71 rincip implementace o stav želvy: souřadnice x, y a aktuální natočení heading • vykreslený obrazec: seznam souřadnic 61/71 x = 50 y = 50 heading = 0 lines = [] def left(angle): global heading heading -= angle def right(angle): global heading heading += angle 62/71 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. closeQ 64/71 jde o názornou ukázku principů, nikoliv dobrou knihovnu: • příliš malá funkcionalita • chybí dokumentace nevhodné použití globálních proměnných - lepší přes objektovou reprezentaci 65/71 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 66/71 Absolutní vs. relativní vykreslování (souřadnice vs. želva) 67/71 68/71 69/71 • 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 < -ž ► < -Š ► 70/71 • 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 71/71