Článek vznikl jako lehce upravený překlad anglického originálu od Onuralpa Sezera. Oproti originálu je vysvětleno pár nejasností, doplněno pár drobností a použité obrázky s českými popisky.
Svět technologií se rozvíjí a nároky na počítačové vidění, umělou inteligenci a strojové učení rostou každým dnem. Co to vlastně je počítačové vidění? Jednoduše řečeno, je to technologie, která umožňuje počítačům, mobilním telefonům (ale také třeba robotům) vidět své okolí. První pokusy s počítačovým viděním se datují do 50. let minulého století a od té doby urazila tato oblast obrovský kus cesty a našla si cestu nejen do našich mobilních telefonů, ale také třeba do aut. V tomto článku si ukážeme knihovnu OpenCV - jednu z nejrozšířenějších knihoven pro počítačové vidění.
Poznámka: OpenCV existuje pro C++, Python, Javu a MATLAB. V tomto seriálu budeme používat OpenCV pro Python.
Co je to OpenCV?
OpenCV (Open Source Computer Vision Library) je open-source knihovna pro počítačové vidění a strojové učení. OpenCV bylo vytvořené, aby vytvářelo společnou infrastrukturu pro aplikace, které používají počítačové vidění, a aby zrychlilo použití počítačového vnímání v komerčních produktech. Obsahuje více než 2500 optimalizovaných algoritmů, zahrnujících obsáhlou sadu klasických i moderních algoritmů pro počítačové vidění a strojové učení. Tyto algoritmy mohou být použité pro rozpoznávání obličejů, identifikaci objektů, klasifikaci lidských akcí ve videu, stanovení markerů pro použití v rozšířené realitě a mnoho dalšího.
Instalace OpenCV na Fedoře
Abychom mohli začít používat OpenCV, nainstalujeme ji pomocí příkazu níže:
$ sudo dnf install opencv opencv-contrib opencv-doc python3-opencv python3-matplotlib python3-numpy
Poznámka: Pokud používáte Fedoru Silverblue, můžete buď použít Toolbox a OpenCV nainstalujete do něj pomocí příkazu výše, nebo potřebné balíky překryjete pomocí rpm-ostree install opencv opencv-doc python3-opencv python3-matplotlib python3-numpy. Autor překladu ovšem pracuje v Toolboxu na Silveblue a vše v něm testuje, vše by tedy mělo být plně funkční i bez nutnosti overlaye.
Pro jistotu si ověříme, že je OpenCV v pořádku nainstalované:
$ <b>python</b>
Python 3.9.6 (default, Jul 16 2021, 00:00:00)
[GCC 11.1.1 20210531 (Red Hat 11.1.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> <strong>import cv2 as cv</strong>
>>> <strong>print( cv.__version__ )</strong>
4.5.2
>>> <strong>exit()</strong>
Pokud vidíte verzi OpenCV podobně jako v ukázce výše, je vše v pořádku.
Začínáme s OpenCV
Poté, co jsme úspěšně nainstalovali OpenCV, můžeme s jeho pomocí napsat náš první skript, který načte obrázek. Vytvořme tedy soubor starry_night.py a otevřeme ho v oblíbeném editoru. Do souboru vložíme následující:
import cv2 as cv
import sys
img = cv.imread(cv.samples.findFile("starry_night.jpg"))
if img is None:
sys.exit("Could not read the image.")
cv.imshow("Display window", img)
k = cv.waitKey(0)
if k == ord("s"):
cv.imwrite("starry_night.png", img)
Následně můžeme náš program spustit (z nově otevřeného okna se dostaneme stiskem klávesy S, při zavření okna křížkem zůstane skript běžet a je třeba ho ukončit pomocí ctrl + Z):
$ python starry_night.py
Hurá! Právě jsme otevřeli obrázek pomocí OpenCV. Co můžeme udělat dál? Třeba převést obrázek do stupňů šedi, když funkci cv.imread předáme druhý parametr cv.IMREAD_GRAYSCALE:
img = cv.imread(cv.samples.findFile("starry_night.jpg"), <strong>cv.IMREAD_GRAYSCALE</strong>)
Stejným způsobem můžeme použít celkem tři parametry:
- cv.IMREAD_GRAYSCALE nebo 0: Načtení obrázku v odstínech šedi.
- cv.IMREAD_COLOR nebo 1: Načtení barevného obrázku, jakákoli průhlednost ale bude odstraněna. Toto je základní mód, který se používá, pokud není mód specifikovaný.
- cv.IMREAD_UNCHANGED nebo -1: Načtení obrázku včetně alfa kanálu (průhlednosti).
Zobrazování vlastností obrázku
Vlastnosti obrázku zahrnují rozměry (počet pixelů v jednom řádku/sloupci), počet kanálů, typ obrazových dat, počet pixelů a další. Některé z nich si níže zobrazíme. Začněme třeba rozměry obrázku a datovým typem pro ukládání hodnot kanálů:
import cv2 as cv
img = cv.imread(cv.samples.findFile("starry_night.jpg"))
print("Image size is", img.shape)
print("Data type of image is", img.dtype)
Image size is (600, 752, 3)
Data type of image is uint8
Kromě jiného si můžeme třeba zobrazit i hodnoty jednotlivých pixelů, jelikož samotný obrázek je uložený jako 2D pole:
print(f"Image 2D numpy array n {img}")
Image 2D numpy array
[[[0 0 0]
[0 0 0]
[0 0 0]
...
[0 0 0]
[0 0 0]
[0 0 0]]
[[0 0 0]
[0 0 0]
[0 0 0]
...
Poznámka: námi vybraný obtázek má po krajích černý "rámeček", proto nám výstup vypisuje samé nuly.
V kódu jsme použili atributy img.shape a img.dtype. První z nich určuje rozměry obrázku (výšku, šířku a počet kanálů), druhý z nich datový typ pro ukládání jedné hodnoty jednoho kanálu.
Už jsme si zobrazili obrázek pomocí OpenCV, teď si ho zobrazíme pomocí matplotlibu:
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread(cv.samples.findFile("starry_night.jpg"),0)
plt.imshow(img)
plt.show()
Co to?
Obrázek byl sice načtený jako šedotónový, ale to nutně neznamená, že tak bude zobrazený i pomocí matplotlibu. Funkce plt.imshow totiž v základu používá jiné mapování barev. Nic ale není ztraceno, pokud chceme zobrazit obrázek šedotónově, prostě přidáme druhý parametr funkci imshow následovně:
plt.imshow(img,cmap='gray')
Stejný problém se vyskytne i v situaci, kdy budeme otevírat obrázek v barevném módu. Matplotlib v základu počítá s barevným formátem RGB, OpenCV naproti tomu používá formát BGR (modrá a červená složka si vyměnily místa). Tento formát byl dříve velmi populární mezi výrobci videokamer a tvůrci software a původní tvůrci OpenCV proto (tehdy zcela logicky) zvolili tento formát. Časy se mění, BGR stále zůstává.
Zpět ke kódu! Pokud chceme, aby se i barevné obrázky zobrazily správně, je třeba konvertovat je z BGR do RGB:
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread(cv.samples.findFile("starry_night.jpg"),cv.IMREAD_COLOR)
fig, (ax1, ax2) = plt.subplots(1,2)
ax1.imshow(img)
ax1.set_title('Barvy BGR')
ax2.imshow(img[:,:,::-1])
ax2.set_title('Obrácené BGR (neboli RGB)')
plt.show()
Rozdělování a spojování kanálů
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread(cv.samples.findFile("starry_night.jpg"),cv.IMREAD_COLOR)
b,g,r = cv.split(img)
fig,ax = plt.subplots(2,2)
ax[0,0].imshow(r,cmap='gray')
ax[0,0].set_title("Červený kanál");
ax[0,1].imshow(g,cmap='gray')
ax[0,1].set_title("Zelený kanál");
ax[1,0].imshow(b,cmap='gray')
ax[1,0].set_title("Modrý kanál");
# Sloucime kanaly do BGR obrazku
imgMerged = cv.merge((b,g,r))
# Ukaz vysledek
ax[1,1].imshow(imgMerged[:,:,::-1])
ax[1,1].set_title("Spojené kanály");
plt.show()
- cv.split: Rozdělí jeden vícekanálový obrázek (nebo, chcete-li, pole) na několik jednokanálových obrázků.
- cv.merge: Jednotlivé jednokanálové obrázky zase spojí do jednoho vícekanálového. Všechny vstupní obrázky přitom musí mít stejné rozměry.
Poznámka: Čím jsou jednotlivé barevné složky tmavší, tím méně se dané barvy na daném místě nachází. Obrázek pro červený kanál je nejtmavší, což odpovídá tomu, že v originálu je červené barvy nejméně.
Konverze do jiných barevných prostorů
Funkce cv.cvtColor nám umožňuje konvertovat obrázek z jednoho barevného prostoru do druhého. Když transformujeme mezi barevnými prostory RGB a BGR, pomocí BGR2RGB a RGB2BGR specifikujeme, jakým směrem převádíme. Pokud někde narazíte na informaci, že základní barevný prostor v OpenCV je RGB, je to ve skutečnosti BGR! Pokud si tedy představíme standardní barevnou hloubku (24 bitů), pak prvních 8 bitů bude určovat modrou, dalších 8 bitů bude určovat zelenou, a konečně zbývající osmice nám určí červenou složku (kanál) pixelu. Následujích 24 bitů bude určovat složky druhého pixelu a tak dále... Níže se podíváme na použití výše zmíněné funkce cv.cvtColor:
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread(cv.samples.findFile("starry_night.jpg"),cv.IMREAD_COLOR)
img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()
Další informace
Pro podrobnější informace navštivte online dokumentaci. V příštím díle si ukážeme základní úpravy obrázků.