Documente Academic
Documente Profesional
Documente Cultură
1 Introducere 3
1.1 Structura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Tipuri de date specifice . . . . . . . . . . . . . . . . . . . . . 5
1.2.1 Matrice . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.2 Reprezentarea imaginilor . . . . . . . . . . . . . . . . 8
1.3 Manipularea imaginilor . . . . . . . . . . . . . . . . . . . . . 9
1.3.1 Interfaţa grafică . . . . . . . . . . . . . . . . . . . . . 12
2 Prelucrarea imaginilor 15
2.1 Filtare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.1 Netezire . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.2 Extragerea contururilor . . . . . . . . . . . . . . . . . 17
2.2 Morfologie matematica . . . . . . . . . . . . . . . . . . . . . . 19
2.2.1 Morfologie matematica in OpenCV . . . . . . . . . . . 19
2.3 Transformări geometrice . . . . . . . . . . . . . . . . . . . . . 22
2.4 Histograma . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3 Analiza imaginilor 33
3.1 Segmentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.1 Segmentarea cu prag . . . . . . . . . . . . . . . . . . . 33
3.1.2 Creşterea regiunilor . . . . . . . . . . . . . . . . . . . 38
3.1.3 Segmentare piramidală . . . . . . . . . . . . . . . . . . 38
3.1.4 Algoritmul Watershed . . . . . . . . . . . . . . . . . . 40
1
2 CUPRINS
Capitolul 1
Introducere
3
4 CAPITOLUL 1. INTRODUCERE
1.1 Structura
OpenCV conţine peste 500 de funcţii pentru numeroase domenii ce folosesc
Computer VisionOpenCV şi este structurată ı̂n 5 componente principale
(cum este parţial prezentat şi ı̂n figura 1.1):
1.2.1 Matrice
Pentru reprezentare matricelor se foloseşte tipul CvMat. Cu toate că OpenCV
este implementat complet ı̂n limbajul C, ı̂ntre structurile CvMat şi IplImage
există o relaţie ce se aseamănă foarte mult cu moştenirea din C++ (IplImage
este “derivată” din CvMat) (a se vedea figura 1.2). Clasa CvArr este o clasă
abstract din care este derivată la rândul ei CvMat.
În OpenCV pentru noţiunea de vector se poate folosi o matrice cu o
singură coloană.
6 CAPITOLUL 1. INTRODUCERE
Un element al matricei CvMat poate reprezenta unul sau mai multe nu-
mere, permiţând reprezentarea culorii pe cele trei canale (RGB). Struc-
tura CvMat este relativ simplă, are o lăţime, o ı̂nălţime, un tip (lungimea
rândului ı̂n bytes) şi un pointer către matricea de date. Elementele se pot
accesa direct printr-un pointer referinţă către CvMat sau prin funcţiile de
acces furnizate. De exemplu, pentru obţinerea dimensiunii unei matrice, se
poate apela cvGetSize(CvMat*), care returnează o structură CvSize, sau
se poate accesa independent lăţimea şi ı̂nălţimea, astfel matrix->height şi
matrix->width.
Matricile pot fii create ı̂n mai multe feluri. O primă variantă este
apelând funcţia cvCreateMat(); alte este apelând cvCreateMatHeader()
(care crează structura fără să-i aloce memorie) ı̂mpreună cu cvCreateData()
(care alocă memorie datelor), fie apelând cvCloneMat(CvMat*), care crează
o nouă matrice dintr-una deja existentă. În cazul ultimei, dealocarea se face
cu cvReleaseMat(CvMat**). Există un constructor cvMat() care crează o
structură CvMat. Dar el nu alocă memorie, ci doar crează header-ul, precum
cvInitMatHeader(). Acesta este folositor ı̂n cazul ı̂n care datele există deja
şi se doreşte ı̂mpachetarea lor ı̂ntr-o strutură.
De exemplu, pentru crearea unei matrici bidimensionale se apelează
cvCreateMat:
cvMat* cvCreateMat(int rows, int cols, int type)
unde type este orice tip predefinit (CV adâncime-bit(S|U|F)Cnumăr de
canale); de exemplu CV 32 FC1 este o matrice care conţine date de tipul 32
bit float.
Pentru interogarea matricilor avem următoarele operaţii:
Cel mai simplu mod pentru accesarea unui element dintr-o matrice este
cu macro-ul CV MAT ELEM(). Acesta primeşte matricea, tipul elementului
dorit, numărul liniei şi al coloanei. De exemplu:
CvMat* mat = cvCreateMat(4, 4, CV 32FC1);
float element 3 2 = CV MAT ELEM(*mat, float, 3, 2);
Pentru scrierea unui element la o poziţie dată se foloseşte macro-ul
CV MAT ELEM PTR(). De exemplu:
CvMat* mat = cvCreateMat(4, 4, CV 32FC1);
float element 3 2 = 3.3;
*( (float*)CV MAT ELEM PTR(*mat, 3, 2) ) = element 3 2;
Din păcate aceste macro-uri recalculează pointer-ul la fiecare apel, drept
care nu reprezintă cea mai bună metodă de accesare a matricei, fiind utile
doar ı̂n cazul 1D şi 2D). În cazul matricilor N dimensionale, OpenCV-ul fur-
nizează funcţii de tipul cvPtr*D şi cvGet*D. (* este, de fapt, dimensiunea
N). Pentru N ı̂ntre 1D şi 3D, funcţiile au ca argument pointerul CvArr*,
urmat de numerele corespunzătoare indicilor şi un argument opţional care
indică tipul parametrului la ieşire. Funcţiile returnează un pointer al ele-
mentului dorit. În cazul N-dimensional, al doilea argument este un pointer
la matricea care conţine numerele corespunzătoare indicilor.
În cazul unei imagini RGB (cu 3 canale) reprezentată de o matrice bi-
dimensională: datele sunt stocate ı̂ntreţesut: RGBRGBRGB.... De aceea
pentru mutarea pointer-ului la următorul canal se adaugă pur şi simplu 1.
Dacă se doreşte mutarea către următorul pixel se adaugă un ofset egal cu
numărul canalelor, ı̂n cazul nostru 3. Pasul de parcurgere al elementelor
matricei este lungimea ı̂n bytes a unei linii, drept care nu sunt suficienţi
parametrii col şi width pentru deplasarea ı̂ntre liniile matricei. În cazul
unei matrice de ı̂ntregi pasul către următoare linie se face adăugând step/4,
ı̂n cazul tipului double se adaugă step/8 (se ţine cont că C va ı̂nmulţi auto-
mat offsetul pe care-l adăugăm cu dimensiunea tipului ı̂n bytes). Funcţiile
cvSet*D şi cvSetReal*D setează elementul unei matrice sau al unei imagini
dintr-un singur apel.
Prototipurile funcţii set element pentru CvMat (şi, aşa cum vom vedea
IplImage):
void cvSetReal1D( CvArr* arr, int idx0, double val );
void cvSetReal2D(
CvArr* arr,
int idx0,
int idx1,
double val );
void cvSetReal3D( CvArr* arr,
int idx0,
int idx1,
int idx2,
double val);
void cvSetRealND( CvArr* arr, int* idx, double val );
void cvSet2D(CvArr* arr, int idx0, int idx1, CvScalar val);
8 CAPITOLUL 1. INTRODUCERE
• IPL DEPTH 8U
• IPL DEPTH 8S
• Controlul ferestrelor:
cvNamedWindow("win1", CV WINDOW AUTOSIZE); creează o fereastră
cu numele win1 şi cu proprietatea de automodificare a dimensiunii ı̂n
acord cu imaginea afişată;
cvMoveWindow("win1", 100, 100); - mută fereastra cu 1oo de pi-
xeli ı̂n jos şi la dreapta faţă de colţul din stânga sus al imaginii;
cvDestroyWindow("win1"); - distruge ferestra win1;
Prelucrarea imaginilor
Deşi obiectivul final este să ofere suport pentru meotde de computer vision,
OpenCV-ul oferă suport pentru o multitudine de operaţii primare ce ţin de
domeniul prelucrării imaginilor şi, ı̂n mod tradiţional, sunt folosite fie pentru
preprocesare imaginilor fie drept bază pentru extragerea de trăsături. În
acest capitol vom inventaria câteva dintre cele mai relevante dintre acestea.
2.1 Filtare
Tradiţional, ı̂n capitolul de “filtrare” se discută despre filtrare liniară res-
pectiv neliniară, şi despre extragerea contururilor respectiv mediere (sau
netezire).
Operaţia de filtrare (convoluţie liniară cu un nucleu) poate fi implemen-
tată cu ajutorul funcţiei:
void cvFilter2D(
const CvArr* src,
CvArr* dst,
const CvMat* kernel,
CvPoint anchor=cvPoint(-1, -1))
Parametrii funcţiei au semnificaţia
15
16 CAPITOLUL 2. PRELUCRAREA IMAGINILOR
2.1.1 Netezire
Pentru netezire OpenCV-ul oferă funcţia cvSmooth. Prototipul acestei funcţii
este:
void cvSmooth(
const CvArr* src,
CvArr* dst,
int smoothType,
int param1,
int param2,
double param3,
double param4);
Parametrii funcţiei au semnificaţia următoare:
void cvLaplace(
const CvArr* src,
CvArr* dst,
int apertureSize=3)
Parametrii:
Valoarea cea mai mică dintre threshold1 şi threshold2 este folosită
pentru a uni contururile, iar valoarea cea mai mare este folosită pentru a
determina segmentele de contururi puternice.
2.2. MORFOLOGIE MATEMATICA 19
IplConvKernel* B = NULL,
int iterations = 1
);
unde parametrii sunt:
• scr: imaginea sursă;
• dst: imaginea destinatie (poate fi tot src);
• B: nucleul, elementul structurant (implicit dat ca valoare NULL este
echivalent cu un nucleu 3*3 cu originea in mijloc)
• iterations: numărul de câte ori se repetă operaţia (implicit 1).
Rezultatul funcţiilor este că valoarea fiecărui pixel din imaginea sursă
este ı̂nlocuită ı̂n imaginea destinaţie cu maximul, respectiv minimul, dintre
valorile tuturor pixelilor acoperiţi de elementul structurant, ce are originea
suprapusă peste pixelul curent.
Un element structurant plat nu are nevoie de valori numerice şi doar
indică poziţii ı̂n spaţiu care sunt considerate la calculul maximului sau mini-
mului local. Originea elementului structurant arată cum este aliniat nucleul
peste fiecare pixel al imaginii sursă.
Pentru realizarea altor tipuri de elemente structurante se foloseste func-
tia:
IplConvKernel* cvCreateStructuringElementEx(
int cols,
int rows,
int anchor x,
int anchor y,
int shape,
int* values=NULL
);
unde parametrii folosiţi sunt :
• cols, rows - dimensiunea dreptunghiului care conţine elementul struc-
turant;
• anchor x, anchor y - coordonatele originii ı̂n dreptunghiul determinat
anterior;
• shapes identifică forma elementului structurant si poate avea valorile
predefinite:
– CV SHAPE RECT - element structurant dreptunghiular
– CV SHAPE CROSS - element structurant ı̂n formă de cruce
– CV SHAPE ELLIPSE - element structurant elliptic
– CV SHAPE CUSTOM - element structurant definit de utilizator
• values: utilizat când shape= CV SHAPE CUSTOM; conţine rows × cols
elemente; o valoare diferită de zero indică prezenţa punctului ı̂n ele-
mentul structurant.
2.2. MORFOLOGIE MATEMATICA 21
A ◦ B = (A ⊕ B) ⊖ B (2.4)
A • B = (A ⊖ B) ⊕ B (2.5)
– CV MOP GRADIENT - realizează gradientul morfologic (are nevoie
de temp);
– CV MOP TOPHAT - implemntează transformata Top Hat (are nevoie
de temp doar dacă src = dst);
– CV MOP BLACKHAT - implemntează transformata Black Hat (are
nevoie de temp doar dacă src = dst).
gradient(src) = (A ⊕ B) − (A ⊖ B) (2.6)
Transformatele Top Hat şi Black Hat Aceşti operatori sunt utilizaţi
pentru a izola regiuni care sunt mai luminoase, respectiv mai ı̂ntunecate
decât zonele imediat invecinate:
T opHat(src) = A − (A ◦ B)
(2.7)
BlackHat(src) = (A • B) − A
int flags,
CvScalar fillval = cvScalarAll(0));
Parametrii suplimentari ai funcţiei sunt:
2.4 Histograma
Histograma reprezintă o aproximare practică a densităţii de probabilitate,
a cărei realizare particulară este imaginea dată . Cel mai des, histograma
conţine valorile de luminozitate ale pixelilor, ı̂n intervalul 0 (negru) - 255
(alb). Aplicaţiile practice ale histogramelor includ:
2.4. HISTOGRAMA 27
void cvCalcContrastHist (
IplImage **src,
CvHistogram* hist,
int dontClear,
IplImage* mask);
unde parametrii au semnificaţia :
Analiza imaginilor
Deşi cuprinde rutine pentru mai multe operaţii din categoria analizei ima-
ginilor, cum ar fi descrierea formelor sau a contururilor, aici vom prezenta,
pe scurt, doar câteva din soluţiile existente ı̂n OpenCV ı̂n ceea ce priveşte
segmentarea.
3.1 Segmentare
Segmentarea ese operaţia ı̂n care o imagine este partiţionată ı̂n segmentele
constituente, segmente ce se doreşte să corespundă obiectelor vizibile ı̂n
imagine.
33
34 CAPITOLUL 3. ANALIZA IMAGINILOR
#include <highgui.h>
void sum rgb( IplImage* src, IplImage* dst ) {
// aloca planurile individuale de culoare.
IplImage* r = cvCreateImage( cvGetSize(src), IPL DEPTH 8U, 1 );
IplImage* g = cvCreateImage( cvGetSize(src), IPL DEPTH 8U, 1 );
IplImage* b = cvCreateImage( cvGetSize(src), IPL DEPTH 8U, 1 );
// imparte imaginea in planurile de culoare
cvSplit( src, r, g, b, NULL );
// in s este stocata imaginea temporar
IplImage* s = cvCreateImage( cvGetSize(src), IPL DEPTH 8U, 1 );
// Aduna ponderi egale pentru valorile rgb
cvAddWeighted( r, 1./3., g, 1./3., 0.0, s );
cvAddWeighted( s, 2./3., b, 1./3., 0.0, s );
// truncheaza valorile mai mari de 100
cvThreshold( s, dst, 100, 100, CV THRESH TRUNC );
cvReleaseImage( &r );
cvReleaseImage( &g );
cvReleaseImage( &b );
cvReleaseImage( &s );
}
int main()
{
char *name=NULL;
name="fruits.jpg";
// creeaza o fereastra numita ca fisierul imagine
cvNamedWindow( name, 1 );
// incarca imaginea cu numele dat
IplImage* src = cvLoadImage(name );
IplImage* dst = cvCreateImage( cvGetSize(src), src->depth, 1);
sum rgb( src, dst);
// afiseaza imaginea in fereastra creeata mai sus
cvShowImage( name, dst );
// asteapta sa fie apasata tasta ‘‘ESC’’
while( 1 ) { if( (cvWaitKey( 10 )&0x7f) == 27 ) break; }
// dezaloca spatiul folosit
cvDestroyWindow( name);
cvReleaseImage( &src );
cvReleaseImage( &dst );
36 CAPITOLUL 3. ANALIZA IMAGINILOR
}
În exemplul de mai sus se poate observa că valorile pentru r, g şi b nu
sunt adunate ı̂ntr-un vector pe 8 biţi deoarece este posibil ca suma să iasa
din domeniul de definiţie. De aceea, am folosit adunarea cu ponderi egale
a celor trei canale de culoare prin intermediul funcţiei cvAddWeighted().
Apoi, rezultatele sunt trunchiate la valoarea 100. Functia cvThreshold()
se poate aplica numai imaginilor sursă pe 8 biţi sau ı̂n virgula mobilă dar cu
niveluri de gri. Imaginea destinaţie trebuie să fie de acelaşi tip cu imaginea
sursă sau o imagine pe 8 biţi. De fapt, cvThreshold() permite ca imaginea
sursă şi cea destinaţie să fie identice.
Pentru segmentarea cu prag adaptiv, ı̂n OpenCv exista funcţia numită
cvAdaptiveThreshold() ı̂n care pragul este variabil:
void cvAdaptiveThreshold(
CvArr* src,
CvArr* dst,
double max val,
int adaptive method = CV ADAPTIVE THRESH MEAN C
int threshold type = CV THRESH BINARY,
int block size = 3,
double param1 = 5 );
Funcţia cvAdaptiveThreshold permite două tipuri diferite de segmen-
tare adaptivă care depind de setarea adaptive method. În ambele cazuri,
pragul adaptiv T (x, y) este ales calculând o medie ponderată a pixelilor
dintr-o vecinatate b × b din jurul pixelului central minus o constantă, unde b
este dat de către parametrul block size, iar constanta este dată de param1.
Dacă metoda aleasă este CV ADAPTIVE THRESH MEAN C atunci toţi pixelii din
vecinătate au ponderi egale. Dacă metoda este CV ADAPTIVE THRESH GAUSSIAN C
atunci pixelii din vecinătate au ponderi gaussiene. Parametrul threshold type
poate avea aceleaşi valori ca pentru cvThreshold() din tabelul 3.1.
Metoda segmentării cu prag adaptiv este utilă atunci când există gradienţi
puternici datorită iluminării sau unor reflexii extreme care trebuie să nu
influenţeze rezultatul segmentării. Această funcţie se poate aplica numai
imaginilor cu un singur canal pe 8 biţi sau ı̂n virgulă mobilă şi trebuie să
aibă imaginea sursă diferită de cea destinaţie.
Un exemplu pentru evidenţierea diferenţelor dintre cvAdaptiveThreshold()
şi cvThreshold() este aratat ı̂n exemplul de mai jos:
#include <cv.h>
#include <highgui.h>
#include <math.h>
IplImage *Igray=0,*It = 0, *Iat;
int main()
{
//setarile pragurilor
double threshold = (double)15;
int threshold type = 1 ?
3.1. SEGMENTARE 37
CvSeq** comp,
int level,
double threshold1,
double threshold2);
unde storage stochează rezultatele secvenţei de componente conectate,
la care pointează comp.
[3] Bradski, G., Kaehler, A.: Learning OpenCV. OReilly Media, Inc (2008)
[5] Heckbert, P.: A Seed Fill Algorithm. (Graphics Gems I), New York:
Academic Press (1990)
[6] Meyer, F.: Color image segmentation. In: 303-306 (ed.) Proceedings of
the International Conference on Image Processing and Its Applications
(1992)
[8] Tomasi, C., Manduchi, R.: Bilateral filtering for gray and color images.
on-line (2009)
41