1
UNIVERSITATEA „ALEXANDRU IOAN CUZA” IAŞI
FACULTATEA DE INFORMATICĂ
LUCRARE DE LICENŢĂ
PaintCam – program de desenare prin
intermediul camerei web
propusă de
Preisler Raluca
Sesiunea: iulie, 2016
Coordonator ştiinţific
Asist. Dr. Vasile Alaiba
2
UNIVERSITATEA „ALEXANDRU IOAN CUZA” IAŞI
FACULTATEA DE INFORMATICĂ
PaintCam – program de desenare prin
intermediul camerei web
Preisler Raluca
Sesiunea: Iunie-Iulie, 2016
Coordonator științific
Asist. Dr. Vasile Alaiba
3
DECLARAŢIE PRIVIND ORIGINALITATEA ŞI RESPECTAREA
DREPTURILOR DE AUTOR
Prin prezenta declar că Lucrarea de licenţă cu titlul „PaintCam – program de
desenare prin intermediul camerei web” este scrisă de mine şi nu a mai
fost prezentată niciodată la o altă facultate sau instituţie de învăţământ
superior din ţară sau străinătate. De asemenea, declar că toate sursele
utilizate, inclusiv cele preluate de pe Internet, sunt indicate în lucrare, cu
respectarea regulilor de evitare a plagiatului:
- toate fragmentele de text reproduse exact, chiar şi în traducere proprie
din altă limbă, sunt scrise între ghilimele şi deţin referinţa precisă a
sursei;
- reformularea în cuvinte proprii a textelor scrise de către alţi autori
deţine referinţa precisă;
- codul sursă, imaginile etc. preluate din proiecte open-source sau
alte surse sunt utilizate cu respectarea drepturilor de autor şi deţin
referinţe precise;
- rezumarea ideilor altor autori precizează referinţa precisă la textul original.
Iaşi, Absolvent
Preisler Raluca
4
DECLARAŢIE DE CONSIMŢĂMÂNT
Prin prezenta declar că sunt de acord ca Lucrarea de licență cu titlul
„PaintCam – program de desenare prin intermediul camerei web”, codul
sursă al programelor şi celelalte conţinuturi (grafice, multimedia, date de test
etc.) care însoţesc această lucrare să fie utilizate în cadrul Facultăţii de
Informatică.
De asemenea, sunt de acord ca Facultatea de Informatică de la Universitatea
„Alexandru Ioan Cuza” Iași să utilizeze, modifice, reproducă şi să
distribuie în scopuri necomerciale programele-calculator, format executabil
şi sursă, realizate de mine în cadrul prezentei lucrări de licenţă.
Iaşi,
Absolvent
Preisler Raluca
5
Cuprins
Introducere ....................................................................................................................................... 6
Despre Computer Vision ............................................................................................................. 6
Obiectivele lucrării ...................................................................................................................... 7
Motivație ...................................................................................................................................... 8
Contribuții .................................................................................................................................... 9
Structura lucrării ........................................................................................................................ 10
Capitolul 1 Arhitectura Aplicației și tehnologii folosite ............................................................... 11
Arhitectura aplicației .................................................................................................................. 11
Tehnologii folosite ..................................................................................................................... 14
Java SE și pachetul JavaFX .................................................................................................... 14
Biblioteca OpenCV ................................................................................................................ 16
Clase și funcții OpenCV folosite ............................................................................................ 16
Capitolul 2 Recunoaşterea mâinii și numărarea degetelor ............................................................ 19
1. Filtrarea culorii pielii .......................................................................................................... 19
2. Găsirea conturului mâinii ................................................................................................... 24
3. Numărarea degetelor........................................................................................................... 27
Capitolul 3 Desenarea .................................................................................................................... 31
Desenarea normală ..................................................................................................................... 31
Combinarea desenului cu poza .................................................................................................. 32
Capitolul 4 Interfața Grafică cu Utilizatorul.................................................................................. 36
Concluzii ........................................................................................................................................ 39
Referințe bibliografice ................................................................................................................... 40
6
Introducere
Despre Computer Vision
Motto:
„Ochii văd numai ceea ce mintea este pregatită să înțeleagă.”
– Robertson Davies
Citatul romancierului canadian Robertson Davies a fost folosit probabil pentru prima oară
cu sens metaforic, însă capătă o însemnătate literală în contextul domeniului Computer Vision.
Oamenii se folosesc de simțul văzului pentru a sesiza obiectele, culorile și formele din
mediul înconjurător. Imaginea este întâi percepută cu ajutorul ochiului și apoi transmisă mai
departe pe căile optice către creier, care o decodează. Domeniul Computer Vision își propune să
reproducă sau chiar să întreacă această abilitate umană cu ajutorul instrumentelor software și
hardware.
Computer Vision (în traducere Vedere Automată) este definit drept un domeniu care
include metode de dobândire, procesare, analiză și înțelegere a imaginilor sau a datelor de mari
dimensiuni din lumea înconjurătoare, cu scopul de a produce informații numerice sau simbolice,
de exemplu sub forma unor decizii[1]. Computer Vision se folosește de cunoștințe de
informatică, inginerie electrică, matematică, fiziologie, biologie și stiințe cognitive pentru a
înțelege și a simula funcționarea sistemului vizual uman.
Este un fapt general acceptat că părintele domeniului Computer Vision este Larry
Roberts, care, în teza sa de doctorat la MIT (circa 1960), discuta posibilitățile de extragere a
informațiilor geometrice 3D pornind de la vederi 2D în perspectivă ale poliedrelor. Mai mulți
cercetători în Inteligență artificială i-au continuat munca, întâi în contextul poliedrelor, iar mai
apoi folosind imagini din lumea reală [2].
Astăzi, Computer Vision își găsește aplicații în domenii precum recunoașterea optică a
caracterelor, realitate augmentată sau robotică.
7
Computer Vision se folosește de domenii precum procesarea imaginilor (image
processing) și învățarea automată (machine learning), precum și de metode matematice.
În știința imagistică, image processing reprezintă procesarea imaginilor cu ajutorul
operațiilor matematice, folosindu-se orice formă de prelucrare a semnalului. Output-ul operației
poate fi fie o imagine, fie un set de caracteristici sau parametri legați de imagine.
Învățarea automată este un domeniu de studiu care oferă calculatoarelor capacitatea de a
învăța fără a fi programate în mod explicit, după cum scrie Arthur Samuel în 1959 [3]. Machine
learning explorează studiul și construirea de algoritmi care pot învăța și a face predicții pe baza
unor date[4].
O problemă fundamentală cu care se ocupă domeniul Computer Vision este de a identifica
și separa diversele obiecte care apar într-o imagine. Unul sau mai multe obiecte sau clase de
obiecte pre-specificate pot fi recunoscute, împreună cu poziția lor 2D în imagine sau cu poziția
lor în cadrul scenei 3D. Această sarcină, denumită generic object recognition, stă la baza
aplicației noastre.
Obiectivele lucrării
Lucrarea de față prezintă procesul de dezvoltare a aplicației PaintCam, aplicație de tip
Image Processing dezvoltată pe platforma Java SE (Standard Edition) și cu ajutorul unei
biblioteci special create pentru domeniul Computer Vision, OpenCV.
În această lucrare voi prezenta un background teoretic referitor la tehnologiile folosite, voi
descrie arhitectura și modul de utilizare a aplicației și voi detalia pașii urmați în crearea acesteia,
detaliind pentru fiecare pas metodele folosite și considerațiile practice și teoretice.
8
Motivație
Lucrarea își propune să exploreze și să aducă în atenție posibilitățile oferite de domeniul
Computer Vision, prin biblioteca OpenCV, oferind de asemenea o aplicație interactivă și
interesantă pentru utilizator.
Aplicația PaintCam permite desenarea prin intermediul camerei web, mai exact cu
ajutorul mâinii care se mișcă în aer. Diverse setări precum culoarea imaginii sau dimensiunea
peniței pot fi ajustate pentru a obține o imagine creativă.
Ca fundal pentru desenul realizat de utilizator se poate folosio imagine goală (blank
image) sau un screenshot al fluxului video preluat de la camera web. Imaginile astfel obținute pot
fi salvate sau distribuite pe rețelele de socializare, constituind un prilej de distracție și
amuzament.
9
Contribuții
Aplicația prezentată în această lucrare a fost dezvoltată de mine, folosindu-mă de
biblioteca OpenCV. Aceasta se află sub incidentța unei licențe de tip BSD și poate fi folosită fără
restricții.
Mai exact, contribuțiile mele la această lucrare sunt:
ideea unui program de desenare prin intermediul camerei web;
metoda de detectare a mâinii și numărare a degetelor;
metoda de desenare;
interfața grafică cu utilizatorul (GUI);
clasa care realizează managementul imaginilor.
10
Structura lucrării
În capitolul I am prezentat arhitectura aplicației și o scurtă descriere a tehnologiilor
folosite, cu accent pe biblioteca OpenCV și cele mai folosite clase și funcții de Image Processing
ale acesteia.
În capitolul al II-lea am expus metoda folosită pentru recunoașterea mâinii și numărarea
degetelor. Am inclus de asemenea descrierile și sintaxele funcțiilor OpenCV folosite.
În capitolul al III-lea am prezentat cele două metode de desenare folosite: desenarea pe un
fundal gol, dar și desenarea pe o imagine.
În capitolul al IV-lea am alcătuit un scurt ghid de utilizare al programului, cu un exemplu
de folosire.
11
Capitolul 1 Arhitectura Aplicației și
tehnologii folosite
Arhitectura aplicației
Arhitectura Aplicației PaintCam este structurată pe trei nivele, ce vor fi prezentate prin
intermediul unor diagrame UML. Cele trei nivele sunt: Nivelul de prezentare al aplicației,
Nivelul de logică al aplicației, Nivelul de date al aplicației.
Nivelul de prezentare descrie modul în care un utilizator interacționează cu aplicația. În
acest scenariu, realizat printr-o diagramă de tip Use Case, se poate observa cum un actor
configurează aplicația, desenează în fața camerei web și primește rezultatul sub forma unor
imagini, pe care acesta poate decide dacă să le salveze sau nu.
Diagrama Use Case
12
La nivelul de logică este descris modul de funcționare al aplicației, prin intermediul unei
diagrame de stare. Etapa inițială de configurare este obligatorie, fie că utilizatorul decide să
păstreze setările implicite sau să le ajusteze. Aplicația preia fluxul video de la camera web și îl
analizează cadru cu cadru. Pentru fiecare cadru în parte, imaginea este prelucrată pentru a
identifica mâna utilizatorului și numărul de degete ridicate. În funcție de acest număr, aplicația
decide dacă se desenează sau nu. Prin intermediul butoanelor interfeței grafice, utilizatorul
ajustează culoarea, grosimea brush-ului sau începe o imagine nouă. Odată ce utilizatorul oprește
interfața de desenare, se deschide o alta, unde i se prezintă imaginile realizate.
Diagrama de stare a aplicației PaintCam
Nivelul de date al aplicației prezintă pachetele împreună cu clasele (cu cele mai
importante metode atribute) care fac posibilă funcționarea aplicației PaintCam, prin intermediul
unei diagrame de clase.
Pachetele sunt următoarele:
- core, pachetul de bază, cu subpachetele:
handRecognition – conține clasele care se ocupă cu identificarea mâinii dintr-o
imagine;
imageProcessing – se ocupă cu prelucrarea imaginii și cu extragerea anumitor
informații din aceasta;
13
utils – conține diverse clase ajutătoare, dar care nu participă în mod direct la
recunoașterea mâinii (exemplu: MathFunctions);
- mainWindow – conține clasa Main ce lansează interfața grafică cu utilizatorul(GUI);
- gui – conține clase ce servesc strict la redarea imaginilor prin intermediul interfeței
grafice;
- imgSetting – clase ce se ocupă cu obținerea setărilor legate de desenare prin instrumente
precum butoane sau glisoare.
Diagrama de Clase
14
Tehnologii folosite
Java SE și pachetul JavaFX
Limbajul de programare Java este un limbaj cu scop general, concurent, bazat pe clase și
orientat-obiect.[5] A fost creat la începutul anilor ’90 de către Sun Microsystems, acum parte a
Oracle Corporation, și lansat în 1995.
Limbajul Java se bazează pe principiul „scrie o dată, rulează oriunde” (în original: „write
once, run anywhere”), ceea ce înseamnă că un cod Java poate rula pe toate platformele care
suportă Java, fără a mai fi nevoie de recompilare.
Oracle furnizează 4 platforme Java: Java Card, pentru smartcard-uri (carduri cu cip), Java
Platform, Micro Edition (Java ME) pentru hardware cu resurse limitate precum telefoanele
mobile), Java Standard Edition (Java SE)— pentru sisteme gen workstation, este ceea ce se
găsește pe PC-uri, și Java Enterprise Edition (Java EE) — pentru sisteme de calcul mari, eventual
distribuite[6].
Limbajul de programare Java se remarcă prin următoarele caracteristici:
Independența de platformă: Compilatoarele Java compilers nu produc cod pentru o
anumită platformă, ci cod de octeți (engleză: byte-code), care este intermediar între codul mașină
(dependent de tipul calculatorului) și codul sursă.
Orientarea-obiect – Java este un limbaj pur orientat-obiect. Acest lucru înseamnă că orice
entitate dintr-un program Java este un obiect și toate descind din clasa rădăcină, Object.
Bibliotecă standard bogată – Una din cele mai atractive caracteristici ale limbajului Java
este biblioteca standard a acestuia. Mediul de programare Java include sute de clase și metode,
grupate în șase mari grupuri:
o Language Support - pentru caracteristici lingvistice avansate, cum ar fi șiruri de
caractere, tablouri, fire de execuție și de tartarea excepțiilor.
o Utility - generator de numere aleatoare, funcții pentru oră și dată, și clasele
container.
o Input/output – clase de intrare/ieșire pentru a scrie și a citi date dintr-o varietate de
surse.
o Networking – permite comunicarea printr-o rețea locală sau Internet.
15
o Abstract Window Toolkit pentru crearea interfețelor grafice cu utilizatorul (GUI)
independende de platformă.
o Applet este o clasă care permite dezvoltatorilor să creeze programe Java ce pot fi
descărcate și rulate în browserul unui client.
Sintaxă asemănătoare cu cea a limbajului C++, care a influențat adoptarea rapidă a
limbajului Java.
Garbage Collection - Java nu le cere programatorilor să elibereze explicit memoria
alocată în mod dynamic. Acest fapt face programele Java mai ușor de scris și mai puțin
susceptibile la erori legate de memorie. [7]
JavaFX este un set de instrumente grafice și pachete care permite dezvoltatorilor
proiectarea, crearea, depanarea și implementarea unor sisteme informatice ce implică
interacțiune cu utilizatorul, care operează în mod consistent pe diverse platforme.
Următoarele caracteristici sunt incluse în JavaFX 8:
Java API. JavaFX este o bibliotecă Java care cuprinde clase și interfețe scrise în Java.
Aceste API-uri sunt proiectate pentru a fi o alternativă accesibilă la limbajele de programare care
folosesc Java Virtual Machine (Java VM), precum JRuby sau Scala.
Limbajul FXML și utilitarul Scene Builder. FXML este un limbaj declarativ de adnotare
bazat pe HTML folosit pentru construirea interfețelor cu utilizatorul în JavaFX. Un designer
poate să scrie cod direct în FXML sau să folosească JavaFX Scene Builder pentru a proiecta
interfața grafică cu utilizatorul în mod interactiv.
Interoperabilitate cu tehnologia Swing. Aplicațiile Swing existente pot fi actualizate cu
funcționalități JavaFX. În JavaFX 8 este inclusă clasa SwingNode, care permite includerea
conținutului creat în Swing în aplicații JavaFX.
Canvas API. API-ul Canvas permite desenarea direct în cadrul unei zone a unei scene
JavaFX care consistă dintr-un singur element grafic(nod).
Built-in UI controls and CSS. JavaFX furnizează toate controalele necesare pentru a
dezvolta o interfață cu utilizatorul completă. Componentele pot fi particularizate folosind
tehnologii web standard precum CSS[8].
16
Biblioteca OpenCV
Modulul de recunoaștere a mâinii, fără de care aplicația PaintCam nu ar fi putut fi
disponibilă în forma actuală, se bazează pe funcții din biblioteca OpenCV (Open Source
Computer Vision). Aceasta este disponibilă sub incidența unei licențe BDS, care permite
utilizarea ei fără niciun fel de restricții.
OpenCV este o bibliotecă de funcții de vedere și învățare automată, cu interfețe pentru
limbajele de programare C, C++, Java și Python, și care suportă sistemele de operare Windows,
Linux, Mac OS, iOS și Android. OpenCV a fost creat pentru a oferi o infrastructură comună
pentru aplicațiile de Computer Vision și pentru a încuraja folosirea percepției automate(machine
perception) în cadrul produselor comerciale.
Biblioteca dispune de mai mult de 2500 algoritmi optimizați, care pot fi folosiți pentru a
detecta și recunoaște fețe, pentre a identifica obiecte, pentru a depista mișcarea camerei sau a
obiectelor, pentru a extrage modele 3D ale obiectelor, pentru a găsi imagini similare într-o bază
de date și multe altele. Biblioteca este utilizată pe scară largă în companii, grupuri de cercetare și
de organisme guvernamentale.
Clase și funcții OpenCV folosite
Când salvăm o imagine în format digital, ceea ce salvăm de fapt reprezintă o serie de
valori numerice numite pixeli. Un pixel este unitatea de bază a unei imagini digitale,
reprezentând o singură culoare sau nivel de luminozitate.
Ce vede omul
Ce vede camera (RGB)
(51, 64, 60), (73, 75, 56)...
(92, 88, 70)...
17
Modalitatea de stocare a pixelilor variază în funcție de nevoi, însă orice imagine din
lumea digitală poate fi redusă la o matrice de pixeli și alte informații referitoare la matrice.
În OpenCV, imaginile sunt accesate prin intermediul clasei Mat.
Clasa Mat este alcătuită din două părți: un antet(header) care conține informații precum
dimensiunea matricei, modalitatea de stocare, adresa la care e stocată matricea ș.a.md., și un
pointer către matricea ce conține valorile pixelilor. Mărimea antetului este constantă, însă
dimensiunea matricii variază de la imagine la imagine.
Un obiect de tip Mat poate stoca:
o imagine RGB (o matrice cu trei canale, R, G și B);
o imagine Grayscale (o matrice cu un singur canal ce stochează valori între 0 și 255) ;
un vector 2D complex (folosind tipul 32FC2) și multe altele.
Alte structuri de date comune din OpenCV, utilizate de asemenea în cadrul aplicației,
sunt :
CvPoint (int x, int y), folosit pentru puncte dintr-o imagine;
Size (int width, int height) reprezintă dimensiunea unei imagini;
Rect (int x, y, width, height), folosit pentru regiuni dreptunghiulare ;
Scalar (double v1, double v2, double v3, double v4), folosit pentru reprezentarea unei
culori.
OpenCV oferă și funcții de desenare de bază precum circle(), line(), ellipse(), rectangle().
18
OpenCV are o structură modulară, ceea ce înseamnă că pachetul include mai multe
biblioteci partajate sau statice. OpenCV oferă următoarele module:
core – un modul compact ce definește structurile de date de bază, inclusiv tabloul multi-
dimensional și funcțiile de bază folosite de celelalte module.
imgproc – un modul de procesare a imaginilor care include modalități de filtrare liniare și
non-liniare ale imaginilor, transformări geometrice ale imaginilor (redimensionare, deformare,
remapare), conversia spațiului de culori, histograme șamd.
video – un modul de analiză a secvențelor video care include estimarea mișcării (motion
estimation), eliminarea fundalului (background subtraction), și algoritmi de urmărire a obiectelor
(object tracking).
calib3d – algoritmi geometrici de bază, calibrarea camerei de tip single sau stereo,
estimarea poziției unui obiect, elemente de reconstrucție 3D șamd.
features2d – detectarea trăsăturilor dominante (salient feature detectors), descriptori.
objdetect – detectarea obiectelor și a instanțelor unor clase predefinite (de exemplu fețe,
ochi, oameni, mașini șamd).
highgui – o interfață ușor de folosit ce oferă funcții de captură codec-uri pentru imagine
sau video, la fel ca și funcționalități de bază pentru interfața cu utilizatorul.
gpu – algoritmi accelerați de GPU din diverse module OpenCV.
alte module auxiliare.
Modulele OpenCV folosite în cadrul aplicației sunt Core, ImgProc, Video și HighGui.
19
Capitolul 2 Recunoaşterea mâinii și
numărarea degetelor
Pentru a putea desena, avem nevoie în primul rând de comenzi, pe care utilizatorul le
transmite prin ridicarea unui anumit număr de degete și, după caz, prin poziționarea acestora într-
o anumită zonă. Cea mai dificilă sarcină cu care se ocupă programul nostru constă, de fapt, în
identificarea mâinii utilizatorului și numărarea degetelor ridicate. Pașii parcurși în acest scop sunt
următorii:
1. Filtrarea culorii pielii
2. Găsirea conturului mâinii
3. Numărarea degetelor
Acești pași sunt repetați pentru fiecare cadru preluat de la camera web.
1. Filtrarea culorii pielii
În mod implicit, imaginea preluată de la camera web prin intermediul bibliotecii OpenCV
este reprezentată în spațiul de culori BGR (Blue Green Red). Pentru a folosi mai eficient funcțiile
de image processing, am ales să convertim imaginea în spațiul de culori HSV (Hue, Saturation,
Value). Este important să înțelegem semnificația acestor noțiuni, întrucât de selectarea potrivită a
acestora depinde recunoașterea mâinii.
În spațiul HSV, o culoare este definită de trei elemente constituente:
Nuanța (Hue- H) unei culori se referă la culoarea pură. Toate tonurile de roșu au aceeași
valoare H.
Saturația (Saturation- S) unei culori descrie cât de mult alb conține aceasta. Albul are
saturația 0, o culoare pură de roșu are saturație maximă, iar tonurile de roșu au o valoare
intermediară.
Valoarea (Value- V) unei culori, numită și luminozitate, descrie cât de întunecată e
aceasta. Negrul are valoarea 0, aceasta crescând odată cu luminozitatea.
20
Diversele programe software folosesc gradații diferite pentru HSV. În OpenCV, intervalul
pentru Hue este[0, 179], pentru Saturation [0, 255], iar pentru Value [0, 255].
Funcția care realizează această conversie este următoarea:
Imgproc.cvtColor(source mat, destination mat1, Color_Conversion_Code);
Metoda cvtColor() primește trei parametri, care sunt: matricea imaginii sursă, matricea
imaginii destinație și tipul conversiei (aici, Imgproc.COLOR_BGR2HSV) .
În spațiul HSV e mult mai ușor să se reprezinte o culoare decât în spațiul BGR. Odată ce
am convertit imaginea din BGR în HSV, ne putem folosi de aceasta pentru a extrage un obiect
colorat. La acest pas, încercăm să extragem obiectele de culoarea mâinii din imagine. În acest
scop, ne folosim de funcția inRange, care verifică dacă elementele matricii sursă se află între
două valori date.Sintaxa funcției este următoarea:
public static void Core.inRange(Mat src, Scalar lowerb, Scalar upperb, Mat dst);
Parametri :
1. Mat src – Matricea sursă ;
2. Scalar lowerb – este un obiect de tip Scalar, alcătuit din 3 valori de tip double. Acestea
reprezintă valorile minime pe care le pot lua elementele H, S și V pentru ca pixelul
corespunzător să fie selectat. (Exemplu pentru culoarea pielii: Scalar minim = new Scalar (3, 100,
100));
3. Scalar upperb – similar cu lowerb, dar conține valorile maxime pe care elementele le
pot lua (Exemplu pentru culoarea pielii: Scalar minim = new Scalar (17, 255, 255));
4. Mat dst – Matricea destinație, în care salvăm output-ul.
După aplicarea funcției inRange(), adesea vom obține obiecte cu un contur nu foarte bine
definit, de aceaa este necesar să nivelăm imaginea obținută folosind funcțiile erode() și dilate(),
care îndepărtează, respectiv adaugă pixeli la conturul unui obiect.
Imgproc.erode(source, destination, element);
Imgproc.dilate(source, destination, element);
21
Ultimul parametru reprezintă un element structural utilizat pentru eroziune și dilatare,
dacă lucrăm pe un Mat () se folosește un element structural dreptunghiular 3x3.
Combinate, funcțiile de mai sus arată în felul următor:
public static Mat BGRtoHSV(Mat mImage, Scalar hsv_min, Scalar hsv_max){
Mat image=mImage.clone();
Mat HSVmat=new Mat();
Imgproc.cvtColor(image, HSVmat, Imgproc.COLOR_BGR2HSV);
Mat rezultat= new Mat();
Core.inRange(HSVmat, hsv_min, hsv_max, rezultat);
Imgproc.erode(rezultat, rezultat, Constants.procKernel);
Imgproc.dilate(rezultat, rezultat, Constants.procKernel);
image.release();
HSVmat.release();
return rezultat;
}
Rezultatul filtrării se poate vedea în imaginea următoare:
Rezultatul filtrării după culoare
După cum se vede, după aplicarea acestor funcții, încă nu am reușit să obținem obiecte
omogene, de aceea e nevoie să prelucrăm în continuare imaginea. Pentru început, aplicăm un
filtru gaussian care estompează imaginea:
22
GaussianBlur(Mat src, Mat dst, Size ksize, double sigmaX);
Parametrul size reprezintă dimensiunea kernel-ului Gaussian, iar parametrul sigmaX
deviația standard a acestuia în direcția X.
După aplicarea acestui filtru vom obține o imagine cu pixelii colorați în alb, negru și
diferite tonuri de gri. Avem nevoie să transformăm imaginea într-una binară, cu pixeli de culoare
albă sau neagră. Pixelii albi vor corespunde zonelor colorate cu culoarea pielii, iar cei negri
zonelor de alte culori.
Thresholding-ul este cea mai simplă metodă de segmentare a imaginilor, care separă
regiunile unei imagini în funcție de variația intensității dintre pixelii obiectului și cei din
background. Această metodă este oferită de OpenCV prin funcția threshold(),:
static double threshold(Mat src, Mat dst, double thresh, double maxval, int type)
Parametri :
1. Mat src – Matricea sursă;
2. Mat dst – Matricea destinație;
3. double thresh – valoarea, între 0 și 255, după care se face thresholding-ul;
4. double maxval – valoarea maximă folosită cu tipurile THRESH_BINARY and
THRESH_BINARY_INV;
5. int type - Tipul thresholding-ului pe care vrem să îl obținem: THRESH_BINARY,
THRESH_BINARY_INV, THRESH_TRUNC sau THRESH_TOZERO (aici: Imgproc.
THRESH_BINARY).
Tipul folosit în aplicație e THRESH_BINARY . Dacă intensitatea pixelului src(x, y) e
mai mare decât valoarea thresh, atunci pixelul primește intensitatea MaxVal. Altfel, pixelul
primește valoarea 0.
𝑑𝑠𝑡(𝑥, 𝑦) = {max 𝑉𝑎𝑙, 𝑑𝑎𝑐ă 𝑠𝑟𝑐(𝑥, 𝑦) > 𝑡ℎ𝑟𝑒𝑠ℎ
0, 𝑎𝑙𝑡𝑓𝑒𝑙
public static Mat colorThreshold(Mat mImage){
Mat image = mImage.clone();
Imgproc.GaussianBlur(image, image, new
23
org.opencv.core.Size(15,15), 5);
Imgproc.erode(image, image, Constants.procKernel);
Mat thresh = new Mat();
Imgproc.threshold(image, thresh, 80, 255, Imgproc.THRESH_BINARY);
return thresh;
}
După aplicarea acestor filtre, vom obține o imagine nouă, binară, în care rămân doar
obiectele de culoarea pielii din imaginea inițială.
Rezultatul operației de thresholding
24
2. Găsirea conturului mâinii
După ce filtrăm imaginea în funcție de culoarea pielii, se poate să rămânem cu zero, unul
sau mai multe obiecte (fața, mâna și altele). Dintre acestea, programul identifică drept mână
conturul cu aria cea mai mare.
Contururile dintr-o imagine sunt obținute grație funcției OpenCV findContours():
void findContours(InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode, int method, Point offset=Point())
Parametri:
1. InputOutputArray image – imaginea sursă, de tip single-channel (un singur număr este
folosit pentru a specifica culoarea fiecărui pixel – de exemplu, grayscale). În urma
operației inRange(), imaginea noastră este de tip single-channel. Funcția modifică
imaginea sursă.
2. OutputArrayOfArrays contours – contururile detectate. Fiecare contur e salvat ca
vector de puncte.
3. OutputArray hierarchy – un vector opțional ce conține informații despre topologia
imagiii. Numărul elementelor este egal cu numărul contururilor.
4. int mode – modul de obținere a contururilor. Există mai multe astfel de modalități:
CV_RETR_EXTERNAL extraxe doar contururile exterioare. Setează hierarchy[i][2]=
hierarchy[i][3]= -1 pentru toate contururile.
o CV_RETR_LIST extrage toate contururile fără să stabilească relații ierarhice.
o CV_RETR_CCOMP extrage toate contururile și le organizează într-o ierarhie pe
două nivele. La nivelul superior se află granițele exterioare ale componentelor. La
nivelul al doilea se află granițele cavităților. Dacă există un alt contur în cadrul
cavității unei componente conexe, este pus tot la nivel superior.
25
o CV_RETR_TREE extrage toate contururile și reconstruiește o ierarhie completă a
contururilor interioare.
În cadrul aplicației s-a folosit CV_RETR_CCOMP.
5. int method – metoda de aproximare a contururilor. Poate lua una din valorile:
o CV_CHAIN_APPROX_NONE stochează toate punctele conturului. Oricare două
puncte consecutive din conturi (x1,y1) și (x2,y2) vor fi vecini orizontali, verticali sau
diagonali, cu alte cuvinte max (abs (x1-x2), abs(y2-y1))==1.
o CV_CHAIN_APPROX_SIMPLE comprimă segmentele orizontale, verticale și
diagonale, lasând doar punctele de început și sfârșit. De exemplu, conturul unui
dreptunghi va fi reprezentat ca un șir de 4 puncte.
o CV_CHAIN_APPROX_TC89_L1 și CV_CHAIN_APPROX_TC89_KCOS aplică
algoritmul de aproximație Teh-Chin.
În aplicație am folosit metoda CV_CHAIN_APPROX_SIMPLE.
6. Point offset – un decalaj(offset) opțional, cu care este deplasat fiecare punct al
conturului. Acest parametru este util atunci când extragem o regiune de interes care
trebuie analizată în contextul întregii imagini. În cazul nostru, acesta este 0.
Imgproc.findContours(thresholdedCopy, contours, hierarchy, Imgproc.RETR_CCOMP,
Imgproc.CHAIN_APPROX_SIMPLE);
//daca exista minim un contur
if (hierarchy.size().height > 0 && hierarchy.size().width > 0){
int maxArea = 0, maxAreaIdx = -1;
for( int idx = 0; idx<contours.size();idx++ ){
Mat contour = contours.get(idx);
double contourarea = Imgproc.contourArea(contour);
if(contourarea>maxArea) {
maxArea=(int) contourarea;
maxAreaIdx=idx;
}
}
26
Metoda folosită poate fi problematică deoarece mâna nu este întotdeauna cel mai mare
obiect de culoarea pielii din imagine și poate fi confundată, de exemplu, cu fața utilizatorului.
Pentru o mai bună utilizare a programului se recomandă o setare precisă a valorilor HSV,
plasarea utilizatorului la apoximativ un metru distanță față de laptop și, dacă este posibil,
folosirea unei camere web externe care să fie rotită pentru a cuprinde în cadru doar mâna.
27
3. Numărarea degetelor
Având la dispoziție conturul obținut în etapa anterioară, ne vom folosi de înfășurătoarea
convexă a acestuia pentru a extrage numărul de degete.
Înfășurătoarea convexă (în engleză: convex hull) a unui set de n puncte este cel mai mic
poligon convex care conține acele puncte (inclusiv pe contur)[9]. În OpenCV, putem obține o
reprezentare a acestui poligon folosind funcția convexHull():
void convexHull (InputArray points, OutputArray hull, bool clockwise = false, bool
returnPoints = true )
Funcția găsește înfășurătoarea convexă a unui set de puncte 2D folosind algoritmul lui
Sklansky, care are complexitatea O(N logN) în implementarea curentă.
Parametri:
1. InputArray points - conturul de intrare, reprezentând un set de puncte 2D, de tip
vector sau mat;
2. OutputArray hull - înfășurătoarea convexă. Este fie un vector de indici sau un
vector de puncte. În primul caz, elementele sunt indicii punctelor înfășurătorii convexe din array-
ul initial (din moment de setul de puncte ale înfășurătorii convexe reprezintă un subset al setului
original de puncte). În al doilea caz, elementele vectorului returnat sunt chiar punctele
înfășurătorii.
3. bool clockwise – Dacă are valoarea true, înfășurătoarea convexă este orientată în
sensul acelor de ceasornic. Altfel, în sens opus.
4. bool returnPoints - În cazul unei matrici, când această valoare este true, funcția
returnează punctele înfășurătorii convex. Altfel, returnează indicii punctelor. Când vectorul de
ieșire este de tip Vector, flag-ul este ignorat, iar rezultatul depinde de tipul vectorului.
28
Aplicarea înfășurătorii convexe
Reprezentarea sub formă de poligon a mâinii poate fi acum interpretată în mod
matematic.
Un defect de convexitate reprezintă un gol, o cavitate a unui obiect (în cazul nostrum, un
contur) segmentat dintr-o imagine. Aceasta este o zonă care nu aparține obiectului, dar localizată
în interiorul înfășurătorii convexe.
Defectele de convexitate sunt în strânsă legătură și ne pot oferi informații referitoare la
numărul de degete, întrucât, după cum se poate observa, cele mai mari defecte se află între
degetele ridicate.
Defectele de convexitate ale conturului mâinii
Vom extrage defectele de convexitate ale mâinii folosind funcția OpenCV
convexityDefects(), cu sintaxa:
29
void convexityDefects (InputArray contour, InputArray convexhull, OutputArray
convexityDefects);
Parametri:
1. InputArray contour – conturul original.
2. InputArray convexhull – Înfășurătoarea convexă obținută prin intermediul funcției
convexHull(), care ar trebui să conțină indicii punctelor conturului care alcătuiesc înfășurătoarea
convexă.
3. OutputArray convexityDefects – Vectorul de ieșire, care conține defectele de convexitate.
În interfețele pentru C++, Python și Java, fiecare defect de convexitate este reprezentat ca un
vector cu patru elemente întregi (ex: Vec4i):
start_index – indicele punctului (din conturul original) de unde începe defectul de
convexitate;
end_index – indicele punctului unde se sfârșește defectul de convexitate;
farthest_pt_index – indicele celui mai îndepărtat punct față de înfășurătoarea convexă;
fixpt_depth – aproximarea distanței dintre înfășurătoarea convexă și cel mai îndepărtat
punct al conturului din cadrul defectului. Pentru a obține valoarea în virgulă mobilă a
adâncimii, se calculează fixpt_depth/256.0.
În segmentul de cod de mai jos obținem punctele înfășurătorii convexe și ale defectelor:
MatOfInt hullIndices = new MatOfInt();
Imgproc.convexHull(handContour, hullIndices);
MatOfPoint convexHull = HandRecognition.getNewContourFromIndices(handContour,
hullIndices);
outputImage = HSVProcess.drawContour(thresholdedImage, convexHull);
int fingerNumber = HandRecognition.getNoOfFingers(handContour, hullIndices);
În continuare vom analiza aceste defecte pentru a calcula numărul de degete.
30
În figura de mai sus, punctele verzi reprezintă punctele de start și de sfârșit ale defectelor,
iar cele roșii reprezintă punctele cele mai îndepărtate de hull dintr-un defect (farthest_pt_index).
Între aceste trei puncte ce caracterizează un defect se poate forma un unghi cu originea în
farthest_pt_index.
După cum se poate observa, unghiurile dintre degete sunt ascuțite (mai mici decât 90 de
grade), în timp ce celelalte unghiuri care se formează în imagine sunt obtuze (mai mare de 90 de
grade). De asemenea, defectele de convexitate dintre degete au o adâncime mai mare decât cea a
celorlalte defecte.
int angles= 0;
for (int i = 0; i < convexityDefectsInt.length; i += 4) {
double convexityDepth = (double) convexityDefectsInt[i+3] /
256.0;
Point start = contourPoints[convexityDefectsInt[i]];
Point end = contourPoints[convexityDefectsInt[i+1]];
Point inner = contourPoints[convexityDefectsInt[i+2]];
double defAngle = MathFunctions.angle(start, inner, end);
if (convexityDepth > Constants.minFingerDepth && defAngle <
Constants.maxFingerAngle) {
++angles;
}
}
unde minFingerDepth=30.0 și maxFingerAngle=90.
31
Capitolul 3 Desenarea
Desenarea normală
Atunci când aplicația PaintCam identifică un singur deget ridicat pe ecran, un cerc de rază
ajustabilă va fi desenat în punctul cel mai de sus din contur. Acest punct este chiar primul
element al vectorului în care este salvat conturul mâinii.
Desenarea cercului se face prin funcția OpenCV circle(), cu următorii parametri:
Mat image – imaginea peste care este salvat cercul;
Point center – centrul cercului;
int radius – raza cercului;
Scalar color – culoarea cercului;
int thickness – grosimea cercului.
public static Mat drawPoint(Mat image,Scalar color, int thickness){
Mat returnedMat = image.clone();
if(finger.x != -1 && finger.y!=-1)
drawCircle(returnedMat,finger, color, thickness);
return returnedMat;
}
public static void drawCircle(Mat image, Point p, Scalar color, int
radius){
Imgproc.circle(image, p, radius, color, 8);
}
Desenul astfel obținut este păstrat într-un obiect de tip Mat care, la comanda utilizatorului,
poate fi salvat pe calculatorul personal în format .jpg.
32
Combinarea desenului cu poza
Pornim de la două imagini reprezentate ca Mat-uri: captura preluată de la camera web în
momentul salvării și imaginea desenată până atunci (cu fundal transparent). Vom combina aceste
două imagini astfel încât desenul utilizatorului să apară deasupra fundalului reprezentat de
fotografia acestuia.
Primul pas este să selectăm o regiune de interes. În contextul Image Processing, o regiune
de interes (region of interest – ROI), reprezintă un subset selectat de mostre dintr-un set de date,
identificat pentru un anumit scop[10].
În cazul nostru, regiunea de interes este chiar imaginea peste care vrem să desenăm. Însă,
dacă am fi folosit imagini de dimensiuni diferite, atunci regiunea de interes ar fi fost o parte a
primei imagini, de dimensiunea celei de-a doua. Vom obține regiunea de interes folosind funcția
submat(), care extrage o bucată dintr-o imagine situată între două puncte date.
În continuare, vom aplica o serie de transformări asupra imaginii 2, similare cu cele
folosite în recunoașterea mâinii.
Mai întâi, imaginea este convertită în spațiul de culori Grayscale. O imagine de tip
Grayscale (în tonuri de gri) este o imagine în care fiecare pixel este caracterizat printr-o singură
valoare, care reprezintă intensitatea culorii. Astfel de imagini, de asemenea cunoscute sub
denumirea de alb-negru, sunt compuse exclusiv din nuanțe de gri, variind de la negru (cea mai
slabă intensitate) la alb (cea mai puternică). Pentru obținerea imaginii Grayscale folosim funcția
cvtColor(), descrisă în cadrul capitolului al doilea, înlocuind parametrul ce semnifică tipul
transformării cu Imgproc.COLOR_BGR2GRAY.
33
După convertirea imaginii în spațiul Grayscale vom aplica operația de thresholding
descrisă de asemenea în capitolul al doilea. Astfel, vom obține o imagine alcătuită în întregime
doar din pixeli albi sau negri (fără gri). Vom inversa apoi pixelii imaginii asfel încât negrul să
devină alb și vice versa.
Se face o operație de tip AND binar între regiunea de interes (imaginea inițială) și inversa
obținută la punctul anterior, elementele nenule ale acesteia din urmă specificând pixelii ce trebuie
modificați. Pe captura de ecran apare acum forma desenată de culoare neagră.
O nouă operație de tip AND binar are loc de data aceasta între imaginea desenată și
aceeași imagine, dar supusă operației de thresholding:
34
(fundal transparent) (fundal negru)
Cele două imagini obținute la punctele anterioare sunt adăugate prin funcția OpenCV
add(), care calculează suma pixelilor celor două matrici. Avem o imagine ce conține un desen
negru și un fundal colorat și o imagine ce conține un desen colorat și un fundal negru. Valoarea
pentru negru fiind 0, suma celor două imagini va fi exact ceea ce ne dorim să obținem.
Mai jos se află codul pentru combinarea celor două imagini :
public static Mat combine(Mat camerainput, Mat canvas){
Mat img1 = camerainput.clone();
Mat img2 = canvas.clone();
// crearea regiunii de interes (ROI)
int rows = img2.rows();
int cols = img2.cols();
Mat roi = img1.submat(0, rows, 0, cols);
//crearea imaginii grayscale, a măștii alb-negru și a inversei măștii
Mat img2gray = new Mat();
Imgproc.cvtColor(img2, img2gray, Imgproc.COLOR_BGR2GRAY);
Mat mask = new Mat();
Imgproc.threshold(img2gray, mask, 10, 255, Imgproc.THRESH_BINARY);
Mat mask_inv = new Mat();
Core.bitwise_not(mask, mask_inv);
//adăugarea desenului negru asupra imaginii
35
Mat img1_bg = new Mat();
Core.bitwise_and(roi, roi, img1_bg, mask_inv);
//prelucrarea desenului
Mat img2_fg = new Mat();
Core.bitwise_and(img2, img2, img2_fg, mask);
//suprapunerea imaginii modificate cu cea inițială
Mat dst = new Mat();
Core.add(img1_bg,img2_fg, dst);
return dst;
}
36
Capitolul 4 Interfața Grafică cu Utilizatorul Interfața grafică este împărțită în 6 panouri. Două sunt pentru vizualizarea modului în care
programul prelucrează cadrele preluate de la camera web, două sunt pentru desenarea simplă,
respectiv pe fundal, și ultimele două pentru configurarea instrumentului de desenare (brush-ul)
ului și a valorilor HSV.
Întrucât modul în care camera web percepe culoarea pielii variază în funcție de
luminozitate, uilizatorul începe prin a ajusta valorile HSV minime și maxime, după care se face
filtrarea, prin intermediul a șase butoane glisante (slider). Acest lucru asigură recunoașterea
optimă a mâinii. În timpul etapei de configurare nu se creează desene.
Odată găsită configurația potrivită, utilizatorul pornește desenarea prin intermediului
Butonului Start/Stop. Culoarea și grosimea peniței pot fi modificate de asemenea. După obținerea
desenului dorit, acesta se poate salva fie simplu, prin intermediul butonului Save Canvas, fie
având ca fundal captura preluată de la camera web în momentul respectiv, prin intermediul
butonului Save Painted Camera Capture.
37
Utilizatorul poate de asemenea să întrerupă desenarea în cazul în care apare nevoia de a
ajusta configurația. În timpul desenării, interfața grafică arată în felul următor:
Odată finalizat desenul dorit, utilizatorul îl poate salva, simplu (prin intermediul butonului
Save Canvas) sau folosind ca fundal captura preluată de la camera web (prin intermediul
butonului Save Painted Camera Capture).
38
Diverse clase ale pachetului Javafx au fost folosite pentru a reda captura video și
imaginile create (Image, ImageView), pentru organizare (Hbox, Vbox), pentru preluarea setărilor
de la utilizator (Slider, ColorPalette) și pentru salvarea imaginii rezultate (FileChooser).
Clasa VideoCapture din OpenCV este folosită pentru captura video de la camera web,
care este preluată cadru cu cadru la un interval de 33 milisecunde. Cadrele sunt primite sub forma
unor obiecte de tip Mat.
În secvența de cod următoare, un obiect de tip Mat este transformat într-un byte array și
apoi într-un obiect de tip Image (specific pachetului Javafx), pentru a putea fi integrat în interfața
grafică:
public static Image matToImage(Mat input){
MatOfByte bytemat = new MatOfByte();
Imgcodecs.imencode(".jpg", input, bytemat);
byte[] bytes = bytemat.toArray();
InputStream in = new ByteArrayInputStream(bytes);
Image returnedimg = new Image(in);
return returnedimg;
}
Funcția imencode() codează o imagine într-un buffer de memorie. Parametri:
ext – extensia caracteristică output-ului;
img – imaginea ce se dorește a fi scrisă;
buf – buffer-ul de ieșire, redimensionat pentru a se potrivi imaginii compresate;
params – parametri specifici formatului.
Odată cu fiecare cadru se afișează desenul stocat până la momentul respectiv, iar desenul pe
fundal se obține de fiecare dată prin combinarea cadrului cu desenul.
39
Concluzii Pe parcursul capitolelor anterioare am detaliat structura, modul de funcționare și pașii
parcurși în realizarea aplicației PaintCam, pe care am dezvoltat-o în cadrul Facultății de
Informatică a Universității „Alexandru Ioan Cuza” din Iași. Această aplicație propune un mod
interactiv de desenare prin intermediul computerului, cu ajutorul camerei web.
În cadrul lucrării, am inclus descrierea funcționalităților librăriei open-source OpenCV,
folosită pentru detectarea mâinii utilizatorului, și metodele folosite în acest scop. Am prezentat de
asemenea punctele tari și slabe ale aplicației, dificultățile întâmpinate pe parcurs și modul în care
aceasta ar putea fi îmbunătățită.
Aplicația poate constitui un punct de plecare pentru crearea altor programe în domeniul
Computer Vision, inclusiv al unui program de desenare prin intermediul camerei web frontale a
laptop-ului, care să permită recunoașterea mâinii utilizatorului chiar și atunci când aceasta
acoperă fața. Diverse brush-uri, fundaluri și alte funcționalități de desenare ar putea fi adăugate,
din resurse proprii sau prin intermediul unui API.
Computer Vision este un domeniu ce dispune de un potențial imens și care nu și-a atins
încă apogeul. Numeroase cercetări sunt efectuate în domeniu, care vor aduce cu siguranță
facilități majore și vor schimba modul în care percepem programele software.
40
Referințe bibliografice
[1] https://en.wikipedia.org/wiki/Computer_vision
[2] Yiannis, Aloimonos. Special Issue on Purposive and Qualitative Active Vision. CVGIP B:
Image Understanding, Vol. 56 , 1992.
[3] Phil, Simon. Too Big to Ignore: The Business Case for Big Data. Wiley, 2013.
[4] Ron, Kohavi; Foster, Provost. Glossary of terms. Special Issue on Applications of Machine
Learning and the Knowledge Discovery Process (volume 30, Number 2/3), 1998.
[5] James, Gosling; Bill, Joy; Guy, Steele; Gilad, Bracha; Alex, Buckley. The Java Language
Specification(PDF) (Java SE 8 ed.). 2014.
https://docs.oracle.com/javase/specs/jls/se8/jls8.pdf
[6] https://ro.wikipedia.org/wiki/Java_(limbaj_de_programare)
[7] http://groups.engin.umd.umich.edu/CIS/course.des/cis400/java/java.html
[8] http://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-overview.htm
[9] http://algopedia.ro/wiki/index.php/Clasele_11-12_lec%C8%9Bia_3_-_1_oct_2014
[10] Ron, Brinkmann. The Art and Science of Digital Compositing. Morgan Kaufmann, 1999.