12. Bitmapová grafika

Uspořádané k-tice

Jednou z nejjednodušších struktur, se kterou jsme se už setkali v případě, kdy jsme chtěli prohodit obsah dvou proměnných, jsou uspořádané k-tice (anglicky „tuple“). Podobně jako v matematice se jedná o uspořádaný soubor prvků dané velikosti. Svým způsobem jsou podobné seznamům, avšak na rozdíl od nich vytvořené k-tice už nemůžeme nikterak měnit. Pro vytvoření uspořádané k-tice používáme čárku. Při práci s uspořádanými k-ticemi můžeme použít tzv. pattern matching, kde jednotlivé prvky k-tice můžeme jednoduše přiřadit do zvláštních proměnných.

Základní vytvoření obrázku

Obrázek si v dnešním cvičení můžeme představit jako mřížku pixelů, kde každému pixelu přísluší nějaká barva. Pixel, jehož souřadnice jsou (0, 0) se nachází v levém horním rohu. Pro zápis barvy typicky používáme RGB model.

ukázka míchání světelných kanálu v RGB modelu

Barvu v RGB modelu měníme nastavením různé intenzity červeného (R), zeleného (G) a modrého (kanálu). Každý kanál může nabývat hodnot od 0 do 255.

from PIL import Image

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)


# Zjisti, zda je zadany bod uvnitr kruhu o danem polomeru, jehoz stred
# je uprostred ctvercoveho obrazku.
def in_circle(size: float, radius: float, x: float, y: float) -> bool:
    return (x - size / 2) ** 2 + (y - size / 2) ** 2 < radius ** 2


def circle(size: int = 150, radius: int = 50) -> None:
    # Vytvorime objekt pro manipulaci s obrazkem
    im = Image.new("RGB", (size, size))
    # Prochazime vsechny pixely naseho obrazku a kontrolujeme, zda lezi
    # v kruhu
    for x in range(size):
        for y in range(size):
            if in_circle(size, radius, x, y):
                # Pixel na souradnicich (x, y) obarvime na cerno.
                im.putpixel((x, y), BLACK)
            else:
                # Pixel na souradnicich (x, y) obarvime na bilo.
                im.putpixel((x, y), WHITE)
    # obrazek si muzeme zobrazit
    im.show()
    # nebo jej muzeme ulozit do souboru
    im.save("demo_circle.png")


# Vykresli kruh o polomeru 50 na bilem ctverci o strane 150.
circle()

Abyste mohli bez problému pracovat s obrázky, musíte mít nainstalovanou knihovnu Pillow. Následně musíte importovat třídu Image:

from PIL import Image

12.1. Kreslení

12.1.1. Čtverec

Na bílé pozadí o zadané velikosti nakreslete černý čtverec o zadané straně, jehož střed bude umístěn do středu obrázku.

from PIL import Image


def square(size: int = 250, a: int = 70) -> None:
    pass

12.1.2. Trojúhelník

Na bílé pozadí o zadané velikosti nakreslete černý rovnostranný trojúhelník o dané straně, jehož těžiště bude umístěno do středu obrázku.

def triangle(size: int = 150, a: int = 50) -> None:
    pass

12.1.3. Kruh

Na bílé pozadí o zadané velikosti nakreslete černý kruh o zadaném poloměru a se středem na zadaných souřadnicích.

def circle(size: int = 150, center: Tuple[int, int] = (75, 75), radius: int = 20) -> None:
    pass

12.1.4. Elipsa

Na bílé pozadí o zadané velikosti nakreslete černou elipsu o zadaných poloměrech, jejíž střed bude umístěn do středu obrázku.

def ellipse(size: int = 150, radius_a: int = 50, radius_b: int = 20) -> None:
    pass

12.1.5. Spirála

Na bílé pozadí o zadané velikosti nakreslete spirálu začínající ve středu obrázku.

def spiral(size: int = 150, turn_inc: int = 5, tstep: float = 0.05) -> None:
    pass

12.1.6. Šachovnice

Nakreslete šachovnicový vzor o zadané velikosti obrázku a šířce pruhu.

def chessboard(size: int = 150, stripe: int = 30) -> None:
    pass

12.1.7. Šachovnice s kruhy

Nakreslete následující obrázek, parametrem kreslící funkce je velikost obrázku, šířka šachovnicových pruhů a šířka mezikruží.

_images/bitmapova_grafika_chessboard_with_circles.png

12.1.8. Překrývající se kruhy

Napište funkci, která vykreslí více černých kruhů. Každé překrytí kruhů invertuje pozadí kruhu. Kruhy jsou zadané pomocí seznamu, v němž každá položka je dvojice střed a poloměr, viz ukázka:

_images/bitmapova_grafika_circles_overlap.png
def circles_overlap(size: int, circles: List[Tuple[Tuple[int, int], int]]) -> None:
    pass


circles = [
    ((20, 20), 40),
    ((200, 150), 100),
    ((200, 300), 140),
    ((300, 200), 50),
]
circles_overlap(500, circles)

12.1.9. Vlny

Napište funkci, která vykreslí následující obrázek. Parametrem je velikost obrázku a počet vln.

_images/bitmapova_grafika_waves.png
def waves(size: int, k: int) -> None:
    pass

12.1.10. Vlny a čtverec

Napište funkci, která vykreslí následující obrázek. Parametrem je velikost obrázku, počet vln a strana čtverce, jehož střed je ve středu obrázku.

_images/bitmapova_grafika_waves_square.png
def waves_square(size: int, k: int, a: int) -> None:
    pass

12.2. Transformace

V následujících příkladech budeme pracovat s již existujícími obrázky, a ty pak nějakým způsobem transformovat. Na rozdíl od předchozích příkladů budeme potřebovat otevřít soubor s již existujícím obrázkem:

im = Image.open(filename).convert("RGB")

# muzeme jednoduse zjistit rozmery obrazku
width, height = im.size

Podobně jako pro zápis barvy, existuje i rozhraní pro čtení barvy daného pixelu.

r, g, b = im.getpixel((0, 0))
im.putpixel((0, 0), (r + 1, g + 1, b + 1))

Pokud nemáte žádný vhodný obrázek po ruce, stáhněte si tento:

_images/bitmapova_grafika_transformation_source.jpg

12.2.1. Odstranění barvy

Napište funkci, která odstraní z daného obrázku zelenou barvu (alternativně červenou nebo modrou):

_images/bitmapova_grafika_without_green.png
def without_green(filename: str) -> None:
    pass

12.2.2. Invertování barev

Napište funkci, která invertuje barvy v daném obrázku.

_images/bitmapova_grafika_invert_colors.png
def invert_colors(filename: str) -> None:
    pass

12.2.3. Stupně šedi

Napište funkci, která převede barevný obrázek na obrázek ve stupních šedi.

def grayscale(filename: str) -> None:
    pass

12.2.4. Zrcadlení

Napište funkci, která daný obrázek transformuje do “zrcadlení”.

_images/bitmapova_grafika_mirror.png
def mirror(filename: str) -> None:
    pass

12.2.5. Převrácení

Napište funkci, která daný obrázek převrátí vzhledem k jeho vertikální ose.

_images/bitmapova_grafika_invert.png
def invert(filename: str) -> None:
    pass

12.3. Šifry

12.3.1. Hledej v modré

Vyřešte následující obrázkovou šifru.

_images/bitmapova_grafika_cipher_1.png

12.3.2. Hrany

Vyřešte následující obrázkovou šifru.

_images/bitmapova_grafika_cipher_2.png

12.3.3. XOR se šachovnicí

Vyřešte následující obrázkovou šifru.

_images/bitmapova_grafika_cipher_3.png

12.3.4. Schovávačka

Představte si, že máte jeden černo-bílý obrázek (tedy takový, který obsahuje jen pixely černé a bílé barvy) a chtěli byste jej schovat do jiného obrázku, takovým způsobem, aby nebyla jeho přítomnost zřejmá. Naprogramujte tedy dvě funkce, encode, která dostane dva obrázky a jeden ukryje do druhého, a funkci decode, která z obrázku vytáhne ten ukrytý.

Jak na to? Poznáte okem rozdíl mezi pixely s barvami (88, 63, 149) a (88, 62, 149)? Spíš ne, takže co takhle ten jeden bit využít pro zakódování jednoho pixelu z tajného obrázku…

Poznámka: úkolem schovávání informací tak, aby nebyly zjistitelné se zabývá vědní disciplína steganografie.