Hlavní navigace

Létající cirkus (19)

Jan Švec 7. 11. 2002

Další povídání nad jazykem Python pokračuje dílem věnovaným knihovně pro práci s grafickými daty, knihovně Python Imaging Library, zkráceně PIL.

CO JE PIL?

Python Imaging Library je balíček modulů pro práci s grafickými daty z prostředí jazyka Python. Zdrojový tarball si můžete stáhnout z domovské stránky knihovny. Instrukce pro nainstalování jsou zahrnuty v balíčku. Je rovněž možné použít předkompilované balíčky pro vaši distribuci Linuxu (v Debianu např. python2.2-imaging).

Po nainstalování máte přístupný pythonovský balíček PIL, někdy (např. v Debianu) může být instalace Pythonu nakonfigurována tak, že cesta sys.path již obsahuje adresář tohoto balíčku, takže všechny jeho moduly jsou přístupné přímo bez uvedení prefixu (jméno balíčku).

JAK A K ČEMU LZE PIL POUŽÍT?

Především je PIL výhodná pro manipulaci s grafickými daty v různých formátech. Lze pomocí ní vytvářet náhledy souborů, generovat nové grafické soubory, provádět různé úpravy parametrů obrázku (změna kontrastu, jasu). Lze dokonce vytvořit u uživatelské filtry, které modifikují původní obrázek. Budete-li Python Imaging Library používat delší dobu, zjistíte, jaká síla se v ní ukrývá.

Jestliže budete chtít vytvořit nový obrázek, máte několik možností. Především lze obrázek načíst z nějakého souboru. Paleta podporovaných souborových formátů je velice široká, proto je téměř vyloučena možnost, že PIL nedokáže nějaký formát přečíst. Další možností, kterou máte, je vytvoření obrázku „na zelené louce“. Konečně, obrázek lze získat i operacemi nad jiným obrázkem (výřez, získání barevného kanálu apod.).

Grafická data lze přečíst ze souboru pomocí funkce open() modulu Image:

>>> import Image
>>> obrazek = Image.open('galeon.png')

Tím jsme získali instanci třídy Image.Image, která nám poskytuje přístup k různým informacím ohledně obrázku:

>>> obrazek.size
(48, 48)
>>> obrazek.format
'PNG'
>>> obrazek.mode
'RGBA'

Je důležité vědět, že PIL se nepokouší načíst celý obrázek hned při volání funkce open(), ale až při požadavku na nějaká grafická data. Proto lze při otevření souboru načíst relativně malou hlavičku informující o velikosti obrázku, případně jeho módu, a vyčkávat, zda uživatel nebude chtít obrázek zpracovávat. Proto můžeme velice rychle přečíst informace o mnoha souborech najednou.

Instance třídy Image.Image podporují také množství metod, díky kterým můžeme s obrázkem pracovat. Například již zmíněný náhled získáme voláním metody thumbnail():

>>> obrazek.thumbnail((96, 96))

Metoda thumbnail() modifikuje původní obrázek, abychom si mohli výsledky našeho snažení prohlédnout, musíme obrázek uložit pomocí volání metody save():

>>> obrazek.save('galeon.thumb.png')

V tomto případě je formát výstupního souboru určen na základě přípony souboru, pokud nebude knihovna moci formát souboru rozpoznat, musíte jí předat formát, který požadujete, např: obrazek.save(‚ga­leon.thumbnail‘, ‚PNG‘)

Pro účely ladění se může hodit metoda show(), která obrázek uloží do dočasného PPM souboru a zavolá program xv pro jeho zobrazení. Pokud se budete PIL zabývat více, zjistíte, že existuje dokonce rozhraní, které umožňuje z obrázku vytvořit Tk widget, tudíž takto lze spolupracovat s nejrozšířenějším toolkitem pro Python.

Pokročilejší metody nabízejí manipulaci s výřezy obrázku. Z obrázku můžete získat podobrázek, ten třeba upravit a navrátit ho zpět do původního:

>>> pulka = obrazek.crop((0, 0, 24, 47))

>>> pulka.load()
>>> pulka = pulka.transpose(Image.FLIP_LEFT_RIGHT)
>>> obrazek.paste(pulka, (0, 0))

Metoda crop() z původního obrázku získá obdélníkový podobrázek, přičemž levý horní roh má souřadnice [0, 0] a pravý dolní [24, 47]. Následné volání metody load() je sichr, metoda crop() totiž může, ale NEMUSÍ vytvořit nezávislou kopii data, tudíž změny v původním obrázku se nám mohou promítat do naší poloviny, se kterou chceme dále pracovat. Metoda load() zajistí, že se data zkopírují vždy, tudíž se přeruší jakékoli vazby mezi původním a novým obrázkem.

Následné volání metody transpose() zrcadlově překlopí obrázek. Existují i další možné konstanty, kterými lze řídit chování této funkce (např. FLIP_TOP_BOTTOM, ROTATE90, ROTATE180, ROTATE270). Tato funkce vrací nový obrázek, který vznikl aplikováním dané transformace na původní obrázek, v našem případě jsme tento obrázek uložili zpět do proměnné pulka. Nakonec jsme polovinu obrázku překlopenou kolem horizontální osy vložili zpět do původního obrázku pomocí metody paste(), té musíme předat obrázek a souřadnice, kam se má vložit.

Pro získání obrázku o jiných rozměrech, než má původní obrázek, použijte metodu resize() a jako první argument jí předejte velikost nového obrázku. Druhý argument může být volitelně jedna z konstant: Image.NEAREST, Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS. Ty specifikují filtr použitý pro přepočítání do jiného rozlišení. Implicitně je použit Image.NEAREST:

>>> obraz = obrazek.resize((128, 128), Image.ANTIALIAS)

PRÁCE S JEDNOTLIVÝMI KANÁLY

Jak nám již prozradila hodnota atributu mode, náš obrázek pracuje v módu RGBA, což znamená, že hodnota jednoho pixelu je složena ze čtyř složek – červené, zelené, modré a míry průhlednosti. PIL umožňuje práci s těmito barevnými módy: 1 (černá a bílá, 1b/px), L (256 odstínů šedi, 8b/px), P (mapování do jiného módu za použití palety, 8b/px), RGB (3×8b/px), RGBA (RGB a míra průhlednosti 4×8b/px), CMYK (4×8b/px), YCbCr (3×8b/px), I (32bitové celočíselné hodnoty pixelů), F (32bitové reálné hodnoty pixelů).

Knihovna bez problémů podporuje převody mezi jednotlivými módy, umožňuje to metoda convert(), které předáte řetězec reprezentující nový mód:

>>> cmyk_obrazek = obrazek.convert('CMYK')

Co víc, PIL dokáže pracovat s jednotlivými kanály odděleně. Metodou split() můžeme obrázek rozdělit na tuple dílčích obrázků (s módem L), které reprezentují každý jeden kanál (tj. jeden reprezentuje červenou složku obrázku, druhý zelenou, třetí modrou a čtvrtý míru průhlednosti).

S těmito obrázky potom můžeme pracovat jako s klasickými instancemi Image.Image. Pokud budeme chtít obrázek získat zpět z daných kanálů, použijeme funkci Image.merge(), které předáme jako první argument mód nového obrázku a jako druhý tuple, jehož prvky jsou kanály nového obrázku. Následující fragment kódu vzájemně prohodí všechny tři barevné kanály obrázku:

>>> R, G, B, A = obrazek.split()
>>> obrazek2 = Image.merge('RGBA', (G, B, R, A))

KRESLENÍ DO OBRÁZKU

Jak jsme si již řekli, obrázek můžeme vytvořit celý od základů. Nyní si povíme, jak na to. Ti, kteří znají nějaký grafický toolkit (např. QT nebo GTK+), jistě ví, že pro kreslení do nějakého primitiva se používá tzv. „plátno“, čili objekt, pomocí jehož metod se dají kreslit čáry, obdélníky a další. Obdobou tohoto plátna je instance třídy ImageDraw.Draw. Jejímu konstruktoru jednoduše předáte obrázek a již můžete kreslit:

>>> import ImageDraw
>>> obrazek3 = Image.new('RGB', (120, 90))
>>> platno = ImageDraw.Draw(obrazek3)
>>> MODRA = 0xff0000
>>> BILA = 0xffffff
>>> CERNA = 0x000000
>>> platno.rectangle((0, 0, 120, 90), fill = BILA, \
...   outline = CERNA)
>>> platno.ellipse((40, 20, 50, 30), fill = MODRA, \
...   outline = CERNA)
>>> platno.ellipse((70, 20, 80, 30), fill = MODRA, \
...   outline = CERNA)

>>> platno.arc((20, 0, 100, 80), 25, 155, fill = MODRA)
>>> del platno

Nyní máme v proměnné obrazek3 obrázek, který jsme si vlastnoručně nakreslili. Můžeme s ním manipulovat jako s jakýmkoli jiným obrázkem. (Zkuste si ho uložit a uvidíte, co těch několik řádek kódu kreslí.) Toto je jen ukázka, jak lze velice jednoduše kreslit do obrázku, budete-li chtít více informací, nahlédněte do dokumentace k této knihovně.

ZMĚNA PARAMETRŮ OBRÁZKU

Pokud budete chtít změnit parametry obrázku (např. jas, kontrast, barevný tón apod.), bude se vám hodit modul ImageEnhance. Ten nabízí několik filtrů, které jsou všechny odvozeny od třídy ImageEnhance._En­hance. Ta definuje jednotné rozhraní – konstruktor __init__(), kterému předáme obrázek, jehož parametry chceme měnit, a metodu enhance(), jež přebírá argument, pomocí kterého se řídí parametry filtru, přičemž po aplikování filtru vrátí nový obrázek. Například pro změnu jasu použijete třídu ImageEnhance.Brig­htness:

>>> obrazek4 = Image.open('galeon.png')
>>> import ImageEnhance
>>> filtr = ImageEnhance.Brightness(obrazek4)
>>> jas = filtr.enhance(0.5)

>>> del filtr

Další možné filtry, které můžete použít, jsou: Color (vyvážení barev), Brightness (jas), Contrast (kontrast) a Sharpness (vyostření).

VYTVÁŘENÍ POSTSCRIPTOVÝCH SOUBORŮ

Poslední zajímavou featurou knihovny PIL, kterou se budeme dnes zabývat, je možnost vytváření postscriptových souborů. (Každý UNIXový mág jistě ví, k čemu se nám hodí, lze pomocí nich například vyřešit tisk z programu apod.) Modul PSDraw nám poskytuje třídu PSDraw, pomocí jejíchž metod můžeme PS soubor vytvářet. Jejímu konstruktoru předáme pouze soubor otevřený pro zápis. Ten můžeme dokonce vynechat, pak PIL použije standardní výstup. Následně zavoláme metodu begin_document(), vesele si kreslíme, a až bude po všem, zavoláme end_document() a PSDraw nám postscriptový dokument uloží do souboru:

>>> import PSDraw
>>> soubor = open('output.ps', 'w')
>>> ps = PSDraw.PSDraw(soubor)
>>> INCH = 72
>>> ps.begin_document()

>>> ps.line((INCH, INCH), (5*INCH, INCH))
>>> ps.line((INCH, INCH), (3*INCH, 4*INCH))
>>> ps.line((3*INCH, 4*INCH), (5*INCH, INCH))
>>> ps.end_document()
>>> soubor.close()

Pozor, všechny kreslící metody PSDraw používají souřadnicový systém PostScriptu, kde souřadnice [0, 0] odpovídá levému dolnímu rohu. Dále jeden palec odpovídá 72 bodům, proto každou souřadnici násobíme konstantou 72 (INCH). Více se dozvíte ze zdrojových souborů modulu PSDraw nebo z dokumentace knihovny PIL.

PŘÍŠTĚ

Další, již dvacátý díl Létajícího cirkusu věnujeme knihovně Numeric Python (zkráceně NumPy).

Našli jste v článku chybu?
DigiZone.cz: ČRo rozšiřuje DAB do Berouna

ČRo rozšiřuje DAB do Berouna

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Podnikatel.cz: EET zvládneme, budou horší zákony

EET zvládneme, budou horší zákony

Lupa.cz: Google měl výpadek, nejel Gmail ani YouTube

Google měl výpadek, nejel Gmail ani YouTube

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

DigiZone.cz: ČRa DVB-T2 ověřeno: Hisense a Sencor

ČRa DVB-T2 ověřeno: Hisense a Sencor

Vitalia.cz: Tesco: Chudá rodina si koupí levné polské kuře

Tesco: Chudá rodina si koupí levné polské kuře

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

DigiZone.cz: NG natáčí v Praze seriál o Einsteinovi

NG natáčí v Praze seriál o Einsteinovi

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?