Ve třetím článku o populárních a často využívaných knihovnách určených pro vývojáře používající programovací jazyk Python se seznámíme se základními vlastnostmi knihovny Pillow, která vznikla z dnes již nevyvíjené knihovny PIL (Python Imaging Library). Knihovna Pillow umožňuje práci s rastrovými obrázky uloženými v mnoha podporovaných formátech, aplikaci různých filtrů na obrázky, manipulaci s jednotlivými pixely, kreslení základních geometrických tvarů i textů do obrázků apod.
Obsah
1. Užitečné knihovny a moduly pro Python: knihovna Pillow určená pro manipulaci s rastrovými obrázky
2. Podporované formáty souborů s rastrovými obrázky
4. Získání testovacího obrázku používaného demonstračními příklady
5. Základní příklad – načtení testovacího obrázku do operační paměti
6. Získání základních informací o načteném obrázku
7. Uložení testovacího obrázku do různých formátů
8. Zobrazení načteného a/nebo modifikovaného obrázku
9. Vytvoření náhledu na obrázek, zobrazení náhledu
10. Obrazové filtry nabízené knihovnou Pillow
11. Aplikace jednoduchého filtru typu Blur
12. Aplikace dalších filtrů nabízených knihovnou Pillow
13. Modul pro aplikaci složitějších konverzních funkcí
14. Konverze obrázku na stupně šedi
15. Konverze obrázku na černobílou bitmapu s využitím ditheringu
16. Automatická úprava kontrastu
17. Snížení počtu bitů v každém barvovém kanálu
18. Obsah následující části seriálu
19. Repositář s demonstračními příklady
1. Užitečné knihovny a moduly pro Python: knihovna Pillow určená pro manipulaci s rastrovými obrázky
V dnešní části seriálu věnovaném populárním knihovnám určeným pro programovací jazyk Python si popíšeme knihovnu pojmenovanou Pillow vytvořenou Alexem Clarkem a dalšími přispěvateli. Tato poměrně často využívaná knihovna vznikla tzv. přátelským forkem dnes již nevyvíjené knihovny nazvané PIL neboli Python Imaging Library jejímž autorem je Fredrik Lundh a která byla původně vyvinuta pro potřeby postaršího Pythonu 2.x (ostatně i při pohledu na domácí stránku projektu PIL je zřejmé, že se provádí jen základní údržba; porovnejte se stránkou projektu Pillow). Většinu informací, které si dnes řekneme, je sice možné aplikovat i na původní knihovnu PIL, ovšem pokud máte možnost volby, je lepší začít všude používat již novější a lépe podporovanou knihovnu Pillow (původní PIL se stále používá v některých starších aplikacích naprogramovaných v Pythonu 2).
Poznámka: všechny demonstrační příklady, s nimiž se v dnešním článku seznámíme, byly otestovány na Fedoře 27/28 s Pythonem 3. Převod příkladů na starší Python 2 a původní knihovnu PIL je samozřejmě možný a většinou se obejde bez větších zásahů do zdrojových kódů. Z tohoto důvodu existují všechny dnes popisované zdrojové kódy ve dvou variantách, což je ostatně patrné i při pohledu na dvě tabulky uvedené v devatenácté kapitole.
2. Podporované formáty souborů s rastrovými obrázky
Jedním z důvodů poměrně velké popularity knihovny Pillow je podpora poměrně velkého množství formátů souborů s rastrovými obrázky. Podporovány jsou všechny důležité formáty, tj. zejména GIF, JPEG, PNG i WebP. Ovšem v případě potřeby je možné pracovat i s dalšími formáty, mezi něž patří ICO, varianty PPM, TIFF (některé varianty) a dokonce i PSD (pro čtení) a v omezené míře i PDF.
Mezi podporované formáty patří například:
Formát/zkratka | Stručný popis |
---|---|
GIF | formát umožňující i uložení jednoduchých animací |
PNG | Portable Network Graphics |
JPEG | populární formát se ztrátovou kompresí |
JPEG 2000 | formát založený na vlnkové transformaci |
WebP | formát určený nejenom pro webové aplikace vyvíjený Googlem |
BMP | formát používaný ve Windows, bez komprese i s kompresí |
PCX | postarší formát pro PC s jednoduchou RLE kompresí |
PPM | unixový PPM a jeho varianty |
TIFF | Tagged Image File Format, pro zápis |
TGA | jednoduchý formát fy.Targa |
EPS | Encapsulated PostScript, pro čtení vyžaduje Ghostscript |
MSP | starý formát MS Paintu |
ICO | ikony |
PIXAR | jen pro čtení |
PSD | jen pro čtení |
pouze pro zápis |
3. Instalace knihovny Pillow
Instalace knihovny Pillow je stejně snadná, jako instalace většiny balíčků určených pro programovací jazyk Python. Instalaci provedeme nástrojem pip, zde konkrétně jeho variantou nazvanou pip3, která je určena pro Python 3. Při instalaci, přesněji řečeno při volání pip3, je použit přepínač --user, který zajistí, že se knihovna nainstaluje do podadresáře .local/lib/python3.x (například v našem případě .local/lib/python3.6) umístěného v domovském adresáři přihlášeného uživatele. Z tohoto důvodu není nutné mít práva administrátora (roota) ani se žádným způsobem nezmění obsah /usr:
$ pip3 install --user pillow
Průběh samotné instalace vypadá takto:
Collecting pillow Downloading https://files.pythonhosted.org/packages/d1/24/f53ff6b61b3d728b90934bddb4f03f8ab584a7f49299bf3bde56e2952612/Pillow-5.2.0-cp36-cp36m-manylinux1_x86_64.whl (2.0MB) 100% |████████████████████████████████| 2.0MB 387kB/s Installing collected packages: pillow Successfully installed pillow-5.2.0
Po instalaci si ještě pro jistotu ověříme, jestli je možné knihovnu Pillow skutečně v Pythonu použít. Spustíme tedy interpret jazyka Python 3:
$ python3 Python 3.6.6 (default, Jul 19 2018, 16:29:00) [GCC 7.3.1 20180303 (Red Hat 7.3.1-5)] on linux Type "help", "copyright", "credits" or "license" for more information.
Po zobrazení výzvy (prompt) se pokusíme o import knihovny Pillow. Pokud je knihovna nainstalována korektně, nedojde k zobrazení žádné chyby:
>>> from PIL import Image
V případě, že při instalaci došlo k nějaké neočekávané chybě, nebo není knihovna Pillow z nějakého důvodu nalezena, vypíše se chybová zpráva:
Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named 'PIL'
Povšimněte si přitom, že se název této knihovny (Pillow) odlišuje od jména modulu použitého v příkazu from. Je tomu tak z toho důvodu, aby byla do co největší míry zajištěna zpětná kompatibilita s původní knihovnou PIL.
Jakmile byl import (doufejme, že bezchybně) proveden, můžeme si zobrazit nápovědu ke knihovně:
>>> help("PIL")
Po zadání výše uvedeného příkladu by se měla vypsat úvodní obrazovka poměrně rozsáhlé nápovědy:
Help on package PIL: NAME PIL - Pillow 5.2.0 (Fork of the Python Imaging Library) DESCRIPTION Pillow is the friendly PIL fork by Alex Clark and Contributors. https://github.com/python-pillow/Pillow/ Pillow is forked from PIL 1.1.7. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. Copyright (c) 1999 by Secret Labs AB. Use PIL.__version__ for this Pillow version. PIL.VERSION is the old PIL version and will be removed in the future. ;-)
Pro zajímavost se podívejme na výstup, který získáme při použití původní knihovny PIL a dnes již vlastně historického Pythonu 2.7.5:
$ python Python 2.7.5 (default, Nov 3 2014, 14:33:39) [GCC 4.8.3 20140911 (Red Hat 4.8.3-7)] on linux2 Type "help", "copyright", "credits" or "license" for more information.
Následně po zobrazení výzvy zadáme příkaz:
>>> help("PIL")
S přibližně následujícím výsledkem:
Help on package PIL: NAME PIL FILE /usr/lib64/python2.7/site-packages/PIL/__init__.py DESCRIPTION # The Python Imaging Library. # $Id$ # # package placeholder # # Copyright (c) 1999 by Secret Labs AB. # # See the README file for information on usage and redistribution. #
4. Získání testovacího obrázku používaného demonstračními příklady
Před popisem jednotlivých operací, které knihovna Pillow podporuje, a před spuštěním demonstračních příkladů je nutné získat testovací obrázek, který bude do příkladů načítán a dále zpracováván. Vzhledem k tomu, že si budeme mj. popisovat i různé konvoluční filtry aplikované na rastrové obrázky, použijeme testovací obrázek s fotkou Lenny (Leny), který se v oblasti počítačové grafiky a zpracování obrazu používá již několik desetiletí, konkrétně od roku 1973 (více viz stránka Lenna 97: A Complete Story of Lenna).
Obrázek 1: Testovací obrázek Lenny použitý ve všech dnešních demonstračních příkladech.
Testovací obrázek, který má dnes již „klasické“ rozlišení 512×512 pixelů, je možné získat například z Wikipedie, a to následujícím jednoduchým skriptem. Skript je vhodné spustit ve stejném adresáři, kde se nachází i demonstrační příklady získané z repositáře popsaného v devatenácté kapitole:
original_image_address="https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png" wget -v -O Lenna.png $original_image_address
Po spuštění tohoto skriptu by se měl v pracovním adresáři objevit nový soubor nazvaný Lenna.png. O tom se samozřejmě můžeme velmi snadno přesvědčit pomocí příkazů ls a file:
$ ls -l Lenna.png -rw-rw-r--. 1 ptisnovs ptisnovs 473831 10. kvě 17.16 Lenna.png
$ file Lenna.png Lenna.png: PNG image data, 512 x 512, 8-bit/color RGB, non-interlaced
5. Základní příklad – načtení testovacího obrázku do operační paměti
Podívejme se nyní na pravděpodobně zcela nejjednodušší demonstrační příklad, na němž si ukážeme základní vlastnost knihovny Pillow. V tomto příkladu se pokusíme o načtení testovacího obrázku, který jsme získali skriptem zmíněným v předchozí kapitole. Pro načtení rastrového obrázku slouží funkce Image.open(), které je nutné předat jméno souboru s rastrovým obrázkem a volitelně taktéž režim přístupu k souboru (implicitně „r“ – značící režim čtení). Ze jména souboru a především pak z jeho hlavičky si knihovna Pillow odvodí formát souboru a použije příslušnou specializovanou třídu pro načtení a dekódování obrazových dat:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
test_image = Image.open(filename)
test_image.load()
print(test_image)
Po spuštění tohoto příkladu by se měly pouze vypsat informace o vytvořené instanci třídy Image reprezentující rastrový obrázek:
<PIL.PngImagePlugin.PngImageFile image mode=RGB size=512x512 at 0x7F31EE799198>
Pokud ovšem dojde k nějaké chybě při načítání nebo dekódování obrazových dat, nebude náš první skript na tuto skutečnost nijak připraven a skonči s chybou. Pokud například načítaný soubor neexistuje:
Traceback (most recent call last): File "./01_basic_usage.py", line 8, in test_image = Image.open(filename) File "/usr/lib64/python3.6/site-packages/PIL/Image.py", line 2410, in open fp = builtins.open(filename, "rb") FileNotFoundError: [Errno 2] No such file or directory: 'Lenna.png'
Chyba, i když jiného typu, vznikne i ve chvíli, kdy není možné rastrový obrázek z nějakého důvodu dekódovat:
Traceback (most recent call last): File "./01_basic_usage.py", line 9, in test_image.load() File "/usr/lib64/python3.6/site-packages/PIL/ImageFile.py", line 253, in load raise_ioerror(err_code) File "/usr/lib64/python3.6/site-packages/PIL/ImageFile.py", line 59, in raise_ioerror raise IOError(message + " when reading image file") OSError: unrecognized data stream contents when reading image file
Reakci na chyby je samozřejmě možné do skriptu přidat; typicky s využitím programové konstrukce try-except. Podívejme se na upravenou variantu skriptu, která již dokáže na chybu zareagovat adekvátně, tj. bez přímého pádu (ve skutečnosti prozatím na chybu nijak adekvátně zareagovat nedokážeme, ovšem alespoň zabráníme okamžitému pádu aplikace). Tato upravená varianta bude použita jako základ pro všechny další demonstrační příklady:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
print(test_image)
except Exception as e:
print(e)
6. Získání základních informací o načteném obrázku
Ve chvíli, kdy je obrázek úspěšně načten a dekódován, je ve skriptu reprezentován objektem typu Image. To mj. znamená, že máme přístup ke všem metodám tohoto objektu, resp. přesněji řečeno k metodám deklarovaným přímo ve třídě Image.. Kromě toho lze přistupovat i k atributům objektu typu Image, přičemž tyto atributy nesou základní informace o zpracovávaném obrázku. V dalším demonstračním příkladu, jehož úplný zdrojový kód naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/03_image_info.py, je ukázáno použití atributů pojmenovaných format, size a mode.
Atribut format obsahuje formát souboru, ze kterého byl obrázek načten, popř. hodnotu None pro obrázky vzniklé programově. V atributu size jsou uloženy rozměry obrázku a jelikož jsou rastrové obrázky dvourozměrné, je hodnotou atributu size dvojice (tuple) obsahující horizontální rozlišení (tj. počet pixelů na obrazovém řádku) a rozlišení vertikální (tj. počet obrazových řádků). A konečně atribut mode obsahuje informaci o tom, jaký barvový prostor je použit („L“, „RGB“ atd.):
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
print("Image loaded")
print("Format: {fmt}".format(fmt=test_image.format))
size = test_image.size
print("Resolution: {width}x{height} px".format(width=size[0],
height=size[1]))
print("Mode: {mode}".format(mode=test_image.mode))
except Exception as e:
print(e)
Po spuštění tohoto demonstračního příkladu by se na konzoli/terminálu měly zobrazit následující základní informace o načteném obrázku:
Image loaded Format: PNG Resolution: 512x512 px Mode: RGB
7. Uložení testovacího obrázku do různých formátů
Kromě načítání rastrových obrázků v různých formátech je možné obrázky i ukládat a to opět s možností volby vhodného formátu. Pro tento účel se používá metoda Image.save(), které se předá jméno souboru, do něhož se má obrázek uložit. Na základě koncovky souboru („.png“, „.gif“ atd.) se určí formát rastrových souborů, který se má použít. Pokud budete z nějakého důvodu potřebovat, aby se obrázek uložil ve formátu, který neodpovídá koncovce souboru, můžete při volání metody Image.save() použít nepovinný parametr pojmenovaný format. Ukažme si nyní, jak testovací obrázek Lenny postupně uložíme do formátů JPEG, BMP a TIFF. Zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/04_save_image.py:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
test_image.save("Lenna.jpg")
test_image.save("Lenna.bmp")
test_image.save("Lenna.tiff")
except Exception as e:
print(e)
Informace o výsledných souborech opět získáme s využitím nástrojů ls a file:
$ ls -l Lenna.* -rw-rw-r--. 1 ptisnovs ptisnovs 786486 13. srp 13.51 Lenna.bmp -rw-rw-r--. 1 ptisnovs ptisnovs 37788 13. srp 13.51 Lenna.jpg -rw-rw-r--. 1 ptisnovs ptisnovs 473831 10. kvě 17.16 Lenna.png -rw-rw-r--. 1 ptisnovs ptisnovs 786572 13. srp 13.51 Lenna.tiff
$ file Lenna* Lenna.bmp: PC bitmap, Windows 3.x format, 512 x 512 x 24 Lenna.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 512x512, frames 3 Lenna.png: PNG image data, 512 x 512, 8-bit/color RGB, non-interlaced Lenna.tiff: TIFF image data, little-endian, direntries=10, height=512, bps=134, compression=none, PhotometricIntepretation=RGB, width=512
8. Zobrazení načteného a/nebo modifikovaného obrázku
Pro usnadnění ladění aplikací, které pracují s rastrovými obrázky, existuje velmi užitečná metoda pojmenovaná Image.show(). Jak již název této metody naznačuje, slouží k zobrazení právě zpracovávaného obrázku v samostatném okně. Demonstrační příklad, který nejprve načte testovací obrázek Lenny ze souboru typu PNG a posléze ho zobrazí, je ve skutečnosti velmi jednoduchý:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
test_image.show()
except Exception as e:
print(e)
9. Vytvoření náhledu na obrázek, zobrazení náhledu
V případě, že je zapotřebí vytvořit (zmenšený) náhled na zpracovávaný obrázek, je možné k tomuto účelu použít metodu Image.thumbnail. Této metodě se předá n-tice (konkrétně dvojice) s požadovaným rozměrem náhledu. V dalším demonstračním příkladu je zvolena velikost náhledu 128×128 pixelů. Podívejme se nyní na zdrojový kód tohoto příkladu:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
thumbnail_size = 128, 128
def print_image_info(image):
print("Format: {fmt}".format(fmt=image.format))
size = image.size
print("Resolution: {width}x{height} px".format(width=size[0],
height=size[1]))
print("Mode: {mode}".format(mode=image.mode))
try:
test_image = Image.open(filename)
test_image.load()
print("Original:")
print_image_info(test_image)
print()
test_image.thumbnail(thumbnail_size)
print("Thumbnail:")
print_image_info(test_image)
test_image.show()
except Exception as e:
print(e)
Náhledový obrázek má samozřejmě zcela stejné vlastnosti a možnosti, jako jakýkoli jiný obrázek načtený knihovnou Pillow. To mj. znamená, že je možné takový obrázek uložit na disk, což je ukázáno v následujícím příkladu, v němž vytvoříme nový soubor „thumbnail.png“ s obrázkem o rozměrech pouhých 128×128 pixelů:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
filename = "Lenna.png"
thumbnail_size = 128, 128
def print_image_info(image):
print("Format: {fmt}".format(fmt=image.format))
size = image.size
print("Resolution: {width}x{height} px".format(width=size[0],
height=size[1]))
print("Mode: {mode}".format(mode=image.mode))
try:
test_image = Image.open(filename)
test_image.load()
print("Original:")
print_image_info(test_image)
print()
test_image.thumbnail(thumbnail_size)
print("Thumbnail:")
print_image_info(test_image)
test_image.save("thumbnail.png")
except Exception as e:
print(e)
Obrázek 2: Náhledový obrázek vytvořený předchozím demonstračním příkladem.
10. Obrazové filtry nabízené knihovnou Pillow
Knihovna Pillow obsahuje poměrně velké množství (konvolučních) filtrů, které je možné aplikovat na upravované rastrové obrázky. Popišme si nyní ten nejjednodušší filtr: při úpravách fotografií nebo naskenovaných obrázků se poměrně často můžeme setkat s požadavkem na odstranění šumu z obrazu nebo z jeho vybrané části. Nejjednodušším a taktéž nejrychlejším filtrem, který dokáže odstranit vysoké frekvence v obrazu a tím i šum (bohužel spolu s ostrými hranami) je filtr nazvaný příznačně Blur. Tento filtr pracuje velmi jednoduše – spočítá průměrnou hodnotu sousedních pixelů tvořících pravidelný blok a tuto hodnotu uloží do pixelu ležícího přesně uprostřed bloku (operace je samozřejmě provedena pro všechny pixely v obrazu). Výsledkem je sice obraz s odstraněným vysokofrekvenčním šumem, ale současně s potlačením šumu došlo k rozmazání všech jednopixelových hran na přechody široké minimálně tři pixely.
11. Aplikace jednoduchého filtru typu Blur
Filtr typu Blur zmíněný v předchozí kapitole, se na již načtený obrázek aplikuje velmi jednoduše s tím, že výsledkem aplikace filtru bude nový (rozmazaný) obrázek:
blurred_image = test_image.filter(ImageFilter.BLUR)
Obrázek 3: Výsledek aplikace filtru typu Blur.
Úplný zdrojový kód demonstračního příkladu, který načte obrázek Lenny a následně na něj aplikuje rozmazávací filtr, naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/08_simple_filter.py:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image, ImageFilter
filename = "Lenna.png"
def print_image_info(image):
print("Format: {fmt}".format(fmt=image.format))
size = image.size
print("Resolution: {width}x{height} px".format(width=size[0],
height=size[1]))
print("Mode: {mode}".format(mode=image.mode))
try:
test_image = Image.open(filename)
test_image.load()
print("Original:")
print_image_info(test_image)
print()
blurred_image = test_image.filter(ImageFilter.BLUR)
print("Filtered image:")
print_image_info(blurred_image)
test_image.show()
blurred_image.show()
except Exception as e:
print(e)
12. Aplikace dalších filtrů nabízených knihovnou Pillow
Ve skutečnosti v knihovně Pillow nalezneme větší množství konvolučních filtrů, z nichž některé provádí rozmazání, další naopak zaostření, zvýraznění hran, vytvoření „plastického“ otisku atd. Jedná se o následující filtry, které můžeme použít popř. libovolným způsobem kombinovat:
ImageFilter.BLUR ImageFilter.CONTOUR ImageFilter.DETAIL ImageFilter.EDGE_ENHANCE ImageFilter.EDGE_ENHANCE_MORE ImageFilter.EMBOSS ImageFilter.FIND_EDGES ImageFilter.SHARPEN ImageFilter.SMOOTH ImageFilter.SMOOTH_MOR
Základní možnosti těchto filtrů jsou ukázány v devátém demonstračním příkladu, jehož zdrojový kód je vypsán pod tímto odstavcem:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image, ImageFilter
filename = "Lenna.png"
IMAGE_FILTERS = (
ImageFilter.BLUR,
ImageFilter.CONTOUR,
ImageFilter.DETAIL,
ImageFilter.EDGE_ENHANCE,
ImageFilter.EDGE_ENHANCE_MORE,
ImageFilter.EMBOSS,
ImageFilter.FIND_EDGES,
ImageFilter.SHARPEN,
ImageFilter.SMOOTH,
ImageFilter.SMOOTH_MORE
)
def print_image_info(image):
print("Format: {fmt}".format(fmt=image.format))
size = image.size
print("Resolution: {width}x{height} px".format(width=size[0],
height=size[1]))
print("Mode: {mode}".format(mode=image.mode))
def apply_filter_and_save_image(image, prefix, image_filter):
filter_name = image_filter.__name__.lower()
print("Applying filter {filter_name}".format(filter_name=filter_name))
filename = "{prefix}{filter_name}.png".format(prefix=prefix, filter_name=filter_name)
filtered_image = image.filter(image_filter)
filtered_image.save(filename)
try:
test_image = Image.open(filename)
test_image.load()
print("Original:")
print_image_info(test_image)
for image_filter in IMAGE_FILTERS:
apply_filter_and_save_image(test_image, "Lenna_", image_filter)
except Exception as e:
print("An exception:")
print(e)
Podívejme se na výsledky produkované zmíněnými filtry:
Obrázek 4: Filtr typu CONTOUR.
Obrázek 5: Filtr typu DETAIL.
Obrázek 6: Filtr typu EDGE_ENHANCE.
Obrázek 7: Filtr typu EDGE_ENHANCE_MORE.
Obrázek 8: Filtr typu EMBOSS.
Obrázek 9: Filtr typu FIND_EDGES.
Obrázek 10: Filtr typu SHARPEN.
Obrázek 11: Filtr typu SMOOTH.
Obrázek 12: Filtr typu SMOOTH_MORE.
13. Modul pro aplikaci složitějších konverzních funkcí
V knihovně Pillow nalezneme i velmi zajímavý a pravděpodobně ne tak často používaný modul nazvaný ImageMath, jehož možnosti jsou popsány na stránce https://pillow.readthedocs.io/en/5.2.x/reference/ImageMath.html. V tomto modulu jsou definovány obecnější funkce pro operace prováděné nad jedním obrázkem či současně nad několika rastrovými obrázky. Tyto operace jsou skutečně definovány velmi obecně takovým způsobem, aby je bylo možné modifikovat uživatelem-programátorem a aplikovat tak na obrázek i operace, s nimiž tvůrci knihovny Pillow nepočítali. Dnes se seznámíme jen s naprostými základy nabízenými tímto modulem.
14. Konverze obrázku na stupně šedi
V desátém demonstračním příkladu je ukázán převod rastrového obrázku na stupně šedi s využitím univerzální konverzní funkce ImageMath.eval(). Této funkci se předá volání „convert(src, 'L')“ znamenající konverzi obrázku do barvového prostoru „luminance“:
modified_image = ImageMath.eval("convert(src, 'L')", src=test_image)
Výsledek aplikace této konverzní funkce:
Obrázek 13: Převod obrázku na stupně šedi.
Zdrojový kód tohoto demonstračního příkladu vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageMath
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
modified_image = ImageMath.eval("convert(src, 'L')", src=test_image)
test_image.show()
modified_image.show()
except Exception as e:
print(e)
15. Konverze obrázku na černobílou bitmapu s využitím ditheringu
V některých případech, například při konstrukcích masky pro další rastrové operace atd., se používají čistě černobílé bitmapy, přičemž slovo „bitmapa“ je zde použito v původním významu – každý pixel je představován jediným bitem. Konverze na černobílou bitmapu využívá algoritmu ditheringu a provede se tímto příkazem:
modified_image = ImageMath.eval("convert(src, '1')", src=test_image)
Obrázek 14: Převod obrázku na černobílou bitmapu.
Zdrojový kód tohoto demonstračního příkladu vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageMath
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
modified_image = ImageMath.eval("convert(src, '1')", src=test_image)
test_image.show()
modified_image.show()
except Exception as e:
print(e)
16. Automatická úprava kontrastu
Zajímavá a potenciálně užitečná může být funkce pro automatickou úpravu (typicky zvětšení) kontrastu, která se zavolá takto:
modified_image = ImageOps.autocontrast(test_image)
S výsledkem:
Obrázek 15: Automatické zvýšení kontrastu.
Celý příklad, který automatické zvýšení kontrastu provede, vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageOps
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
modified_image = ImageOps.autocontrast(test_image)
test_image.show()
modified_image.show()
except Exception as e:
print(e)
U funkce ImageOps.autocontrast() lze zadat i hodnotu určující, kolik procent krajních hodnot z histogramu je odříznuto, aby se zbylá část histogramu v barvovém spektru roztáhla a zvýšila tak kontrast:
Obrázek 16: Automatické zvýšení kontrastu, hodnota cutoff=0.
Obrázek 17: Automatické zvýšení kontrastu, hodnota cutoff=25.
Obrázek 18: Automatické zvýšení kontrastu, hodnota cutoff=75.
Příklad, který vygeneroval předchozí tři obrázky, vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageOps
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
modified_image_1 = ImageOps.autocontrast(test_image, cutoff=0)
modified_image_2 = ImageOps.autocontrast(test_image, cutoff=50)
modified_image_3 = ImageOps.autocontrast(test_image, cutoff=75)
test_image.show()
modified_image_1.show()
modified_image_2.show()
modified_image_3.show()
except Exception as e:
print(e)
17. Snížení počtu bitů v každém barvovém kanálu
U plnobarevných (truecolor) rastrových obrázků lze snížit počet bitů pro každý barvový kanál a tak postupně snižovat počet barev až na limitních osm barev. Tato operace se provede funkcí ImageOps.posterize(), které se kromě vstupního obrázku předá i počet bitů na barvový kanál s výsledkem:
Obrázek 19: Počet bitů na barvový kanál=4, celkový počet barev=4096.
Obrázek 20: Počet bitů na barvový kanál=3, celkový počet barev=512.
Obrázek 21: Počet bitů na barvový kanál=2, celkový počet barev=64.
Obrázek 22: Počet bitů na barvový kanál=1, celkový počet barev=8.
Tyto obrázky byly vytvořeny příkladem:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageOps
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
test_image.show()
for bits in range(8, 0, -1):
modified_image = ImageOps.posterize(test_image, bits)
modified_image.show()
except Exception as e:
print(e)
Tutéž operaci je možné provést i s obrázkem převedeným na stupně šedi:
Obrázek 23: Počet bitů na barvový kanál=4, celkový počet barev=16.
Obrázek 24: Počet bitů na barvový kanál=3, celkový počet barev=8.
Obrázek 25: Počet bitů na barvový kanál=2, celkový počet barev=4.
Obrázek 26: Počet bitů na barvový kanál=1, celkový počet barev=2.
Dnešní poslední demonstrační příklad, který vytvořil předchozí čtyři obrázky, má tvar:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
from PIL import Image
from PIL import ImageMath
from PIL import ImageOps
filename = "Lenna.png"
try:
test_image = Image.open(filename)
test_image.load()
test_image.show()
grayscale_image = ImageMath.eval("convert(src, 'L')", src=test_image)
for bits in range(8, 0, -1):
modified_image = ImageOps.posterize(grayscale_image, bits)
modified_image.show()
except Exception as e:
print(e)
18. Obsah následující části seriálu
V navazující části seriálu věnovaného populárním knihovnám určeným pro programovací jazyk Python si ukážeme další možnosti nabízené knihovnou Pillow. Popíšeme si zejména moduly nazvané ImageMath (o tomto modulu jsme se dnes zmínili jen krátce), modul ImagePath (vektorová grafika), ImageMorph a navíc pak i třídu PixelAccess, která zajišťuje přístup k rastrovým obrázkům na úrovni jednotlivých pixelů.
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro programovací jazyk Python 3 a knihovnu Pillow byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následujících dvou tabulkách.
Příklady pro jazyk Python 3 a knihovnu Pillow:
Příklady pro jazyk Python 2 a knihovnu PIL:
20. Odkazy na Internetu
- PIL: The friendly PIL fork (home page)
https://python-pillow.org/ - Python Imaging Library (PIL), (home page)
http://www.pythonware.com/products/pil/ - PIL 1.1.6 na PyPi
https://pypi.org/project/PIL/ - Pillow 5.2.0 na PyPi
https://pypi.org/project/Pillow/ - Python Imaging Library na Wikipedii
https://en.wikipedia.org/wiki/Python_Imaging_Library - Pillow na GitHubu
https://github.com/python-pillow/Pillow - Pillow - dokumentace na readthedocs.io
http://pillow.readthedocs.io/en/5.2.x/ - How to use Pillow, a fork of PIL
https://www.pythonforbeginners.com/gui/how-to-use-pillow - Lenna (Wikipedia)
https://en.wikipedia.org/wiki/Lenna - Seriál Grafický formát GIF
https://www.root.cz/serialy/graficky-format-gif/ - PNG is Not GIF
https://www.root.cz/clanky/png-is-not-gif/ - JPEG - král rastrových grafických formátů?
https://www.root.cz/clanky/jpeg-kral-rastrovych-grafickych-formatu/ - Grafický formát BMP - používaný a přitom neoblíbený
https://www.root.cz/clanky/graficky-format-bmp-pouzivany-a-pritom-neoblibeny/ - Grafický formát PCX - výlet do historie PC
https://www.root.cz/clanky/graficky-format-pcx-vylet-do-historie-pc/ - Grafické formáty ve znamení Unixu
https://www.root.cz/clanky/graficke-formaty-ve-znameni-unixu/ - Grafický formát TGA - jednoduchý, oblíbený, používaný
https://www.root.cz/clanky/graficky-format-tga-jednoduchy-oblibeny-pouzivany/