Sunteți pe pagina 1din 34

Pagini de eliminat

DETECTAREA FEȚEI PRIN METODA CULOARE ȘI


POTRIVIRE DE ȘABLOANE

Absolvent: Iulia Iarina UNGUREANU

Coordonator Conf. Univ. Dr. Ing. Tiberiu Marița


științific:

2023
CUPRINS

CAPITOLUL 1. INTRODUCERE ................................................................. 2

CAPITOLUL 2. OBIECTIVELE PROIECTULUI ..................................... 3

CAPITOLUL 3. STUDIU BIBLIOGRAFIC ................................................. 3

CAPITOLUL 4. ANALIZĂ ȘI FUNDAMENTARE TEORETICĂ ........... 3

CAPITOLUL 5. PROIECTARE DE DETALIU ȘI IMPLEMENTARE ... 7

CAPITOLUL 6. TESTARE ȘI VALIDARE ............................................... 11

CAPITOLUL 7. CONCLUZII...................................................................... 17

BIBLIOGRAFIE ............................................................................................ 18

ANEXA 1 ........................................................................................................ 19

i
Capitolul 1. Introducere

Domeniul detectării și localizării fețelor este un subdomeniu al computer vision,


care a evoluat semnificativ de-a lungul anilor. În cele ce urmează prezentăm un scurt
istoric al dezvoltării acestui domeniu:
În anii 1990 au fost propuse mai multe abordări în dezvoltarea sistemelor de
detectare și localizare a fețelor:

Metoda Stable color regions (1995) (a se vedea [3] și referintele conținute):


Această metodă a fost una dintre primele încercări de a detecta fețe în imagini. Se baza
pe identificarea regiunilor de culori stabile, care erau apoi considerate a fi posibile fețe.
Cu toate acestea, metoda era limitată de precizia și robustețea sa.
Metoda Skin Colour Model (1997) [4]: Această metodă a fost printre primele
abordări care au folosit detectarea culorii pielii pentru a identifica fața. Acest model de
culoare a culorii pielii s-a dovedit a fi eficient în identificarea regiunilor de interes care
puteau conține fețe.
În anii 2000, metodele de detectare și localizare a fețelor au devenit mai
avansate și mai precise:
Metode bazate pe extragerea caracteristicilor (2001) [ a se vedea [6] și
referințele din [6]): Aceste metode au introdus conceptul de extragere a caracteristicilor
specifice unui chip, precum ochii, nasul și gura, pentru localizarea feței umane. Această
abordare a oferit rezultate mai bune în comparație cu metodele anterioare, dar a avut
încă anumite probleme în cazul unor poziții sau unghiuri neobișnuite ale feței.
Metode bazate pe cascade de clasificatori Haar (2001)[5]: Această tehnologie,
introdusă de Paul Viola și Michael Jones, a fost o inovație semnificativă în domeniul
detectării fețelor. Aceasta se bazează pe antrenarea unor clasificatori de tip AdaBoost,
care pot detecta fețele în timp real.
O dată cu creșterea puterii de calcul și dezvoltarea tehnicilor de învățare
automată, în anii 2010, metodele de detectare și localizare a fețelor au ajuns la o precizie
și robustețe semnificativ îmbunătățite:
Metode bazate pe învățarea profundă (2014): Odată cu apariția rețelelor
neuronale convoluționale profunde, cunoscute sub numele de CNN-uri, detecția feței a
făcut un mare pas înainte. Arhitecturi precum R-CNN, Fast R-CNN și Faster R-CNN
au permis o detectare precisă a feței umane și localizarea punctelor cheie ale feței cu o
acuratețe și o eficiență remarcabile.
Sisteme de detectare și localizare folosite în produse comerciale (2015-
prezent): În prezent, metodele bazate pe învățarea profundă au fost încorporate în
numeroase produse comerciale. Aplicații de la identificarea chipului în fotografii și
recunoașterea facială în telefonie mobilă, la sisteme de supraveghere video și
identificarea în timp real a feței umane beneficiază de aceste tehnologii avansate pentru
o precizie și performanță remarcabile.

În concluzie, principalele metode de detectare și localizare a fețelor pot fi


grupate astfel
1. Metode bazate pe cunoștințe:
2. Metode bazate pe trăsături invariante ( poziție, orientare, perspectivă)

2
Capitolul 2. Obiectivele proiectului

3. Metode bazate pe potrivirea de șabloane (template matching) - șabloane


pentru intreaga fata sau pentru anumite părți.
4. Metode bazate pe aparențe - modele / template-uri învățate pe un set de
imagini de antrenare care să surprindă variabilitatea aprentelor faciale

În proiectul nostru, utilizăm o metodă bazată pe modele de culoare (ce folosesc


spațiul de culoare LAB) și potrivire de șabloane pentru detectarea feței umane.

Capitolul 2. Obiectivele proiectului

Scopul acestui proiect este dezvoltarea un sistem de detectare a feței bazat pe


spatiul de culoare Lab și potrivirea de șabloane.

Capitolul 3. Studiu bibliografic

Metoda adoptată pentru realizarea acestui proiect (detectarea feței bazată pe


culoare și potrivire de șabloane) urmează îndeaproape pe cea aplicată în [1], [2] ( a se
vedea și [7]) pentru realizarea detectării faciale prin utilizarea spațiului de culoare RGB.
Principala diferență între modul de realizare a detecției faciale din [1], [2] și cea din
proiectul de față constă în utilizarea spațiului de culoare Lab în locul celui RGB și
respectiv YCbCr în [1] și, respective, [2] precum și în utilizarea unor distanțe diferite
pentru segmentarea zonelor imaginii.

Capitolul 4. Analiză și fundamentare Teoretică

Lab este o reprezentare a spectrului vizibil uman și este cunoscută pentru


capacitatea sa de a separa culorile de luminozitate. Spațiul de culoare Lab are trei
componente.
L - Luminozitate (Intensitate).
a - componenta de culoare care variază de
la Verde la Magenta.
b - componenta de culoare care variază de
la Albastru la Galben.

Canalul L este independent de informația de culoare și codifică doar luminozitatea, iar


celelalte două canale codifică doar culoarea. Astfel, Lab este diferit de spațiul de
culoare RGB, unde informația este separată în trei canale, care codifică atât culoarea
cât și informația despre luminozitate.
Metoda adoptată pentru detectarea feței este bazată pe culoare și potrivire de
șabloane, iar implementarea ei necesită parcurgerea mai multor etape precum:
detectarea pielii, analiza regiunilor și potrivirea cu un șablon. Pașii importanți în
atingerea acestor etape sunt următorii:
1. Construirea unui model de culoare pentru piele folosind un set de imagini test
din care se aleg zone de piele.

3
Capitolul 4. Analiză și fundamentare Teoretică

2. Realizarea histogramelor asociate celor două caracteristici 𝑎 și 𝑏 ale modelelor


de piele alese.
3. Caracterizarea statistică a variabilelor aleatoare generate de cele 2 caracteristici.
4. Segmentarea zonelor prin determinarea, pentru fiecare pixel, a distanței
(mahalanobis) la media distributiei gausiene antrenate.
5. Binarizare adaptive.
6. Realizarea de operații morfologice - umplere de regiuni, eroziune și dilatare.
7. Etichetare.
8. Calcul nr. Euler (pentru fiecare etichetă).
9. Calcul factor de alungire (aspect ratio – AR).
10. Template matching (potrivire de șabloane).

La pasul 1, se consideră un număr de N mostre de piele, din imagini color, pentru a


extrage din ele distribuția culorii pielii umane în spațiul cromatic de culoare. Mostrele
sunt prelevate de obicei de la persoane de diferite etnii: asiatice, caucaziene și africane.
Pe măsură ce probele de piele sunt extrase din imagini color, acestea ar putea fi și filtrate
folosind un filtru trece-jos pentru a reduce efectul zgomotului din probe.
În cazul modelului bazat pe Lab vor fi extrase și analizate doar caracteristicile 𝑎 și 𝑏,
pentru care se realizeaza și histogramele în cazul pasului al 2 lea.
Pasul 3 presupune determinarea mediei (ma,mb) a vectorului aleator x = (a,
b),ma=E(a), mb=E(b) precum și a matricei de covarianță
𝐸(𝑎 − 𝑚𝑎 )2 𝐸(𝑎 − 𝑚𝑎 )(𝑏 − 𝑚𝑏 )
cov(x)=E(x-E(x))( x-E(x))T =( ).
𝐸(𝑎 − 𝑚𝑎 )(𝑏 − 𝑚𝑏 ) 𝐸(𝑏 − 𝑚𝑏 )2
În pasul 4 se determină valoarea probabilității ca fiecare pixel din imaginea supusă
procesului de detectare facială să fie un pixel al pielii. Imaginea cu probabilitatea
pixelilor va fi o imagine în nuanțe de gri ale cărei valori reprezintă probabilitatea ca
pixelul să aparțină pielii. Pentru calculul probabilității de mai sus se poate folosi
formula
1
𝑃(𝑥̅ ) = exp⁡(− 2 (𝑥̅ − 𝐸(𝑥))𝑇 𝐶 −1 (𝑥̅ − 𝐸(𝑥)), unde

𝑥̅ = (𝑎̅, 𝑏̅)⁡(𝑜⁡𝑖𝑛𝑠𝑡𝑎𝑛𝑡𝑖𝑒𝑟𝑒⁡𝑎⁡𝑣𝑒𝑐𝑡𝑜𝑟𝑢𝑙𝑢𝑖⁡𝑥), 𝑥 = (𝑎, 𝑏)⁡ș𝑖⁡𝐶 = 𝑐𝑜𝑣(𝑥)


sau distanța (mahalanobis) la media distributiei gausiene antrenate, adică
𝐷(𝑥̅ ) = √(𝑥̅ − 𝐸(𝑥))𝑇 𝐶 −1 (𝑥̅ − 𝐸(𝑥)) sau chiar distanța euclidiană

𝐷𝐸 (𝑥̅ ) = √(𝑥̅ − 𝐸(𝑥))𝑇 (𝑥̅ − 𝐸(𝑥)), ambele normalizate (0,1).

Toate regiunile pielii (cum ar fi fața, mâinile și brațele) vor apărea mai luminoase decât
regiunea non-piele. Cu toate acestea, este important de reținut că regiunile detectate pot
să nu corespundă neapărat pielii. Singura concluzie certă este că regiunea detectată are
aceeași culoare ca cea a pielii. Deoarece regiunile pielii sunt mai luminoase decât
celelalte părți ale imaginilor, regiunile pielii pot fi separate(segmentate) de restul
imaginii prin utilizarea unui prag. Pentru a procesa imagini ale unor persoane cu piele
diferită, nu putem utiliza un prag cu o valoare fixă. În acest caz este necesar un proces
adaptiv pentru a obține valoarea pragului optim pentru fiecare rulare.
Limitarea adaptivă se bazează pe observația că scăderea valorii pragului poate crește în
mod intuitiv regiunea segmentată. Cu toate acestea, creșterea regiunii segmentate va
scădea treptat (pe măsură ce procentul regiunilor cutanate detectate se apropie de

4
Capitolul 4. Analiză și fundamentare Teoretică

100%), dar va crește brusc atunci când valoarea pragului este considerabil prea mică
pentru a fi incluse și alte regiuni care nu sunt piele. Valoarea prag la care se observă
creșterea minimă a dimensiunii regiunii, în timp ce scade valoarea pragului, va fi pragul
optim. De exemplu valoarea pragului poate fi redusă de la 0,65 la 0,05 în pași de 0,1.
Dacă creșterea minimă are loc atunci când valoarea pragului a fost modificată de la 0,45
la 0,35, atunci pragul optim va fi considerat 0,4.

În pasul 5 se realizeaza binarizarea adaptivă, adică procesul de conversie a unei imagini


color sau în tonuri de gri a unei fețe într-o imagine binară. Binarizarea este procesul de
utilizare a unui prag pentru a împărți o imagine în două regiuni distincte, de obicei alb
și negru. Scopul binarizării adaptive este de a îmbunătăți acuratețea și robustețea
algoritmilor de detectare a feței prin abordarea variațiilor de iluminare și a altor factori
care pot afecta procesul de detectare.
În binarizarea adaptivă, valoarea pragului pentru binarizare este determinat automat pe
baza caracteristicilor vecinătății locale din jurul fiecărui pixel. Aceasta înseamnă că
diferite părți ale imaginii pot avea valori de prag diferite, permițând o mai bună adaptare
la variațiile locale ale condițiilor de iluminare sau alți factori. Această metodă ajută la
diferențierea trăsăturilor faciale și a fundalului, sporind acuratețea algoritmilor de
detectare a feței. Binarizarea adaptivă poate fi realizată folosind diverse tehnici precum
metoda lui Otsu, metoda lui Niblack sau metoda lui Sauvola.
Realizarea de operații morfologice - umplere de regiuni, eroziune și dilatare
(pasul 6) în procesul de detectare facială se referă la utilizarea unor tehnici de prelucrare
a imaginilor pentru a identifica și manipula regiunile faciale dintr-o imagine.
Umplerea de regiuni (numită și "inundare") este o tehnică în care se umple automat o
regiune într-o imagine, pornind de la o valoare a unui pixel de referință. Aceasta se
folosește în detectarea facială pentru a separa și delimita regiunea feței de restul
imaginii. De exemplu, se poate găsi un punct de referință reprezentând nasul și apoi să
se umple automat regiunea facială în jurul acestuia.

Eroziunea și dilatarea sunt două operații morfologice opuse care sunt folosite în
procesul de subțiere și lărgire a regiunilor. Eroziunea presupune eliminarea pixelilor
marginali dintr-o regiune, ceea ce permite conturarea mai precisă a unei structuri, cum
ar fi marginile feței, în timp ce dilatarea implică adăugarea de pixeli în jurul unei
regiuni, pentru a o extinde. Aceste operații pot fi utilizate pentru a îmbunătăți
identificarea și delimitarea feței în imagini.

Realizarea de operații de umplere de regiuni, eroziune și dilatare în procesul de


detectare facială ajută la separarea și delimitarea regiunilor faciale într-o imagine,
permițând o identificare mai precisă a caracteristicilor feței și o detectare facială mai
precisă.
În pasul 7 are loc etichetarea regiunilor de piele. O regiune a pielii este definită ca o
regiune închisă din imagine, care poate avea 0, 1 sau mai multe găuri în interiorul ei.
Limita sa de culoare este reprezentată de pixeli cu valoarea 1 pentru imaginile binare.
Regiunile sunt o multime de componente conexe din cadrul unei imagini [8]. Toate
găurile dintr-o imagine binară au valoarea pixelilor zero (negru).

Pentru a determina câte regiuni avem într-o imagine binară se trece la etichetarea
acestor regiuni. O etichetă este o valoare întreagă. Se poate stabili că o vecinătate cu 8
conexiuni (adică toți vecinii unui pixel) determină etichetarea unui pixel. Dacă vreunul
dintre vecini avea o etichetă, etichetăm pixelul curent cu acea etichetă. Dacă nu, atunci

5
Capitolul 4. Analiză și fundamentare Teoretică

folosim o nouă etichetă. La final, numărăm numărul de etichete și acesta va fi numărul


de regiuni din imaginea segmentată.
La pasul 8 se determină numărul lui Euler. Pentru a reprezenta o față, o regiune a pielii
ar trebui să aibă cel puțin o gaură în interiorul său. Prin urmare, în acestă etapă se
elimină regiunile care nu au găuri. Pentru a determina numărul de găuri în interiorul
unei regiuni, calculăm numărul lui Euler E (a se vedea [5]) al regiunii, definit ca:
𝐸⁡ = ⁡𝐶⁡ − ⁡𝐻
unde C este numărul de componente conexe, iar H este numărul de găuri din regiune.
Adesea se considera formula 𝐸 = 1 − 𝐻, deoarece se consideră că limităm regiunea la
o singură bucată de piele.
Un număr Euler pozitiv indică faptul că imaginea este simplu conexă (o regiune fără
găuri), în timp ce un număr Euler negativ indică prezența unor găuri.
Odată ce sistemul a determinat că o regiune a pielii are mai mult de o gaură în interiorul
regiunii, trecem la analizarea unor caracteristici în acea regiune. Însă, mai întâi se
crează o nouă imagine numai cu regiunea respectivă.

Pasul 9 presupune calcularea factorului de alungire (aspect ratio - AR).


Pentru aceasta se determina coordonatele centrului de masă al regiunii
1 1
𝑥𝐶 = 𝐴 ∑⁡𝑖=1,𝑛,𝑗=1,𝑚 𝑗𝐵(𝑖, 𝑗) , 𝑦𝐶 = 𝐴 ∑⁡𝑖=1,𝑛,𝑗=1,𝑚 𝑖𝐵(𝑖, 𝑗),⁡unde B este matricea 𝑛 × 𝑚 -
reprezentand regiunea, iar A este aria in pixeli a regiunii.
Deși majoritatea fețelor sunt orientate vertical, există și unele cu o mică înclinație. Prin
determinarea inclinatiei axei de alungire se va determina inclinarea regiunii.
Unghiul de înclinare al acestei axe este
1 𝑏
𝜃 = 2 𝑎𝑟𝑐𝑡𝑔 (𝑎+𝑐), unde

𝑎= ∑ (𝑥 ′ 𝑖𝑗 )⁡2 𝐵(𝑖, 𝑗)⁡,⁡


𝑖=1,𝑛,𝑗=1,𝑚

𝑏= ∑ 𝑥 ′ 𝑖𝑗 𝑦 ′ 𝑖𝑗 𝐵(𝑖, 𝑗),
𝑖=1,𝑛,𝑗=1,𝑚

𝑐= ∑ (𝑦 ′ 𝑖𝑗 ) ⁡2 𝐵(𝑖, 𝑗)
𝑖=1,𝑛,𝑗=1,𝑚
𝑥 ′ = 𝑥 − 𝑥𝐶 , 𝑦 ′ = 𝑦 − 𝑦𝐶 , iar 𝑥 ′ 𝑖𝑗 , 𝑦 ′ 𝑖𝑗 reprezintă coordonatele pixelului (i,j) al
regiunii.
Prin calculele de mai sus s-a determinat centrul regiunii și înclinația acesteia. Încă
trebuie să determinăm lățimea și înălțimea regiunii pentru a redimensiona fața
șablonului, astfel încât aceasta să aibă aceeași lățime și înălțime ca regiunea noastră.
Imaginea este rotită cu unghiul 𝜃 astfel încât să fie verticală. Acum procedăm la
determinarea înălțimii și lățimii prin deplasarea a 4 indicatori: unul din stânga, dreapta,
sus și jos a imaginii. Dacă găsim o valoare a pixelului diferită de 0, ne oprim și aceasta
este coordonata unei frontiere. Când avem cele 4 valori, calculăm înălțimea (lungimea
axei majore) scăzând valorile de jos și de sus și lățimea (lungimea axei minore) scăzând
valorile din dreapta și din stânga.
Se determinp AR = lg. axa majoră / lg. axa minoră și se rețin doar regiunile pentru care
raportul AR ia valori între 3.5 și 1. Se procedează astfel pentru că, deși raportul
înălțime/lățime al fețelor umane este de aproximativ 1, pentru a se evita eliminările
eronate de fețe în situațiile în care persoana nu are cămașă sau este îmbrăcată în așa fel
încât o parte a gâtului și dedesubt să fie descoperită, s-a stabilit că raportul 3.5 este
potrivit.

6
Capitolul 5. Proiectare de detaliu și implementare

În ultimul pas se potrivește o imagine șablon peste regiunea decisă ca fiind față în
etapele precedente și se obțin coordonatele ce marchează fața.
Astfel, pentru imaginea corespunzătoare regiunii pielii, umplem găurile din regiune și
înmulțim această imagine cu cea originală. Se redimensioneaza fața frontală a
șablonului în funcție de înălțimea și lățimea regiunii calculate în secțiunea anterioară.
Se rotește fața șablonului redimensionat cu unghiul theta, astfel încât fața șablonului să
fie aliniată în aceeași direcție în care este regiunea pielii.
Se calculează centrul feței șablonului, rotit așa cum se arată mai sus.
Se calculează valoarea corelației încrucișate între partea din imagine corespunzătoare
regiunii pielii și fața șablonului procesată și centrată corespunzător.
În mod empiric s-a stabilit că o valoare prag bună pentru clasificarea unei regiuni ca
față este dacă valoarea de autocorelare rezultată este mai mare decât 0,6.
După ce sistemul a decis că regiunea pielii corespunde unei fețe, se marchează fața în
imaginea dată.

Capitolul 5. Proiectare de detaliu și implementare

1. Construirea unui model de culoare pentru piele in spatiul Lab


După lansarea funcției principale, trebuie citită imaginea de procesat. Aceasta
este convertită la modelul de culoare Lab, utilizând funcția „cvtColor” cu parametrul
CV_BGR2Lab, predefinită in OpenCV C++. În urma împărțirii pe canale, rezultă 3
matrice pentru fiecare componentă de culoare. Pentru calcularea likelihood-ului avem
nevoie doar de canalele a și b, mostrele de imagini utilizate și numărul de mostre. Prima
dată deschidem mostrele de piele și le împărțim, ca mai înainte, utilizând funcția
„cv::split” în cele 3 canale dintre care păstram doar canalele a și b. Pentru crearea
histogramelor parcurgem matricele obținute la pasul anterior și incrementam
histograma pe indexul egal cu valoarea actuală din matrice. Media pentru fiecare
componentă de culoare este calculată parcurgând histograma respectivă și adunând
produsele dintre valoarea actuală și index. Rezultatul este împărțit la dimensiunea
mostrelor (în cazul nostru 16x16) și la numărul de mostre. Pătratul deviației standard,
se calculează folosind histograma și este media aritmetică a pătratelor diferențelor
dintre valoarea actuală medie.

2. Segmentarea zonelor
După calcularea medie și a deviației standard putem obține matricea likelihood-
ului: pentru fiecare pixel se calculează distanța euclidiană la media distribuției gausiene
antrenate, se realizează o normalizare și se obține “skin likelihood” și reprezentarea ca
o imagine grayscale.

3. Binarizare
Imaginea rezultată este trimisă la funcția de binarizare adaptivă „Binarizare” ,
care returnează o imagine binarizată a imaginii sursă.
Mat dst = Mat(src.rows, src.cols, CV_8UC1); - Se creează o nouă imagine dst
cu aceleași dimensiuni ca și imaginea de intrare src, dar având un singur canal și pixeli
de tip 8-bit uchar (CV_8UC1). Se determină valorile maxime (imax) și minime (imin)
de intensitate ale pixelilor din imaginea de intrare.

7
Capitolul 5. Proiectare de detaliu și implementare

Se calculează un prag de binarizare (threshold) ca fiind media dintre valoarea


maximă și valoarea minimă a pixelilor. Începe o buclă care ajustează pragul de
binarizare până când diferența dintre pragul curent și cel precedent este mai mică de
0.1. În interiorul buclei, se calculează mediile pixelilor sub prag și peste prag pentru a
obține noile valori ale pragului. Se parcurge imaginea de intrare și se setează pixelii din
imaginea binarizată (dst) la 0 sau 255 în funcție de relația lor cu pragul de binarizare.
Imaginea binarizată rezultată (dst) este returnată.

Această funcție de binarizare este utilă pentru a transforma o imagine într-o


imagine binară, unde pixelii mai mici decât un prag specific sunt setați la 0, iar cei mai
mari sunt setați la 255.

4. Operatii morfologice
La pasul următor sunt aplicate diferite operații morfologice. In urma rezultatelor
experimentale am ajuns la crearea unei structuri de dimensiune 3x3 și aplicarea a două
dilatări și a 3 eroziunii. Aceste operații sunt implementate in OpenCV ca dilate și erode.

5. Etichetare
Pasul al cincilea constă în etichetarea obiectelor din imagine (colorate în alb).
Funcția se numește LabelingBreadthFirstTraversal și am utilizat Traversarea în lățime.
Aceasta este o metodă directă pentru etichetare, care se bazează pe traversarea în lățime
a grafului format de imaginea binară. Primul pas este inițializarea matricei de etichete
cu valoarea zero pentru toți pixelii, indicând faptul că inițial totul este neetichetat. Apoi,
algoritmul va căuta un pixel de tip obiect care este neetichetat. Dacă acest punct este
găsit, el va primi o etichetă nouă, pe care o va propaga vecinilor lui. Vom repeta acest
proces până când toți pixelii obiect vor primi o etichetă.
Algoritmul este descris de următorul pseudocod:

Structura de date de tip coadă menține lista punctelor care trebuie etichetate cu eticheta
curentă ”label”. Deoarece este o structură FIFO, se va obține traversarea în lățime. Vom
marca nodurile vizitate setând eticheta pentru poziția lor, în matricea de etichete. Dacă
structura de date se schimbă într-o stivă, se va obține o traversare în adâncime. Pentru
afișarea etichetării asociem fiecărei etichete câte o culoare și colorăm imaginea.
Rezultatul este salvat în matricea de întregi globală labels, fiecare pixel din imaginea
sursă are asociat în această imagine câte un label. Variabila globala label memorează
numărul de componente găsite.

8
Capitolul 5. Proiectare de detaliu și implementare

6. Numărul lui Euler


Pasul al șaselea este calcularea numărului lui Euler și este opțional. Acesta
constă în calcularea numărului de găuri din fiecare obiect și eliminarea obiectelor cu
mai puțin de o gaură. Funcția care implementează acest algoritm este eulerEtichete
împreuna cu Label1Holes.

Funcția Label1Holes primește o imagine binară (Mat src) și un număr de


etichetă (int label). Scopul funcției este să returneze true dacă obiectul cu label-ul
specificat conține mai mult de o gaură.
Găsirea contururilor: Se utilizează funcția findContours pentru a găsi
contururile obiectelor din imaginea binară. Se creează două imagini (singleLevelHoles
și multipleLevelHoles) pentru stocarea găurilor de nivelul unu și nivelul mai mult de
unu. Se parcurg contururile găsite și, dacă un contur are un părinte (în ierarhie), atunci
acel contur reprezintă o gaură. Se desenează găurile de nivelul unu pe imaginea
singleLevelHoles folosind funcția drawContours. Se neagă imaginea sursă
(bitwise_not(src, src)). Se face un bitwise_and între imaginea negată și găurile de
nivelul unu pentru a obține găurile de nivel mai mult de unu (multipleLevelHoles).
Se găsesc din nou contururile pentru găurile de nivel mai mult de unu.
Se numără numărul total de găuri identificate (numberOfHoles) și se afișează acest
număr împreună cu eticheta corespunzătoare.
Funcția returnează true dacă cel puțin o gaură este identificată, altfel false.

Funcția eulerEtichete primește o imagine binară etichetată (Mat src). Scopul


acestei funcții este reetichetarea și eliminarea componentelor din imagine care au mai
puțin de o gaura.
Se inițializează un vector de booleeni (goodLabel) pentru a ține evidența
etichetelor valide. Se parcurg etichetele existente și se creează imagini temporare pentru
fiecare etichetă. Se apelează funcția Label1Holes pentru fiecare componentă și se
determină dacă este o etichetă validă (goodLabel). Se parcurg pixelii imaginii și se
elimină componentele care nu sunt considerate bune, atribuindu-le valoarea 0 și
actualizând matricea labels. Se realizează reetichetarea componentelor rămase.

7. Template matching și calcularea factorului de alungire


Funcția templateMatching conține o metodă de identificare a fețelor sau a altor
componente identificate prin etichetare. Aceasta apelează alte doua funcții care vor fi
explicate în continuare.

Funcția calculateElongations primește o imagine binară (binaryImage) și


calculează caracteristici de alungire pentru obiectele din imagine. Se utilizează funcția
findContours pentru a găsi contururile obiectelor din imaginea binară și se determină
dreptunghiul de încadrare cu suprafață minimă (boundingBox) pentru cel mai mare
contur (presupus a fi obiectul). După aceea se extrag lungimile axelor majore și minore
ale dreptunghiului și se afișează, precum și raportul aspectului (AR). La final rezultatele
sunt împachetate într-o structură de date ElongationResults și returnate.

Funcția Axa primește o imagine binară etichetată (src) și efectuează


următoarele operații:

9
Capitolul 5. Proiectare de detaliu și implementare

• Calculul caracteristicilor de alungire: Se apelează funcția


calculateElongations pentru a obține caracteristicile de alungire (result)
pentru imaginea dată.
• Calculul centrului de masă: Se calculează centrul de masă al
obiectului din imagine și se actualizează în structura result.
• Etichetare și desenarea axei de alungire: Se utilizează o funcție numită
Labeling pentru a extrage conturul obiectului. Se calculează unghiul de
alungire și se actualizează în structura result.
Aceasta completează structura result care conține rezultatele și o returnează la
final.

Funcția templateMatching se ocupă de procesul de


potrivire a unui șablon cu obiectele identificate în imagine. Se
parcurg toate etichetele identificate în imagine și se verifică
dacă raportul aspectului (AR) al obiectului este în intervalul
specificat (1-3.5). Se încarcă o imagine de șablon
(templateFace), care se redimensionează și se rotește șablonul
pentru potrivirea cu obiectul identificat (după calcularea
unghiului de rotatie). După modificare se suprapune șablonul Figură 1 templateFace
peste obiectul din imagine și se calculează diferența de
intensitate între șablon și imaginea originală. Imaginea rezultată se afișează și se
evaluează diferența.

În imaginea de mai sus este explicat procesul de template Matching după rotirea și
alungirea template-ului reprezentat de pătratul roșu cu centrul în (height/2,height/2).
Imaginea este reprezentata de dreptunghiul negru și punctul (ri,ci) reprezintă centrul
componentei detectate ca față. Astfel putem începe procesul de matching:
Iterăm în șablon începând de la punctul (0,0) pana la punct (height, height) și
comparăm fiecare pixel cu cel de sub el în imaginea originală. Acest pixel poate fi
calculat folosind punctele centrale astfel:
Punct(0,0) în șablon <=> Punct(ri – centru, ci-centru) in imagine
Punct (0,1) în șablon <=> Punct(ri – centru, ci+1 - centru) in imagine
Punctul (1,1) în șablon <=> Punct(ri + 1 – centru, ci+1 - centru) in imagine

10
Capitolul 6. Testare și validare

Astfle ajungem la formula acesta:


For (i=0<height)
For (j=0<height)
pixel actual in template: Point(i,j)
pixel actual in imagine: Point(i+ri-center,j+ci-center)
diferenta: Point(i,j) - Point(i+ri-center,j+ci-center)

Dacă diferența este sub un anumit prag (THRESHOLD_FACE), se consideră


că potrivirea este reușită și se desenează o cruce peste centru de greutate al
componentei.

Capitolul 6. Testare și validare

Testarea v-a fi realizata pe mai multe tipuri de imagini cu mai multe sau mai
puține persoane de diferite etnii, în mai multe condiții de iluminare.

Figură 4 Figură 4 Figură 4

11
Capitolul 6. Testare și validare

Prima imagine de test conține o singura persoana. După construirea unui model
de culoare pentru piele in spațiul Lab obținem a doua imagine în care se poate observa
că pielea are o probabilitate mai mare de fi piele, apoi urmează părul persoanei și umbra
sa, iar cu o probabilitate mica se afla bluza și fundalul. În imaginea a treia se află
imaginea după binarizarea adaptivă, toate zonele cu piele au rămas, dar și o parte din
zona marginală a părului a fost păstrata. În consolă au fost afișate media și deviația
standard calculate împreuna cu distanțele maxime și minime.

Figură 7 Figură 7 Figură 7

După aplicarea operaților de dilatare și eroziune obținem imaginea din Figura


7. Prin aplicarea funcției de etichetare obținem nouă componente care se afișează cu
câte o culoare diferită rezultând imaginea din Figura 6.
Pentru eliminarea obiectelor care au mai puțin de un gol aplicam numărul lui
Euler și obținem o singura componentă (Figura 5). Acest pas s-a dovedit foarte
important deoarece elimină foarte multe procesări inutile pentru restul componentelor.
Fără acest pas restul algoritmilor ar trebui aplicați pe încă 8 componente, ceea ce ar
marii mult timpul de rurale ar programului. În consolă se pot observa numărul de găuri
pentru fiecare componentă, de sus în jos. Pe de alta parte, vom observa în exemplele
următoare ca acest pas elimină și multe componente care chiar conțin o față. Acest lucru
se întâmplă în cazurile în care ochii sau gura au un likelihood asemănător pielii și nu
pot fi diferențiate de aceasta.

12
Capitolul 6. Testare și validare

Funcția template
Matching selectează
fiecare componentă pe
rând și modifică template-
ul în funcție de aceasta.
Putem observa
măsurătorile componentei
în consolă, aceasta are o
lungime de 234 de pixeli și
o lățime de 105 pixeli. Cu Figură 10
Figură 10
un aspect ratio de 2.2 (care
aparține intervalului căutat 1- Figură 10
3.5) și un unghi de -17 grade. Template-ul
modificat poate fi observat in Figura 10.
Template-ul este suprapus peste
imaginea originală și, pentru a observa zona
analizată, am înlocuit pixelii în imaginea
originală cu pixeli albi. Astfel, putem observa
eroarea în contextul imaginii actuale: din cauza
bluzei fără gat, gatul este considerat parte din
față, ceea ce rezultă într-o diferență a pixelilor
prea mare, deci nu este detectată nici o față.

Figură 11 Figură 10 Figură 9

A doua imagine de test conține doua persoane (Figura 11). Figura 10 reprezintă
likelihood-ul. În Figura 9 se află imaginea după binarizarea adaptivă, unde se observă
că toate zonele cu piele au rămas.

13
Capitolul 6. Testare și validare

Figură 14 Figură 13 Figură 12

După aplicarea operaților de dilatare și eroziune obținem imaginea din Figura


14. Prin aplicarea funcției de etichetare obținem nou componente care se afișează cu
cate o culoare diferita rezultând imaginea din Figura 13.
Pentru eliminarea obiectelor care au mai puțin de un gol aplicăm numărul lui
Euler și obținem doua componente (Figura 12). În consolă se pot observa numărul de
găuri pentru fiecare componenta, de sus în jos. Au fost găsite 5 componente la pasul de
etichetare, dintre care numai două componente conțin cel puțin un gol, componenta cu
label 1 și componenta cu label 2.

Funcția template Matching selectează fiecare componentă pe rând și modifică


template-ul în funcție de aceasta. Putem observa rotirea template-ului în cazurile
următoare și detectarea a două fețe în imaginea finală.

14
Capitolul 6. Testare și validare

Alte rezultate experimentale:

15
Capitolul 6. Testare și validare

16
Capitolul 7. Concluzii

Detectarea feței este un proces non-invaziv și rapid, care permite identificarea unei
fețe dintr-o imagine și localizarea acesteia. În plus, sistemul pe care l-am dezvoltat
poate detecta mai multe fețe în același timp. Beneficiile aduse de dezvoltarea acestei
aplicații sunt multiple, câteva dintre ele fiind următoarele:
-Identificarea automată a fețelor într-o imagine, ceea ce poate fi util în domenii
precum securitatea, monitorizarea și recunoașterea facială.
-Eficientizarea procesului de căutare a fețelor, prin eliminarea nevoii de a căuta
manual într-o imagine.
-Oportunitatea de a îmbunătăți performanța aplicațiilor de realitate augmentată
și de îmbunătățire a imaginii, prin detectarea precisă a fețelor.
Prin urmare, proiectul de detectare a feței bazat pe modelul LAB are potențialul
de a aduce contribuții semnificative în domeniul detecției feței și în domeniile conexe,
ajutând la îmbunătățirea eficienței și preciziei procesului de detecție facială.

17
BIBLIOGRAFIE

[1] http://www-cs-students.stanford.edu/~robles/ee368/skinsegment.html.

[2] Tiberiu Marita, Interactiune Om-Calculator-Notițe de curs,


https://users.utcluj.ro/~tmarita/HCI/HCICurs.htm

[3] Cai, J., & Goshtasby, A. (1999). Detecting human faces in color images. Image and
Vision Computing, 18(1), 63-75.

[4] Cotton, S., Claridge, E., & Hall, P. (1999, July). A skin imaging method based on a
colour formation model and its application to the diagnosis of pigmented skin lesions.
In Proceedings of Medical Image Understanding and Analysis (Vol. 99, pp. 49-52).
Oxford: BMVA.

[5] P. Viola and M. Jones. Rapid object detection using a boosted cascade of simple
features. In Proceed-ings of the 2001 IEEE Computer Society Conference on Computer
Vision and Pattern Recognition.CVPR 2001, volume 1, pages 511–518 vol.1, 20(14)

[6] Hjelmås, E., & Low, B. K. (2001). Face detection: A survey. Computer vision and
image understanding, 83(3), 236-274.

[7] Wang, T., Bu, J. J., & Chen, C. (2003). A color based face detection system using
multiple templates. Journal of Zhejiang University-SCIENCE A, 4(2), 162-165

[8] J. Cai & A. Goshtasby & C. Yu, Detecting Human Faces in Color Images, Wright
State University, U. of Illinois.

18
Anexa 1

Anexa 1

// -----------------------------------PROIECT----------------------------

Mat processAndDisplayImages(const std::string& directory, int numSamples,


Mat a, Mat b) {

Mat samples[15]; //numSamples

for (int i = 1; i <= numSamples; ++i) {


std::string filename = directory + std::to_string(i) + ".JPG";
cout<< filename << endl;
// Read the image
samples[i] = imread(filename, IMREAD_COLOR);
//imshow(to_string(i), samples[i]);
//waitKey(0);
}

int height = a.rows;


int width = a.cols;

// 1. Split the sampels by the Lab color space


// Save the two chanels into 2 vectors (we don't need the lightness
channel)
Mat a_channel[15];
Mat b_channel[15];
for (int i = 1; i <= numSamples; i++) {
std::vector<cv::Mat> channels;
cv::split(samples[i], channels);
cv::Mat L_channel = channels[0];
a_channel[i] = channels[1];
b_channel[i] = channels[2];
}

int histGlobabalaA[260] = { 0 };
int histGlobabalaB[260] = { 0 };

for (int i = 1; i <= numSamples; i++) {


for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
int value = a_channel[i].at<uchar>(k, l);
histGlobabalaA[value] ++;
}
}
}

19
Anexa 1

for (int i = 1; i <= numSamples; i++) {


for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
int value = b_channel[i].at<uchar>(k, l);
histGlobabalaB[value]++;
}
}
}

double meanA = 0 ,meanB = 0;

for (int i = 0; i <= 255; i++) {


meanA += i*histGlobabalaA[i];
meanB += i*histGlobabalaB[i];
}

meanA = (double) meanA / (numSamples * 16 * 16);


meanB = (double) meanB / (numSamples * 16 * 16);
printf("\nmeanA: %f meanB: %f", meanA, meanB);

// 2. compute the expection (miu) for both channels for all the samples'
pixels:
double ma = 0;
for (int i = 1; i <= numSamples; i++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
ma += a_channel[i].at<uchar>(k, l);}}}
ma = ma / (numSamples * 16 * 16);

double mb = 0;
for (int i = 1; i <= numSamples; i++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
mb += b_channel[i].at<uchar>(k, l);}}}
mb = mb / (numSamples * 16 * 16);

printf("\nma: %f mb: %f\n", ma, mb);

// 3. DEviatia standard:
double devA = 0, devB = 0;
for (int g = 0; g <= 255; g++) {
devA += (g- meanA)*(g-
meanA)*histGlobabalaA[g]/(numSamples * 16 * 16);
}
double devA2 = devA;
devA = sqrt(devA);

for (int g = 0; g <= 255; g++) {

20
Anexa 1

devB += (g - meanB) * (g - meanB) * histGlobabalaB[g] /


(numSamples * 16 * 16);
}
double devB2 = devB;
devB = sqrt(devB);

printf("deviata A:%f deviatia B:%f\n", devA2, devB2);

Mat C = Mat(2, 2, CV_64FC1);

double sum = 0;
for (int i = 1; i <= numSamples; i++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
int x = a_channel[i].at<uchar>(k, l);
sum += (x - ma) * (x - ma);
}
}
}
sum = sum / 16 / 16 / numSamples;
C.at<double>(0, 0) = sum;

//Covariance
sum = 0;
for (int i = 1; i <= numSamples; i++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
int x = b_channel[i].at<uchar>(k, l);
sum += (x - mb) * (x - mb);
}
}
}
sum = sum / 16 / 16 / numSamples;
C.at<double>(1, 1) = sum;
C.at<double>(0, 1) = 0;
C.at<double>(1, 0) = 0;

printf("(0,0):%f (1,1) %f", C.at<double>(0, 0), C.at<double>(1,


1));

/*
Mat Cinv = Mat(2, 2, CV_32FC1);
float determinant = 1 / (C.at<float>(1, 1) * C.at<float>(0, 0) -
C.at<float>(0, 1) * C.at<float>(1, 0));
Cinv.at<float>(0, 0) = determinant * (C.at<float>(1, 1));
Cinv.at<float>(1, 0) = -determinant * (C.at<float>(1, 0));
Cinv.at<float>(0, 1) = -determinant * (C.at<float>(1, 0));
Cinv.at<float>(1, 1) = determinant * (C.at<float>(0, 0));

21
Anexa 1

printf("\n%f %f %f %f", Cinv.at<float>(0, 0), Cinv.at<float>(0, 1),


Cinv.at<float>(1, 0), Cinv.at<float>(1, 1));

Mat Cinv = Mat(2, 2, CV_64FC1);


Cinv.at<double>(0, 0) = 1/(C.at<double>(1, 1));
Cinv.at<double>(1, 0) = 0;
Cinv.at<double>(0, 1) = 0;
Cinv.at<double>(1, 1) = 1/(C.at<double>(0, 0));
*/

Mat dst = a.clone();


float max = -1000;
float min = 300;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
float valA = a.at<uchar>(i, j);
float valB = b.at<uchar>(i, j);
//printf(" %f, %f ", valA, valA);
float mat[2];
mat[0] = (valA - ma);
mat[1] = (valB - mb);
//printf(" %f, %f ", mat[0], mat[1]);
float value = (mat[0] * mat[0] + mat[1] * mat[1]); // ->
euler
// float value = (mat[0] * mat[0] / devB2 + mat[1] * mat[1]
/ devA2);
//float value = (Cinv.at<double>(0,0)*mat[0]*mat[0] +
Cinv.at<double>(1, 1) * mat[1] * mat[1] + 2 * Cinv.at<double>(0, 1) * mat[0] * mat[1]
);
//printf(" %f ", exponent);
double result = sqrt(value);
//printf(" %f ", result);
dst.at<uchar>(i, j) = (uchar)result;
if (max < result) max = result;
if (min > result) min = result;

}
}

// 0-1 -> 0 ->255


printf("\n max Value: %f min Value : %f", max, min);
cout << endl << "3.-------------------------------------------------------" <<
endl;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
dst.at<uchar>(i, j) = (dst.at<uchar>(i, j)-min)/(max -
min)*255 ;
}
}

22
Anexa 1

Mat dst2 = Mat(height, width, CV_8UC1);


for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
uchar val = dst.at<uchar>(i, j);
uchar neg = 255 - val;
dst2.at<uchar>(i, j) = neg;
}
}

return dst2;
}

Mat Binarizare(Mat src)


{
int L = 255;
Mat dst = Mat (src.rows,src.cols, CV_8UC1);

int height = src.rows;


int width = src.cols;

int imax = 0, imin = 300;


for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int p = src.at<uchar>(i, j);
if (imax < p) imax = p;
if (imin > p) imin = p;
}
}
float trashhold = (imax + imin) / 2;
float lastT = 0;
while ((trashhold - lastT) > 0.1) {
//printf("%f ", trashhold - lastT);
float medMin = 0, medMax = 0;
int x = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
uchar p = src.at<uchar>(i, j);
if (p < trashhold) {
medMin += p;
x++;
}
else medMax += p;
}

23
Anexa 1

}
medMin = medMin / x;
medMax = medMax / (height * width - x);
lastT = trashhold;
trashhold = (medMax + medMin) / 2;
}

for (int i = 0; i < height; i++)


{
for (int j = 0; j < width; j++)
{
uchar p = src.at<uchar>(i, j);
if (p < trashhold) {
dst.at<uchar>(i, j) = 0;
}
else {
dst.at<uchar>(i, j) = 255;
}
}
}

return dst;
}

int** labels; // 0=> fundal // 1->label => complonenta


int label;

Mat LabelingBreadthFirstTraversal(Mat src) {


Mat dst(src.rows, src.cols, CV_8UC3);
int height = src.rows;
int width = src.cols;

label = 0;
labels = (int**)calloc((height + 1), sizeof(int*)); /* GLOBAL
VARIABLE*/
for (int i = 0; i < height + 1; i++) {
labels[i] = (int*)calloc((width + 1), sizeof(int));
}

int di[8] = { -1, 0, 1, 0, -1, -1, 1, 1 };


int dj[8] = { 0, -1, 0, 1, 1, -1, 1, -1 };

for (int i = 0; i < height; i++)


{
for (int j = 0; j < width; j++)
{
uchar pixel = src.at<uchar>(i, j);
if (pixel == 255 && labels[i][j] == 0) {
//pixel == white
label++;

24
Anexa 1

std::queue<Point2i> Q;
labels[i][j] = label;
Q.push(Point2i(i, j));
while (!Q.empty()) {
Point2i q = Q.front();
Q.pop();
for (int k = 0; k < 8; k++) { // pt fiecare
din cei 8 vecini
Point n = Point(q.x + di[k], q.y +
dj[k]);
if (isInside(src, n.x, n.y) == true)
{
uchar neighbors =
src.at<uchar>(n.x, n.y);
if (neighbors == 255 &&
labels[n.x][n.y] == 0) { // color neighbor
labels[n.x][n.y] =
label;
Q.push(n);
}
}
}
}
}
}
}

std::vector<Vec3b> colors;
std::default_random_engine eng;
std::uniform_int_distribution<int> d(0, 255);

// cate o culoare pentru fiecare label:


for (int i = 0; i <= label; i++)
colors.push_back(Vec3b(d(eng), d(eng), d(eng)));

for (int i = 0; i < height; i++)


{
for (int j = 0; j < width; j++)
{
if (labels[i][j] != 0)
dst.at<Vec3b>(i, j) = colors[labels[i][j]];
}
}

printf("\n componente After labeling: %d \n", label);


cout << endl << "6.-------------------------------------------------------" <<
endl;
//imshow("5. Etichetare: ", dst);
return dst;

25
Anexa 1

bool Label1Holes(Mat src, int label) {

vector<vector<Point> > contours;


vector<Vec4i> hierarchy;

findContours(src.clone(), contours, hierarchy, CV_RETR_TREE,


CV_CHAIN_APPROX_NONE);

Mat singleLevelHoles = Mat::zeros(src.size(), src.type());


Mat multipleLevelHoles = Mat::zeros(src.size(), src.type());

for (vector<Vec4i>::size_type idx = 0; idx < hierarchy.size(); ++idx)


{
if (hierarchy[idx][3] != -1)
drawContours(singleLevelHoles, contours, idx,
Scalar::all(255), CV_FILLED, 8, hierarchy);
}

bitwise_not(src, src);
bitwise_and(src, singleLevelHoles, multipleLevelHoles);

findContours(multipleLevelHoles, contours, hierarchy,


CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
// Filter contours (remove the outer boundary)

// Count the number of holes


int numberOfHoles = contours.size();

std::cout << "Number of holes: " << numberOfHoles <<" OF Label :


"<< label<< std::endl;

//Inverse source image.


//imshow("Result0.jpg", src);

//Holes before the bitwise AND operation.


//imshow("Result1.jpg", singleLevelHoles);

//Holes after the bitwise AND Operation.

//imshow("Result2.jpg", multipleLevelHoles); // !!!!!!!!!!!!


bool result = false;
if (numberOfHoles >=1) result = true;

return result;
}

26
Anexa 1

Mat eulerEtichete(Mat src){


bool* goodLabel = (bool*) calloc(label,sizeof(bool));
// pentru reetichetare:
int nrLabel = 0;
int newLabel[1000];
for (int i = 1; i <= label; i++) {
Mat comp = src.clone();
for (int x = 0; x < src.rows; x++) {
for (int y = 0; y < src.cols; y++) {
if (labels[x][y] != i) {
comp.at<uchar>(x, y) = 0;
}
}
}

/*std::stringstream ss;
ss << i;
std::string stringValue = ss.str();
imshow(stringValue, comp);*/

goodLabel[i]= Label1Holes(comp, i);


if (goodLabel[i])
{
nrLabel++;
newLabel[i] = nrLabel;
}
}
Mat dst = src.clone();
for (int x = 0; x < src.rows; x++) {
for (int y = 0; y < src.cols; y++) {
if (!goodLabel[labels[x][y]]) {
dst.at<uchar>(x, y) = 0; // eliminam componenta
labels[x][y] = 0; // devine fundal
}
else {
labels[x][y] = newLabel[labels[x][y]];
}
}
}
label = nrLabel;
cout << "componente after Euler:" << label << endl;

//Afisare noua etichetare:


/*
std::vector<Vec3b> colors;
std::default_random_engine eng;
std::uniform_int_distribution<int> d(0, 255);

// cate o culoare pentru fiecare label:


for (int i = 0; i <= label; i++)

27
Anexa 1

colors.push_back(Vec3b(d(eng), d(eng), d(eng)));

Mat dst2 = Mat(src.rows, src.cols, CV_8UC3);


for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
if (labels[i][j] != 0)
dst2.at<Vec3b>(i, j) = colors[labels[i][j]];
}
}
imshow("5. Etichetare 2: ", dst2);*/

imshow("After euler:", dst);


return dst;

//-------------------Axe:

struct ElongationResults {
double majorAxis;
double minorAxis;
double angle; // unghi aza de alongatie
int ri; //rows pentru centrul de greutate
int ci; // cols ----
};

ElongationResults calculateElongations(Mat binaryImage) {

ElongationResults result;

std::vector<std::vector<cv::Point>> contours;
cv::findContours(binaryImage, contours, cv::RETR_EXTERNAL,
cv::CHAIN_APPROX_SIMPLE);

// Find the minimum area bounding rectangle for the largest contour
(assumed to be the object)
cv::RotatedRect boundingBox = cv::minAreaRect(contours[0]);

// Extract major and minor axes lengths


double majorAxis = max(boundingBox.size.width,
boundingBox.size.height);
double minorAxis = min(boundingBox.size.width,
boundingBox.size.height);

cout << endl << "Major axis: " << majorAxis << " Minor axis: " <<
minorAxis << endl;

28
Anexa 1

cout << endl << "AR: " << majorAxis / minorAxis<<endl;


float ar = majorAxis / minorAxis
result.majorAxis = majorAxis;
result.minorAxis = minorAxis;
return result;
}

ElongationResults Axa(Mat src) {


int height = src.rows;
int width = src.cols;

// axe:
ElongationResults result = calculateElongations(src);

Mat dst = src.clone();


int ci = 0, ri = 0, surface = 0; //centre de masa
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
if (dst.at<uchar>(i,j)==255) { // pixel obiect
ci = ci + j;
ri = ri + i;
surface++;
}
}
ri = ri / surface;
ci = ci / surface;
result.ri = ri;
result.ci = ci;

// 3.2.3.Extragerea conturului mainii / fetei


Labeling("Contur", dst, false);

// 3.2.4. Desenarea axei de alungire a mainii /fetei


float aux1 = 0, aux2 = 0, aux3 = 0, slope;
for (int i = 0; i < dst.rows; i++)
for (int j = 0; j < dst.cols; j++)
if (255 == dst.at<uchar>(i, j))
{
aux1 = aux1 + (i - ri) * (j - ci);
aux2 = aux2 + (j - ci) * (j - ci);
aux3 = aux3 + (i - ri) * (i - ri);
}

slope = atan2((2 * aux1), (aux2 - aux3)) / 2.0;


result.angle = slope;

// SHOW:
printf("%f", slope);

29
Anexa 1

//line(dst, Point(ci, ri), Point((int)(ci + 300 * cos(slope)), (int)(ri + 300 *


sin(slope))), Vec3b(100, 100, 0));
//line(dst, Point(ci, ri), Point((int)(ci + 300 * cos(slope + PI)), (int)(ri +
300 * sin(slope + PI))), Vec3b(100, 100, 0));
//imshow("Slope", dst);

return result;
}

// -----------------TeamplateMatching:

int THRESHOLD_FACE = 70;

void templateMatching(Mat src, Mat originalImage, Mat color) {


// pentru fiecare label:
cout << endl << "7. 8. -------------------------------------------------------"
<< endl;
std::string filename =
"D:\\an4\\IOC\\proiect\\sampleset\\average_face_clipped.jpg";

Mat fin = src.clone();

for (int l = 1; l <= label; l++) {


cout << endl << "LABEL: " << l << endl;
// A. verificam 7. Calcul factor de alungire (aspect ratio - AR)
Mat fata = src.clone();
for (int x = 0; x < src.rows; x++) {
for (int y = 0; y < src.cols; y++) {
if (labels[x][y] != l) {
fata.at<uchar>(x, y) = 0;
}
}
}

ElongationResults result = Axa(fata);


double AR = result.majorAxis / result.minorAxis;
if (1 <= AR && AR <= 3.5) {
//PRINT:
std::stringstream ss;
ss << l;
std::string stringValue = ss.str();
imshow(stringValue, fata);
cout << endl<< "Aspect ratio:" << AR << endl;

// B. verificam 8. Template matching


// 8.1 Deschidem template-ul

30
Anexa 1

Mat templateFace = imread(filename,


IMREAD_GRAYSCALE);

cout << "dsfdsf";


// 8.2. Facem resize la template:
cv::resize(templateFace, templateFace,
cv::Size(result.minorAxis, result.majorAxis));
//imshow("face template resize:", templateFace);
cout << "dsfdsf";
// 8.3. Facem rotire la template:
double angle = -90 + abs(result.angle / PI) * 180;
cout << endl << "angle in degrees: " << angle << endl;
Size size;
size.height = templateFace.rows;
size.width = templateFace.rows;
Mat temp = Mat(size.height, size.width, CV_8UC1);

for (int i = 0; i < size.height; i++)


for (int j = 0; j < size.height; j++)
temp.at<uchar>(i, j) = 0;

for (int i = 0; i < size.height; i++) {


int y = 0;
for (int j = (size.height - templateFace.cols) / 2; j
< (size.height + templateFace.cols) / 2; j++) {
temp.at<uchar>(i, j) =
templateFace.at<uchar>(i, y);
y++;
}
}

cv::Point2f center(static_cast<float>(temp.cols / 2),


static_cast<float>(temp.rows / 2));
cv::Mat rotationMatrix =
cv::getRotationMatrix2D(center, angle, 1.0);
cv::warpAffine(temp, temp, rotationMatrix,
temp.size());
std::string stringValue2 = "Face rotation for label:" +
ss.str();
imshow(stringValue2, temp);

// 8.4. Suprapunem template-urile:


// avem centrul fetelor in result și cunoastem centrul
templateului:
int c = size.height / 2;
double medie = 0;
int nr = 0;
for (int i = 0; i < size.height; i++) {
for (int j = 0; j < size.height; j++) {
uchar pixelTemp = temp.at<uchar>(i, j);

31
Anexa 1

if (pixelTemp != 0 &&
isInside(originalImage, i + result.ri - c, j + result.ci - c)) { // pixelTEmp !=0 => parte
din fata
uchar pixelImg =
originalImage.at<uchar>(i + result.ri - c, j + result.ci - c);
originalImage.at<uchar>(i +
result.ri - c, j + result.ci - c) = 255;
double diference = abs(pixelTemp
- pixelImg);
medie += diference;
nr++;
}
}
}
imshow("final", originalImage);
medie = (double)medie / nr;
printf("\n Diferenta intensitatilor: %f\n", medie);
if (medie < THRESHOLD_FACE) {
cout << "BUN!!";
DrawCross(color, Point(result.ci, result.ri), 9,
Scalar(0, 0, 255), 1);
}
else {
cout << "RAU!!";
}
}
}

imshow("final Image color", color);


}

void test() {
char fname[MAX_PATH];
while (openFileDlg(fname))
{
Mat image = imread(fname, IMREAD_COLOR);
//imshow("input image", image); // !!!!!!!!!!!!
int height = image.rows;
int width = image.cols;
Mat imgLAB = Mat(height, width, CV_8UC3);
// Post. Aplicare FTJ gaussian pt. eliminare zgomote: essential
sa il aplicati
//GaussianBlur(image, image, Size(5, 5), 0, 0);

// 1. Construirea unui model de culoare pentru piele: Lab + 2.


Segmentarea zonelor
cv::cvtColor(image, imgLAB, CV_BGR2Lab);
// Split LAB image into L, a, and b channels
std::vector<cv::Mat> channels;

32
Anexa 1

cv::split(imgLAB, channels);
cv::Mat L_channel = channels[0];
cv::Mat a_channel = channels[1];
cv::Mat b_channel = channels[2];
//imshow("A:", a_channel); imshow("B:", b_channel);
Mat likelihood =
processAndDisplayImages("D:\\an4\\IOC\\proiect\\sampleset\\", 13, a_channel,
b_channel);
imshow("1+2. Likelihood Image:", likelihood); // !!!!!!!!!!!!

// 3. Binarizare adaptiva
Mat binarizare = Binarizare(likelihood);
double threshold = 180;
imshow("3. Binarizare adaptiva:", binarizare); // !!!!!!!!!!!!

// 4. Operati morfologice
Mat element = getStructuringElement(MORPH_RECT, Size(3,
3));

erode(binarizare, binarizare, element, Point(-1, -1), 3);


dilate(binarizare, binarizare, element, Point(-1, -1), 2);
//imshow("Dilate:", binarizare);
imshow("4. Operati morfologice:", binarizare);

// 5. Etichetare:
Mat etich = LabelingBreadthFirstTraversal(binarizare);
imshow("5. Etichetare: ", etich);

// 6. Calcul nr. Euler


binarizare = eulerEtichete(binarizare);

// 7. Calcul factor de alungire (aspect ratio - AR) + 8. Template


matching
Mat gray;
cv::cvtColor(image, gray, CV_BGR2GRAY);
templateMatching(binarizare, gray, image);

waitKey();
}
}

33

S-ar putea să vă placă și