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

3. Instalace knihovny Pillow

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

20. Odkazy na Internetu

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í
PDF 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:

# Demonstrační příklad Popis Cesta
1 01_basic_usage.py načtení rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/01_basic_usage.py
2 02_error_handling.py reakce na chyby, které mohou při načítání obrázku nastat https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/02_error_handling.py
3 03_image_info.py získání základních informací o načteném obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/03_image_info.py
4 04_save_image.py uložení rastrového obrázku s využitím zvolených formátů https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/04_save_image.py
5 05_show_image.py zobrazení načteného obrázku v samostatném okně https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/05_show_image.py
6 06_show_thumbnail.py vytvoření a zobrazení náhledu na obrázek https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/06_show_thumbnail.py
7 07_save_thumbnail.py uložení (zmenšeného) náhledu na obrázek https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/07_save_thumbnail.py
8 08_simple_filter.py aplikace jednoduchého konvolučního filtru https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/08_simple_filter.py
9 09_other_filters.py aplikace dalších filtrů z knihovny Pillow https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/09_other_filters.py
10 10_convert_image_to_grayscale.py konverze do obrázku ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/10_convert_image_to_grayscale.py
11 11_convert_image_to_black_white.py konverze do čistě černobílé bitmapy https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/11_convert_image_to_black_white.py
12 12_image_ops_autocontrast.py automatická úprava kontrastu rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/12_image_ops_autocontrast.py
13 13_image_ops_autocontrast_cutoff.py automatická úprava kontrastu rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/13_image_ops_autocontrast_cutoff.py
14 14_image_ops_posterize.py posterizace původně plnobarevného obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/14_image_ops_posterize.py
15 15_image_ops_posterize_grayscale.py posterizace obrázku ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/15_image_ops_posterize_grayscale.py
16 skript pro stažení testovacího obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pillow/

Příklady pro jazyk Python 2 a knihovnu PIL:

# Demonstrační příklad Popis Cesta
1 01_basic_usage.py načtení rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/01_basic_usage.py
2 02_error_handling.py reakce na chyby, které mohou při načítání obrázku nastat https://github.com/tisnik/most-popular-python-libs/blob/master/pil/02_error_handling.py
3 03_image_info.py získání základních informací o načteném obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/03_image_info.py
4 04_save_image.py uložení rastrového obrázku s využitím zvolených formátů https://github.com/tisnik/most-popular-python-libs/blob/master/pil/04_save_image.py
5 05_show_image.py zobrazení načteného obrázku v samostatném okně https://github.com/tisnik/most-popular-python-libs/blob/master/pil/05_show_image.py
6 06_show_thumbnail.py vytvoření a zobrazení náhledu na obrázek https://github.com/tisnik/most-popular-python-libs/blob/master/pil/06_show_thumbnail.py
7 07_save_thumbnail.py uložení (zmenšeného) náhledu na obrázek https://github.com/tisnik/most-popular-python-libs/blob/master/pil/07_save_thumbnail.py
8 08_simple_filter.py aplikace jednoduchého konvolučního filtru https://github.com/tisnik/most-popular-python-libs/blob/master/pil/08_simple_filter.py
9 09_other_filters.py aplikace dalších filtrů z knihovny Pillow https://github.com/tisnik/most-popular-python-libs/blob/master/pil/09_other_filters.py
10 10_convert_image_to_grayscale.py konverze do obrázku ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/pil/10_convert_image_to_grayscale.py
11 11_convert_image_to_black_white.py konverze do čistě černobílé bitmapy https://github.com/tisnik/most-popular-python-libs/blob/master/pil/11_convert_image_to_black_white.py
12 12_image_ops_autocontrast.py automatická úprava kontrastu rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/12_image_ops_autocontrast.py
13 13_image_ops_autocontrast_cutoff.py automatická úprava kontrastu rastrového obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/13_image_ops_autocontrast_cutoff.py
14 14_image_ops_posterize.py posterizace původně plnobarevného obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/14_image_ops_posterize.py
15 15_image_ops_posterize_grayscale.py posterizace obrázku ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/pil/15_image_ops_posterize_grayscale.py
16 skript pro stažení testovacího obrázku https://github.com/tisnik/most-popular-python-libs/blob/master/pil/

20. Odkazy na Internetu

  1. PIL: The friendly PIL fork (home page)
    https://python-pillow.org/
  2. Python Imaging Library (PIL), (home page)
    http://www.pythonware.com/products/pil/
  3. PIL 1.1.6 na PyPi
    https://pypi.org/project/PIL/
  4. Pillow 5.2.0 na PyPi
    https://pypi.org/project/Pillow/
  5. Python Imaging Library na Wikipedii
    https://en.wikipedia.org/wiki/Python_Imaging_Library
  6. Pillow na GitHubu
    https://github.com/python-pillow/Pillow
  7. Pillow – dokumentace na readthedocs.io
    http://pillow.readthedocs.io/en/5.2.x/
  8. How to use Pillow, a fork of PIL
    https://www.pythonforbeginners.com/gui/how-to-use-pillow
  9. Lenna (Wikipedia)
    https://en.wikipedia.org/wiki/Lenna
  10. Seriál Grafický formát GIF
    https://www.root.cz/serialy/graficky-format-gif/
  11. PNG is Not GIF
    https://www.root.cz/clanky/png-is-not-gif/
  12. JPEG – král rastrových grafických formátů?
    https://www.root.cz/clanky/jpeg-kral-rastrovych-grafickych-formatu/
  13. Grafický formát BMP – používaný a přitom neoblíbený
    https://www.root.cz/clanky/graficky-format-bmp-pouzivany-a-pritom-neoblibeny/
  14. Grafický formát PCX – výlet do historie PC
    https://www.root.cz/clanky/graficky-format-pcx-vylet-do-historie-pc/
  15. Grafické formáty ve znamení Unixu
    https://www.root.cz/clanky/graficke-formaty-ve-znameni-unixu/
  16. Grafický formát TGA – jednoduchý, oblíbený, používaný
    https://www.root.cz/clanky/graficky-format-tga-jednoduchy-oblibeny-pouzivany/