2024. április 26., péntek

Gyorskeresés

OpenCV és a teljesítmény

Írta: | Kulcsszavak: OpenCV

[ ÚJ BEJEGYZÉS ]

Akik tudják, mit takar az OpenCV, azoknak nem kell bemutatnom: [link]

Most röviden leírom, milyen érdekes dolgokat fedeztem fel pár függvénnyel kapcsolatban.

Az OpenCV-ben rengetek függvény van sok mindenre, de előfordulhat, hogy mi szeretnénk kézzel valamit számolni az egyes pixeleken, de OpenCV-ben nincs rá függvény.

Először is, az OpenCV teljesítményét kézzel nem fogjuk elérni, de néhány dolog valamiért pluszba belassít.
(OpenCV teljesítmény: egyszer végre tényleg bele kéne másznom a kódjába, hogy hogyan valósítják meg ezt, mert nagyon jól jönne:) )

Itt egy példa, végig akarunk menni minden pixelen:

Itt egy kép:
cv::Mat imgmat(cv::Size(640,480),CV_8UC3);

Ezen akarunk végigmenni:
for(int row=0; row<imgmat.rows; row++)
{
for(int col=0; col<imgmat.cols; col++)
{

}
}

Két féle módon férhetünk hozzá az adott pixelhez:
1. a beépített függvény:

Olvasás:
unsigned char b = imgmat.at<cv::Vec3b>(row,col)[0]; // blue
unsigned char g = imgmat.at<cv::Vec3b>(row,col)[1]; // green
unsigned char r = imgmat.at<cv::Vec3b>(row,col)[2]; // red

Írás:
imgmat.at<cv::Vec3b>(row,col)[0] = b;
imgmat.at<cv::Vec3b>(row,col)[1] = g;
imgmat.at<cv::Vec3b>(row,col)[2] = r;

Előnyei:
Szép, érthető és egyszerű.

Hátrányai:
Ha ezt a fenti 6 sort berakjuk a két for ciklus közé, akkor iszonyatosan megnő a CPU terhelés. Most nem készültem tesztekkel, de aki nem hiszi, járjon utána (később lehet kaptok tesztet:) ).
CPU terhelés növekedése akkor látszik igazán, amikor van egy 640x480-as webkamera, ami 30 FPS-sel küldi a képeket, és szeretnéd, ha minden frame-re végrehajtódna a fenti elemzés. Szó szerint a kimeneti képen látszik, hogy kicsit akad.

Észrevételem szerint minden Mat-os tagfüggvénynél fennáll ez a jelenség. Tehát dupla for cikluson belül kerüljük pl. a imgmat.channels() és imgmat.size().width féle parancsokat, ezeket vigyünk a dupla for cikluson kívülre, ott már nem okoznak zavart.

2. képtömb címzése:
A régi hagyományos mód, ahol van az 1D-s tömb és ki kell találni, melyik elem kell nekem.

Melyik elem:
Most nem írom le, hogy tárolja az OpenCV (és sok más) a képeket. Magyarázat helyett itt egy kép:


Forrás: [link]

A for cikluson kívülre: szükségem van a csatornák számára:
int ch = imgmat.channels();

A for ciklusokon belül:

Hol van a tömbben:
int ptr = row*imgmat.cols*ch + col*ch;

Olvasás:
unsigned char b = imgmat.data[ptr+0]; // blue
unsigned char g = imgmat.data[ptr+1]; // green
unsigned char r = imgmat.data[ptr+2]; // red

Írás:
imgmat.data[ptr+0] = b;
imgmat.data[ptr+1] = g;
imgmat.data[ptr+2] = r;

Még a szorozgatások ellenére is üti az 1. módszert.
Amennyiben a teljes képen végigmegyünk, akkor a szorzásra sincs szükségünk:

A for cikluson kívülre:
int iterator = 0;

A for ciklusokon belül:
unsigned char b = imgmat.data[iterator+0];
unsigned char g = imgmat.data[iterator+1];
unsigned char r = imgmat.data[iterator+2];

imgmat.data[iterator+0] = b;
imgmat.data[iterator+1] = g;
imgmat.data[iterator+2] = r;

iterator+=ch;

Fontos:
Ennél az esetnél fontos, hogy a for ciklusok milyen sorrendben hajtódnak végre. A fent látható ciklusnál a sor végigjárás van kívül és az oszlop végigjárás van belül. Ebben az esetben jól működik a kód, de ha felcseréljük, akkor marhaságot fogunk kapni. A magyarázat abban rejlik, hogyan tárolódik a kép az 1D-s tömbben.

Előnyei:
Ennél gyorsabb nincs.

Hátrányai:
Észnél kell lennie a tömb címzésénél. Másik hátrány, hogy az a bizonyos widthStep érték nem mindig egyezik meg a szélesség*csatornaszám szorzattal, így előfordul, hogy "elcsúszik a kép".
Erre az esetre a következőt tanácsolom:

int ptr = row*imgmat.cols*ch + col*ch;

helyett

int ptr = row*widthstepmat + col*ch;

ahol a widthstepmat:

int widthstepmat = imgmat.step1();
Persze ezt is tegyük for cikluson kívülre.

Végszó
Lényében ennyi. Tudom, hogy nem találtam fel a spanyol viaszt, de nekem csak nem rég tűnt fel, hogy a .at és a Mat.függvények() mennyire leeszi a procit ilyen esetben.

Hátha ezzel valakinek segítettem, hogy jobb progit írjon :).

Copyright © 2000-2024 PROHARDVER Informatikai Kft.