Skip navigation

Maszkolás

Maszkolás feladata

Maszkok segítségével meg tudjuk határozni, hogy a kép mely részletének feldolgozása fontos a számunkra. A maszk jellemzően egy logikai vagy szürkeárnyalatos képnek tekinthető, ahol az intenzitásértékek jelzik a kapcsolódó képpontok tulajdonságait: része-e az érdekes régiónak (nullától különböző, például 1 vagy maximális érték) vagy sem (0 érték), amennyiben igen, milyen mértékben? A maszk kép sor- és oszlopszáma megegyezik a kapcsolódó kép sor-oszlop méretével.  Csatornaszámban viszont eltérhetnek: egy többcsatornás képet indexelhetünk egycsatornás maszkkal is. Ekkor értékadás esetén a csatornaszámnak megfelelő méretű tömböt lehet értékül adni.

Maszkokat létre tudunk hozni manuális rajzolással, algoritmikusan, vagy szegmentáló algoritmusokkal, amelyekkel később foglalkozunk. Jelenleg elfogadjuk, hogy ilyenek rendelkezésre állnak, és azt vizsgáljuk, hogyan tudunk ezekkel dolgozni.

Maszkolással oldanak meg filmtrükköket, ahol a cselekményt zöld vagy kék háttér előtt veszik fel, és utólag, mesterségesen a háttér területre más, például számítógépes grafikus eszközökkel készült részleteket másolnak. Példaként nézzük meg az alábbi két videót:

Hasonlóan, videókonferencia alkalmazásokban, ahol a személy mögötti szobát egy kiválasztott háttérképre cserélhetjük. Ebben az esetben a webkamera élőképen a személy maszkjának szegmentálása képkockáként megtörténik, és a külső területre másolódik a megfelelő méretű háttérkép.

Használati mód

Sok OpenCV függvény lehetőséget biztosít arra, hogy függvényparaméterként maszkot is átadhassunk. A művelet ekkor csak a nem 0 értékű helyeken hajtódik végre a maszkhoz kapcsolódó képmátrixban.

Ahogyan előző részben láttuk, a Numpy lehetőséget biztosít arra, hogy képmátrixot feltételkiértékeléssel indexeljünk, ami maszkolási feladatoknál jól használható. Ezt a megközelítést alkalmazva például egy háromcsatornás img képmátrix csak azon elemei lesznek vörös színűek, ahol az np.uint8 típusú mask nem 0:

img[mask > 0] = [0, 0, 255]

A maszkokkal való munka általános megközelítése, ha for ciklusokkal bejárjuk a teljes képet, és képpontonként ellenőrizzük a kritériumot. Látni fogjuk, hogy ez rendkívül lassú, helyette célszerű Numpy vagy OpenCV műveletekkel dolgozni! A sebességkülönbség több százszoros is lehet!

03_02_car_np_masked.py

Szükséges importálások. A time csomag az időméréshez kell.

import cv2
import time

Kép, maszk és maszk körvonal beolvasása.

img = cv2.imread('car_numberplate_rs.jpg', cv2.IMREAD_COLOR)
mask = cv2.imread('car_numberplate_rs_mask.png', cv2.IMREAD_COLOR)
edge = cv2.imread('car_numberplate_rs_mask_edge.png', cv2.IMREAD_GRAYSCALE)

Képmátrix méretek ellenőrzése. Az assert után egy feltételt adhatunk meg. Ha az nem teljesül, akkor a program futása hibaüzenttel megszakad. A shape attribútum első két elemét nézzük, ami a sorok és oszlopok számát veszi csak figyelembe.

assert img.shape[0:2] == mask.shape[0:2]
assert img.shape[0:2] == edge.shape[0:2]

Kép és maszk megjelenítése.

cv2.imshow('img', img)
cv2.imshow('mask', mask)

Színes képen a maszkon kívüli terület kinullázása és megjelenítése. Mivel a feltételezzük, hogy a maszk kép csak 0 és 255 értékeket tartalmaz, a logikai ÉS művelet a maszkon belüli, vagyis a 255 értékkel fedésbe kerülőket tartja meg, a többit nullázza. Megjegyezzök, hogy Numpy feltételes indexeléssel is megoldhattuk volna, gyakorlásként választottuk most a logikai bitműveletet.

masked = cv2.bitwise_and(img, mask)
cv2.imshow('masked', masked)
cv2.waitKey(0)

Eredeti színes képen a maszkon belüli rész fehér színűre változtatása Numpy feltételes indexeléssel.

img_roi = img.copy()
img_roi[mask > 0] = 255
cv2.imshow('img_roi', img_roi)

cv2.waitKey(0)

Vörös színű maszk körvonal elhelyezése az eredeti képen. Először Numpy feltéletes indexelést használunk. Vegyük észre, hogy nem elegendő a vörös csatornára értéket írni, a másik kettő kinullázása is szükséges a korrket vörös szín előállításához! A megoldást időméréssel látjuk el.

img_np_edge = img.copy()
start_time = time.perf_counter()
img_np_edge[edge > 0] = [0, 0, 255]
end_time = time.perf_counter()
print('Numpy élrajzolás:', (end_time - start_time) * 1000.0, "ezredmásodperc.")
cv2.imshow('img_np_edge', img_np_edge)
cv2.waitKey(0)

Második megközelítésként kettős for ciklussal bejárjuk a maszkot, a 255 értékek helyén az eredeti képmátrixban vörös színt állítunk be. A ciklust időméréssel látjuk el.

img_edge = img.copy()
start_time = time.perf_counter()
for y in range(0, img.shape[0]):
for x in range(0, img.shape[1]):
if edge[y, x] > 0:
img_edge[y, x] = [0, 0, 255]

end_time = time.perf_counter()
print((end_time - start_time) * 1000.0, "ezredmásodperc.")
cv2.imshow('img_edge', img_edge)
cv2.waitKey(0)

Vörös színű körvonal rávetítés OpenCV logikai függvényekkel, időméréssel. A működésének a lényege, hogy az eredeti BGR képet csatornákra bontjuk, a vörös csatornába a VAGY művelettel beégetjük a vörös körvonalat (ahol a maszk körvonal kép 255 értékű, ott a vörös csatorna is ezt az értéket veszi fel, ahol a körvonal 0 értékű, ott megmarad az eredetileg ott szereplő érték), a másik két csatornán nullázzuk ugyanezen helyeket (a ~ operátor a bitenkénti negálás, vagyis ellentettre fordítás, ezzel ÉS-elve kapjuk a körvonal pontokban a nulla eredményt).

img_ocv_edge = img.copy()
start_time = time.perf_counter()
b, g, r = cv2.split(img_ocv_edge)
r = cv2.bitwise_or(r, edge)
g = cv2.bitwise_and(g, ~edge)
b = cv2.bitwise_and(b, ~edge)
img_ocv_edge = cv2.merge((b, g, r))
end_time = time.perf_counter()
print((end_time - start_time) * 1000.0, "ezredmásodperc.")
cv2.imshow('img_ocv_edge', img_ocv_edge)
cv2.waitKey(0)

Program szabályos lezárása.

cv2.destroyAllWindows()

A program a laptopon futtatva az alábbi időmérési eredményeket adta. A Numpy és az OpenCV megoldás nagyjából 4 és 9 ezredmásodperc alatt lefutott. Az egymásba ágyazott for ciklusokkal bejárt kép esetén ez 1,2 másodperc (a Numpy 283-szor gyorsabb!), és használat közben érezhetően várni kell az eredményre. Pedig nem is nagy a bemeneti kép mérete.

Numpy élrajzolás: 4.24610000000003 ezredmásodperc.
Élrajzolás for ciklusokkal: 1202.1564000000003 ezredmásodperc.
OpenCV élrajzolás: 8.807400000000243 ezredmásodperc.