Skip navigation

Képmátrix típusok és műveletek

Célok

Mint az előző témakörnél láttuk, a képmátrixokat az OpenCV Python felülete Numpy tömb objektumokban biztosítja számunkra. A Python tömb objektumhoz képest különbség, hogy a tömb elemek típusa előre rögzített numerikus típus lehet. Így hatékonyabb adatelérés valósítható meg.

Ebben a fejezetben áttekintjük a Numpy és az OpenCV által támogatott képpont típusokat, a képmátrix objektumok közötti típuskonverziós lehetőségeket. Az aritmetikai műveletek kapcsán látni fogjuk a Numpy és az OpenCV közötti alapvető különbséget. Végül példákat látunk képfeldolgozási feladatokban felmerülő aritmetikai és logikai műveletekre.

Rövid összefoglaló

A Numpy a képmátrixban található számokat egyforma típussal definiálja. Alapesetben ez a [0, 255] egész tartomány reprezentálására alkalmas előjel néküli bájt (uint8) típus. Rendelkezésre áll ezen túl többek között az előjel nélküli 16 bites egész (uint16), az előjeles bájt (int8), az előjeles 16 bites egész (int16), és a 32 bites lebegőpontos (float32).

Az OpenCV is definiál típus konstansokat, amennyiben típus paramétert kell OpenCV függvénynek átadnunk (pl. cv2.CV_8UC3).

Más típusra alakításra szükségünk lehet, amennyiben a [0, 255] értékkészlet nem elegendő egy művelet elvégzéséhez. Ilyen például a komponens cimkézés, ahol minden képponthoz hozzárendeljük, hogy melyik összefüggő komponenshez tartozik, és a komponensek száma 255 feletti lehet. Élkereséskor a gradiens magnitúdó kép értékkészlete is kivezethet ebből a tartományból. Lebegőpontos számokra lehet szükségünk bizonyos konvolúciós műveleteknél (például az előbb említett gradiens magnitúdó számítás), vagy Fourier transzformációhoz.

A mátrix típusok közötti konverzió a dst = np.új_típus(src) módon könnyen megvalósítható. Figyelni kell viszont arra, hogy lebegőpontosból egészre alakításnál a tört értékek csonkolódnak az egész értékre (nem kerekítés történik!), valamint az új típus értéktartományán kívüli számok megváltoznak!

Az átalakítást két lépésben célszerű végezni.

  • Amennyiben szűkebb értékkészletű típusról (pl. np.uint8) alakítunk bővebbe (pl. np.uint16 vagy np.float32), akkor először a típus konverzióját kell elvégezni, utána az értéktartomány igazítását, ha szükséges.
  • Fordított esetben (pl. np.float32-ről np.uint8-ra) először az értékkészelet alakítását, és csak utána a típuskonverziót.

A mátrix típus konverzió a képmátrix megjelenítésére is hatással van. Az OpenCV imshow() függvénye az egész típusú képmátrixok esetén az ábrázolható minimum értéket képezi le a fekete színre, a maximálisat a fehérre. Típus konverzió esetén, ha a képet meg is szeretnénk jeleníteni, akkor erre is gondolni kell.

A képmátrixok képfájlba mentése np.uint8 típus esetén működik megfelelően. Más képmátrix típus esetén a mentés előtt vissza kell alakítani np.uint8-ra!

A mátrix értékkészletét skalár értékkel végzett aritmetikai műveletekkel (szorzás, osztás, kivonás, összeadás) vagy az értékek normalizálásával is változtathatjuk.

Aritmetikai műveleteket két egyforma méretű kép között, valamint kép és skalár érték között is elvégezhetjük. Ez utóbbi esetben a skalárral történő művelet minden képpontra egyenként végrehajtódik, az eredmény pedig az eredetivel megegyező méretű mátrix lesz.

Aritmetikai végrehajtására érdemesebb az OpenCV függvényeit használni. Az OpenCV úgynevezett telített aritmetikát használ (pl. maximális érték + bármi = maximális érték), míg a Numpy "körkörös" csonkolást (maximális érték + 1 = minimális érték). Képek esetén az OpenCV megközelítése természetesebb eredményt ad.

A többcsatornás képen a Numpy operátorokkal végzett skalár műveletek minden csatornára végrehajtódnak, az OpenCV esetén viszont csak a [0] indexű csatorna értékeire! OpenCV esetén így a cv2.split() függvénnyel csatornákra kell bontani, csatornánként elvégezni a műveleteket, majd a cv2.merge() segítségével összevonni az így módosított csatornákat.

Lehetőség van két képmátrix, vagy képmátrix és skalár érték között bitenkénti logikai műveleteket is elvégezni. Feltételvizsgálattal egybekötve például maszkolhatjuk a képünket (egyes műveletek hatása csak a maszkon belüli képpontokra érvényesül). A maszkoláshoz igénybe vehetünk OpenCV függvényeket, Numpy feltételes indexelést, vagy egymásba ágyazott for ciklusokkal bejárhatjuk a képet. Ez utóbbi nagyon lassú művelet, több másodpercig eltarthat, csak végső esetben (nincs jobb ötletünk) használjuk!

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, vagy videókonferencia alkalmazásokban, ahol a személy mögötti szoba képét egy kiválasztott háttérképre cserélhetjük.