Licenta_Andrei_Elena_21Iunie [308137]

[anonimizat], CALCULATOARE ȘI INGINERIE ELECTRICĂ

PROGRAMUL DE STUDII ______________________

PROIECT DE DIPLOMĂ

Absolvent

(nume și prenume)

Conducător științific

(titlu, nume și prenume)

Pitești

Sesiunea iulie 2017

[anonimizat], CALCULATOARE ȘI INGINERIE ELECTRICĂ

PROGRAMUL DE STUDII ______________________

PROIECT DE DIPLOMĂ

(TITLUL)

Absolvent

(nume și prenume)

Conducător științific

(titlu, nume și prenume)

Pitești

Sesiunea iulie 2017

LISTĂ FIGURI ȘI TABELE

Figura 1. [anonimizat] 10

Figura 1.1. [anonimizat] 14

Figura 1.2. [anonimizat] (cluster), pag 15

Figura 1.3. [anonimizat], pag 15

Figura 1.4. [anonimizat] 18

Figura 1.5. [anonimizat] 19

Figura 1.6. Arhitectura hardware a [anonimizat] 20

Figura 1.7. [anonimizat] 21

Figura 1.8. [anonimizat] 23

Figura 1.9. [anonimizat] 24

Figura 1.10. [anonimizat] 25

Figura 1.11. [anonimizat] 25

Figura 1.12. Fluxul de compilare a programului, pag 26

Figura 1.13. Instanța și baza de date Oracle.Arhitectura, pag 30

Figura 1.14. Arhitectura generală a EmguCV, pag 34

Figura 1.15. [anonimizat] 36

Figura 1.16. [anonimizat] 36

Figura 1.17. [anonimizat] 37

Figura 1.18. [anonimizat] 37

Figura 1.19. [anonimizat] 38

Figura 1.20. [anonimizat] 39

Figura 1.21. [anonimizat] 40

Figura 1.22. [anonimizat] 40

Figura 1.23. [anonimizat] 41

Figura 2.1. [anonimizat] 44

Figura 4.1. [anonimizat] a tabelelor, pag 53

Figura 4.2. [anonimizat] 54

Figura 4.3. [anonimizat] 55

Figura 4.4. [anonimizat] 57

Figura 4.5. [anonimizat], pag 58

Figura 5.1. [anonimizat] 66

Figura 5.2. [anonimizat] 68

Figura 5.3. [anonimizat] 69

Figura 5.4. [anonimizat] 69

Figura 5.5. Parametrii de rețea ai mașinii virtuale., pag 70

Figura 5.6. [anonimizat] 71

Figura 5.7. [anonimizat] 71

Figura 5.8. Mesaj afișat după instalarea cu success a [anonimizat] 73

Figura 5.9. [anonimizat] 73

Figura 5.10. Conținutul fișierului bash_profile, pag 74

Figura 5.11. [anonimizat] 74

Figura 5.12. [anonimizat] 75

Figura 5.13. [anonimizat] 76

Figura 5.14. Includerea in toolbox a [anonimizat] 77

Figura 5.15. [anonimizat], pag 78

Figura 5.16. [anonimizat], pag 79

Figura 5.17. Rezultate execuție pentru imaginea de 64×64 pixeli, pag 80

Figura 5.18. Rezultate execuție pentru imaginea de 256×256 pixeli, pag 80

Figura 5.19. Rezultate execuție pentru imaginea de 512×512 pixeli, pag 81

Figura 5.20. Rezultate execuție pentru imaginea de 1024×1024 pixeli, pag 81

……………………………….

Tabel 5.1. Caracteristici Nvidia Geforce GTX 850M, pag 67

Tabelul 5.2. Rezultate testelor la creșterea dimensiunii setului de date, pag 78

Tabel 5.3. Rezultatele testelor la modificarea complexității operațiilor, pag 79

ACRONIME

CPU (Central Processing Unit) – un procesor serial optimizat pentru a oferi performanță crescută în cazul operațiilor secvențiale

GPU (Graphics Processing Unit) – un procesor de flux configrat pe o placă video specializat pentru calcule intensive paralelizate

CUDA (Compute Unified Device Architecture) – o arhitectură de programare paralelă dezvoltată de NVIDIA pentru a oferi performanță crescută operațiilor paralele

NVCC (Nvidia Cuda Compiler) – un compilator ce transformă fișierele sursă (.cu) și creează două fișiere, unul pentru execuția pe GPU, iar unul pentru CPU

GPGPU (General Purpose GPU) – un tip de procesare ce utilizează puterea de calcul a GPU-urilor în alte scopuri decât procesarea grafică, cum ar fi, de exemplu, înmulțirea matricilor

Host – în contextul unui program CUDA , host-ul reprezntă tot ceea ce este în afara plăcii video, de exemplu CPU-ul sau memoria sistemului

Kernel – o funcție sau o metodă ce este executată de procesorul paralel

PTX (Parallel Thread eXecution) – un tip de fișier rezultat în urma compilării codului CUDA

API (Application Programming Interface) – un set de sub-programe și instrumente de programare

VM (Virtual Machine) – este o simulare a unui sistem informatic ce oferă funcționalitatea unui sistem fizic

OS (Operating System) – un software ce gestionează resursele hardware și software prin anumite servicii

NAT(Network Address Translation) – un mod de a accesa o rețea externă dintr-o mașină virtuală

OL(Oracle Linux) – sistem de operare dezvoltat de Oracle similar cu Red Hat

RPM (RPM Package Manager) – un sistem de gestionare a pachetelor

RDBMS (Relational database management system) – sistem de gestiune a bazelor de date bazat pe modelul relațional

SQL (Structured Query Language)- limbaj de gestiune al bazelor de date

PL/SQL (Procedural Language/Structured Query Language) – limbaj procedural dezvoltat de Oracle ce integrează limbajul SQL

OUI (Oracle Universal Installer) – utilitar Oracle pentru instalarea software-ului și al bazelor de date

DBCA (Database Configuration Assistant) – utilitar pentru creerea, configurarea sau ștergerea bazelor de date

ASM (Automatic Storage Management) – un manager de disk-uri și un sistem de fișiere Oracle

.NET- un framework dezvoltat de Microsoft ce asigură interoperabilitatea între limbaje de programare

UI (User interface) – interfața cu utilizatorul

XML(Extensible Markup Language) – este un meta-limbaj folosit ca model de stocare a datelor nestructurate

HOG (Histogram of oriented gradients) – un descriptor de carcteristici utilizat în prelucrarea imaginilor în scopul detectării de obiecte

RGB (red, green, blue) – model aditiv de culoare în care culorile se obțin prin combinații între roșu, verde și albastru

ASCII (American Standard Code for Information Interchange) – un sistem de codificare al caracterelor bazat pe alfabetul englez

INTRODUCERE

Plăcile grafice sunt foarte utilizate pentru a accelera jocurile și aplicațiile grafice 3D. GPU-ul (Graphics Processing Unit) al unei plăci grafice este conceput pentru calcule intense cu grad mare de paralelizare. O dată cu dezvoltarea API-uilor de nivel înalt (cum ar fi CUDA- Compute Unified Device Architecture) puterea GPU-urilor a crescut, fiind utilizată pentru cât mai multe aplicații și domenii, cum ar fi în accelerarea operațiilor bazelor de date, rezolvarea ecuațiilor diferențiale și calcul geometric.

De-a lungul timpului, domeniul procesării grafice a reunit specialiști în matematică, informatică, proiectare și chiar artiști, în încercarea de a crea o realitate virtuală bine conturată, care să îmbine caracteristicile mediului real cu informații generate de calculator. [1]

Procesarea imaginilor este, un domeniu de interes comun multor comunități științifice, urmărindu-se continuu reducerea timpului de procesare și proiectarea unor algoritmi optimizați de prelucrare[2] în acest context. Este o formă de procesare a semnalelor, în care intrarea este o imagine, iar rezultatul poate fi o imagine sau orice altceva care suferă o anumită prelucrare semnificativă. Alterarea unei imagini pentru a fi mai luminoasă sau mai întunecată este un exemplu comun de procesare a imaginii, fiind disponibil în editoarele de imagine de bază.

De multe ori, procesarea este realizată pe întreaga imagine, și aceiași pași sunt aplicați pentru fiecare pixel al imaginii. Acest lucru înseamnă o mulțime de repetari ale aceluiași lucru, ceea ce este echivalent cu un timp mai mare de procesare. Odată cu avansarea tehnologiei și apariția CUDA, programarea pentru GPU este simplificata, iar procesarea secvențială a fost înlocuită cu procesarea paralelă. Aceasta a avut un impact deosebit asupra problemelor dintr-o gamă largă de domenii. Costurile mici ale tehnicilor de procesare paralelă, corelate cu cerințele de performanță din ce în ce mai mari ale aplicațiilor, au fost unele dintre cele mai convingătoare argumente care au susținut, de-a lungul timpului, acest model de programare.[3]

O parte dintre funcțiile ce pot beneficia de accelerarea paralelă pe GPU sunt:

Aplicații de imagistică medicală

Rețele neuronale

Dinamica fluidelor

Filtrul median

FFT, Convoluția

Compresie video și de imagine

Transformări 2D și 3D

Detecții ale fetelor, pietonilor, colturilor, caracteristicilor, etc.

Calculul histogramei

Reducerea zgomotului

Alte filtre

Figura 1. Exemplu de aplicații ce pot fi accelerate paralel CUDA

Aplicația pe care am proiectat-o, și anume un XXXXXX , poate să vină în ajutorul celor care își doresc să aibă controlul asupra unei încăperi sau locuințe prin supravegherea acestora printr-un sistem inteligent ce oferă și posibilitatea selectării datelor importante după anumite criterii.

De la acest sistem se dorește punerea la dispoziție de servicii de monitorizare în timp real și de detecție și stocare a posibilor suspecți.

Lucrarea are ca scop sublinierea performanțelor noii tehnologii de prelucrare paralelă CUDA și integrarea acesteia într-un sistem real de prelucrare și beneficiile pe care le aduce. Înainte de a parcurge implementarea practică a aplicației am realizat testarea performanțelor tehnologiei CUDA, modul cum aceasta gestionează problemele în raport cu modul de lucru al procesorului general și timpul de lucru necesar completării operațiilor.

Lucrarea ce descrie acest sistem, precum și testele inițiale este alcătuită din cinci capitole ce vor fi descrise în continuare.

În primul capitol sunt evidențiate aspecte teoretice și cuprinde descrierea amănunțită a tehnologiilor utilizate. Sunt prezentate aspecte istorice ale evoluției programării paralele și facilitățile sale precum și tehnologiile și librăriile folosite, cum ar fi bazele de date Oracle și modul de virtualizare sau librăria Emgu Cv.

Cel de-al doilea capitol reprezintă partea de proiectare a aplicațiilor ce au rolul de a testa procesorul grafic în raport cu paralelismul oferit de CPU. În acest capitol a fost de asemenea evidențiat modul de lucru al librăriei ManagedCuda și faptul că aceasta permite utilizatorului să folosească propriul kernel CUDA, implementat în limbajul CUDA C.

Capitolul al treilea urmărește etapele de proiectare și implementare ale aplicației de detecție a feței în limbajul CUDA C. Ideea de bază este aplicarea algoritmului Viola-Jones în paralel pentru a obține un timp de execuție cât mai bun.

Capitolul patru prezintă pașii urmăriți în dezvoltarea sistemului, configurarea componentelor și implementarea aplicației de prelucrare a imaginilor provenite de la senzori video. Aceștia au fost cu ajutorul unor figuri ce urmăresc evoluția, având ca scop conturarea unei idei mai clare despre funcționalitatea aplicației. În acest capitol se urmăresc etapele proiectării și implementării aplicațiilor ce interacționează cu procesorul grafic și baza de date. Se prezintă metodele prin care s-a realizat modul de operare în timp real.

Capitolul următor conține instalarea sistemului și rularea aplicațiilor precum și etapele de configurare necesare pentru a le putea utiliza. Se urmăresc pașii de instalare și configurare pentru fiecare mediu utilizat, de la realizarea mașinii virtuale, instalarea sistemului de operare și a abazei de date, până la aspecte legate de configurarea driver-ului NVIDIA pentru CUDA și instalarea librăriilor.

Ultimul capitol cuprinde o mare parte din codul sursă al aplicațiilor, iar în final sunt extrase concluziile din acest proiect unde sunt sintetizate ideile despre funcționalitatea acestuia.

TEMA DE PROIECTARE

Ideea proiectului XXXX este de a realiza un mod de interacțiune a arhitecturii paralele CUDA cu tipurile de prelucrare serială pentru a obține sisteme eterogene capabile să rezolve probleme complexe de prelucrare grafică într-un timp mai rapid.

Se vor proiecta aplicații ce vor realiza inițial testarea arhitecturii paralele NVIDIA CUDA pentru calcule uzuale cum ar fi adunările sau inmulțirile și apoi aplicația specifică prelucrării de imagine ce realizează detecția fețelor prin algoritmul Viola-Jones.

În final se va proiecta o aplicație capabilă să funcționeze ca un sistem de supraveghere inteligent ce îmbină programarea serială în limbajul C# cu programele executat în paralel de către GPU. Datele sunt preluate de la senzori video, în cazul meu camerele web conectate la calculator iar asupra lor se aplică algoritmi de detecție a fețelor sau a pietonilor ce sunt executați în timp real. Rezultatul procesării este salvat într-o bază de date și poate fi utilizat pentru analiză ulterioară.

CONTEXTUL PROIECTULUI

Asigurarea unui nivel înalt de securitate a locurilor publice de acces sau a proprietăților private reprezintă o mare provocare. Un număr mare de noi tehnologii se pot aplica pentru a analiza anumite aspect de securitate cum ar fi sisteme biometrice pentru a verifica identitatea sau supraveghere video pentru a monitoriza activitatea. Aceste sisteme video din prezent acționează ca înregistratoare video la scară mare de tip analogic sau digital. Au rolul de a asigura un mod de a detecta și de a reacționa la posibilele amenințări și de salvare a probelor ce vor fi folosite în procesul de investigare.

Deoarece construirea unui asemenea sistem inteligent necesită utilizarea unor funcții complexe acesta ar putea fi inadecvat pentru existența noțiunii de real-time dar și pentru investigații juridice.

Din perspectiva aplicațiilor de detective a amenințărilor în timp real este bine cunoscut fapul că atenția vizuală a omului poate scădea în anumite condiții mult sub limitele acceptate chiar în cazul unui personal foarte bine antrenat, iar din perspectiva investigațiilor juridice, provocarea de a cerceta amănunțit colecții foarte mari de material video este chiar mai obositor și poate induce în eroare investigatorul uman.

Tehnologiile de video analiză automata se pot aplica pentru a dezvolta sisteme inteligente ce cresc eficiența și ajută la scăderea și prevenirea infracțiunilor. Conceperea acestor sisteme necesită optimizarea operațiilor și scăderea timpului de procesare a imaginilor achiziționate de la senzorii video ce pot avea o rezoluție mare.

Programarea paralelă a imaginilor folosind noua tehnologie a fost introdusă în mai multe domenii ale informaticii. Noile mașini de calcul sunt echipate cu plăci grafice performante ale căror resurse pot fi utilizate în aceste scopuri.

Din ce în ce mai multe dispozitive sunt conectate la internet și trimit date care trebuiesc procesate și stocate. Procesarea rapidă a datelor devine astfel necesară, ținând cont de volumul de date în continuă creștere. Procesarea imaginilor și a videoclipurilor reprezintă una dintre cele mai importante provocări ale secolului.

Procesarea datelor se poate face prin intermediul algoritmilor secvențiali, dar în anumite cazuri aceasta se poate face parțial în paralel. Procesarea paralelă reprezintă divizarea task-ului în sub-task-uri pentru care se alocă procesoare diferite ceea ce face ca procesarea să fie mult mai rapidă. Cu cât este folosită mai mult paralelizarea în implementarea algoritmilor, cu atât crește performanța în prelucrarea datelor.

Inițial, prelucrarea datelor în paralel era posibilă doar în centrele de date informatice de mari dimensiuni, având mai multe dezavantaje, deoarece acestea erau prohibitive pentru a construi, stoca, și funcționa.

O alternativă la acest tip de procesare a apărut în ultimii ani, una care utilizează hardware grafic de capacități intrinseci de prelucrare a datelor în paralel.

CUDA (Compute Unified Device Architecture) este software-ul și arhitectura hardware creată cu scopul calculării datelor paralele, dezvoltată de compania NVIDIA.

Tehnologia CUDA a apărut o dată cu linia G80 a plăcilor grafice GeForce. CUDA constă dintr-o extensie a limbajului C ++ și specifică funcții speciale (cores) care urmează să fie executate pe GPU. CUDA atribuie un thread pentru fiecare element din structură, ceea ce crește performanța de calcul. Procesoarele grafice de ultimă generație combină puterea lor cu puterea CPU-ului, crescând performanța. Acest procedeu este denumit co-procesare.

NVIDIA reușește să asigure prin tehnologia CUDA arhitecturi complexe și caracteristici cum ar fi memorie unificată și partajată, citiri secvențiale din memorie, dar și unelte pentru dezvoltatori cum e NAVIGRAPH.

Cu peste câteva milioane de GPU CUDA vândute, NVIDIA a devenit cea mai utilizată tehnologie pentru procesare de imagini si video, biologie computationala si chimie, simularea dinamicii fluidelor, reconstrucție a imaginilor, analiza seismică, de calcul financiar, de către dezvoltatorii de software, oameni de știință și cercetători.

Cea mai recentă arhitectură lansată de NVIDIA CUDA a introdus noi caracteristici pentru a îmbunătăți eficiența în ceea ce privește performanța și puterea. Acest lucru oferă aproape 3584 de procesoare (nuclee CUDA) și simplifică programarea prin partajarea memoriei între CPU și GPU.

Cele mai importante avantaje pe care le aduce CUDA sunt acelea că pentru a dezvolta aplicații programatorii folosesc un limbaj de nivel înalt, C, însă poate fi de asemenea utilizat pentru un număr mare de limbaje. Acesta oferă o memorie partajată de dimensiuni mari create pentru a crește viteza în timp ce firele de execuție comunică între ele.

De asemenea, CUDA are unele limitări, deoarece este folosită doar pentru GPU-uri NVIDIA, de asemenea, aceasta poate provoca întârzieri ale GPU și CPU din cauza latenței între ele, iar limbajul de programare nu acceptă funcțiile recursive, dar ele pot fi implementate prin bucle.

CAPITOLUL 1. NOȚIUNI TEORETICE ȘI CONCEPTE

Arhitecturi de calculatoare

Există mai multe moduri de clasificare a calculatoarelor în funcție de arhitectură. O clasificare intens folosită este Taxonomia lui Flynn ce grupează calculatoarele în patru tipuri diferite în funcție de cum sunt repartizate către procesoare datele și instrucțiunile.

Single Instruction Single Data (SISD) – este arhitectura tradițională a unui calculator serial. Există o singură unitate de procesare, iar la un anumit moment de timp este executată o singură instrucțiune asupra unui singur flux de date.

Single Instruction Multiple Data (SIMD) – este arhitectura paralelă. Există mai multe unități de procesare ce execută aceiași instrucțiune la un anumit moment de timp, fiecare operând pe seturi diferite de date.

Multiple Instruction Single Data (MISD) – fiecare core operează asupra aceluiași set de date prin fluxuri separate de instrucțiuni.

Multiple Instruction Multiple Data (MIMD) – se referă la o arhitectură paralelă în care mai multe core-uri operează pe mai multe seturi de date, fiecare executând instrucțiuni independente.

Figura 1.1. Clasificare după Taxonomia lui Flynn

Arhitecturile de calculatoare se mai clasifică și în funcție de organizarea memoriei și se grupează în două categorii:

Multi-nod cu memorie distribuită

Multiprocesor cu memorie distribuită

Într-un sistem multi-nod mai multe procesoare sunt interconectate prin rețea. Fiecare procesor are o memorie locală, însă poate distribui informații din memoria sa prin rețea către celelalte procesoare. Figura 1.2 ilustrează un sistem tipic mult-nod cu memorie distribuită. Acest tip de sistem este adesea denumit ca și cluster.

Figura 1.2. Sistem multi-nod (cluster)

Sistemele multiprocesor conțin de la două procesoare până la câteva zeci sau sute conectate fizic la aceiași memorie sau care partajează o legătură de latență mică (cum ar fi PCI-Express sau PCIe).

Figura 1.3. Sistem multi-procesor

GPU reprezintă o arhitectură multi-core ce reprezintă aproape toate tipurile de paralelism descrise anterior: multithread, MIMD, SIMD și paralelism la nivel de instrucțiuni. De aceea, NVIDIA a inventat termenu de SIMT (Single Instruction, Multiple Thread) pentru acest tip de arhitectură.

Unitățile de procesare grafică și CPU nu au un strămoș comun. Din punct de vedere istoric, GPU-urile sunt acceleratoare grafice. Doar recent acestea au evoluat pentru a fi procesoare paralele puternice, de uz general, complet programabile, ideale pentru a aborda problemele masive de calcul paralel.

Chiar dacă termenii de "many-core" și "multicore" sunt folosite pentru a eticheta arhitecturile GPU și CPU, un nucleu GPU este destul de diferit de un nucleu al procesorului.

Un nucleu CPU este special conceput pentru o logică de control foarte complexă care urmărește optimizarea executării programelor secvențiale, in schimb ce un nucleu GPU este optimizat pentru sarcini paralele cu o logică de control mai simplă, concentrându-se pe realizarea programelor paralele.

GPU – Prelucrarea imaginilor

GPU (Graphics Processing Unit) este un procesor de pe placa grafică specializat pe calcule complexe, intens paralelizate. Este proiectat pentru a susține operații de transformare, randare și accelerare a graficii. Construit din milioane de tranzistori, mai multi decât în cazul CPU (Central Processing Unit), este specializat în calcule cu virgulă mobilă. GPU-urile au evoluat în procesoare multiThread, paralelizate, cu o putere excepțională de calcul, dominând tehnologia jocurilor și a aplicațiilor 3D încă de la apariția sa, în 1999.

Principala diferență între CPU și GPU este accea că CPU este un procesor serial, în timp ce GPU este un procesor de plux. Procesorul serial, bazat pe arhitectura Von Neumann execută instrucțiunile secvențial, câte una într-un anumit moment de timp. Procesoarele de flux (stream processor) execută o funcție denumită kernel pe un set de date de intrare (stream) simultan. Elementele primite ca intrare sunt executate de kernel în același timp, fără a avea dependințe cu alte elemente, ceea ce face ca programul să poată fi executat paralel.

Termenul de GPU a fost introdus de Nvidia în 1999, ce a adus pe piața GeForce 256 ca fiind “primul GPU din lume”. A fost prezentat ca “procesor single-chip cu dispozitiv integrat de transformare, iluminare, triunghi de configurare / tăiere, și motoare de randare”.[4]

Este important de subliniat că procesarea GPU nu înseamnă înlocuirea completă a CPU-urilor. Există anumiți algoritmi care sunt executați mai eficient pe CPU decât pe GPU deoarece nu orice poate fi executat în mod paralel. GPU-urile oferă totuși o alternativă eficientă pentru anumite tipuri de probleme. Principalii algoritmi canditați pentru procesarea paralelă sunt algoritmii care a componente ce necesită o execuție repetată a acelorași calcule, iar acele componente trebuie să poată fi executate independent una de cealaltă. În acest caz se încadrează și algoritmii de prelucrare de imagini.

În cele mai recente generații de procesoare grafice capacitățile operațiunilor per-pixel și de texturare au crescut considerabil. Milioane de GPU-uri sunt deja prezente în calculatoarele a utilizatorilor din întreaga lume, de aceea, în prezent se pot aplica cu ușurință aceste metode dezvoltate pentru modelarea 3D și randare pentru mai multe probleme clasice de procesare a imaginilor pentru a realiza o creștere semnificativă a aplicațiilor față de cele executate doar de CPU fără un compromis asupra calității imaginii finale.

GPU-urile moderne folosesc majoritatea tranzistorilor pentru a face calcule necesare graficii 3D. Ele au fost inițial folosite pentru a accelera lucrul intensiv cu memoria în operații de randare, fiind apoi introduse in calucle geometrice cum ar fi rotația și translația în diferite sisteme de coordonate. Deoarece cele mai multe dintre aceste calcule implică operațiuni cu matrice și vectori, inginerii și oameni de știință au studiat din ce în ce mai mult utilizarea GPU-urilor pentru calcule de bază non-grafice.

Platforma CUDA

NVIDIA CUDA este o arhitectură de prelucrare paralelă dezovltată de NVIDIA ce are ca scop accelerarea operațiilor de calcul și reducerea sarcinii de lucru pentru CPU în anumite aplicații, folosind puterea disponibilă de calcul a procesoarelor grafice (GPU). Aceasta include CUDA Instruction Set Architecture (ISA) si motorul de prelucrare ce folosește procesorul grafic. Pentru a programa arhitectura CUDA, dezvoltatorii pot folosii limbajul C, unul dintre cele mai raspandite si utilizate limbaje de programare de nivel înalt și care poate fi executat pe procesoarele CUDA oferind performanțe foarte bune.

CUDA este o platformă dezvoltată atât software cât și hardware pentru a utiliza puterea de calcul a procesoarelor grafice (GPU) în aplicații de uz general pe trei nivele:

Software – permite programarea procesoarelor GPU ca o extensie a SIMD pentru a obține performanța necesara;

Firmware – oferă driver orientat pentru programarea GPGPU, fiind compatibil cu cel utilizat pentru randare;

Hardware – expune paralelismul GPU-urilor pentru prelucrări uzuale printr intermediul unui număr de multiprocesoare dotate cu nuclee și o ierarhie a memoriei.

Arhitectura CUDA si software-ul asociat acesteia au fost proiectate urmărind anumite aspecte:

Să fie realizat ca o extensie a unui limbaj standard de programare, cum este C, ce oferă posibilitatea implementării directe a algoritmilor paraleli.

Să sprijine calcule eterogene, folosind atât CPU cât și GPU. Cele două procesoare, CPU și GPU sunt privite ca și dispozitive separate, având memorie proprie. Această implementare permite folosirea în același timp a CPU-ului si a GPU-ului.

Procesoarele grafice capabile să folosească tehnologia CUDA au sute de nuclee ce pot rula în paralel mii de thread-uri, la nivel architectural fiind considerate coprocesoare matematice ale CPU. Fiecare nucleu are în comun resurse de memorie. Acest tip de structurare a memoriei permite distribuirea datelor între nuclee fără a fi transmise pe o magistrală de date.

Deși arhitectura CUDA impune un anumit stil de programare paralelă, nu obligă programatorul să se ocupe de gestiunea explicită a firelor de execuție. Acesta are, în general, sarcina de a partiționa problema în sub-probleme ce urmează a fi rezolvate prin cooperare între firele de execuție.[5]

Firele de execuție pot fi identificate și organizate în blocuri (threads blocks) care la rândul lor formează grid-uri (grids of thread blocks). Fiecare bloc poate fi identificat în interiorul grid-ului cu ajutorul unui index, iar execuția mai multor blocuri este posibilă în orice ordine, secvențial sau paralel. Un grid reprezintă, de fapt, un șir de blocuri care execută același kernel, citesc date din memoria globală și asigură mecanismele de sincronizare în cazul apelurilor dependente. Unui thread i se asociază automat un threadID, accesibil în interiorul kernel-ului, un contor de program, regiștrii, o locație proprie în memoria comună, date de intrare și date de ieșire. Fiecare bloc are alocat un spațiu de memorie comună, vizibil tuturor firelor de execuție din bloc. Pe lângă accesul la memoria comună, toate firele de execuție au drept de citire a memoriei de constante și a celei de texturi. [6]

Analizând numărul mare de aplicații dezvoltate pentru a fi executate pe procesoarele grafice Nvidia CUDA putem spune că impactul a fost uriaș. În Februarie 2011 erau aproximativ 90 de aplicații, urmând ca doar trei ani mai tarziu, în 2014, numărul de aplicații să ajungă la 275, fiind în continua creștere. Cele mai multe dintre aplicații sunt folosite de cercetători și ingineri și susțin dezvoltarea științifică.

Pentru a utiliza CUDA, sistemul trebuie să respecte următoarele cerințe minime:

Un procesor grafic ce este capabil să utilizeze CUDA

Microsoft Windows XP, Vista, 7, 8 sau Windows Server 2003, 2008

NVIDIA CUDA Toolkit

Microsoft Visual Studio 2008,2010 sau o versiune alternativă a Microsoft Visual C++ Express

Implementarea hardware

Din punct de vedere hardware, platforma de dezvoltare a aplicațiilor GPGPU presupune lucrul cu unul sau mai multe procesoare grafice (devices) și procesorul central (host) al sistemului.

O diferență majoră la nivel de arhitectură între CPU care sunt procesoare de uz general și GPU-uri ce sunt procesoare optimizate pentru calcule paralele pentru multiple seturi de date este modalitatea de acces la memorie. GPU-urile au ajuns în prezent să folosească până la 8 canale pentru a accesa memoria, pe cîând CPU-urile folosesc între 1 și 3 canale tot de câte 64 de biți.

Arhitectura CUDA este construită pe o matrice de multi-procesoare (SM – Streaming Multiprocessors) fiecare având M core-uri, în care ierarhia firelor de execuție corespunde chiar ierarhiei procesoarelor din cadrul GPU, asa cum este ilustrat în figura 1.4. Când un program care rulează pe GPU invocă un kernel, blocurile sunt numărate și distribuite către multi-procesoarele libere. [7]

Figura 1.4. Arhitectura hardware CUDA

Din punct de vedere al arhitecturii hardware a memoriei integrată, fiecare multi-procesor are câte un registru, are acces la o memorie distribuită precum și la memorii cache de tip Read-Only. Memoria global este de fapt memoria video ce este mult mai rapidă decât memoria sistemului, însă poate fi de aproximativ 500 de ori mai lentă decât memoria partajată de multi-procesoare.

Fluxul tipic al unui program CUDA începe prin încărcarea datelor în memoria gazdă și de acolo transferul în memoria dispozitivului. Când se execută o instrucțiune firele pot prelua datele necesare din memoria dispozitivului. Totuși, accesul la memorie poate fi lent și poate avea o lățime de bandă limitată. Cu mii de fire care fac apeluri de memorie, acest lucru poate fi un dezavantaj. Pentru realiza descongestionarea traficului CUDA oferă mai multe tipuri de memorie care îmbunătățesc eficiența execuției.

Figura 1.5. Transferul datelor între Host și Device

Există patru tipuri majore de memorii ale dispozitivului: memorie globală, memorie pentru constante, zona de memorie partajată și regiștrii. Memoria globală are cea mai mare latență de acces în rândul celor trei. O variabilă globală este declarată utilizând cuvântul cheie __device__. Aceasta este cel mai simplu de folosit și poate fi ușor citit și scris de către host utilizând API-urile CUDA, dar și poate fi accesat cu ușurință de dispozitiv. Datele din memoria globală sunt disponibile pe toată durata aplicației și sunt accesibile prin orice fir din orice rețea. Memoria globală este singura modalitate prin care firele din diferite blocuri să comunice între ele.

Figura 1.6. Arhitectura hardware a memoriei GPU

Thread-urile de execuție CUDA pot accesa date din mai multe zone de memorie în timpul execuției, asa cum este ilustrat în figura 1.6. Pentru fiecare thread există o zonă privată de memorie, fiecare bloc de thread-uri are o memorie distribuită vizibilă tuturor firelor de execuție din matricea bloc, iar toate firele au acces și la o memorie globală.

Memoria pentru constante este foarte asemănătoare cu memoria globală. De fapt acestea sunt singurele două memorii pe care host-ul le poate citi și le scrie. Principala diferență față de memoria globală este aceea că memoria constantă este de tip read-only pentru dispozitiv deoarece este proiectată pentru acces mai rapid la date paralele. Datele sunt stocate în memoria globală, dar sunt memorate în cache pentru a fi accesate eficient. Oferă o lățime de bandă mare și latență mică atunci când toate firele citesc simultan din aceeași locație. O variabilă constantă este declarată prin utilizarea cuvântului cheie __constant__. Ca și memoria globală, memoria constantă este accesibilă pe tot parcursul execuției aplicației.

Memoria partajată este o memorie pe dispozitiv pe care host-ul gazda nu o poate accesa. Acest tip de memorie este alocat la un nivel de bloc și poate fi accesat numai de firele acestui bloc. Memoria partajată este cea mai eficientă modalitate de a coopera între firele din același bloc, de obicei prin sincronizarea fazelor de citire și scriere și este o modalitate mult mai rapidă decât utilizarea memoriei globale pentru schimbul de informații într-un bloc. Memoria partajată este declarată utilizând cuvântul cheie __shared__, utilizat de obicei în interiorul kernel-ului. Conținutul memoriei persistă pe întreaga durată a invocării kernel-ului.

Ultimul tip de memorie este memoria de regiștrii. Regiștrii sunt alocați fiecărui fir și reprezintă o zonă de memorie privată. Dacă există 1 milion de fire care declară o variabilă, 1 milion de versiuni vor fi create și stocate în regiștrii lor până la terminarea execuției kernelului, moment în care memoria este eliberată. Variabilele declarate în interiorul unui kernel (care nu sunt matrice și fără un cuvânt cheie) sunt stocate automat în regiștrii. [8]

Programarea CUDA este un tip de programare eterogenă care implică rularea codului pe două platforme diferite: o gazdă și un dispozitiv. Sistemul gazdă constă în principal din CPU, memoria principală și arhitectura sa. Dispozitivul este în general placa video formată dintr-o unitate de procesare grafică CUDA și arhitectura sa.

Codul sursă pentru un program CUDA constă atât în codul pentru sistemul gazdă, cât și în cel al dispozitivului grafic, combinate în același fișier. Deoarece codul sursă vizează două arhitecturi de procesare diferite sunt necesari pași suplimentari în procesul de compilare. [9]

Figura 1.7 reprezintă diferitele straturi ale software-ului într-o aplicație CUDA, de la accesarea din aplicație până la accesarea driver-ului CUDA ce utilizează hardware-ul GPU.

Figura 1.7. Arhitectura software CUDA [9]

Rutina CUDA (uneori abreviată ca și CUDART) este librăria utilizată de către caracteristicile de integrare ale limbajului CUDA. Fiecare versiune a setului de instrumente CUDA are propria versiune specifică a acestei rutine, iar programele construite cu acel set de instrumente vor folosi versiunea corespunzătoare. Un program nu va funcționat corect decât dacă versiunea corectă de CUDART este disponibilă în calea unde este instalat software-ul.

Driverul CUDA este conceput pentru a fi compatibil și cu versiunile mai vechi, fiind compatibil sa execute și programele de o versiune inferioară. Acesta exportă un driver API low-level (în cuda.h) ce oferă dezvoltatorilor posibilitatea de a gestiona resursele și timpii de inițializare.

Două interfețe de programare sunt existente în prezent pentru a scrie programe CUDA: C for CUDA (CUDA C) și CUDA driver API. Acestea sunt folosite exclusiv în programe, ceea ce înseamnă că într-o aplicație se poate folosi fie o metodă fie cealaltă.

Driver-ul CUDA API este un API C low-level ce pune la dispoziție funcții pentru a încărca kernel-urile ca module ale codului binar sau de asamblare și pentru a le lansa în execuție. Codul binar și cel de asamblare sunt de obicei obținute prin compilarea kernel-urilor scrise în limbajul C.

C pentru CUDA vine cu o rutină API, iar rutina împreună cu drver-ul API pun la dispoziție funcții pentru a aloca sau dezaloca memoria, pentru a transfera date între memoria sistemului gazdă și cea a dispozitivului, pentru a putea lucra cu mai multe dispozitive grafice în același timp, etc. Orice fișier sursă ce conține unele dintre aceste extensii trebuie compilat cu utilitarul nvcc.

În contrast cu aceasta, driver-ul API CUDA necesită scrierea a mai mult cod, este mai greu de programat și de testat, însă oferă un nivel mai mare de control și este independent de un limbaj deoarece folosește cod binar sau de asamblare. [10]

Provocarea este de a dezvolta aplicații software ce folosesc în mod transparent procesoarele grafice, indifferent de evoluția acestora. Modelul de programare paralelă CUDA este proiectat pentru a depăși această provocare în timp ce menține o curbă de învățare scăzută pentru programatori familiarizați cu programarea standard, în limbaje, cum ar fi C. În centrul său sunt trei modele cheie – o ierarhie de thread-uri, memorii partajate și o barieră de sincronizare – care sunt pur și simplu expuse la programator ca un set de extensii de limbaj.

Atunci când se utilizează C pentru CUDA se folosesc conceptele de bază ale limbajului, și anume definirea de funcții(kernel-uri) ce sunt executate de un număr N de ori în paralel de N fire de execuție.

Cuda C

CUDA este o extensie a limbajului C la care s-au adăugat cuvinte cheie suplimentare, cum ar fi:

Calificatori de tip – global, device, shared. Local, constant

__device__ float array[N];

Cuvinte cheie – threadIdx, blockIdx, gridDim, blockDim

region[threadIdx.x] = image[i];

Intrinseci – __syncthreads()

Runtime API – memoria, simboluri

// Allocate memory in the GPU

void *myimage;

cudaMalloc(&myimage, bytes);

Funcții kernel pentru a lansa în execuție cod GPU

// 100 thread blocks, 10 threads per block

convolve<<<100, 10>>> (myimage);

CUDA extinde limbajul C cu un nou tip de funcții, kernel-urile, ce execută codul în paralel pe toate thread-urile active. Funcția tipică main() combină atât execuții secvențiale cât și paralele ale funcțiilor CUDA ce sunt lansate în execuție în mod asincron, iar rezultatul este transmis imediat către CPU. Nu se execută o nouă funcție până cânt toate cele de înaintea sa nu s-au terminat de executat.

Figura 1.8. Execuția programelor

Kernel-urile

CUDA C a extins limbajul de programare C prin adăugarea posibilității de a defini funcții C denumite kernel, care, atunci când sunt apelate, sunt executate de N ori în paralel de N Cuda thread-uri diferite, în comparație cu limbajul C, unde funcțiile se execută pe un thread.

Un kernel este definit folosind specificatorul "__global__" și numărul de thread-uri CUDA ce execută acest kernel atunci cand este apelat. Se specific folosind simbolurile <<<…>>>. Fiecare thread este identificat unic printr-un unic thread ID ce este accesibil în cadrul kernel-ului în variabila predefinită "threadIdx".

Ca un exemplu, următorul cod sursă reprezintă o funcție ce adună doi vectori A și B de dimensiune N și depune rezultatul într-un alt vector C:

__global__ void VecAdd(float* A, float* B, float* C)

{

int i = threadIdx.x;

C[i] = A[i] + B[i];

}

int main()

{

// Metoda de apelare Kernel cu N thread-uri

VecAdd<<<1, N>>>(A, B, C);

}

[NVIDIA Corporation, NVIDIA CUDA™ Programming Guide Version 2.3.1, 2701 San Tomas Expressway Santa Clara, August 2009 ]

Terminologie

Host: CPU și memoria de pe placa de bază

Device: Placa grafică (GPU și memoria video)

Figura 1.9. Exemplificarea noțiunii de host și device

CUDA execută un program pe device (GPU) ceea ce poate fi văzut ca un co-processor pentru host(CPU)

CUDA poate fi vazută ca o librărie de funcții ce conține trei tipuri de elemente:

Host – controlează și accesează device-ul

Device – conține funcții specific pentru procesoarele grafice

All – conține tipuri de date și un set de rutine suportate de ambele părți.

Fluxul de procesare CUDA

O aplicație CUDA constă în două părți: codul ce se execută pe device scris în CUDA și codul executat de host, asa cum este ilustrat în Figura 1.10.

Figura 1.10. Componente ale unei aplicații CUDA

Primul pas în fluxul de procesare este copierea datelor din memoria principală în memoria plăcii grafice (1). După aceasta, CPU-ul pornește procesele plăcii grafice (2) ce vor fi executate în paralel (3).În acest moment datele din memorie sunt încărcate în memorii cache pentru a mării performanța. La sfârșitul operației, rezultatul obținut de la GPU este copiat din memoria plăcii grafice în memoria principală a sistemului (4). Toți pașii sunt ilustrați grafic în figura 1.11.

Figura 1.11. Fluxul de procesare CUDA

Compilarea programului

Pentru a compila fișierele sursă se utilizează NVCC, un compilator la nivel de driver oferit gratuit de Nvidia. Un program poate conține cod destinat executării pe GPU, cât și cod pentru CPU. Rezultatul compilării va fi cod PTX (Parallel Thread eXecution) executat de GPU, fie cod C ce urmează a fi executat pe CPU.

Fișierele sursă pentru aplicații Cuda reprezintă o combinație între limbajul C++ covențional interpretat de procesoarele de uz general și funcții pentru dispozitivul GPU. Traiectoria de compilare separă funcțiile dispozitivului grafic de cele ale dispoziivului gazdă, compilează funcțiile CUDA utilizând compilatoarele și assembler-ul NVIDIA, compilează codul pentru procesorul gazdă utilizând un compilator C++ disponibil și apoi incorporează funcțiile GPU compilate ca imagini binare în fișierele interpretate de CPU. În etapa de realizare a legăturilor sunt adăugate biblioteci specifice de execuție CUDA pentru a putea manipula corespunzător dispozitivul de proceare grafică prin metode specifice pentru alocarea buffer-elor de memorie sau transferul de date între host și device.

În faza de compilare CUDA se convertește un fișier sursă codificat în limbajul extins CUDA într-un fișier sursă obișnuit ANSI C++ care poate fi transferat unui compilator gazdă C++ de uz general pentru compilare globală și stabilirea legăturilor. Pașii urmați în timpul compilării sunt descriși în figura 1.12.

Figura 1.12. Fluxul de compilare a programului

Un exemplu de apelare a compilatorului nvcc pentru unul dintre programele dezvoltate în cadrul proiectului este:

E:\LICENTA\LICENTA\CudaPrj>"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\bin\nvcc.exe" -gencode=arch=compute_20,code=\"sm_20,compute_20\" –use-local-env –cl-version 2013 -ccbin "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin" -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include" -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include" -G –keep –keep-dir Debug -maxrregcount=0 –machine 32 –compile -cudart static -g -DWIN32 -D_DEBUG -D_CONSOLE -D_MBCS -Xcompiler "/EHsc /W3 /nologo /Od /Zi /RTC1 /MDd " -o Debug\kernel.cu.obj "E:\LICENTA\LICENTA\CudaPrj\kernel.cu"

Virtual Box

Oracle VM VirtualBox este un hypervisor gratuit și open-source pentru computerele x86 oferită de Oracle Corporation. Acesta reprezintă una dintre cele mai cunoscute și utilizate soluții pentru generarea mașinilor virtuale, iar sistemele emulate de VirtualBox sunt perfect funcționale și dispun de conexiune la rețea sau de suport USB.

VirtualBox poate fi instalat pe un număr mare de sisteme de operare gazdă, inclusiv: Linux, OS X, Windows, Solaris și OpenSolaris.

Terminologie:

Sistemul de operare gazdă (host OS). Acesta este sistemul de operare al mașinii fizice, pe care VirtualBox a fost instalat.

Sistemul de operare virtual (guest OS). Este sistemul de operare instalat pe mașina virtuală. Teoretic, Virtual Box poate folosi orice sistem de operare X86 (Windows, DOS,etc.), dar pentru a obține performanțe apropiate de cele ale unei mașini reale, anumite modificări specifice fiecărui OS trebuie făcute.

Mașina virtuală (VM). Este un mediu special creat de Virtual Box pentru a instala un alt sistem de operare. În general, aceasta reprezintă un set de parametrii ce îi determină comportamentul. Aceștia includ parametrii ce alocă resursele, cum ar fi memoria folosită, spațiul de stocare al datelor,etc.

Setări specifice (Guest Additions). Reprezintă un pachet software instalat pe mașina virtuală, ce are rolul de a crește performanțele sistemului virtual și de a adăuga caracteristici suplimentare.[11]

Virtualizarea

Utilizatorii de VirtualBox pot încărca mai multe sisteme de operare într-un singur sistem de operare gazdă (host OS). Fiecare sistem de operare individual poate fi pornit, utilizat si oprit în mod independent, în interiorul unei mașini virtuale proprie (VM) ce poate fi configurată în funcție de necesități. Sistemul de operare gazdă și cel ce rulează în mașina virtuală pot comunica înre ele prin diferite mecanisme cum ar fi clipboard comun sau prin rețea. De asemenea, sistemele instalate in mașini virtuale pot comunica intre ele daca sunt configurate în acest scop.

Acest sistem simulează de asemenea existența unui hard-disk real, salvat intr-unul din cele trei formate: VDI-este specific Virtual Box (VirtualBox Disk Image), VMDK – acest format este folosit de produsele VMWare cum ar fi VMWare Workstation sau VMWare Player, VHD – este folosit de Windows Virtual PC și reprezintă tipul de disc virtual folosit de sistemele de operare Microsoft Windows, începând cu Windows 7 și Windows Server 2008 R2.

În mod implicit Virtual Box oferă suportul părții grafice prin intermediul unei plăci grafice virtuale care este compatibilă VESA. Ca și adaptor de rețea Ethernet, Virtual Box utilizează următoarele tipuri: AMD PCNET PCI II (Am79C970A), AMD PCNET-Fast III (Am79C973), Intel Pro / 1000 MT Desktop (82540EM), Intel Pro / 1000 MT Server (82545EM), Intel Pro / 1000 T Server (82543GC), adaptor de rețea paravirtualizat (virtio-net).[12]

Plăcile de rețea simulate de sistem oferă sistemelor de operare posibilitatea rulării fără a fi nevoie instalarea în prealabil a driverelor pentru componentele de rețea deoarece sunt “moștenite” de la sistemul de operare gazdă. Un adaptor special de rețea, paravirtualizat, este disponibil. Acesta crește performanța deoarece elimină necesitatea de a avea un driver specific pentru adaptorul folosit.

VirtualBox folosește modul NAT ca mod implicit. Se poate folosi de asemenea și modul Bridged network. Suportă folosirea în paralel a 36 de adaptoare de rețea însă doar patru pot fi configurate prin intermediul interfeței grafice.

Ca și placă de sunet Virtual Box folosește Intel HD Audio, Intel ICH AC'97 sau SoundBlaster. Un controler USB 1.1 este simulat, astfel orice dispozitiv USB folosit de către gazdă poate fi accesat și de mașina virtuală.

Oracle Linux

Oracle Linux (OEL) este o distribuție Linux distribuită de către Oracle în mod gratuit, disponibil sub licența GNU General Public începând cu sfârșitul anului 2006. Este utilizat de către Oracle Cloud și Oracle Engineered Systems cum ar fi Oracle Exadata și altele Deoarece este dezvoltat intern de către Oracle se consideră că are un grad de compatibilitate foarte ridicat cu bazele de date Oracle.

Oracle Linux poate fi instalat și folosit pe diverse arhitecturi de servere cum ar fi IBM, Hewlett-Packard, Dell, Lenovo si Cisco. Din 2010 a fost anunțat suportul pentru Oracle VM Server pentru x86 și Oracle Linux. Acesta este disponibil și pe Amazon EC2 ca o masina virtuală și pe Microsoft Windows Azure.[13]

Serverele Oracle/Sun cu procesoarex86-64 pot fi configurate de asemenea sa funcționeze cu sistemul de operare Oracle Linux.

Oracle Linux 7 poate fi instalat pe sisteme x86-64 cu până la 2048 procesoare logice și 64 TB de memorie. Limita maximă teoretică este de 5120 procesoare logice și 64 TB de memorie, ânsă această configurație nu est suportată.

Ca și configurație minimă sunt necesare 2 procesoare logice, fiind recomandat 1 GB de memorie pentru fiecare CPU logic. De asemenea este nevoie de 1GB de spațiu pe disc necesar instalări. Valoarea recomadată este de 5GB.

Baza de date Oracle

Baza de date Oracle (la care se face referire adeseori ca Oracle RDBMS sau Oracle) este un sistem de gestiune al datelor bazat pe obiecte produs și comercializat de Oracle Corporation. Baza de date este o colecție de date tratate ca unitate. Scopul unei baze de date este de a stoca și prelua informații conexe.

Un server de baze de date este cheia pentru rezolvarea problemelor de gestionare a informațiilor. În general, un server gestionează în mod fiabil o cantitate mare de date într-un mediu cu mai mulți utilizatori, astfel încât mai mulți utilizatori să poată accesa simultan aceleași date, iar toate acestea sunt realizate în timp ce oferă performanțe ridicate. Un server de baze de date previne, de asemenea, accesul neautorizat și oferă soluții eficiente pentru recuperarea datelor șu repararea defecțiunilor.

DBMS (Database Management System) este un software ce controlează resursele fizice, spațiul pe disc, organizarea și coordonează regăsirea datelor. DBMS gestionează trei aspecte importante:datele, motorul bazei de date care permite accesul, blocarea și modificarea datelor, schema bazei de date care definește structura logică.Aceste trei elemente fundamentale ajută la asigurarea concurenței, securității, datelor și procedurilor uniforme de administrare.

Operațiile tipice de administrare a bazelor de date suportate de DBMS includ managementul schimbărilor la nivel de bază de date, monitorizarea și îmbunătățirea performanțelor precum și operațiile de backup și recuperarea datelor. Multe sisteme de gestionare a bazelor de date sunt, de asemenea, responsabile pentru reveniri automate la stări anterioare ale datelor, reporniri și recuperări de date, precum și logarea și auditarea activității. [14]

În mod normal, DBMS-ul conține următoarele elemente:

Codul Kernel-ului – acest element gestionează memoria și resursele fizice

O stuctură ce memorează datele despre date ( denumita și repository sau dicționarul datelor).

Un limbaj de interogare – acesta permite accesul la date

Modelul relational

Modelul relational reprezintă un model de date utilizat pentru a elabora structura logică a une baze de date, fără a ține cont de detaliile fizice sau de modul de acces la date. Scopul modelului relațional este de a furniza o metodă declarativă pentru specificarea datelor și a interogărilor: utilizatorii specifică în mod direct ce informații conține baza de date și ceea ce vor sa afișeze și lasă software-ul sistemului de gestiune al bazelor de date să se ocupe de modul în care datele sunt stocate sau de procedurile apelate pentru a returna valorile cerute.

Acest model asigură omogenitatea prin structurarea datelor în tabele ale căror linii au același format și reprezintă baza pentru un sistem relațional de gestiune al bazelor de date. O linie a unui tabel reprezintă un obiect.

Proprietatile tabelelor:

Numele coloanelor și rândurile din tabel nu pot avea valori duplicate

Nu se ține cont de ordinea rândurilor și a coloanelor

Se definește o relație ca fiind o tabelă cu linii și coloane

Structura fizică și logică a bazelor de date Oracle

O bază de date Oracle este identificată unic printr-un SID și conține cel puțin o instanță a bazei de date precum și zona unde se stochează datele.

Instanța reprezintă fizic un set de procese de sistem și zone de memorie ce interacționează cu zona de stocare.

Sistemul de gestiune al Oracle poate stoca și executa funcții și proceduri. Prin intermediul limbajului procedural PL/SQL sau a limbajului orientat pe obiecte Java aceste structuri pot fi create, apelate sau modificate.

Figura 1.13. Instanța și baza de date Oracle.Arhitectura [15]

Structuri de stocare ale unei baze de date

La nivel fizic – fișierele ce stochează date

Fișierele de date (unul sau mai multe) ce conțin datele bazei de datele structurate in tabele sau indexi.

Fișierele de control – conține datele despre structura fizică a bazei de date, inclusiv numele și locația fișierelor de date.

Fișierele jurnal – seturi de două sau mai multe fișiere de jurnalizare în care se înregistrează orice modificare a datelor.

Alte fișiere importante sunt fișierele de parametrii sau fișierele de configurare a rețelei. Fișierele de backup și fișierele de log arhivate sunt de asemenea importante pentru backup și recovery.

La nivel logic – permit RDBMS-ului controlul utilizării spațiului pe disc

Blocurile de date – cel mai mic nivel de reprezentare pe disc. Datele sunt stocate în blocuri, iar un bloc reprezintă un anumit număr de biți pe disc.

Extent-urile – un număr de blocuri continue pe disc

Segmente – un set de extent-uri alocate pentru un obiect

Tablespace-uri – structură ce grupează logicsegmente. Fiecărui tablespace îi corespunde cel puțin un fișier de date

Stocarea datelor

Oracle RDBMS stochează datele la nivel logic în structuri denumite tablespace-uri și fizic în fișierele salvate pe disc denumite "datafile".

Oracle Database grupează informațiile relaționate în structuri logice denumite scheme, iar obiectele pe care le conține sunt denumite obiectele unei scheme. Atunci când se realizează o conexiune la baza de date sunt necesare un utilizator și o parolă ceea ce indică schema la cer se realizează conectarea. Pentru o bază de date Oracle, numele de utilizator și schema sunt identice.

Accesul la baza de date poate fi obținut printr-un program client, cum ar fi SQL Plus sau SQL Developer. Acestea sunt interfețe ce utilizează limbajul SQL (Structured Query Language) sau PL/SQL(Procedural Language/SQL).

Instalarea și crearea bazelor de date

Pentru a instala software-ul Oracle se apelează utilitarul grafic Oracle Universal Installer (OUI). În timpul procesului de instalare există posibilitatea creării unei baze de date. Dacă alegi această opțiune, OUI pornește automat Oracle Database Configuration Assistant (DBCA) , utilitarul grafic de creere al bazelor de date.

Înainte de a instala software-ul corespunzător, OUI realizează verificări automate pentru a se asigura că sistemul îndeplinește cerințele minime de instalare.

Printre acestea se numara:

Minim 1 GB de memorie

Spațiu suficient pentru a fi alocat

Un format al sistemului de fișiere adecvat

Bazele de date Oracle sunt mai versatile în comparație cu celelalte baze de date existente, putând rula și gestiona un număr mare de tranzacții, de aceea se potrivețte pentru sisteme intens folosite.

Au fost dezvoltate noi metode pentru a asigura protecția datelor cum ar fi Oracle Data Guard sau Oracle Real Application Cluster, dar și metode pentru a înbunătății performanța și a gestiona eficient resursele serverelor cum este Oracle Exadate.

Alte avantaje ale bazelor de date Oracle:

Metode de partiționare și sub-partiționare eficiente ca o soluție pentru a personaliza soluțiile de bussines

Cel mai avansat optimizator care poate să interpreteze și cele mai sofisticate tipuri de modelare a datelor. Acesta include interogările subdimensionate sau recursive

Conține cele mai avansate funcții analitice construite la nivelul limbajului C

Are cel mai organizat mod de gestionare a codului în baza de date, iar prin limbajul PL/SQL se pot definii pachete, funcții, proceduri, ceea ce permite reutilizarea codului ce interacționează cu datele și obiectele din baza de date.

Au implementat indexii de tip BitMap, o unealtă puternică pentru a gestiona date cu anumite tipuri de cardinalitate

Permite personalizarea hardware-ului în funcție de necesitățile soluției implementate cu o mare varietate de opțiuni de design ce înbunătățesc performanțele

Pune la dispoziție un set de instrumente pentru a gestiona și monitoriza activitatea, cum ar fi cele pentru creerea bazelor de date sau de backup al datelor. Backup-ul se poate realiza, fără ca activitatea să fie întreruptă, spre deosebire de metode ale altor software-uri de baze de date

Ceea ce face ca baza de date Oracle să nu fie uilizată în aplicațiile de dimensiune mică este faptul că multe dintre facilitățile oferite nu sunt disponibile gratuit. Se poate utiliza însă versiunea de Oracle Express Edition (XE) ce este disponibilă gratuit, dar vine cu anumite limitări.

Librăria ManagedCuda

ManagedCuda asigură accesul intuitiv la diverul API al Cuda pentru orice limbaj .NET. Este echivalentul unei rutine API, dar scrisă în totalitate în limbajul C# pentru .NET. În contrast cu rutinele API, managedCuda presupune un mod diferit de reprezentare a elementelor specifice CUDA, fiind orientat pe obiect.

În general, există câte o clasă C# corespondentă unei metode oferită de driver-ul API CUDA. De exemplu, în locul variabilelor pentru GPU (device) pe care driver-ul original le accesează prin intermediul pointerilor C standard managedCuda oferă posibilitatea reprezentării pin clasa Cuda[Pitched]DeviceVariable. Aceasta este o clasă generică ce permite accesul la diverul API în mod sigur și orientat pe obiect. Cum obiectul CudaDeviceVariable iși cunoaște tipul de date, dimensiunea vectorului, dimensiunea zonei d ememorie necesară, simpla apelare a funcției CopyToHost(“hostArray”) este suficientă pentru a copia variabila din memoria sistemului de progesare Cuda în memoria sistemului gazdă.

Pentru a folosi această librărie sunt în de asemenea necesare și cunoștințe de limbaj CUDA-C, kernel-ul fiind în totalitate conceput în acest limbaj. Se utilizează managedCuda penru a facilita comunicația între device și host și pentru a folosii cu ușurință un limbaj orientat pe obiect, cum ar fi C#.

Librăria OpenCV

OpenCV (Open Source Computer Vision Library) este o librărie open-source ce include câteva sute de algoritmi de prelucrare vizuală. Structura acestei librării este modulară, ceea ce înseamnă că întregul pachet conține anumite librării statice sau distribuite. Sunt disponibile următoarele module:

Core – un modul compact ce definește structurile de date de bază, introducând și tipul Mat ce reprezintă o matrice multidimensional, dar și funcții folosite de către celelalte module;

Imgproc – un modul pentru procesarea de imagine ce include filtarea liniară sau ne-liniară, transformări geometrice ale imaginii, histograme și asa mai departe;

Video – un modul pentru analiză video ce include estimare a miscarii și obiect algoritmi de urmărire;

Calib3d – ce conține printer altele și elemente de reconstrucție 3D;

Features2d

Objdetect – un modul pentru detecția de obiecteși instanțe ale ale unor clase predefinite de exemplu fețe, ochi, masini;

Highgui – utilizat pentru captură de video sau imagini, precum și elemente simple de UI;

Gpu – foloseste algoritmi ce folosesc puterea de calcul a GPU-urilor.

Modulul GPU

Modulul GPU al OpenCv utilizat în realizarea lucrării este un set de clase și funcții ce folosesc puterea de calcul a GPU-urilor. Librăria este implementată folosind NVIDIA CUDA Runtime API și suportă doar procesoarele graficele NVIDIA și include funcții și primitive de nivel jos pentru prelucrare grafică, precum și algoritmi de nivel înalt. Funcțiile și primitivele low-level oferă o infrastructură pentru a dezvolta algoritmi mult mai rapizi ce se folosesc de puterea de calcul oferită de procesoarele grafice actuale.

Libraria Emgu CV

Emgu CV este o platformă .NET a librariei Open CV specializată pentru procesarea de imagini. Aceasta permite funcțiilor din Open CV să fie accesare dintr-o aplicație .NET și este compatibilă cu limbaje precum C#, VB, VC++, Python etc., fiind scrisă integral în C#. OpenCV, librărie dezvoltată de Intel, este utilizată pentru procesarea grafică în timp real și se poate utiliza în mod gratuit de către dezvoltatori.

Arhitectura – prezentare generală

Librăria Emgu CV este alcătuită din două structuri, asa cum se poate observa în Figura 1.14.:

Stratul de bază (layer 1) conține funcții, structuri și referințe către funcții ale OpenCV

Cel de-al doilea strat (layer 2) ce combină funcțiile din stratul de bază cu beneficiile limbajelor .NET.

Figura 1.14. Arhitectura generală a EmguCV [16]

Detecția fețelor folosind tehnologia CUDA

În ultimii ani, recunoașterea fețelor a atras mai multă atenție, iar studiile despre acest domeniu au fost dezvoltate nu doar de către ingineri, dar și de neurologi deoarece există mai multe potențiale aplicații în sisteme de acces automate sau sisteme de comunicare vizuală. În mod deosebit, detecția fetelor este o parte importantă în recunoașterea fețelor, fiind primul pas în recunoașterea automată.

Cu toate acestea, detectarea nu este simplă deoarece există multe variații ale aspectului imaginii cum ar fi poziția feței (frontală, non-frontală), expresiile faciale, condițiile de iluminat,etc. [17]

Pornind de la ideea că o imagine reprezintă o matrice de valori ce redau intensitatea luminii, numite pixeli, s-au dezvoltat numeroși algoritmi pentru detectarea feței prin analiza lor.

Această operație este una costisitoare în deoarece există o variație foarte mare a formei și culorii feței umane și este un obiect dinamic.

Un algoritm de detecție a feței este metoda Viola&Jones, o metodă propusă de Paul Viola și de Michael Jones în lucrarea "Rapid Object Detection using a Boosted Cascade of Simple Features" publicată în 2001 și se bazează pe calcularea și identificarea în sub-regiuni ale imaginii inițiale a unor trăsături de tip Haar utilizând un clasificator de tip cascadă. Aceste trăsături reprezintă contrastul între grupuri adiacente de pixeli și valori ale variației intensității luminii. Prin variația contrastului se pot determina zonele întunecate sau luminate din imagine, iar prin determinarea a două sau trei grupuri adiacente se identifică o caracteristică Haar. Caracteristicile pot fi redimensionate ceea ce permite detectarea obiectelor de diferite mărimi. [18]

Se pornește de la ideea că pentru a calcula un clasificator nu se consumă mult timp, dar pentru a calcula toți clasificatorii dintr-o imagine de dimensiuni mai mari este imposibil. Pentru a crește eficiența algoritmilor acest calcul se face în cascadă, aplicându-se pe rând clasificatorii până când unul din ei nu este valid, fie până când imaginea conține o față. Prin aplicarea algoritmului Viola&Jones se pot obține astfel prin aplicarea a doar 200 de clasificatori simpli rezultate cu o acuratețe de 95% cu o medie de 5 frame-uri pe secundă. [19]

Implementarea acestui algoritm se poate realiza prin parcurgerea anumitor pași prezentați în următorul pseudocod. [20]

Pentru numărul de scări în piramida imaginilor execută

Trecere la o nouă scară

Calculează imagine integrală pentru treapta curentă

Pentru fiecare portiune din imagine execută

Pentru fiecare etapă în clasificator execută

Aplică filtru de detecție

Centralizare rezultate pentru treapta curentă

Dacă rezultatul final nu trece de limita impusă

Iese din bucla și returnează că nu există nicio față în imagine

Dacă trece de de toate limitele pe fiecare treaptă

Se transmite că s-a detectat o față

Altfel rezultatul este negativ

Piramida imaginilor este o reprezentare pe mai multe trepte a unei imagini, asa cum este reprezentat în figura 1.15, fiind necesară pentru a detecta fețele mai mici sau mai mari în funcție de apropiere folosind aceiați fereastră de detecție.

Figura 1.15. Piramida imaginilor [21]

Imaginea integrală (sau suma suprafeței tabelului) este modul în care se însumează valorile pixelilor dintr-o regiune dreptunghiulară (definită de exemplu de către punctele A, B, C, D ca în figura 1.16) ce se dovedește a fi eficientă dacă vrem să însumăm pixelii din mai multe regiuni de interes ale une imagini.

Figura 1.16. Suma suprafețelor tabelului [22]

O fereastră de detecție se deplasează pe toată suprafața imaginii pe fiecare treaptă cu scopul de a detecta fețele, asa cum este reprezentat în figura 1.17 următoare. De fiecare dată când fereastra se deplasează regiuniea din imagine va trece prin clasificatorul cascadă ce av fi explicat în continuare.

Figura 1.17. Deplasarea ferestrei de detecție [23]

Clasificatorul cascadă constă în mai multe tipuri de filter, asa cum este reprezentat în figura 1.18. Pentru fiecare regiune din imagine se va trece pas cu pas prin acești clasificatori. Dacă regiunea nu depășește limita inpusă a treptei, clasificatorul va respiinge imediat regiunea ca a nu conține o fată. Dacă această regiune trece de limitele stabilite va fi marcată ca un candidat pentru a reprezenta o față și reprocesată ulterior.

Figura 1.18. Clasificatorii cascadă [24]

OpenCV pune la dispoziție mai multe sisteme pre-instruite de clasificatori pentru față, ochi, zâmbet sub forma unor fișiere XML ce se pot include ulterior în diverse proiecte. Pentru acești clasificatori pre-instruiți există câte 25 de etape, iar fiecare conține mai multe filter Haar ce variază. Pentru modul de implementare în paralel pe GPU, într-o etapă sub-regiunea unei imagini va trece prin toate aceste filtre în paralel. Rezultatele acestor filtre vor fi însumate și comparate cu un prag pe baza căruia se va lua decizia de detectare. Timpul de procesare se va reduce prin respingerea unei regiuni în stagii incipiente, dar pe de altă parte induce dependențe între etape.

Fiecare etapă a clasificatorului cascadă conține mai multe filtre pentru a detecta proprietățile Haar. Câteva exemple ale acestor ferestre de detecție sunt reprezentate în figura 1.19.

Figura 1.19. Proprietăți Haar [25]

Acest algoritm însă nu este foarte eficient în cazul imaginilor de rezoluție foarte mare,ceea ce ar face ca detecția să nu se mai realizeze în timp real, de aceea s-au căutat soluții pentru a realiza rapid detecția având costuri rezonabile. Paralelizarea s-a dovedit cea mai eficientă metodă pentru a obține timpi de execuție mai buni.

Pentru a detecta fețele într-o anumită imagine trebuie să o parcurgem pe sub-ferestre și să verificăm dacă reprezintă sau nu o față. Acest pas al verificării unei sub-imagini constă de fapt în executarea unui aceluiași set de operații de fiecare dată. Calculele din fiecare regiune nu depind de o altă fereastră de detecție, ceea ce face ca detecția fețelor să fie o problemă ce se poate paraleliza, iar procesoarele grafice CUDA pot execua cu ușurință acest tip de operații, reducând considerabil timpul de lucru. [26]

Detecția pietonilor folosind CUDA

Detecția pietonilor este o sarcină importantă și esențială a oricărui sistem inteligent de supraveghere video deoarece aceasta asigură informații fundamentale pentru înțelegerea filmărilor video. Are diverse aplicații cum ar fi robotica sau siguranța persoanelor sau supravegherea traficului.

În ciuda provocărilor, acest tip de detecție rămâne o zonă activă de cercetare în prelucrarea grafică din zilele noastre, fiind propuse mai multe moduri de abordare.

Open CV pune la dispoziție un model pre-antrenat HOG și SVM Linear ce poate fi utilizat pentru a realiza detecția persoanelor atât în imagini, cât și în fluxuri video.

Ideea de bază a acestei funcții este că forma și aspectul unui obiect pot fi caracterizate adesea foarte bine de către distribuția intensității gradienților sau direcția marginilor. Implementare acestor descriptori poate fi realizată prin divizarea imaginii în mici regiuni conectate, denumite celule, iar pentru fiecare celulă să aplice algoritmul pentru histograma gradienților pentru pixelii din celula, iar prin combinarea acestor histograme se reprezintă descriptorul.

Calcularea HOG (Histrogram of Oriented Gradients) se face în mai mulți pași, și anume: normalizarea imaginii globale, calcularea gradienților imaginii pe axele x și y, calcularea histogramelor gradienților, normalizarea blocurilor și stocarea valorlor într-un vector de caracteristici.

Primul pas este opțional și se aplică imaginii globale având scopul de a reduce efectele influenței luminii. În practică se folosește fie compresia gamma fie calcularea rădăcinii pătrate a fiecărei valori a canalului de culoare.

În cel de-al doilea pas se realizează calculul gradienților. Se detectează contururi, siluete și câteva caracteristici de textură. Se folosește canalul de culoare dominantă.

De exemplu, în figura 1.20 ce reprezintă o sub-regiune a imaginii inițiale, au fost delimitate celulele, iar rezultatul calculului gradienților într-o celulă de dimensiune 8×8 pixeli este ilustrat în figura 1.21.

Figura 1.20. Regiune din imaginea initială [27]

Figura 1.21. Celulele RGB și gradient reprezentate cu ajutorul săgeților sau ca numere

Următorul pas este de a crea histograma gradienților din aceste celule. Histograma conține nouă structuri corespondente unghiurilor 0, 20,40,..,160, ilustrat pentru exemplul dat în figura 1.22. Asupra valorilor obținute se aplică apoi din nou operația de normalizare ce ia grupuri locale de celule și normalizează contrastul înainte de a trece la pasul următor, Acela de a colecta din toate blocurile valorile descriptorilor într-un vector de caracteristici. [24]

Figura 1.22. Obținerea histogramei gradienților

Figura 1.23. Rezultatul final

Codarea/Decodarea Base64

Base64 este un grup de scheme de codare similare binary-text ce reprezină informația binară sub formă de șir de caractere ASCII, translatând informația binară în reprezentarea în baza 64. Fiecare caracter ocupă exact 6 biți de date în reprezentarea base64.

Strategia generală este de a alege 64 de caractere ce sunt member comune în mai multe seturi de caractere. Această combinație asigură siguranța transmiterii integrale a datelor, fără a exista posibilitatea modificării acestora de către protocoalele de comunicație care pot interpreta anumite siruri de biți ca și date de control sau caractere speciale.

Base64 poate fi folosit în mai multe context, cum ar fi:

Transmiterea și stocarea textelor ce pot cauza coliziuni

Cei care transmit mesaje spam pentru a evita aplicațiile anti-spam ce nu întotdeauna decodează șirurile base64 și, prin urmare, nu pot detecta cuvintele cheie din mesajele codate.

Integrarea de date binare în fișiere XML

Codarea de fișiere binare, cum ar fi imaginile, pentru a evita dependința de fișierele externe

Integrarea imaginilor în proprietăți CSS, cum ar fi imagini de fundal.

CAPITOLUL 2. Etapele Proiectării și Rezultatele Testelor de Performanță

Proiectarea aplicației pentru testarea performanțelor tehnologiei CUDA vs aplicație CPU Threads

Pentru a realiza testele de performanță s-a proiectat o aplicație cu scopul de a executa aceiași funcție pe GPU și pe CPU.

Primul test implică simpla combinare a pixelilor a două imagini prin trei operații de înmulțire și creșterea dimensiunii setului de date, iar cel de-al doilea presupune păstrarea unui set de date constant de pixeli, corespondentul unei imagini cu rezoluția de 800×800, și creșterea complexității operațiilor. Se execută pentru a testa viteza de calcul pentru o adunare, pentru două adunări și trei înmulțiri sau patru adunări și cinci înmulțiri.

Aplicația utilizată pentru testarea performanțelor procesoarelor grafice în comparație cu procesoarele de uz general a fost realizată folosind limbajul de programare C#. Pentru a utiliza acest limbaj în combinație cu limbajul CUDA C a fost folosită librăria ManagedCuda.

ManagedCuda este o librărie ce combină puterea de calcul a GPU Cuda și flexibilitatea oferită de limbajele .NET. De asemenea include clase pentru interacțiunea usoară cu limbajul CUDA și operații de interoperare cu acesta, precum și folosirea de tipuri predefinite de limbaj, de exemplu float3. Librăria ManagedCuda oferă și posibilitatea de a folosi librăriile de bază dezvoltate de CUDA, cum ar fi CUFFT, CURAND, CUSPARSE, CUBLAS, CUSOLVE, NPP și NVRTC.

Accelerarea aplicației cu ajutorul paralelizării CUDA se poate realiza fără restricții deoarece funcția kernel este scrisă în limbajul CUDA-C și toate funcționalitățile limbajului sunt păstrate. Apariția ulterioară a unor noi versiuni de NVIDIA CUDA nu presupune modificarea aplicației de unde este apelat kernel-ul.

Teste cu varierea dimensunii setului de date

S-a proiectat în cadrul aceastei aplicații o metodă pentru a observa variația timpului de procesare în raport cu dimensiunea setului de date. Se consideră un vector de pixeli de dimensiune, echivalentul unei imagini de 800×800.

Proiectul VisualStudio de testare a performanțelor conține în structura sa două funcții: funcția gazdă, dezvoltată în limbajul C# cu ajutorul librăriei ManagedCuda de unde se apelează funcția kernel ce urmează a fi executată în paralel pe GPU și funcția ce folosește limbajul CUDA-C, realizată pentru a fi executată în paralel.

Implementarea metodelor

În aplicația gazdă există funcții care asigură interacțiunea între CPU și GPU. Se definește un obiect de tip CudaKernel ce este inițializat la apelarea funcției InitKernelsAdd. De asemenea este nevoie de obiecte de tip CudaContext și CUmodule ce supervizează comunicația între Driver API și CUDA și reține datele de control pentru utilizarea device-ului. Pentru o instantă sunt reținute adresele alocate, modulele încărcate ce conțin codul, modul în care se salvează datele în memorie pentru a permite accesul CPU și GPU la aceleași zone de stocare a datelor, etc.

Codul din programul pentru gazdă ce realizează aceste operații este următorul:

static CudaKernel kernelAdd;

static void InitKernelsAdd()

{

CudaContext context = new CudaContext();

CUmodule modul = context.LoadModule(@"C:\Users\Elena\Desktop\threads test\CudaPrj\Debug\kernel.ptx");

kernelAdd = new CudaKernel("_Z6kernelPiS_S_i", modul, context);

kernelAdd.BlockDimensions = NR_THREADS_BLOCK;

kernelAdd.GridDimensions = DIM_VECTOR / NR_THREADS_BLOCK + 1;

}

În aceiași aplicație a fost creată funcția adVectori ce are rolul de a apela kernel-ul Cuda. Codul sursă este:

static Func<int[], int[], int, int[]> adVectori = (a, b, dim) =>

{

// initializare parametrii

CudaDeviceVariable<int> vectA = a;

CudaDeviceVariable<int> vectB = b;

CudaDeviceVariable<int> h_vectorOut = new CudaDeviceVariable<int>(dim);

//executare metoda cuda

kernelAdd.Run(vectA.DevicePointer, vectB.DevicePointer, h_vectorOut.DevicePointer, dim);

// copiere rezultate pe host

int[] rez = new int[dim];

h_vectorOut.CopyToHost(rez);

return rez;

};

Primul pas este inițializarea variabilelor pentru GPU de tip integer, CudaDeviceVariable<int>, necesare pentru a reține în memorie vectorii ce urmează a fi procesați. Prin apelarea funcției Run, cea care lansează în execuție kernel-ul, se obține rezultatul final în varibla _hostOut. Copierea înapoi în memoria sistemului de calcul principal se face prin metoda CopyToHost(output).

Funcția main apelează metodele de mai sus, dar execută și prin procesare serială aceiași operație asupra vectorilor care este realizată de către kernel:

newArray[i] = vectorA[i] * vectorB[i] * vectorA[i] * vectorB[i]

Scenariu de utilizare

Se va executa aplicația pentru o dimensiune diferită a setului de date pentru a observa dependent între volumul de date și timpul de procesare.

Execuția se va realiza în modul sincron, asa cum este ilustrat în Figura 2.1, adică procesorul așteaptă rezultatul de la GPU pentru a-și putea continua procesele.

Figura 2.1. Modul de operare sincron între CPU și GPU [28]

Constanta NR_THREADS_BLOCK are valoarea 1024, ceea ce reprezintă numărul de fire de execuție dintr-un bloc de procesare Cuda.

Teste cu creșterea complexității operațiilor aplicate

Modul de proiectare și structurare al aplicației este asemănător cu cel de mai sus, fiind necesară includerea în același proiect VisualStudio a două module. Programul gazdă este dezvoltat în limbajul C# și utilizează librăria ManagedCuda și apelează kernelul CUDA-C.

Implementarea metodelor

Funcțiile kernel sunt definite având patru parametrii: __global__ void kernelOps(int* a, int* b, int* rezultat, int N), unde a și b reprezintă vectorii de intrare, out este vectorul în care se va calcula rezultatul, iar N reprezintă dimensiunea vectorilor.

Se folosesc și trei variabile puse la dispoziție de limbajul CUDA-C pentru a stabili proziția în matricea de procesare, și anume:

threadIdx.x – reprezintă id-ul thread-ului din blocul curent

blockDim.x – această valoare ne indică numărul de thread-uri existente într-un bloc. După ce kernel-ul este apelat, acest număr rămâne cosnstant.

blockIdx.x – reprezintă blocul curent în care kernel-ul este executat.

Codul funcției este următorul:

__global__ void kernelOps(int* a, int* b, int* rezultat, int N)

{

int i = blockDim.x * blockIdx.x + threadIdx.x;

/*if (i < N)

rezultat[i] = a[i] + b[i];*/

/*if (i < N)

rezultat[i] = a[i] * 0.11 + b[i] * 0.59 + a[i] * 0.3;*/

if (i < N)

rezultat[i] = a[i] * 0.11 + b[i] * 0.59 + a[i] * 0.3 + b[i] * 0.74 + a[i] * 0.52;

}

Pentru a compara execuțiile serială și paralelă se va repeta apelarea unei metode ce execută aceiași funcție de 50 de ori astfel încât testele să fie relevante, iar modul de implementare este prezentat în continuare.

InitKernelsOps();

int[] vectorAo = Enumerable.Range(1, DIM_VECTOR).ToArray();

int[] vectorBo= Enumerable.Range(1, DIM_VECTOR).ToArray();

Console.WriteLine("**************Start test Ops**************");

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

int[] vectorOps = OpsVectori(vectorAo, vectorBo, DIM_VECTOR);

watch2.Stop();

Console.WriteLine("Gpu: " + watch2.Elapsed.ToString());

}

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

Parallel.Invoke(() =>

{

B();

});

watch2.Stop();

Console.WriteLine("Cpu: " + watch2.Elapsed.ToString());

}

Console.ReadKey();

}

Se va executa aplicația pentru fiecare kernel ce conține un număr de operații diferite. Astfel prima funcție combină pixelii celor două imagini prin adunare, cea de-a doua execută două adunări și trei înmulțiri, iar cea de-a treia patru adunări și cinci înmulțiri.

CAPITOLUL 3. Etapele Proiectării aplicației pentru detecția fețelor în paralel

Proiectarea aplicației

Detecția fețelor rămâne în continuare o zonă activă de interes deoarece găsește aplicabilitatea în numeroase domenii din prezent cum ar fi recunoașterea fețelor, inteligența militară și supraveghere, interacțiunea om-calculator.

Algoritmii de detecție ai fețelor sunt consumatori de resurse și timp deoarece realizează calcule complexe, ceea ce face foarte dificilă integrarea acestora în sisteme de detecție în timp real. Aceste efecte și limitări ale algoritmilor de detecție pot fi depășite prin împărțirea sarcinilor de lucru între procesorul de uz general și cel grafic.

Primul algoritm de detecție în timp real a fost propus de Viola și Jones, ce a devenit cel mai utilizat algoritm pentru acest tip de operații. Cu toate acestea, algoritmul nu are efectele dorite atunci când este aplicat pe imagini de rezoluție ridicată, de aceea s-au căutat soluții pentru detecția rapidă ce oferă înaltă performanță la costuri rezonabile. Paralelizarea este cel mai bun mod de a obține acest lucru.

În această etapă a lucrării am dezvoltat și adaptat algoritmul pentru detecția fețelor capabil să fie executat în paralel de către procesoarele grafice.

Modelul dezvoltat combină metodele convenționale de programare folosind CPU cu programarea GP-GPU folosind NVIDIA CUDA pentru a accelera aplicațiile.

Există anumite aspecte care trebuie avute în vedere atunci când se trece de la execuția pe sistemul curent la cel pe platforma CUDA.

Nu există noțiunea de funcție recursivă. Orice funcție recursivă trebuie transformtă mai întâi în bucle.

Lățimea de bandă a magistralei între GPU și CPU poate să introducă întârzieri

Algoritmii se pot utiliza doar pe procesoarele grafice NVIDIA.

Aplicațiile care depind de seturi de date procesate anterior nu pot fi implementate eficient folosind CUDA.

Algoritmul Viola-Jones a adus ca și noutăți în algoritmii de detecție ai fețelor trei noi metode pentru a crește eficiența și rapiditatea, și anume imaginile integrale folosite pentru calcule mai rapide, Adaboost pentru selecția caracteristicilor și metoda cascadării clasificatorilor pentru a aloca eficient resursele. Algoritmul a fost divizat în două părți, partea de antrenare și partea de detecție.

Procedura de detecție clasifică imaginile bazându-se pe caracteristici simple. Caracteristicile utilizate sunt similare cu funcțiile Haar, mai exact se folosesc trei tipuri de caracteristici. Valoarea caracteristicii alcătuită din două dreptunghiuri este diferența dintre suma pixelilor din cele două regiuni ce au aceiași dimesiune și formă și sunt adiacente. Caracteristica alcătuită din trei dreptunghiuri calculează suma dintre dreptunghiurile exterioare și se scade din suma intensităților regiunii din centru, iar caracteristica alcătuită din patru dreptunghiuri calculează diferența dintre perechile de zone de pe diagonale. Fiind dată o rezoluție a detectorului de 24×24 pixeli se obține un număr mare de caracteristici, aproximatix 160000.

Calcularea valorii intensităților din regiunile dreptunghiulare ale unei imagini ar fi proces consumator de timp, dar pentru a îmbunătății viteza de procesare se folosesc imaginile integrale ce sunt reprezentări intermediare ale imaginii. Imaginea integrală în punctul (x,y) conține suma pixelilor de deasupra, din stanga și inclusiv valoarea din acel punct:

,

unde ii(x,y) este imaginea integrală, iar i(x,y) este imaginea originală. Folosind următoarea pereche de recurențe imaginea integrală poate fi calculată într-un singur pas.

unde x(x,y) este suma cumulativă a rândului, s(x,-1)=0, iar ii(-1,y)=0.

Folosind imaginile integrale orice sumă dintr-o suprafață dreptunghiulară poate fi calculată din valorile a patru puncte.

Pentru a utiliza avantajele tehnicii de folosire a imaginii integrale se consideră o abordare în care se creează o piramidă a imaginilor. Ca și alte sisteme de detecție algoritmul scanează imaginea la diferite scale pornind de la rezoluția de 24×24 pixeli la care se adaugă un factor de scalare cu 1.25 mai mare decât ce anterior. Un detector de dimensiune fixă este apoi cel care scanează întreaga imagine și evaluează un set de caracteristici dreptunghiulare la orice scală și în orice regiune.

Un alt aspect important este modul în care se face evaluarea deoarece se folosește un arbore de decizie care se numește “cascadă”. Un rezultat pozitiv din prima etapă generează ca eveniment evaluarea unui al doilea set de clasificatori al cărui rezultat pozitiv conduce către un al treilea set și asa mai departe. Un singur rezultat negativ duce la respingerea imediată a imaginii.

Această structură cascadă se bazează pe faptul că într-o singură imagine conține în mare parte regiuni care sunt negative ce vor fi respinse din cele mai incipiente etape posibile.

În procesarea imaginilor toate sub-ferestrele vor fi normalizate pentru a reduce sau minimiza efectele ale diferitelor tipuri de iluminat. Variația unei sub-ferestre poate fi obținută rapid prin folosirea unei perechi de imagini integrale.

Se pornește de la ideea că , unde σ este deviația standart, m este media, iar x este valoarea pixelului din sub-fereastra. Media unei regiuni poate fi calculată cu ajutorul unei imagini integrale. Suma pixelilor la pătrat se calculează utilizând o imagine integrală a imaginii la pătrat. În timpul scanării efectul normalizării imaginii poate fi obținut prin multiplicarea ulterioară a valorilor caracteristicilor în schimbul operațiilor cu pixeli.

Detectorul final este scanat de-a lungul imaginii la mai multe scale și în mai multe regiuni prin deplasarea ferestrei de detecție cu un anumit număr de pixeli. Scalarea este obținută prin scalarea detectorului, fapt care face ca evaluarea sa aibă același cost pentru fiecare scală. Rezultate bune ale detecției au fost obținute folosind scalarea cu factor de 1.25. [29]

Implementarea aplicației

Arhitectura propusă în această lucrare folosește atât CPU cât și GPU pentru a realiza detecția fețelor folosind CUDA. Principalul rol al procesorului de uz general este preluarea imaginii și convertirea in tonuri de gri, calculul imaginilor integrale precum și gestionarea transferului de date între memoria principală și memoria procesorului grafic. O parte dintre aceste operații sunt realizate c ajutorul librăriei OpenCv.

Algoritmul pentru detecție a fost implementat urmărind pașii pseudocodului descris mai jos:

Preluarea imaginii și convertirea în tonuri de gri

Încărcarea clasificatorilor din fișierul xml pre-antrenat

Calclul imaginilor integrale

Calcularea și salvarea într-un vector a scalelor

Inițializare GPU și alocarea resurselor de memorie necesare

Pentru toate imaginile scalate execută

Crează toate sub-ferestrele

Se asignează fiecare sub-fereastr unui bloc GPU

Pentru fiecare sub-fereastră execută

Calculează caracteristici pentru fiecare în paralel

Clasifică fiecare fereastră ca fiind o față sau nu

Trimite rezultatul înapoi la CPU

În funcție de rezultat se afișează dreptunghiurile

Pentru a încărca imaginea și a o transforma în tonuri de gri am folosit funcția OpenCV cvLoadImage ce primește ca parametrii calea de pe disc unde se găsește imaginea și parametrul CV_LOAD_IMAGE_GRAYSCALE.

IplImage* imagine;

//incarca imaginea in grayscale

if ((imagine = cvLoadImage(imgPath, CV_LOAD_IMAGE_GRAYSCALE)) == 0)

{

cout << "Eroare la incarcarea imaginii" << endl;

system("pause");

return 0;

}

Următorul pas este încărcarea clasificatorilor și a caracteristicilor din fișierul pus la dispoziție de către OpenCv ce conține un sistem cascadă pre-antrenat pentru detecția fețelor frontale. În prima fază se folosește o structură de tip CvHaarClassifierCascade, iar datele sunt apoi transferate în tipuri definite în cadrul aplicației. Acestea sunt DreptGPU,DreptCaracteristicaGPU, CaracteristicaHaarGPU, ClasificatorHaarGPU, ClasifHaarEtapaGPU și HaarCascadeGPU. S-au definit aceste tipuri după tipurile folosite de OpenCv pentru detecție deoarece facilitează transferul daelor în memoria GPU deoarece folosesc vectori 1D în memorie pentru care se folosește offset pentru fiecare etapă de clasificatori în locul pointerilor.

Calcularea imaginilor integrale se face apelând funcția cvIntegral(imagine, sum, sqsum) ai cărei parametrii reprezintă imaginea originală, și două variabile sum si sqsum de tip CvMat în care se vor extrage rezultatele.

Scalarea imaginii se va realiza cu factorul 1.25, iar valorile de scalare se vor stoca în vectorul std::vector<double> scala.

double factor = 1.0f;

float factorScalare = 1.25f;

std::vector<double> scara;

while (factor * HaarCascGpu.dimOrigFereastra.width < w – 10 && factor * HaarCascGpu.dimOrigFereastra.height < h – 10)

{

scara.push_back(factor);

printf("%d ", factor);

factor *= factorScalare;

}

Inițializarea procesorului grafic reprezintă pasul în care se alocă în memoria GPU zone în care se va stoca rezultatul procesării. Se folosesc variabile specifice pentru host și device și se copiază imaginile integrale intr-o zină de memorie a GPU numită zonă de texturi. Această memorie este globală, singura duferență fiind că accesarea zonei se face printr-o zona cache de tip read-only optimizată pentru localizarea spațială în sistemul de coordonate al texturii.

Penru fiecare scală din vectorul de scale obținut anterior se redimensionează ferestrele și caracteristicile și se copiază valorile scalate pe dispozitivul GPU. După stabilirea ferestrelor de detecție se execută în paralel pentru fiecare operația de cascadare a clasificatorilor.

kernelDetectie << <blocks, threads >> >(dev_gpuHC, dev_feteDetectate);

Această funcție presupune inițial că în fereastra pe care o analizează există o față, apoi pentru numărul de etape pe care îl conține obiectul HaarCascadeGPU HCascade parcurge fiecare clasificator în cascadă și calculează pentru fiecare caracteristică suma. Dacă suma obținută în final depășește pragul impus de etapă se trece la analiza următoarei etape, iar în caz contrar procesarea se încheie și se stabilește faptul că nu este prezentă nicio față în acea fereastră. Marchează apoi cu 0 poziția din vectorul de fețe ce va conține rezultatul final pentru a indica CPU-ului că răspunsul este negativ.

for (int i = 0; i < HCascade.nrEtape; i++)

{

loat sumaEtapa = 0.0;

for (int j = 0; j < clasifEtapa[i].nrDeClasificatori; j++)

{

int index = j + clasifEtapa[i].offsetClasificator;

ClasificatorHaarGPU classifier = HCascade.clasificatori_haar_scalati[index];

sumaEtapa += runHaarFeature(classifier, fereastraDetectie, variance_norm_factor, fereasra_inv);

}

// clasificatorul nu a trecut, se iese din cascada

if (sumaEtapa < clasifEtapa[i].prag)

{

//seteaza latimea 0 pt a indica CPU-ului ca aceasta nu e o fata

feteDetectate[offset].width = 0;

break;

}

}

Metoda __device__ float runHaarFeature(ClasificatorHaarGPU clasificator, DreptGPU fereastraDetectie, float variance_norm_factor, float weightScale) a fost definită pentru a analiza caracteristicile unui clasificator. Aceasta calculează suma folosind imaginile integrale și stabilește valoarea rezultată.

__device__ float runHaarFeature(ClasificatorHaarGPU clasificator, DreptGPU fereastraDetectie, float variance_norm_factor, float weightScale)

{

double t = clasificator.prag * variance_norm_factor;

double sum = calculSum(clasificator.caracteristica_haar.drept0.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept0.weight * weightScale;

sum += calculSum(clasificator.caracteristica_haar.drept1.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept1.weight * weightScale;

// daca exista un al treilea dreptunghi

if (clasificator.caracteristica_haar.drept2.weight)

sum += calculSum(clasificator.caracteristica_haar.drept2.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept2.weight * weightScale;

if(sum >= t)

return clasificator.alfa1;

else

return clasificator.alfa0;

}

Calcularea sumei necesită preluarea din memoria de texturi a valorilor imaginii integrale în cele patru puncte A,B,C și D, iar rezultatul se obține prin realizarea diferenței dintre sumele pe diagonală.

__device__ float calculSum(DreptGPU dr, int win_start_x, int win_start_y)

{

float tx = win_start_x + dr.x;

float ty = win_start_y + dr.y;

int A = tex2D(IIsumRef, tx, ty);

int B = tex2D(IIsumRef, tx + dr.width, ty);

int C = tex2D(IIsumRef, tx + dr.width, ty + dr.height);

int D = tex2D(IIsumRef, tx, ty + dr.height);

return (float)(A – B + C – D);

}

Etapele de selectare a fețelor valide și afișarea rezultatelor sunt realizate de către CPU. Etapa de selecție presupune parcurgerea vectorului rezultat și preluarea coordonatelor dreptunghiurilor pentru care vectorul de fețe în care GPU-ul a salvat rezultatul nu este zero.

int selectieFete(std::vector<CvRect> &fete, DreptGPU *FeteGpu, int pixeli)

{

//din vectorul de fete FeteGpu verifica fiecare CvRect.x pentru a determina daca GPU a returnat aceasta fereastra ca fiind o fata

int nrFeteDetectate = 0;

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

{

//extrage doar dreptunghiurile detectate

DreptGPU drFata = FeteGpu[i];

if (drFata.width != 0)

{

CvRect convertDr(drFata.x, drFata.y, drFata.width, drFata.height);

fete.push_back(convertDr);

nrFeteDetectate++;

}

}

return nrFeteDetectate;

}

În final imaginea și rezultatele detecției sunt afișate și se eliberează memoria alocată pentru salvarea datelor.

CAPITOLUL 4. Etapele Proiectării – fluxul de prelucrare în aplicația de detecție a fetelor și pietonilor

Proiectarea aplicației pentru detecția fețelor folosind GPU

Din nevoia existenței unor sisteme din ce în ce mai rapide care să execute operații ce asigură interacțiunea omului cu calculatoarele și roboții s-au dezvoltat aceste dispozitive de prelucrare paralelă ce folosesc puterea de calcul a procesoarelor grafice.

Pentru a evidenția modul de lucru cu procesoarele grafice și modul cum acestea pot fi integrate în aplicații uzuale s-a proiectat o aplicație pentru detecția fețelor intr-un sistem inteligent de supraveghere video.

Sistemul inserează intr-o bază de date pentru termen îndelungat toate imaginile capturate și lasă posibilitatea utilizatorului să le analizeze ulterior.

Proiectarea bazei de date

Aplicația folosește o bază de dare Oracle 12cR1 instalată pe un server. Toate obiectele necesare aplicației aparțin schemei IMAGES_ADMIN. Se procedează astfel din motive de securitate, astfel încât să nu permită modificarea obiectelor altor scheme și pentru evitarea utilizarării contului de administrator al bazei de date ce are cele mai multe privilegii ca user de aplicație.

Tot din motive de securitate se crează și profile de utilizator, dar și pentru a gestiona mai usor proprietățile conturilor de utilizator. De aceea, pentru a evita expirarea parolei user-ului de aplicație am implementat următorul profil:

CREATE PROFILE my_profile LIMIT

PASSWORD_LIFE_TIME unlimited;

ALTER USER images_admin PROFILE my_profile;

Schema ce va conține datele aplicației, și anume imaginile salvate precum și date despre dispozitivele utilizate, are alocat un spațiu propriu pentru a crea obiectele. De aceea am creat un Tablespace denumit “Images” cu un singur Datafile. Definiția sa este următoarea:

CREATE TABLESPACE "IMAGES" DATAFILE

'/u01/app/oracle/oradata/ae001/images01.dbf' SIZE 10485760

AUTOEXTEND ON NEXT 10485760 MAXSIZE 10240M

LOGGING ONLINE PERMANENT BLOCKSIZE 8192

EXTENT MANAGEMENT LOCAL AUTOALLOCATE DEFAULT

NOCOMPRESS SEGMENT SPACE MANAGEMENT AUTO;

ALTER DATABASE DATAFILE

'/u01/app/oracle/oradata/ae001/images01.dbf' RESIZE 817889280;

Definiția creării și acordării de privilegii schemei Images_admin este următoarea:

CREATE USER "IMAGES_ADMIN" IDENTIFIED BY VALUES ‚***’

DEFAULT TABLESPACE "IMAGES"

TEMPORARY TABLESPACE "TEMP"

PROFILE "MY_PROFILE";

Arhitectura bazei de date cuprinde trei tabele:

ImagesB64 – (ID, PICTURE, DETECTED,TIME_DETECT, DEVICE_ID, DET_MODE) – folosit pentru stocarea imaginilor sub forma de șir codat Base64 și a timpului detecției sau informații despre sursa imaginii.

Video_device – (DEVICE_ID, DEVICE_NAME, IDENTIFIER) – memorează dispozitivele de captare a imaginilor și detalii despre acestea pentru a identifica usor sursa capturii.

Detection_Mode – (ID, DET_MODE) – reprezintă tabelul cu modurile de execuție a aplicației.

Figura 4.1. Diagrama Entitate-Relație a tabelelor

Fluxul de date al sistemului

Proiectul pentru detecția fețelor din imagini provenite de la camera video este realizat folosind limbajul C#, iar implementarea s-a realizat cu ajutorul librăriei Emgu CV. Aplicația a fost realizată și testată folosind mediul de programare Visual Studio 2015.

Structura logică este compusă din interfața grafică realizată prin Windows Forms care comunică cu serverul de baze de date și senzorii video. Informațiile preluate și analizate sunt apoi stocate în baza de date.

Figura 4.2. Fluxul de procesare al aplicației

Proiectarea logică

În etapa de proiectare logică a aplicației s-a urmărit implementarea următoarelor cerințe de proiectare:

Crearea unui program software care să colecteze și sa prelucreze informațiile provenite de la senzorii video, și anume camerele web.

Implementarea unui modul de analiză a datelor achiziționate de la senzorii video pentru a realiza detecția fețelor

Implementarea unui modul de analiză a datelor achiziționate de la senzorii video pentru a realiza detecția pietonilor

Salvarea datelor necesare analizei ulterioare în baza de date

Implementarea sistemului

Implementarea sistemului a fost realizată astfel încât să țină cont de specificațiile definite anterior. Astfel întregul proces poate fi exprimat grafic prin diagrama din Figura 4.3.

Figura 4.3. Diagrama de flux

În momentul lansării în execuție a aplicației se pornește fereastra meniului de start ce oferă utilizatorului posibilitatea de a alege modul de lucru al aplicației. Acesta poate alege fie pentru detecția fețelor fie pentru detecția pietoilor. Modul de lucru al celor două funcții din cadrul sistemului este asemănător de aceea am ales să descriu doar unul dintre cazurile existente.

Dacă se alege modul de operare pentru detecția fețelor din fereastra de lucru există posibilitatea alegerii camerei video din zona de acțiune unde se dorește să se analizeze. La apăsarea butonului de start al procesului sunt pornite în paralel două task-uri. Fiecare task lucrează independent și are funcții diferite de realizat.

Task-ul unu este cel care pornește capturarea imaginilor. Pentru fiecare cadru se aplică algoritmul de detecție CUDA ce oferă ca rezultat un număr întreg ce reprezintă umărul de fețe existente în acel cadru. În cazul în care acesta este diferit de 0 se trasează pătrate în funcție de coordonatele din lista de obiecte rectangulare ce este dată ca rezultat tot de către agloritmul de detecție. Aceste pătrate sunt zone ce încadrează și evidențiază fețele existente în imagine. La terminarea acestui pas sau în cazul in care numărul rezultate este zero îmaginile sunt adăugate într-o listă de unde urmează a fi extrase de task-ul doi. Urmează apoi afișare pe ecran a imaginilor rezultate.

Task-ul doi pornește asincron și independent față de task-ul unu și are rolul de a asigura comunicarea cu baza de date și de a insera imaginile captate. Acesta monitorizează în permanență numărul de elemente din lista de imagini precum și conexiunea cu baza de date. Dacă unul dintre acești factori returnează o valoare negativă task-ul își suspendă activitatea și așteaptă noi date de procesat, iar în caz contrar, extrage pe rând imagini din listă și le introduce în baza de date împreună cu informațiile suplimentare.

Proiectarea interfeței grafice

Interfața grafică a fost proiectată prin intermediul Windows Form-urilor C# și conține trei pagini principale. Prima dintre ele este pagina de selectare a modului de operare al aplicației. Sistemul poate fi lansat în execuție ca detector de fețe special conceput ca sistem de alertă pentru încăperi, sau ca și detector de persoane, util pentru spații exterioare sau de dimensiune mare.

Pentru detecția fețelor, la fel ca și pentru detecția pietonilor, s-au realizat formulare separate unde se pot analiza în timp real imaginile provenite de la camerele de supraveghere. Se alege din lista de dispozitive video disponibile zona unde se dorește să se analizeze și se pornește procesul. În partea dreaptă a ferestrei există o zonă de log unde se trec informații despre acțiunile executate.

În fereastra principală de start am adăugat și un buton ce la apăsare creează o nouă fereastră și are rolul de a interoga baza de date în scopul obținerii datelor stocate în funcție de anumite criterii specificate de utilizator, evitând astfel trecerea prin toate imaginile stocate. Se va evita astfel petrecerea unui timp ridicat de analiză vizuală a datelor de către utilizatorul uman. Prin intermediul aplicației se extrag din baza de date doar acele imagini care s-au înregistrat într-un anumit interval de timp sau doar cele pentru care algoritmii de detecție nu au returnat valori nule.

Pentru a stabilii condițiile interogării bazei de date este necesară selectarea datei și a orei de început respectiv de sfârșit din elementele de tip DateTimePicker. În funcție de cele două obiecte de tip CheckBox se stabilește tipul de imagini ce vor fi returnate: rezultatul detecției fețelor nu a fost nul sau rezultatul detecției pietonilor nu a fost nul. La apăsarea butonului Show Images este interogată baza de date ținându-se cont de filtrele aplicate, iar lista rezultată de obiecte de tip ImRow va fi afișată într-un obiect de tip ListBox. La apăsarea cu mouse-ul pe una dintre valorile din listă se va afișa imaginea corespunzătoare id-ului în PictureBox-ul imageFace.

Modul de implementare al claselor

Proiectul conține în total un număr de nouă clase dezvoltate în afară de clasa Program.cs. Structura acestora precum și variabilele globale și metodele sunt evidențiate în figura 4.4 și figura 4.5.

Figura 4.4. Diagrama claselor

Figura 4.5. Diagrama claselor – continuare

Clasa StartMenu.cs

Clasa StartMenu.cs conține elementele grafice ale paginii de pornire. Din această fereastră se poate configura fiecare senzor video pentru un anumit mod de lucru. Selecția se va face dintr-o listă la care se vor adăuga elemente de fiecare dată când aplicația este lansată în execuție. Acest lucru este realizat de către funcția getWebcamIds.

private void getWebcamIds()

{

//detecteaza toate camerele web conectate la pc

DsDevice[] _SystemCamereas = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

WebCams = new Video_Device[_SystemCamereas.Length];

for (int i = 0; i < _SystemCamereas.Length; i++)

{

WebCams[i] = new Video_Device(i, _SystemCamereas[i].Name, _SystemCamereas[i].ClassID); //fill web cam array

cbAvWebcams.Items.Add(WebCams[i]);

}

OracleConnection con = new OracleConnection(connString);

List<string> video_devices_db = new List<string>();

try

{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "SELECT device_name FROM video_device ";

using (var reader = cmd.ExecuteReader())

{

while (reader.Read())

{

if (!reader.IsDBNull(0))

{

video_devices_db.Add(reader.GetString(0));

}

}

}

for (int w = 0; w < WebCams.Length; w++)

{

Console.WriteLine(WebCams.ElementAt(w));

if (!video_devices_db.Contains(WebCams[w].Device_Name))

{

cmd.CommandText = "insert into video_device values(:id, :name, :identifier)";

cmd.Parameters.Add("id", WebCams[w].Device_ID);

cmd.Parameters.Add("name", WebCams[w].Device_Name);

cmd.Parameters.Add("identifier", WebCams[w].Identifier.ToString());

cmd.ExecuteNonQuery();

cmd.CommandText = "Commit";

cmd.ExecuteNonQuery();

}

}

}

catch (Exception)

{

MessageBox.Show("Database not availble!");

}

finally

{

con.Close();

}

}

Clasa chMode.cs

S-a implementat această clasă pentru a realiza selecția modului de lucru dorit. La apelarea acestei clase o fereastră cu opțiuni va fi deschisă și se va putea alege fie modul de lucru ca detector de fețe fie ca detector de pietoni. Interfața grafică va conține trei butoane, iar în funcție de butonul apăsat se va porni aplicația în modul dorit.

Constructorul clasei primește ca parametrii id-ul dispozitivului ce va fi configurat, un mesaj, precum și modurile de lucru.

public chMode(int dev_id,string message,string buttonText1,string buttonText2)

{

InitializeComponent();

label1.Text = message;

button1.Text = buttonText1;

button2.Text = buttonText2;

button3.Text = "Cancel";

device_id = dev_id;

}

private void button1_Click(object sender, EventArgs e)

{

//lanseaza fereastra detectie fete

FluxInsert f = new FluxInsert(device_id,1);

f.Show();

this.Close();

}

private void button2_Click(object sender, EventArgs e)

{

//lanseaza fereastra detectie pietoni

FluxInsertPedestrian fp = new FluxInsertPedestrian(device_id);

fp.Show();

this.Close();

}

private void button3_Click(object sender, EventArgs e)

{

this.Close();

}

Clasa FluxInsert.cs

Această clasă conține atât obiectele ce fac parte din interfața grafică, cât și metode apelate automat, cum ar fi metoda private void startCapturingProcess() sau private void startInsertingProcess(). La începerea procesului o dată cu apăsarea butonului Start Process se inițializează sursa video și se începe capturarea imaginilor de la camera specificată din lista de dispozitive disponibile. După ce se face selecția se lansează în execuție metoda private void executions() ce are rolul de a porni în paralel două procese.

Primul proces are rolul de a porni capturarea imaginilor din zona aleasă. Imaginile sunt apoi analizate prin apelarea metodei Detect din clasa DetectFace și se evidențiază fețele. În funcție de rezultat se crează un obiect de tip DbRow: DbRow row = new DbRow(insertB64, faces.Count, data), unde insertB64 reprezintă codul în baza 64 pentru imaginea obșinută, faces.Count indică numărul de fețe existente în acel cadru, iar data conține valoarea exactă de timp, momentul la care a fost obținut. Acest obiect se adaugă la sfârșitul listei rows, de unde va fi preluat ulterior și introdus în baza de date.

Cel de-al doilea proces preia asincron obiectele din listă și le introduce în baza de date. Se verifică într-o buclă infinită numărul de elemente din listă, iar daca aceasta nu este goală se apelează metoda insertIntoDb() ce stabilește conexiunea cu baza de date și extrage primul obiect din listă și apoi îl șterge.

Inserarea în baza de date se face folosind proprietățile obiectului extras ca și variabile de legătură.

DbRow getRow = rows.ElementAt(0);

cmd.CommandText = "insert into imagesb64 values(:im_code, :img, :nrOfObj,TO_CHAR (SYSDATE, 'DD-MM-YYYY HH:MI:SS'), :device_id, :DetMode)";

cmd.Parameters.Add("im_code", lastProcessedId);

cmd.Parameters.Add("img", getRow.Img);

cmd.Parameters.Add("nrOfObj", getRow.Faces);

cmd.Parameters.Add("device_id", device_id);

cmd.Parameters.Add("DetMode", 1);

cmd.ExecuteNonQuery();

Clasa DetectFace.cs

Clasa DetectFace conține o singură metodă, și anume public static void Detect( IInputArray image, String file, List<Rectangle> faces, out long timeDet). Această funcție verifică existența unui dispozitiv de prelucrare CUDA prin proprietatea obiectului definit de librăria Emgu CV CudaInvoke.HasCuda și în funcție de acest rezultat se ia decizia utilizării fie a dispozitivului Cuda fie a procesorului de uz general pentru a realiza procesarea imaginii.

Au fost implementate ambele metode pentru elimina posibilitatea terminării neașteptate a execuției aplicației în cazul în care nu se poate accesa procesorul grafic sau există defecțiuni la acesta.

Dacă CudaInvoke.HasCuda returnează valoarea true se instanțiază obiectul face din clasa CudaCascadeClassifier al cărui constructor are ca parametru un șir ce reprezintă denumirea fișierului în care au fost salvate rezultatele obținute în urma antrenării sistemului de detecție. Open CV are implementate metode atât pentru antrenare, în cazul în care se dorește creerea unor clsificatori noi, cât și pentru detecție.

Pentru proiect am folosit doar partea de detecție. Open CV conține deja numeroși clasificatori pentru fața, ochi, zâmbet etc. Ca parametru pentru constructor se va folosi fișieru XML haarcascade_frontalface_default.xml.

Metoda DetectMultiScale aplicată asupra unei imagini CudaImage de tip grayscale realizează detecția fetelor de diferite dimensiuni din imaginea dată ca parametru, iar rezultatul apelului funcției este o lista de obiecte de tip Rectangle.

Clasa FluxInsertPedestrian.cs

Clasa FluxInsertPedestrian.cs este cea din care se pornește procesul de detecție al pietonlor, fiind cea care conține obiectele din interfața grafică. Este structurată în același mod ca și clasa FluxInsert.cs și execută în paralel procesele de captură și inserare în baza de date.

Pentru a realiza detecția se apelează metoda Find a clasei PedDetect astfel:

Rectangle[] people = PedDetect.Find(image, out detectionTime);

unde image reprezintă imaginea asupra căreia se aplică aloritmul, iar detectionTime este timpul de procesare în milisecunde.

Din lista de obiecte de tip Rectangle se extrag coordonatele punctelor ce delimitează zona în care un pieton a fost detectat și se evidențiază prin trasarea unui pătrat, astfel:

foreach (Rectangle p in people)

{

CvInvoke.Rectangle(image, p, new Bgr(Color.Red).MCvScalar, 2);

}

Funcția private void getWebcamIds() este apelată în momentul în care se încarcă pagina și a fost creată pentru a realiza o listă cu camerele video conectate la sistem și se pot folosi ca și senzori. În baza de date există o tabelă cu dispozitivele disponibile și informații suplimentare despre acestea. Pentru a implementa această funcție a fost necesară utilizarea librăriei DirectShowLib-2005 special concepută pentru interacțiunea cu echipamentele video sau audio (streaming media). Librăria conține implementate clase precum DsDevice sau necesită de asemenea creerea de noi structuri cum ar fi VideoDevice din care se vor instanția obiecte pentru fiecare senzor detectat.

DsDevice[] _SystemCamereas = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

WebCams = new Video_Device[_SystemCamereas.Length];

for (int i = 0; i < _SystemCamereas.Length; i++)

{

WebCams[i] = new Video_Device(i, _SystemCamereas[i].Name, _SystemCamereas[i].ClassID);

webcamsAvailable.Items.Add(WebCams[i]); }

Clasa PedDetect.cs

Clasa PedDetect.cs este cea care apelează algoritmul de detecție HOG (Histogram Of Gradients) pentru a evidenția persoanele aflate într-un cadru. Metoda Find a fost proiectată în acest scop și primește ca și parametrii imaginea ce trebuie procesată, ca parametru de ieșire timpul necesar efectuării operației și întoarce ca rezultat o listă de obiecte de tip Rectangle[] ce memorează coordonatele.

La apelul metodei se încearcă întâi folosirea dispozitivului de prelugrare grafică CUDA, iar în cazul în care aceta nu este disponibil sau are erori se va utiliza versiunea CPU/OpenCL. Testarea dispozitivului se face automat de librăria EmguCv iar rezultatul este stocat sub formă de variabilă booleană (true sau false) în variabila CudaInvoke.HasCuda.

În cazul în care valoarea returnată este true detecția se va face folosing GPU. De aceea este nevoie să se instanțieze un obiect din clasa CudaHOG pentru a crea un nou descriptor HOG ce are ca parametrii dimensiunea ferestrei de detecție, dimensiunea blocului în celule și pasul.

using (CudaHOG des = new CudaHOG(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8)))

Pentru descriptorul nou creat se setează detectorul SVM (Support vector machine) prin metoda SetSVMDetector ce are ca parametru coeficienții clasificatorului instruiți pentru detectarea persoanelor ce au dimensiunea implicită a ferestrei de detecție.

des.SetSVMDetector(des.GetDefaultPeopleDetector());

La pasul următor se aplică algoritmul de detecție prin apelarea metodei DetectMultiScale ce are ca parametrii imaginea și un vector de pătrate ce va fi returnat ca rezultat.

using (GpuMat cudaBgr = new GpuMat(image))

using (GpuMat cudaBgra = new GpuMat())

using (VectorOfRect vr = new VectorOfRect())

{

CudaInvoke.CvtColor(cudaBgr, cudaBgra,ColorConversion.Bgr2Bgra);

des.DetectMultiScale(cudaBgra, vr);

regions = vr.ToArray();

}

Clasa Structures.cs

Această clasă conține o structură de tip Video_device definită pentru a instanția noi obiecte în momentul în care prin intermediul librăriei DirectShowLib-2005 se detectează camerele video. Aceasta are ca și variabile globale:

public string Device_Name;

public int Device_ID;

public Guid Identifier;

Variabilele reprezintă proprietăți ale obiectelor de tip Video_Device necesare pentru identificarea senzorilor de la care se preiau informațiile.

Constructorul clasei este definit astfel:

public Video_Device(int ID, string Name, Guid Identity = new Guid())

{

Device_ID = ID;

Device_Name = Name;

Identifier = Identity;

}

În cadrul aceleiași clase a fost supradefinită metoda ToString() ce returnează ca rezultat un string format din atributele obiectului.

public override string ToString()

{

return String.Format("[{0}] {1}: {2}", Device_ID, Device_Name, Identifier);

}

Clasa DbRow.cs

Prin intermediul acestei clase se instanțiază obiecte de tipul DbRow care sunt introduse în lista de procesare și urmează a fi introduse în baza de date.

Datele ce vor fi stocate sunt:

public string Img { get; set; }

public int Faces { get; set; }

public string Data { get; set; } ,

unde Img salvează codul în baza 64 al imaginei, Faces reține numărul de fețe detectate, iar Data este timestamp-ul la care s-a realizat procesarea.

S-au definit doi constructori:

public DbRow() { }

public DbRow(string img, int faces, string data)

{

Img = img;

Faces = faces;

Data = data;

}

Clasa getIm.cs

S-a creat o nouă clasă și anume getIm.cs care conține și elementele grafice necesare selecției imaginilor din baza de date. Aceasta oferă utilizatorului posibilitatea de a vizualiza cadrele surprinse și analizate de către senzorii video.

Selecția din baza de date se fac în funcție de opțiunile utilizatorului. Acesta are la dispoziție elemente pentru selecția datei și orei și checkbox-uri pentru a obține imagini ce conțin fețe sau persoane. În funcție de acestea se realizează comanda de selectare a datelor ce va fi executată pe baza de date.

string command = "SELECT id,detected,time_detect FROM imagesb64 WHERE time_detect BETWEEN TO_DATE('" + stDate + "','dd-mm-yyyy hh:mi:ss') AND TO_DATE('" + eDate + "','dd-mm-yyyy hh:mi:ss')";

if (cbFaces.Checked)

{

cmd.CommandText = command + " and detected not like '0' and det_mode=1";

}

else if (cbPers.Checked)

{

cmd.CommandText = command + " and detected not like '0' and det_mode=2";

}

else if (cbFaces.Checked && cbPers.Checked)

{

cmd.CommandText = command + " and detected not like '0' ";

}

else

{

cmd.CommandText = "SELECT id,detected,time_detect FROM imagesb64";

//cmd.CommandText = "SELECT * FROM FACES";

}

using (OracleDataReader reader = cmd.ExecuteReader())

{

DataTable tbl = new DataTable();

tbl.Load(reader);

this.dgvRows.DataSource = tbl;

}

În obiectul de tip DataGridView sunt afișate rezultatele, iar la apăsarea cu mouse-ul pe una dintre celule se va selecta imaginea cu indexul afișat. Funcția de preluare a imaginii este apelată în interioru metodei ce se pornește automat atunci când apare un eveniment de click cu mouse-ul pe celulă.

private void dgvRows_CellClick_1(object sender, DataGridViewCellEventArgs e)

{

if (e.RowIndex > 0)

{

DataGridViewRow row = this.dgvRows.Rows[e.RowIndex];

if (!row.Cells[0].Value.ToString().Equals(""))

{

int index = Int32.Parse(row.Cells["id"].Value.ToString());

getImage(index);

}

}

}

CAPITOLUL 5. Testarea Sistemului și Sinteza Rezultatelor

În lucrarea elaborată se urmărește dezvoltarea de aplicații pentru a testa performanțele unor sisteme ce se bazează pe tehnologia prelucrării paralele folosind procesoarele grafice NVIDIA ce utilizează CUDA precum și modul de integrare al acestei tehnologii în sisteme complexe și interacțiunea cu alte tehnologii.

Testarea se realizează între sistem de prelucrare paralelă și sistem cluster sau în același mediu, dar folosind o aplicație paralelizată pentru CPU.

Pentru a evidenția utilitatea folosirii procesoarelor grafice ce conțin CUDA cores am realizat încă o aplicație ce realizează detecția fețelor sau a pietonilor din cadre provenite de la camere web conectate la sistemul prezentat.

Implementarea și testarea programului s-a realizat pe un sistem ce are următoarele caracteristici (Figura 5.1).

Figura 5.1. Configuratia sistemului de test

Hardware-ul graphic este NVIDIA GeForce GTX 850M, o placa video din gama de top, compatibilă DirectX 11.

GTX 850M este produsă în continuare pe 28nm, iar cantitatea de memorie dedicate este de 4GB DDR3, magistrala de memorie fiind de 128 biți. [30]

Alte caracteristici ale placii grafice:

Tabel 5.1. Caracteristici Nvidia Geforce GTX 850M

Configurația sistemului CUDA pe care se realizează testele de performanță sunt enumerate mai jos:

CUDA Device Query…

There are 1 CUDA devices.

CUDA Device #0

Major revision number: 5

Minor revision number: 0

Name: GeForce GTX 850M

Total global memory: 4294967295

Total shared memory per block: 49152

Total registers per block: 65536

Warp size: 32

Maximum memory pitch: 2147483647

Maximum threads per block: 1024

Maximum dimension 1 of block: 1024

Maximum dimension 2 of block: 1024

Maximum dimension 3 of block: 64

Maximum dimension 1 of grid: 2147483647

Maximum dimension 2 of grid: 65535

Maximum dimension 3 of grid: 65535

Clock rate: 901500

Total constant memory: 65536

Texture alignment: 512

Concurrent copy and execution: Yes

Number of multiprocessors: 5

Kernel execution timeout: Yes

Etape ale instalării și configurării sistemului

Crearea mașiii virtuale

În cadrul proiectului de licență am utilizat Virtual Box pentru a instala un sistem de operare Oracle Enterprise Linux 7. Acesta este folosit ca suport pentru baza de date Oracle Standard Edition 12C.

După instalarea software-ului Virtual Box, din fereastra de start se apasă butonul “New”, urmând apoi specificarea paramerilor de configurare ai mașinii virtuale. În cazul meu, am creat o VM numită “facultate” ce utilizează 4GB de memorie, 2 CPU, un disc virtual pentru stocarea datelor, de tip “vhd”,cu o capacitate de stocare de 50 GB, denumit “facultate.vhd”. Pentru configurarea rețelei am folosit un adaptor de rețea în modul “Bridged network”.

Pașii urmați pentru configurarea mașinii virtuale sunt reprezentați în figurile de mai jos.

Figura 5.2. Specificarea parametrului de memorie

Figura 5.3. Crearea unui disc virtual nou de tip VHD

Figura 5.4. Configurația rezultată

Pentru a asigura integritatea sistemului utilizat și pentru a nu pierde date, am folosit ca metodă salvarea stărilor curente ale mașinii virtuale în anumite momente de timp (Snapshots). Această metodă asigură revenirea la o anumită stare a VM în cazul în care s-au produs modificări accidentale.

Nota: utilizarea unui snapshot și revenirea la o stare anterioară va afecta de asemenea și discul virtual conectat la VM, ceea ce înseamnă că toate fișierele create după starea care a fost salvată vor fi pierdute.

Conținutul unui snapshot:

O copie completă a setărilor VM, a configurației hardware. Deci atunci cand revii la o stare anterioară, se revine și la setările VM din acel moment.

Starea completă adiscurilor virtuale atașate mașinii. Acestea vor fi modificate până se ajunge la momentul salvat.

Starea memoriei, pentru snapshot-urile realizate în timp ce mașina era utilizată.

Rețeaua virtuală a fost configurată în modul Bridged network. Acesta folosește un dispozitiv de pe sistemul gazdă care filtrează datele provenite de la adaptorul fizic de rețea și permite VirtualBox să intercepteze date din rețeaua fizică, creând o nouă interfață de rețea în VM. Serverul are adresă IP static, de clasă C. Modul de configurare al rețelei de poate analiza în imaginea Figura 5.5. Parametrii de rețea ai mașinii virtuale.

Figura 5.5. Parametrii de rețea ai mașinii virtuale

Instalarea sistmului de operare Oracle Linux

Am instalat Oracle Linux 7 folosind interfața grafică, respectând următorii pași:

Selectarea limbii de instalare

Se configurează sistemul prin modificarea parametrilor din lista de opțiuni. Există anumite cerințe minime, acestea fiind marcate ca obligatorii, cum ar fi discul destinație pe care se va face instalarea. După stabilirea configurației necesare se continuă cu instalarea.

Selectarea programelor software ce se doresc a fi adăugate suplimentar

Din fereastra apărută după apăsarea butonului "Software selection" se selectează opțiunea "Server with GUI" apoi se aleg programele software ce se doresc a fi instalate. Pentru configurația pe care doresc să o realizez pentru testarea sistemului am ales să instalez următoarele: File and Storage Server, Java Platform, Large Systems Performance, Network File System Client, Performance Tools, Compatibility Libraries, Development Tools.

Figura 5.6. Modificare parametrii de sistem

Pasul următor constă în instalarea sistemului, fiind necesară stabilirea parolei pentru contul de administrator. Există și posibilitatea creării unor noi utilizatori.

Figura 5.7. Stabilirea parolelor pentru administrator

Instalarea software-ului Oracle Database

Înainte de a instala pe sistemul de operare software-ul de baze de date Oracle 12C este necesar să se configureze mediul de operare. Prin acest proces de configurare se înțelege modificarea unor parametrii de kernel și instalarea sau actualizarea unor pachete pentru sistemul de operare Linux.

Pentru a simplifica acest pas, Oracle pune la dispoziție un pachet RPM oracle-rdbms-server-12cR1-preinstall.

Prin executarea acestui pachet se efectuează următoarele etape de pregătire:

descărcă și instalează automat pachetele software necesare folosind yum;

crează utilizatorul de sistem oracle și grupurile oraInventory(oinstall) și ODSBA(dba) pentru acest utilizator;

modifică fișierul sysctl.conf, adăugând parametrii de kernel, cum ar fi parametrii pentru memoria partajată;

setează limitele fizice și logice pentru resurse în fișierul /etc/security/limits.conf;

Trebuie să se verifice înainte de inceperea instalării și spatiul de stocare existent. Tipul de instalare Standard Edition are nevoie de 6.1 GB liberi pe disc. În cazul în care se optează pentru instalarea Oracle ASM este nevoie de spațiu suplimentar.

Din punct de vedere al memoriei, serverul trebuie să utilizeze minim 1GB de memorie RAM. Valoarea recomandată de Oracle este de 2GB.

Pentru că am folosit Oracle Linux 7 a fost nevoie să se instaleze următoarele pachete: binutils-2.23.52.0.1-12.el7.x86_64, compat-libcap1-1.10-3.el7.x86_64, compat-libstdc++-33-3.2.3-71.el7.i686, compat-libstdc++-33-3.2.3-71.el7.x86_64, gcc-4.8.2-3.el7.x86_64, gcc-c++-4.8.2-3.el7.x86_64, glibc-2.17-36.el7.i686, glibc-2.17-36.el7.x86_64, glibc-devel-2.17-36.el7.i686, glibc-devel-2.17-36.el7.x86_64 , ksh, libaio-0.3.109-9.el7.i686 , libaio-0.3.109-9.el7.x86_64, libaio-devel-0.3.109-9.el7.i686, libaio-devel-0.3.109-9.el7.x86_64, libgcc-4.8.2-3.el7.i686, libgcc-4.8.2-3.el7.x86_64, libstdc++-4.8.2-3.el7.i686, libstdc++-4.8.2-3.el7.x86_64 , libstdc++-devel-4.8.2-3.el7.i686, libstdc++-devel-4.8.2-3.el7.x86_64, libXi-1.7.2-1.el7.i686, libXi-1.7.2-1.el7.x86_64, libXtst-1.2.2-1.el7.i686, libXtst-1.2.2-1.el7.x86_64, make-3.82-19.el7.x86_64, sysstat-10.1.5-1.el7.x86_64. [31]

După ce toate cerințele din pasul de pre-instalare sunt îndeplinite, se poate continua cu instalarea software-ului.

Primul pas este crearea structurii de directoare unde va fi instalată baza de date, asupra cărora utilizatorul oracle va avea drepturi de administrare.

[root@licenta /]# mkdir -p /u01/app/oracle/product

[root@licenta /]# chown -R oracle:oinstall /u01

[root@licenta /]# chmod 775 /u01

Urmează dezarhivarea celor două pachete și lansarea în execuție a utilitarului de instalare.

[root@licenta Downloads]# unzip linux.x64_12cR1_database_1of2.zip

[root@licenta Downloads]# unzip linux.x64_12cR1_database_2of2.zip

[root@licenta Downloads]# cd database/

[root@ licenta Downloads]# ./runInstaller

Figura 5.8. Mesaj afișat după instalarea cu success a instanței Oracle

După terminarea instalării instanței și afișarea mesajului din Figura 5.8. urmează instalarea unei baze de date. Această operațiune este realizată folosind utilitarul DBCA (Database Configuraion Assistant), care se lansează din calea /u01/app/oracle/product/12.1.0/dbhome_1/bin. O bază de date se poate crea din modul graific sau doar din linie de comandă. În cazul meu, am ales opțiunea de a crea baza de date după ce software-ul Oracle a fost instalat. Oracle Universal Installer (OUI) pornește automat utilitarul DBCA.

Ca suport pentru aplicație am nevoie de o singură bază de date pe care am denumit-o ae001. Pentru a avea acces la baza de date cu SQL Plus trebuie înainte configurate anumite variabile de mediu, cum ar fi $ORACLE_HOME și ORACLE_SID. Se apelează în acest scop comanda linux “oraenv”, așa cum este prezentat în Figura 5.9.

Figura 5.9. Setarea variabilelor de mediu

Configurările specific utilizatorului “oracle” pot fi specificate prin editarea unot fișiere la nivelul sistemului de operare: ./ bash_profile și ./ bashrc ce se găsesc în calea /home/oracle și sunt executate atunci când utilizatorul de autentifică pe server.

Figura 5.10. Conținutul fișierului bash_profile

Figura 5.11. Conținutul fișierului bashrc

Autenificarea la baza de date de pe o sursă externă necesită configurarea unui listener ce este un proces separat ce se execută pe serverul de baze de date. Acesta primește cereri de conexiuni de la clienți și gestionează traficul acestor cereri către baza de date. Utilitarul cu ajutorul căruia se administrează acest proces (pornire, oprire) este lsnrctl , fiind pus la dispoziție de către Oracle în mod gratuit și instalat în același timp cu software-ul de baze de date.

Un listener poate fi configurat prin editarea fișierului listener.ora a informațiilor despre protocol, host și port, (ADDRESS=(PROTOCOL=tcp)(HOST=host_name)(PORT=1521)). Procesul se va numi implicit LISTENER, iar conexiunile se vor stabili pe portul TCP/1521. În figura 5.12 se poate observa conținutul fișierului de configurare precum și modul de pornire al procesului.

Figura 5.12. Configurare listener

Pentru a permite autentificarea externă către baza de date este necesară adăugarea unei reguli de firewall pentru portul folosit de către listener-ul bazei de date configurat anterior. Acesta va folosi portul 1521. Pentru a deschide portul 1521 trebuie adăugată o regulă iptables. Am folosit comanda firewall-cmd astfel:

firewall-cmd –zone=public –add-port=1521/tcp –permanent

firewall-cmd –reload

Se poate verifica dacă regula a fost adăugată cu succes:

iptables-save | grep 1521

Instalarea software-ului CUDA

Pentru a putea folosii puterea de calcul a proceoarelor CUDA este necesar să se instaleze cea mai recentă versiune de drivere pentru placa video NVIDIA. Pentru a crea un mediu vast de dezvoltare a aplicațiilor accelerate grafic, cei de la NVIDIA pun la dispoziție un set de instrumente denumit CUDA Toolkit v8.0 necesare pentru dezvoltarea, optimizarea și testarea aplicațiilor. Setul de instrumente include librării accelerate de GPU, instrumente de depanare și optimizare , un compiltor C/C++ și o librărie de execuție pentru implementarea aplicației.

Configurarea instrumentelor de dezvoltare CUDA pe un sistem care rulează pe o versiune de Windows constă în câțiva pași simpli și anume, verificarea sistemului pentru a vedea dacă dispune de un GPU CUDA, descărcarea setului de instrumente și instalarea sa și verificarea că funcționează corect și comunică cu hardware-ul.

Ultima versiune de software se găsește la adresa http://developer.nvidia.com/cuda-downloads. Instalarea se poate face atăt în modul grafic cât și in modul linie de comandă.

Descărcarea librăriei ManagedCuda

Pentru a folosi librăria ManagedCuda nu este nevoie de licență suplimentară și este opes-source. Ultima versiune a acesteia poate fi descărcată de pe pagina de GitHub https://kunzmi.github.io/managedCuda/ sau inclusă direct în proiect prin pachete NuGet. În aplicațiile realizate în cadrul acestui proiect am folosit versiunea ManagedCuda 8.0 x64 din motive de compatibilitate cu versiunea de software CUDA instalată.

Instalarea librariei OpenCV

Pentru dezvoltarea și implementarea proiectului am folosit versiunea 3.0 a librăriei OpenCV disponibilă pe pagina oficială de Sourceforge. Instalarea se face simplu, prin dezarhivarea pachetului descărcat și apoi instalarea din fișierul executabil. Pentru a simplifica procesul se poate stabilii o variabilă de mediu ce face referire la directorul unde este instalată librăria. Din Command Window se poate executa comanda: setx -m OPENCV_DIR D:\OpenCV\Build\x64\vc11.

Instalarea librariei Emgu CV

Pentru a descărca utilitarul de instalare al librăriei am accesat pagina: http://sourceforge.net/projects/emgucv/ de unde se poate descărca ultima versiune disponibilă de software, însă poate fi descărcată și utilizată și o versiune mai veche.

Figura 5.13. Descărcarea librăriei Emgu CV

Instalarea librăriei Emgu CV se realizează în mod similar cu instalarea librăriei Open CV. De asemenea se poate stabilii și o variabilă de mediu care să fie referință a directorului unde a fost instalată (de exemplu: D:\Emgu\emgucv-windows-universal-gpu 2.4.9.1847\bin\x86)

Această librărie pune la dispoziție elemente grafice pentru Visual Studio folosite în aplicații grafice de tip Windows Form. Pentru a putea fi folosite trebuie mai întâi să fie introduse în toolbox-ul aplicației. Acest lucru se poate face prin selectarea din tab-ul "General" a secțiunii "Choose Items.. Browse" de unde se caută librăria Emgu.CV.UI.dll.

Figura 5.14. Includerea in toolbox a obiectelor de UI

Din aceste elemente grafice puse la dispoziție de Emgu CV am folosit în realizare aproiectului "ImageBox", un user control similar cu PictureBox care afișează orice obiect de tip Image<,> în loc de Bitmap. Acesta prevede de asemenea funcționalități suplimentare pentru operațiuni simple cu imagini cum ar fi zoom sau afișarea unui meniu de unde se poate vedea histograma culorilor.[32]

Testarea de performanțelor, probleme apărute și metode de rezolvare

Primul test implică combinarea pixelilor a două imagini prin operații simple. Dimensiunea setului de date va fi modificată, considerând că algoritmul este aplicat pe imagini de rezoluții diferite. Se alege ca și dimensiune a vectorului de pixeli o valoare cuprinsă între 5000 și 5 milioane de elemente, iar fiecare funcție se execută repetat de cincizeci de ori, astfel încât să se obțină date relevante. Rezultatele execuțiilor au fost sintetizate în tabelul 5.2, iar pe baza lor a fost obținut graficul din figura 5.15.

Tabelul 5.2. Rezultate testelor la creșterea dimensiunii setului de date

Figura 5.15. Rezultatele testelor – grafic

Din aceste rezultate se poate extrage concluzia că pentru execuția unor operații simple, chiar și pentru un număr foarte mare de date, paralelizarea GPU nu este foarte eficientă. Timpul de execuție pentru CPU este mai bun, însă GPU-ul nu este cu mult mai în urmă.

În cel de-al doilea caz, testele au fost realizate pe vectori de dimensiunea unei imagini de rezoluție aproximativă de 800×800 de pixeli și se variază dificultatea operațiilor. Se pornește de la o operație simplă de adunare și se crește apoi până la două operații de adunare și trei înmulțiri respectiv patru operații de adunare și cinci înmulțiri. În tabelul 5.3 au fost salvate cele mai relevante rezultate.

Tabel 5.3. Rezultatele testelor la modificarea complexității operațiilor

Figura 5.16. Rezultatele testelor – grafic

Se observă în cazul acestor teste faptul că pentru task-uri mai complexe GPU-ul obține rezultate mult mai bune, reușind să mențină aproximativ constant timpul de procesare indiferent de numărul de operați.

Testele de performanță au fost realizate pe două tipuri de arhitecturi CUDA diferite pentru a observa modul cum se comportă aplicația în medii diferite de execuție. Din acest lucru se observă faptul că dacă GPU-ul nu este suficient de puternic pentru procesări, CPU-ul va avea timpii de execuție mult mai buni chiar și pentru operații complexe. Dar chiar și în cazul unui procesor grafic lent acesta va obține timpi de execuție foarte apropiați de cei ai procesorului principal.

Testarea aplicației de detecție a fețelor CUDA

Aplicația implementată în limbajul C for CUDA poate fi executată pentru diferite imagini de rezoluții diferite. Pentru a indica imagine ce urmează a fi procesată se poate modifica variabila imgPath astfel încât să se indice locația de pe disc a imaginii. În cazul în care imaginea nu poate fi încărcată se va afișa un mesaj de eroare.

Eroare la incarcarea imaginii

Press any key to continue . . .

Se poate modifica de asemenea și factorul de scalare al imaginii. În cazul meu am stabilit valoarea de 1.25f deoarece este valoarea indicată de dezvoltatorii algoritmului ca fiind valoarea pentru care se obțin cele mai bune rezultate.

Pentru a testa aplicația am folosit imagini de dimensiuni diferite și am analizat timpul de execuție în fiecare caz. Se observă că pentru imagini de rezoluție mare algoritmul se dovedește a fi foarte lent.

Dimensiune imagine: 64×64 pixeli

Figura 5.17. Rezultate execuție pentru imaginea de 64×64 pixeli

Dimensiune imagine: 256×256 pixeli

Figura 5.18. Rezultate execuție pentru imaginea de 256×256 pixeli

Dimensiune imagine: 512×512 pixeli

Figura 5.19. Rezultate execuție pentru imaginea de 512×512 pixeli

Dimensiune imagine: 1024×1024 pixeli

Figura 5.20. Rezultate execuție pentru imaginea de 1024×1024 pixeli

Modul de utilizare al aplicației ca sistem de supraveghere inteligent – testare funcțională și probleme apărute

Această aplicație a fost realizată cu scopul de a evidenția modul de lucru cu procesoarele grafce și integrarea acestora în sisteme reale de procesare din necesitatea de a executa operații complexe fără a solicita pre mult procesorul de uz general în cazul în care acesta este utilizat pentru alte task-uri. Acest tip de abordare poate fi utilă în cazul sistemelor ce necesită un răspuns rapid, în timp real.

Pentru a putea fi utilizată aplicația este necesară instalarea acesteia pe un sistem de calcul ce conține un dispozitiv de prelucrare grafică NVIDIA, dar și o conexiune la internet stabilă pentru a putea accesa serverul de baze de date. La această configurație se pot atașa camerele de supraveghere necesare, ne având un număr limită pentru acestea.

Cele mai frecvent întâlnite probleme pe care le-am întâlnit pe parcursul dezvoltării aplicațiilor au fost problem de compatibilitate între versiunile de librării folosite, versiunea driver-ului CUDA și compilarea programului sursă. Pentru a rezolva această problemă am ales să dezvolt aplicațiile și să le compilez pentru modul x64. De aceea trebuiesc modificate proprietățile proiectului la modul de compilare al acestuia la x64.

O altă problemă apărută a fost detectată în momentul în care s-a încercat implementarea în timp real a sistemului de supraveghere. Procesorul de uz general execută secvențial intrucțiunile ce nu se pot executa pe procesorul graphic cum ar fi inserarea în baza de date, fapt care încetinește procesul și împiedică sistemul să se comporte în mod continuu. Pentru a soluționa problema am ales să paralelizez și să separ aceste task-uri, de aceea s-au implementat procese diferite pentru detecție și inserarea în baza de date. Salvarea datelor se va face în mod asincron, în paralel cu procesul de detecție și procesare.

Modul de stocare al datelor în baza de date a reprezentat de asemenea o problemă deoarece în momentul conversiei datelor în format binary sau base64 trebuie să se păstreze formatul de imagine. In cazul în care apar erori la conversie, șirul binar nu poate fi transformat din nou în imagine. [33]

Rezultate testare

Pentru a folosi aplicația utilizatorul va lansa în execuție fisierul executabil x.exe. Prima pagină este pagina de configurare a modului în care vor opera camerele video. Aplicația detectează senzorii video conectați la sistem și alcătuiește o listă de unde se pot alege și configura fie ca detector de fețe fie ca detector de persoane.

Figura X. Pagina de pornire. Configurare senzor video

După ce se va selecta camera web ce urmează a fi configurată se apasă butonul „Go„ de unde se va trece la o altă fereastră ce permite selecția modului de lucru, așa cum este ilustrat în figura X.

Figura X. Alegere mod de lucru

CONCLUZII

Această documentație a fost realizată cu scopul de a analiza capacitățile de procesare ale sistemelor multicore CPU și GPU pentru a facilita prelucrările necesare unui sistem video inteligent de supraveghere.

În urma testelor realizate pentru a stabilii performanțele dar și modul de integrare al procesoarelor grafice în aplicații ce necesită o putere mare de calcul se ajunge la concluzia că aceasta este o metodă ce poate fi utilizată în acest tip de aplicații. Sistemele eterogene pot oferii o putere mare de procesare, reusind să reducă semnificativ timpul necesar pentru executarea unăr task-uri consumatoare de timp și de resurse.

Instalarea sistemului și configurarea acestuia pentru a se comporta cao aplicație inteligentă de supraveghere video poate fi de ajutor în soluționarea situațiilor în care se încearcă pătrunderea într-un spațiu privat a persoanelor neautorizate.

Proiectul se poate dovedi a fi folositor deoarece monitorizarea video complexă se este foarte costisitoare și necesită echipamente avansate, însă metoda descrisă mai sus poate fi instalată pe un sistem de calcul simplu, fiind necesară doar achiziționarea unei plăci video compatibilă cu tehnologia CUDA.

Aplicația pune la dispoziția utilizatorului elemente necesare identificării eventualelor pericole în orice moment de timp datorită execuției acestuia în timp real și continuu dar permite de asemenea și vizualizarea ulterioară a datelor. Acest lucru este de mare ajutor atunci când se sesizează unitatea de poliție deoarece se poate arăta captura feței sau corpului suspectului.

De adaugat idei!!!

Dezvoltări ulterioare

-sinteza probleme+ mod de rezolvare

BIBLIOGRAFIE

[1],[2],[3],[5],[6] – dr. ing. Ștefan Mocanu, prof. dr. ing. Radu Dobrescu, prof. dr. ing. Daniela Saru, as. drd. ing. Ramona Din, ing. Andrei Grumăzescu, “ARHITECTURI COMPLEXE FOLOSITE ÎN PRELUCRAREA PARALELĂ A IMAGINILOR”, în Revista Română de Informatică și Automatică, vol. 21, nr. 2, 2011

[4] – https://en.wikipedia.org/wiki/Graphics_processing_unit

[7], [8] – Jia Tse, IMAGE PROCESSING WITH CUDA, University of Nevada, Las Vegas, August 2012

[9] – Nicholas Wilt, The CUDA Handbook – A Comprehensive Guide to GPU Programming, Addison-Wesley, Indiana, 2013

[10] – NVIDIA Corporation, NVIDIA CUDA™ Programming Guide Version 2.3.1, 2701 San Tomas Expressway Santa Clara, August 2009

[11] – https://en.wikipedia.org/wiki/VirtualBox

[12] – Oracle Corporation, Oracle VM VirtualBox User Manual Version 5.1.4, 2004-2016

[13] – Oracle Corporation, Oracle® Linux Release Notes for Oracle Linux 7, Aprilie 2016

[14] – http://searchsqlserver.techtarget.com/definition/database-management-system

[15] – Lance Ashdown, Tom Kyte, Oracle® Database Concepts 12c Release 1 (12.1) E41396-14, Septembrie 2016

[16] – http://www.emgu.com/wiki/index.php/Main_Page

[17] – https://web.stanford.edu/class/ee368/Project_03/Project/reports/ee368group02.pdf

[18] – http://users.utcluj.ro/~tmarita/HCI/L9/L9.pdf

[15] – http://cristi.phonefinder.ro/2011/11/27/algoritmi-de-detectia-fetelor-clasificatori-harr-algoritm-viola-jones/

[19], [20], [23] – https://sites.google.com/site/5kk73gpu2012/assignment/viola-jones-face-detection

[21] – http://en.wikipedia.org/wiki/Summed_area_table

[22], [24] – http://en.wikipedia.org/wiki/Viola-Jones_object_detection_framework

[25] – Vaibhav Jain, Dinesh Patel, “A GPU based implementation of Robust Face Detection System ”, 2016 International Conference on Computational Science

[26], [27] – https://www.learnopencv.com/histogram-of-oriented-gradients/

[28] – ftp://cs.uoregon.edu/pub/malony/Papers/parco-2009.pdf

[29] – PAUL VIOLA, MICHAEL J. JONES, “Robust Real-Time Face Detection”, International Journal of Computer Vision 57(2), 137–154, 2004

[30] – http://www.i-dei.ro/2014/07/nvidia-geforce-gtx-850m/

[31] – https://docs.oracle.com/database/121/LADBI/pre_install.htm#LADBI8035

[32]https://www.packtpub.com/mapt/book/Application+Development/9781783559527/2/ch02lvl1sec13/Installing+Emgu+CV

[33]https://www.packtpub.com/mapt/book/Application+Development/9781783559527/2/ch02lvl1sec14/Troubleshooting

ANEXĂ

Codul sursă al aplicației utilizata pentru testele de performanță

Program Host

using ManagedCuda;

using ManagedCuda.BasicTypes;

using System;

using System.Diagnostics;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

namespace CSharpTest2

{

class Program

{

//dimensiune vectori ce urmeaza a fi prelucrati

const int DIM_VECTOR = 5000000;

//numarul de thread-uri ale unui bloc

const int NR_THREADS_BLOCK = 1024;

//Kernel-uri CUDA

static CudaKernel kernelOps;

static CudaKernel kernelAdd;

//initializari kernel-uri

static void InitKernelsAdd()

{

CudaContext context = new CudaContext();

CUmodule modul = context.LoadModule(@"C:\Users\Elena\Desktop\threads test\CudaPrj\Debug\kernel.ptx");

kernelAdd = new CudaKernel("_Z6kernelPiS_S_i", modul, context);

kernelAdd.BlockDimensions = NR_THREADS_BLOCK;

kernelAdd.GridDimensions = DIM_VECTOR / NR_THREADS_BLOCK + 1;

}

static void InitKernelsOps()

{

CudaContext context = new CudaContext();

CUmodule modul = context.LoadModule(@"C:\Users\Elena\Desktop\threads test\CudaPrj\Debug\kernel.ptx");

kernelOps = new CudaKernel("_Z6kernelPiS_S_i", modul, context);

kernelOps.BlockDimensions = NR_THREADS_BLOCK;

kernelOps.GridDimensions = DIM_VECTOR / NR_THREADS_BLOCK + 1;

}

//functie ce realizeaza adunarea vectorilor in paralel

static Func<int[], int[], int, int[]> adVectori = (a, b, dim) =>

{

// initializare parametrii

CudaDeviceVariable<int> vectA = a;

CudaDeviceVariable<int> vectB = b;

CudaDeviceVariable<int> h_vectorOut = new CudaDeviceVariable<int>(dim);

//executare metoda cuda

kernelAdd.Run(vectA.DevicePointer, vectB.DevicePointer, h_vectorOut.DevicePointer, dim);

// copiere rezultate pe host

int[] rez = new int[dim];

//copiere rezultat inapoi in memoria principala

h_vectorOut.CopyToHost(rez);

return rez;

};

//functie ce realizeaza teste cu diferite operatii

static Func<int[], int[], int, int[]> OpsVectori = (a, b, dim) =>

{

// initializare parametrii

CudaDeviceVariable<int> vectA = a;

CudaDeviceVariable<int> vectB = b;

CudaDeviceVariable<int> h_vectorOut = new CudaDeviceVariable<int>(dim);

//executare metoda cuda

kernelOps.Run(vectA.DevicePointer, vectB.DevicePointer, h_vectorOut.DevicePointer, dim);

// copiere rezultate pe host

int[] rez = new int[dim];

//copiere rezultat in memoria principala

h_vectorOut.CopyToHost(rez);

vectA.Dispose();

vectB.Dispose();

h_vectorOut.Dispose();

return rez;

};

static void Main(string[] args)

{

//initializari

InitKernelsAdd();

int[] vectorA = Enumerable.Range(1, DIM_VECTOR).ToArray();

int[] vectorB = Enumerable.Range(1, DIM_VECTOR).ToArray();

static int[] vectorA1 = Enumerable.Range(1, DIM_VECTOR).ToArray();

static int[] vectorB1 = Enumerable.Range(1, DIM_VECTOR).ToArray();

static int[] vectRez1 = Enumerable.Range(1, DIM_VECTOR).ToArray();

Console.WriteLine("**************Start test addition**************");

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

int[] vector = adVectori(vectorA, vectorB, DIM_VECTOR);

watch2.Stop();

Console.WriteLine("Gpu: " + watch2.Elapsed.ToString());

}

//start executie seriala

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

Parallel.Invoke(() =>

{

A();

});

watch2.Stop();

Console.WriteLine("Cpu: " + watch2.Elapsed.ToString());

}

InitKernelsOps();

int[] vectorAo = Enumerable.Range(1, DIM_VECTOR).ToArray();

int[] vectorBo= Enumerable.Range(1, DIM_VECTOR).ToArray();

Console.WriteLine();

Console.WriteLine();

Console.WriteLine("**************Start test Ops**************");

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

int[] vectorOps = OpsVectori(vectorAo, vectorBo, DIM_VECTOR);

watch2.Stop();

Console.WriteLine("Gpu: " + watch2.Elapsed.ToString());

//afisare(vectorOps);

}

//start executie seriala

for (int k = 0; k < 50; k++)

{

var watch2 = Stopwatch.StartNew();

Parallel.Invoke(() =>

{

B();

});

watch2.Stop();

Console.WriteLine("Cpu: " + watch2.Elapsed.ToString());

}

Console.ReadKey();

}

static void afisare(int[] a)

{

//functia de afisare a vectorilor

for (int i = 0; i < a.Length; i++)

{

Console.Write(a[i] + " ");

}

Console.WriteLine();

}

static void A()

{

//executa operatie similara kernel CUDA (adunare)

for (int i = 0; i < vectorA1.Length; i++)

{

vectRez1[i] = vectorA1[i] * vectorB1[i] * vectorA1[i] * vectorB1[i];

}

}

static void B()

{

//executa operatie similara kernel CUDA (diferite operatii)

for (int i = 0; i < vectorA1.Length; i++)

{

vectRez1[i] = vectorA1[i] * vectorB1[i] * vectorA1[i] * vectorB1[i];

}

}

}

}

Programul CUDA

include "cuda_runtime.h"

#include <stdio.h>

//adunare vectori

__global__ void kernel(int* a, int* b, int* rezultat, int N)

{

int i = blockDim.x * blockIdx.x + threadIdx.x;

if (i < N)

rezultat[i] = a[i] * b[i] * a[i] * b[i];//a[i] * 0.11 + b[i] * 0.59 + a[i] * 0.3;// a[i] + b[i];

}

__global__ void kernelOps(int* a, int* b, int* rezultat, int N)

{

int i = blockDim.x * blockIdx.x + threadIdx.x;

/*if (i < N)

rezultat[i] = a[i] + b[i];*/

/*if (i < N)

rezultat[i] = a[i] * 0.11 + b[i] * 0.59 + a[i] * 0.3;*/

if (i < N)

rezultat[i] = a[i] * 0.11 + b[i] * 0.59 + a[i] * 0.3 + b[i] * 0.74 + a[i] * 0.52;

}

int main()

{

return 0;

}

Codul sursă al aplicației de detecție a fețelor CUDA

MainDetFaceGPU.cu

#include "detFaceExtras.h"

#include "GPU_HC.h"

#include "GPUdet_h.cuh"

using namespace std;

using namespace cv;

int CalcNrDeClasificatori(CvHaarClassifierCascade *XMLdata);

void afisare(IplImage * imagine, std::vector<CvRect> fete, char * fereastra);

int main( int argc, const char** argv )

{

// fisiere de intrare.

const char *imgPath = "images\\lena_64.jpg";

const char *XMLfile= "data\\haarcascade_frontalface_default.xml";

// incarca imaginea

IplImage* imagine;

//incarca imaginea in grayscale

if ((imagine = cvLoadImage(imgPath, CV_LOAD_IMAGE_GRAYSCALE)) == 0)

{

cout << "Eroare la incarcarea imaginii" << endl;

system("pause");

return 0;

}

//dimensiuni imagine

int w = imagine->width;

int h = imagine->height;

CvSize dimImg = cvSize(w, h);

//printf("Input image: %s\n", imageFileName);

printf(" Dimensiune imagine: %d x %d \n\n", w, h);

// Incarca fisier XML & creaza sistem HaarCascadeGPU

CvHaarClassifierCascade *XMLdata = (CvHaarClassifierCascade*)cvLoad(XMLfile);

HaarCascadeGPU HaarCascGpu;

//transforma datele din fisierul XML in structuri de tip GPU haar Cascade

HaarCascGpu.load(XMLdata, CalcNrDeClasificatori(XMLdata), dimImg);

// Calcul imagini integrale (sum si sqsum rezultate) cu functia openCv cvIntegral

CvMat* sum = cvCreateMat(h + 1, w + 1, CV_32SC1);

//imagine ca i(x,y) la patrat

CvMat* sqsum = cvCreateMat(h + 1, w + 1, CV_64FC1);

cvIntegral(imagine, sum, sqsum);

//Calculul valori de scalare folosite pt a redimensiona fereastra

double factor = 1.0f;

float factorScalare = 1.25f;

std::vector<double> scara;

while (factor * HaarCascGpu.dimOrigFereastra.width < w – 10 && factor * HaarCascGpu.dimOrigFereastra.height < h – 10)

{

scara.push_back(factor);

//printf("%d ", factor);

factor *= factorScalare;

}

// executare algoritm viola & jones

// folosit pt gruparea dreptunghiurilor detectate(afisarea finala)

int minNeighbors = 2;

// porneste detectia fetei GPU

initializareGPU(HaarCascGpu, imagine, sum, sqsum);

//vector in care se depun rezultatele detectiei(dreptunghiuri)

std::vector<CvRect> feteRezGPU = runKernel(scara, minNeighbors);

//opreste gpu

//shutDownGPU();

//afisare rezultate

afisare(imagine, feteRezGPU, "Rezultat GPU");

cvWaitKey(0);

system("pause");

// eliberare memorie

cvReleaseHaarClassifierCascade(&XMLdata);

cvReleaseImage(&imagine);

cvReleaseMat(&sum);

cvReleaseMat(&sqsum);

cvReleaseImage(&imagine);

HaarCascGpu.shutdown();

return 0;

}

int CalcNrDeClasificatori(CvHaarClassifierCascade *XMLdata)

{

//parcurge fisierul xml si calculeaza nr total de clasificatori pt 25 de etape

int nrClasificatori = 0;

for (int i = 0; i < XMLdata->count; i++)

{

//preia etapele pe rand (stage_clasifier[i] este proprietate a structurii CvHaarClassifierCascade )

CvHaarStageClassifier etapa = XMLdata->stage_classifier[i];

nrClasificatori += etapa.count;

}

return nrClasificatori;

}

void afisare(IplImage * imagine, std::vector<CvRect> fete, char * fereastra)

{

// adauga dreptunghiuri pentru a evidentia rezultatul

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

{

//obiect dreptunghi pt fiecare fata

CvRect fata = fete[i];

//deseneaza un dreptunghi (imagine,doua puncte colturi opuse, culoare, grosime linie)

cvRectangle(imagine, cvPoint(fata.x, fata.y), cvPoint((fata.x + fata.width), (fata.y + fata.height)), CV_RGB(125, 275, 255), 3);

}

// afiseaza o fereastra ce contine imaginea

cvNamedWindow(fereastra, 0);

cvShowImage(fereastra, imagine);}

GPUdet.cu

#include "GPUdet_h.cuh"

#include "cuda.h"

#include "lock.h"

//declarare variabile necesare pt detectie GPU

// calcul perf CUDA

cudaEvent_t start, stop;

// memorie device pt haar cascade

HaarCascadeGPU h_gpuHC;

HaarCascadeGPU dev_gpuHC;

//declarare pointeri pentru texture memory GPU – memorie rapida read-only ca si memorie de constante

cudaArray * dev_IIsumVect = NULL;

cudaArray * dev_IIsqSumVect = NULL;

//vectori in care se copiaza rezultatul(fete detectate de GPU) pentru a fi post-procesate de CPU

DreptGPU *feteDetectate, *dev_feteDetectate;

size_t DimVectFeteDetectate;

//initializeaza memorie device pt procesare detectie fete GPU

void initializareGPU(HaarCascadeGPU &gpuHCasc, IplImage * imagine, CvMat *IIsum, CvMat *IIsqSum)

{

int w = imagine->width;

int h = imagine->height;

//defineste si porneste cuda event timing

HANDLE_ERROR( cudaEventCreate( &start ) );

HANDLE_ERROR( cudaEventCreate( &stop ) );

//defineste structurile GPU Haar Cascade <= CvHaarCascade

// incarca gpu haar cascade intr-o copie pt host

h_gpuHC.load(&gpuHCasc);

//alocarea memoriei pe gpu

alocaGPUcasc(h_gpuHC, dev_gpuHC);

//genereaza imaginile integrale si le copiaza in memoria gpu texture memory

//conversie din dubla precizie a imaginii sqsum la float

cv::Mat sqSumMat(IIsqSum->rows, IIsqSum->cols, CV_64FC1, IIsqSum->data.fl);

sqSumMat.convertTo(sqSumMat, CV_32FC1);

CvMat float_sqsm = sqSumMat;

//alocarea memorei texture pentru imaginile integrale si copierea rezultatelor functiei cvIntegral de pe host pe device

alocaIImgGPU(IIsum, &float_sqsm, dev_IIsumVect, dev_IIsqSumVect);

// aloca si copiaza datele unui vector de fete in memoria GPU pentru a salva rezultatele detectiei

//alocare memorie CPU

DimVectFeteDetectate = w * h * sizeof(DreptGPU);

feteDetectate = (DreptGPU *)malloc(DimVectFeteDetectate);

memset(feteDetectate, 0, DimVectFeteDetectate);

//alocare memorie pe GPU si copierea datelor de pe host

HANDLE_ERROR(cudaMalloc((void**)&dev_feteDetectate, DimVectFeteDetectate));

HANDLE_ERROR(cudaMemcpy(dev_feteDetectate, feteDetectate, DimVectFeteDetectate, cudaMemcpyHostToDevice));

}

int selectieFete(std::vector<CvRect> &fete, DreptGPU *FeteGpu, int pixeli)

{

//din vectorul de fete FeteGpu verifica fiecare CvRect.x pentru a determina daca GPU a returnat aceasta fereastra ca fiind o fata

int nrFeteDetectate = 0;

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

{

//extrage doar dreptunghiurile detectate

DreptGPU drFata = FeteGpu[i];

if (drFata.width != 0)

{

CvRect convertDr(drFata.x, drFata.y, drFata.width, drFata.height);

fete.push_back(convertDr);

nrFeteDetectate++;

}

}

return nrFeteDetectate;

}

void pornesteContor()

{

HANDLE_ERROR( cudaEventRecord( start, 0 ) );

}

float opresteContor()

{

HANDLE_ERROR( cudaEventRecord( stop, 0 ) );

HANDLE_ERROR( cudaEventSynchronize( stop ) );

float timp;

HANDLE_ERROR(cudaEventElapsedTime(&timp, start, stop));

return timp;

}

// rulare kernel pt face detection

std::vector<CvRect> runKernel(std::vector<double> scara, int minNeighbors)

{

printf("****Start GPU(Kernel) Detectie****\n\n");

std::vector<CvRect> fete;

float timpTotal = 0.0f;

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

{

//modifica toate caracteristicile pt noua scara

h_gpuHC.setFeaturesForScale(scara[i]);

//copiaza noile valori scalate pe device

size_t dimClasificatorGPU = h_gpuHC.nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU);

HANDLE_ERROR(cudaMemcpy(dev_gpuHC.clasificatori_haar_scalati,h_gpuHC.clasificatori_haar_scalati, dimClasificatorGPU, cudaMemcpyHostToDevice));

dev_gpuHC.scara = h_gpuHC.scara;

dev_gpuHC.dimFereastraCurenta = h_gpuHC.dimFereastraCurenta;

dev_gpuHC.dimImgDetectie = h_gpuHC.dimImgDetectie;

int w = dev_gpuHC.dimImgDetectie.width;

int h = dev_gpuHC.dimImgDetectie.height;

// lanseaza in executie kernel

//adaugat pt a scapa de functia startKernel

//defineste nr de blocuri si threaduri pt a diviza munca

dim3 blocks(w / 16, h / 16);

dim3 threads(16, 16);

//porneste timer

pornesteContor();

//apelare kernel pentru a rula haarcascade pentru fiecare fereastra de detectie intr-o imagine

kernelDetectie << <blocks, threads >> >(dev_gpuHC, dev_feteDetectate);

// Stop timer

float timpDet = opresteContor();

timpTotal += timpDet;

//copiere rezultate de pe device si procesare pe CPU

HANDLE_ERROR(cudaMemcpy(feteDetectate,dev_feteDetectate, DimVectFeteDetectate, cudaMemcpyDeviceToHost));

//scanare vector de feteDetectate de GPU si alegere fete valide

int fete_detectate = selectieFete(fete, feteDetectate, w * h);

//afisare info de performanta GPU pentru etapa curenta

printf("Scala: %d ** Fete Detectate: %d ** Timp GPU: %3.1f ms \n", i, fete_detectate, timpTotal);

}

// afisare perf totala

printf("\n Timp total de executie: %3.1f ms \n\n", timpTotal);

if( minNeighbors != 0)

{

groupRectangles(fete, minNeighbors, GROUP_EPS);

}

//curatare vector de fetedetectate pentru procesari ulterioare

memset(feteDetectate, 0, DimVectFeteDetectate);

HANDLE_ERROR(cudaMemcpy(dev_feteDetectate, feteDetectate, DimVectFeteDetectate, cudaMemcpyHostToDevice));

return fete;}

void stopGPU()

{

//dezaloca texturi si elibereaza memorie host si gpu

elibereazaMemTexturi();

HANDLE_ERROR( cudaEventDestroy( start ) );

HANDLE_ERROR( cudaEventDestroy( stop ) );

h_gpuHC.shutdown();

HANDLE_ERROR(cudaFree(dev_gpuHC.clasificatori_haar));

HANDLE_ERROR(cudaFree(dev_gpuHC.clasificatori_haar_scalati));

free(feteDetectate);

HANDLE_ERROR(cudaFree(dev_feteDetectate));

HANDLE_ERROR(cudaFreeArray(dev_IIsumVect));

HANDLE_ERROR(cudaFreeArray(dev_IIsqSumVect));

HANDLE_ERROR( cudaDeviceReset());

}

GPUdet_h.cuh

#include "detFaceExtras.h"

#include "GPU_HC.h"

#ifndef FACE_DETECT_CUH

#define FACE_DETECT_CUH

#include "lock.h"

// defineste kerneluri ce pot fi pornite

__global__ void kernelDetectie(HaarCascadeGPU haarCascade, DreptGPU * feteDetectate);

//engine detectare GPU

void initializareGPU(HaarCascadeGPU &h_gpuHC, IplImage * imagine, CvMat *IIsum, CvMat *IIsqSum);

std::vector<CvRect> runKernel(std::vector<double> scara, int minNeighbors);

void stopGPU();

//metode initializare kernel

void alocaGPUcasc(HaarCascadeGPU &h_gpuCas, HaarCascadeGPU &dev_gpuCas);

void alocaIImgGPU(CvMat * IIsum, CvMat *IIsqSum, cudaArray *dev_sumVect, cudaArray * dev_sqSumVect);

void elibereazaMemTexturi();

#endif

GPU_Kernels.cu

#include "GPUdet_h.cuh"

#include "cuda.h"

#include "lock.h"

#define CONSTANT_DIM_MEM 32

__constant__ ClasifHaarEtapaGPU clasifEtapa[CONSTANT_DIM_MEM];

texture<int, 2, cudaReadModeElementType> IIsumRef;

texture<float, 2, cudaReadModeElementType> IIsqSumRef;

void alocaGPUcasc(HaarCascadeGPU &h_gpuCas, HaarCascadeGPU &dev_gpuCas)

{

// copiere parametrii generici

dev_gpuCas.flags = h_gpuCas.flags;

dev_gpuCas.nrEtape = h_gpuCas.nrEtape;

dev_gpuCas.dimOrigFereastra = h_gpuCas.dimOrigFereastra;

dev_gpuCas.dimFereastraCurenta = h_gpuCas.dimFereastraCurenta;

dev_gpuCas.dimFereastraImg = h_gpuCas.dimFereastraImg;

dev_gpuCas.scara = h_gpuCas.scara;

dev_gpuCas.nrTotalDeClasificatori = h_gpuCas.nrTotalDeClasificatori;

//aloca spatiu pentru clasificatori pe device si copiaza clasificatorii din memorie host

size_t DimClasificatorGPU = h_gpuCas.nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU);

HANDLE_ERROR(cudaMalloc((void**)&dev_gpuCas.clasificatori_haar, DimClasificatorGPU));

HANDLE_ERROR(cudaMemcpy(dev_gpuCas.clasificatori_haar,h_gpuCas.clasificatori_haar, DimClasificatorGPU, cudaMemcpyHostToDevice));

HANDLE_ERROR(cudaMalloc((void**)&dev_gpuCas.clasificatori_haar_scalati,DimClasificatorGPU));

HANDLE_ERROR(cudaMemcpy(dev_gpuCas.clasificatori_haar_scalati,h_gpuCas.clasificatori_haar_scalati, DimClasificatorGPU, cudaMemcpyHostToDevice));

if (h_gpuCas.nrEtape > CONSTANT_DIM_MEM)

{

printf("ERROR: Nr de etape este mai mare decat dimensiunea maxima a memoriei constanta alocata ");

system("pause");

return;

}

HANDLE_ERROR(cudaMemcpyToSymbol(clasifEtapa,h_gpuCas.clasif_haar_etapa,sizeof(ClasifHaarEtapaGPU) * h_gpuCas.nrEtape));

}

void alocaIImgGPU(CvMat * IIsum, CvMat *IIsqSum, cudaArray *dev_sumVect, cudaArray * dev_sqSumVect)

{

// aloca si referentiaza texture memory pentru imaginea integrala sum

// creaza descrierea canalului pt textura (1 canal, 32 biti, type sugned int)

//channelDesc describes the format of the value that is returned when fetching the texture;

cudaChannelFormatDesc sum_Desc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindSigned );

//aloca memoria pt sum image texture

HANDLE_ERROR(cudaMallocArray(&dev_sumVect, &sum_Desc, IIsum->width, IIsum->height));

//copiaza image data din openCv in memorie device

HANDLE_ERROR(cudaMemcpy2DToArray(dev_sumVect, 0, 0, IIsum->data.i, IIsum->step, IIsum->width * sizeof(int), IIsum->height, cudaMemcpyHostToDevice));

//seteaza parametrii penru CUDA texture reference

IIsumRef.addressMode[0] = cudaAddressModeWrap;

IIsumRef.addressMode[1] = cudaAddressModeWrap;

IIsumRef.filterMode = cudaFilterModePoint; //cudaFilterModeLinear

IIsumRef.normalized = false;

//leaga referinta textura de memoria alocata pe device

HANDLE_ERROR(cudaBindTextureToArray(IIsumRef, dev_sumVect, sum_Desc));

// aloca si referentiaza texture memory pentru imaginea integrala square sum

// creaza descrierea canalului pt textura (1 canal, 64biti, type float)

cudaChannelFormatDesc sqSum_Desc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);

// aloca memoria pt squared sum image texture

HANDLE_ERROR(cudaMallocArray(&dev_sqSumVect, &sqSum_Desc, IIsqSum->width, IIsqSum->height));

//copiaza image data din openCv in memorie device

HANDLE_ERROR(cudaMemcpy2DToArray(dev_sqSumVect, 0, 0, IIsqSum->data.fl, IIsqSum->step, IIsqSum->width * sizeof(int), IIsqSum->height, cudaMemcpyHostToDevice));

//seteaza parametrii penru CUDA texture reference

IIsqSumRef.addressMode[0] = cudaAddressModeWrap;

IIsqSumRef.addressMode[1] = cudaAddressModeWrap;

IIsqSumRef.filterMode = cudaFilterModeLinear;

IIsqSumRef.normalized = false;

//leaga referinta textura de memoria alocata pe device

HANDLE_ERROR(cudaBindTextureToArray(IIsqSumRef, dev_sqSumVect, sqSum_Desc));

}

void elibereazaMemTexturi()

{

//dezaloca texture memory

cudaUnbindTexture(IIsumRef);

cudaUnbindTexture(IIsqSumRef);

}

__device__ float calculateMean(DreptGPU dr)

{

int A = tex2D(IIsumRef, dr.x, dr.y);

int B = tex2D(IIsumRef, dr.x + dr.width, dr.y);

int C = tex2D(IIsumRef, dr.x + dr.width, dr.y + dr.height);

int D = tex2D(IIsumRef, dr.x, dr.y + dr.height);

return (float)(A – B + C – D);

}

__device__ float calculSum(DreptGPU dr, int win_start_x, int win_start_y)

{

float tx = win_start_x + dr.x;

float ty = win_start_y + dr.y;

int A = tex2D(IIsumRef, tx, ty);

int B = tex2D(IIsumRef, tx + dr.width, ty);

int C = tex2D(IIsumRef, tx + dr.width, ty + dr.height);

int D = tex2D(IIsumRef, tx, ty + dr.height);

return (float)(A – B + C – D);

}

__device__ int calcOffset(int x, int y)

{

return x + y * blockDim.x * gridDim.x;

}

__device__ float runHaarFeature(ClasificatorHaarGPU clasificator, DreptGPU fereastraDetectie, float variance_norm_factor, float weightScale)

{

double t = clasificator.prag * variance_norm_factor;

double sum = calculSum(clasificator.caracteristica_haar.drept0.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept0.weight * weightScale;

sum += calculSum(clasificator.caracteristica_haar.drept1.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept1.weight * weightScale;

// daca exista un al treilea dreptunghi

if (clasificator.caracteristica_haar.drept2.weight)

sum += calculSum(clasificator.caracteristica_haar.drept2.r, fereastraDetectie.x, fereastraDetectie.y) * clasificator.caracteristica_haar.drept2.weight * weightScale;

if(sum >= t)

return clasificator.alfa1;

else

return clasificator.alfa0;

}

__device__ float calculateVariance(DreptGPU fereastraDetectie)

{

float fereasra_inv= 1.0f / ((float)fereastraDetectie.width * fereastraDetectie.height);

//fisierul xml haarCascade necesita notmalizarea caracteristicilor

float mean = calculateMean(fereastraDetectie) * fereasra_inv;

float variance_norm_factor = tex2D(IIsqSumRef, fereastraDetectie.x, fereastraDetectie.y) -tex2D(IIsqSumRef, fereastraDetectie.x + fereastraDetectie.width, fereastraDetectie.y) -tex2D(IIsqSumRef, fereastraDetectie.x, fereastraDetectie.y + fereastraDetectie.height) +

tex2D(IIsqSumRef, fereastraDetectie.x + fereastraDetectie.width, fereastraDetectie.y + fereastraDetectie.height);

variance_norm_factor = variance_norm_factor * fereasra_inv – mean * mean;

if(variance_norm_factor >= 0.0f)

variance_norm_factor = sqrt(variance_norm_factor);

else

{

variance_norm_factor = 1.0f;

}

return variance_norm_factor;

}

__global__ void kernelDetectie(HaarCascadeGPU HCascade, DreptGPU * feteDetectate)

{

int x = threadIdx.x + blockIdx.x * blockDim.x;

int y = threadIdx.y + blockIdx.y * blockDim.y;

int offset = calcOffset(x, y);

// daca pixelul curent este in afara ariei return

if (x < HCascade.dimImgDetectie.width || y < HCascade.dimImgDetectie.height)

{

DreptGPU fereastraDetectie;

fereastraDetectie.x = x;

fereastraDetectie.y = y;

fereastraDetectie.width = HCascade.dimFereastraCurenta.width;

fereastraDetectie.height = HCascade.dimFereastraCurenta.height;

float variance_norm_factor = calculateVariance(fereastraDetectie);

float fereasra_inv = 1.0f / ((float)fereastraDetectie.width * fereastraDetectie.height);

// presupun ca fata a fost detectata

feteDetectate[offset] = fereastraDetectie;

//pt fiecare etapa din cascada

for (int i = 0; i < HCascade.nrEtape; i++)

{

float sumaEtapa = 0.0;

for (int j = 0; j < clasifEtapa[i].nrDeClasificatori; j++)

{

int index = j + clasifEtapa[i].offsetClasificator;

ClasificatorHaarGPU classifier = HCascade.clasificatori_haar_scalati[index];

sumaEtapa += runHaarFeature(classifier, fereastraDetectie, variance_norm_factor, fereasra_inv);

}

// clasificatorul nu a trecut, se iese din cascada

if (sumaEtapa < clasifEtapa[i].prag)

{

//seteaza latimea 0 pt a indica CPU-ului ca aceasta nu e o fata

feteDetectate[offset].width = 0;

break;

}}}}

GPU_HC.h

#include "opencv\cv.h"

#ifndef GPUHAARCASCADE_H

#define GPUHAARCASCADE_H

typedef struct DreptGPU

{

float x;

float y;

float width;

float height;

#ifdef __cplusplus

__device__ __host__

DreptGPU(float _x = 0, float _y = 0, float w = 0, float h = 0) : x(_x), y(_y), width(w), height(h) {}

#endif

}DreptGPU;

typedef struct DreptCaracteristicaGPU

{

DreptGPU r;

float weight;

}DreptCaracteristicaGPU;

typedef struct CaracteristicaHaarGPU

{

DreptCaracteristicaGPU drept0;

DreptCaracteristicaGPU drept1;

DreptCaracteristicaGPU drept2;

}CaracteristicaHaarGPU;

typedef struct ClasificatorHaarGPU

{

//se presupune ca exista o singura caracteristica pt clasificator

CaracteristicaHaarGPU caracteristica_haar;

float prag;

// corespunde lui alfa[]

float alfa0;

float alfa1;

}ClasificatorHaarGPU;

typedef struct ClasifHaarEtapaGPU

{

//numar de clasificatori in etapa

int nrDeClasificatori;

// prag pt clasificator

float prag;

//index offset ce pointeaza catre inceputul clasificatorilor pt aceasta etapa

int offsetClasificator;

}ClasifHaarEtapaGPU;

typedef struct HaarCascadeGPU

{

// parametrii CvHaarClassifierCascade

int flags;

int nrEtape;

int nrTotalDeClasificatori;

CvSize dimOrigFereastra;

CvSize dimFereastraImg;

CvSize dimImgDetectie;

CvSize dimFereastraCurenta;

double scara;

//vector ce contine toti clasificatorii de etapa din sistem

ClasifHaarEtapaGPU* clasif_haar_etapa;

//vector ce contine toti clasificatorii din cascada

ClasificatorHaarGPU * clasificatori_haar;

//vector ce contine toti clasificatorii modificati cu valoarea de scalare de mai sus

ClasificatorHaarGPU * clasificatori_haar_scalati;

void load(CvHaarClassifierCascade *cvCascade, int nrClasif, CvSize dimImag)

{

// transfera proprietatile

flags = cvCascade->flags;

nrEtape = cvCascade->count;

dimOrigFereastra = cvCascade->orig_window_size;

dimFereastraCurenta = cvCascade->real_window_size;

dimFereastraImg = dimImag;

scara = cvCascade->scale;

nrTotalDeClasificatori = nrClasif;

//alocare memorie pentru vectori

clasif_haar_etapa = (ClasifHaarEtapaGPU *)malloc(nrEtape * sizeof(ClasifHaarEtapaGPU));

clasificatori_haar = (ClasificatorHaarGPU *)malloc(nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU));

clasificatori_haar_scalati = (ClasificatorHaarGPU *)malloc(nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU));

//parcurge XMl si stocheaza etapele si clasificatorii

int contorGPUclasif = 0;

for(int i = 0; i < cvCascade->count; i++)

{

// preia o etapa de clasificatori

CvHaarStageClassifier etapa = cvCascade->stage_classifier[i];

//creaza clasificator cascada GPU pt etapa

ClasifHaarEtapaGPU etapaGpu;

etapaGpu.prag = etapa.threshold;

etapaGpu.nrDeClasificatori = etapa.count;

etapaGpu.offsetClasificator = contorGPUclasif;

//trece prin toti clasificatorii din etapa

for(int j = 0; j < etapa.count; j++)

{

//open cv clasificator

CvHaarClassifier clasificator = etapa.classifier[j];

//creaza clasificator GPU

ClasificatorHaarGPU clasificatorGpu;

clasificatorGpu.prag = clasificator.threshold[0];

clasificatorGpu.alfa0 = clasificator.alpha[0];

clasificatorGpu.alfa1 = clasificator.alpha[1];

//preia caracteristici clasificator OpenCV Haar

CvHaarFeature caracteristica = clasificator.haar_feature[0];

// Creaza caracteristica GPU

CaracteristicaHaarGPU caracteristicaGpu;

//extrage dreptunghiuri si ponderi

caracteristicaGpu.drept0.r.x = caracteristica.rect[0].r.x;

caracteristicaGpu.drept0.r.y = caracteristica.rect[0].r.y;

caracteristicaGpu.drept0.r.width = caracteristica.rect[0].r.width;

caracteristicaGpu.drept0.r.height = caracteristica.rect[0].r.height;

caracteristicaGpu.drept0.weight = caracteristica.rect[0].weight;

caracteristicaGpu.drept1.r.x = caracteristica.rect[1].r.x;

caracteristicaGpu.drept1.r.y = caracteristica.rect[1].r.y;

caracteristicaGpu.drept1.r.width = caracteristica.rect[1].r.width;

caracteristicaGpu.drept1.r.height = caracteristica.rect[1].r.height;

caracteristicaGpu.drept1.weight = caracteristica.rect[1].weight;

caracteristicaGpu.drept2.r.x = caracteristica.rect[2].r.x;

caracteristicaGpu.drept2.r.y = caracteristica.rect[2].r.y;

caracteristicaGpu.drept2.r.width = caracteristica.rect[2].r.width;

caracteristicaGpu.drept2.r.height = caracteristica.rect[2].r.height;

caracteristicaGpu.drept2.weight = caracteristica.rect[2].weight;

clasificatorGpu.caracteristica_haar = caracteristicaGpu;

//adauga noi clasificatori GPU in vectorul de clasificatori din cascadeGpu

clasificatori_haar[contorGPUclasif] = clasificatorGpu;

clasificatori_haar_scalati[contorGPUclasif] = clasificatorGpu;

contorGPUclasif++;

}

//adauga o noua etapa de clasificatori GPU la vector

clasif_haar_etapa[i] = etapaGpu;

}}

void load(HaarCascadeGPU * cascadeGpu)

{

// Transfera proprietatile

flags = cascadeGpu->flags;

nrEtape = cascadeGpu->nrEtape;

dimOrigFereastra = cascadeGpu->dimOrigFereastra;

dimFereastraCurenta = cascadeGpu->dimFereastraCurenta;

dimFereastraImg = cascadeGpu->dimFereastraImg;

scara = cascadeGpu->scara;

nrTotalDeClasificatori = cascadeGpu->nrTotalDeClasificatori;

//aloca si copiaza clasificatori de etapa

clasif_haar_etapa = (ClasifHaarEtapaGPU *)malloc(nrEtape * sizeof(ClasifHaarEtapaGPU));

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

clasif_haar_etapa[i] = cascadeGpu->clasif_haar_etapa[i];

clasificatori_haar = (ClasificatorHaarGPU *)malloc(nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU));

clasificatori_haar_scalati = (ClasificatorHaarGPU *)malloc(nrTotalDeClasificatori * sizeof(ClasificatorHaarGPU));

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

{

clasificatori_haar[i] = cascadeGpu->clasificatori_haar[i];

clasificatori_haar_scalati[i] = cascadeGpu->clasificatori_haar_scalati[i];

}

}

void setFeaturesForScale(float scaraNoua)

{

scara = scaraNoua;

dimFereastraCurenta.width = cvRound(dimOrigFereastra.width * scara);

dimFereastraCurenta.height = cvRound(dimOrigFereastra.height * scara);

dimImgDetectie.width = cvRound(dimFereastraImg.width – dimFereastraCurenta.width);

dimImgDetectie.height = cvRound(dimFereastraImg.height – dimFereastraCurenta.height);

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

{

CaracteristicaHaarGPU caracteristica_init = clasificatori_haar[i].caracteristica_haar;

CaracteristicaHaarGPU *caracteristica_scalata = &clasificatori_haar_scalati[i].caracteristica_haar;

caracteristica_scalata->drept0.r.x = caracteristica_init.drept0.r.x * scara;

caracteristica_scalata->drept0.r.y = caracteristica_init.drept0.r.y * scara;

caracteristica_scalata->drept0.r.width = caracteristica_init.drept0.r.width * scara;

caracteristica_scalata->drept0.r.height = caracteristica_init.drept0.r.height * scara;

caracteristica_scalata->drept1.r.x = caracteristica_init.drept1.r.x * scara;

caracteristica_scalata->drept1.r.y = caracteristica_init.drept1.r.y * scara;

caracteristica_scalata->drept1.r.width = caracteristica_init.drept1.r.width * scara;

caracteristica_scalata->drept1.r.height = caracteristica_init.drept1.r.height * scara;

if(caracteristica_init.drept2.weight)

{

caracteristica_scalata->drept2.r.x = caracteristica_init.drept2.r.x * scara;

caracteristica_scalata->drept2.r.y = caracteristica_init.drept2.r.y * scara;

caracteristica_scalata->drept2.r.width = caracteristica_init.drept2.r.width * scara;

caracteristica_scalata->drept2.r.height = caracteristica_init.drept2.r.height * scara;

}}}

void shutdown()

{

free(clasif_haar_etapa);

free(clasificatori_haar);

}

}HaarCascadeGPU;

#endif

detFaceExtras.h

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <algorithm>

#include "opencv\cv.h"

#include "opencv\highgui.h"

#include "opencv2/objdetect/objdetect.hpp"

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "cuda_runtime.h"

#include "device_launch_parameters.h"

#ifndef FACEDETECTHELPER_H

#define FACEDETECTHELPER_H

//metoda ce gestioneaza erorile cuda

static void HandleError( cudaError_t eroare, const char *file,int linie ) {

if (eroare != cudaSuccess) {

printf( "%s in %s la linia %d\n", cudaGetErrorString( eroare ),

file, linie );

exit( EXIT_FAILURE );

}

}

#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))

Codul sursă al aplicației sistem de supraveghere inteligent

StartMenu.cs

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

using ManagedCuda;

using ManagedCuda.NPP;

using System.Reflection;

using Oracle.DataAccess.Client;

using System.IO;

using System.Drawing.Imaging;

using Oracle.DataAccess.Types;

using System.Diagnostics;

using Emgu.CV;

using Emgu.CV.CvEnum;

using Emgu.CV.Structure;

using Emgu.CV.UI;

using Emgu.CV.Cuda;

using Emgu.CV.Util;

using Emgu.CV.VideoSurveillance;

using Emgu.Util;

using FaceDetection;

using System.Configuration;

using System.Threading;

using DirectShowLib;

namespace ProcressImages

{

public partial class StartMenu : Form

{

string connString = ConfigurationManager.ConnectionStrings["connStr"].ToString();

Video_Device[] WebCams;

List<string> started = new List<string>();

public StartMenu()

{

InitializeComponent();

getWebcamIds();

}

private void getWebcamIds()

{

//detecteaza toate camerele web conectate la pc

DsDevice[] _SystemCamereas = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

WebCams = new Video_Device[_SystemCamereas.Length];

for (int i = 0; i < _SystemCamereas.Length; i++)

{

WebCams[i] = new Video_Device(i, _SystemCamereas[i].Name, _SystemCamereas[i].ClassID);

cbAvWebcams.Items.Add(WebCams[i]);

}

OracleConnection con = new OracleConnection(connString);

List<string> video_devices_db = new List<string>();

try

{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "SELECT device_name FROM video_device ";

using (var reader = cmd.ExecuteReader())

{

while (reader.Read())

{

if (!reader.IsDBNull(0))

{

video_devices_db.Add(reader.GetString(0));

}

}

}

for (int w = 0; w < WebCams.Length; w++)

{

Console.WriteLine(WebCams.ElementAt(w));

if (!video_devices_db.Contains(WebCams[w].Device_Name))

{

cmd.CommandText = "insert into video_device values(:id, :name, :identifier)";

cmd.Parameters.Add("id", WebCams[w].Device_ID);

cmd.Parameters.Add("name", WebCams[w].Device_Name);

cmd.Parameters.Add("identifier", WebCams[w].Identifier.ToString());

cmd.ExecuteNonQuery();

cmd.CommandText = "Commit";

cmd.ExecuteNonQuery();

}

}

}

catch (Exception)

{

MessageBox.Show("Database not availble!");

}

finally

{

con.Close();

}

}

private void bFace_Click(object sender, EventArgs e)

{

bTrainEngine.Enabled = false;

getIm get = new getIm();

get.Show();

}

private void bPedDet_Click(object sender, EventArgs e)

{

bTrainEngine.Enabled = false;

this.Hide();

FluxInsertPedestrian flux = new FluxInsertPedestrian();

flux.Show();

}

private void bGo_Click(object sender, EventArgs e)

{

bTrainEngine.Enabled = false;

if (cbAvWebcams.SelectedItem ==null)

{

MessageBox.Show("Select first an webcam!");

return;

}

string w = cbAvWebcams.SelectedItem.ToString();

Video_Device vdev = (Video_Device)cbAvWebcams.SelectedItem;

int wId = vdev.Device_ID;

if (started.Contains(w))

{

MessageBox.Show("Already started. Select another");

}

else

{

started.Add(w);

chMode cm = new chMode(wId, "Alege modul de lucru", "Face detection", "Pedestrian Detection");

cm.ShowDialog();}}

private void bTrainEngine_Click(object sender, EventArgs e)

{

TrainingForm tf = new TrainingForm();

tf.Show();

}}}

chMode.cs

namespace ProcressImages

{

//alegere mod de lucru

public partial class chMode : Form

{

public chMode()

{

InitializeComponent();

}

int device_id;

//constructor

public chMode(int dev_id,string message,string buttonText1,string buttonText2)

{

InitializeComponent();

label1.Text = message;

button1.Text = buttonText1;

button2.Text = buttonText2;

button3.Text = "Cancel";

device_id = dev_id;

}

private void button1_Click(object sender, EventArgs e)

{

//lanseaza fereastra detectie fete

FluxInsert f = new FluxInsert(device_id,1);

f.Show();

this.Close();

}

private void button2_Click(object sender, EventArgs e)

{

//lanseaza fereastra detectie pietoni

FluxInsertPedestrian fp = new FluxInsertPedestrian(device_id);

fp.Show();

this.Close();

}

private void button3_Click(object sender, EventArgs e)

{

this.Close();

}}}

DbRow.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace ProcressImages

{

public class DbRow

{

public string Img { get; set; }

public int Faces { get; set; }

public string Data { get; set; }

public DbRow() { }

public DbRow(string img, int faces, string data)

{

Img = img;

Faces = faces;

Data = data;

}}}

DetectieFete.cs

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Drawing;

using Emgu.CV;

using Emgu.CV.Structure;

#if !(__IOS__ || NETFX_CORE)

using Emgu.CV.Cuda;

#endif

namespace FaceDetection

{

public static class DetectieFete

{

public static void detectie(IInputArray imagine, String fileFete, List<Rectangle> fete, out long timpDetectie)

{

Stopwatch contor;

using (InputArray iaImage = imagine.GetInputArray())

{

#if !(__IOS__ || NETFX_CORE)

if (CudaInvoke.HasCuda)

{

using (CudaCascadeClassifier fata = new CudaCascadeClassifier(fileFete))

{

fata.ScaleFactor = 1.1;

fata.MinNeighbors = 10;

fata.MinObjectSize = Size.Empty;

contor = Stopwatch.StartNew();

using (CudaImage<Bgr, Byte> imagineGPU = new CudaImage<Bgr, byte>(imagine))

using (CudaImage<Gray, Byte> imagineGpuGray = imagineGPU.Convert<Gray, Byte>())

using (GpuMat regiune = new GpuMat())

{

fata.DetectMultiScale(imagineGpuGray, regiune);

Rectangle[] regiuneFata = fata.Convert(regiune);

fete.AddRange(regiuneFata);

}

contor.Stop();

}

else

#endif

{

using (CascadeClassifier fata = new CascadeClassifier(fileFete))

{

contor = Stopwatch.StartNew();

using (UMat imGray = new UMat())

{

CvInvoke.CvtColor(imagine, imGray, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);

CvInvoke.EqualizeHist(imGray, imGray);

Rectangle[] feteDet = fata.DetectMultiScale( imGray,1.1, 10, new Size(20, 20));

fete.AddRange(feteDet);

}

contor.Stop();

} }

timpDetectie = contor.ElapsedMilliseconds;}}}}

FluxInsert.cs

namespace ProcressImages

{

public partial class FluxInsert : Form

{

bool fNPP = false;

int facesDetected = 0;

//stringul de conectare

string connString = ConfigurationManager.ConnectionStrings["connStr"].ToString();

//lista care se va folosi ca o coada. se adauga imagini si se introduc asincron in baza de date

List<DbRow> rows = new List<DbRow>();

//id-ul camerei video de la care se preiau imaginile

static int device_id = 0;

AccessDB _dataStoreAccess = new AccessDB();

#region Camera Capture Variables

private Capture _cap = null; //declarare camera

#endregion

private BackgroundSubtractor _detF;

private Mat _Fmask = new Mat();

Thread t1, t2;

bool res = false;

int mode;

//algoritmi emgu cv pentru recunoastere

private FaceRecognizer _faceRec = new EigenFaceRecognizer(0, 50000);

List<Tuple<int, string>> IdAndlabel;

string adr_trainFile = Application.StartupPath + "trainedData.xml";

//constructor

public FluxInsert(int dev_id, int mode)

{

//initializeaza camera video si porneste taskuri in paralel

device_id = dev_id;

this.mode = mode;

InitializeComponent();

executions();

}

//verifica existenta unui device CUDA

private void Form1_Load(object sender, EventArgs e)

{

//verifica configuratia GPU si o afiseaza in textbox-ul de log

NppLibraryVersion versiuneLib = NPPNativeMethods.NPPCore.nppGetLibVersion();

GpuComputeCapability capCalcul = NPPNativeMethods.NPPCore.nppGetGpuComputeCapability();

string rez = string.Format("Versiune librarie NPP CUDA {0}\n", versiuneLib.ToString());

txt_info.AppendText(rez);

fNPP = capCalcul != GpuComputeCapability.CudaNotCapable;

if (fNPP)

{

rez = string.Format("{0} foloseste GPU <{1}>, {2} SM(s) cu", Assembly.GetExecutingAssembly().GetName().Name,

NPPNativeMethods.NPPCore.nppGetGpuName(), NPPNativeMethods.NPPCore.nppGetGpuNumSMs());

txt_info.AppendText(rez);

if (capCalcul > 0)

{

rez = " Capacitate de calcul " + ((int)capCalcul / 100).ToString() + "." + ((int)capCalcul % 100).ToString() + "\n";

txt_info.AppendText(rez);}

else

{ txt_info.AppendText(" Capacitate de calcul necunoscuta\n"); } }

else

{

rez = string.Format(" {0}\n", "Nu au fost identificate dispozitive CUDA");

txt_info.AppendText(rez);

}

NPPWarningHandler.GetInstance().OnNPPWarning += new NPPWarningHandler.NPPWarningEventHandler(NPPwarningEv);

}

private void NPPwarningEv(object sender, NPPWarningHandler.NPPWarningEventArgs e)

{ txt_info.AppendText("NPP Warning: " + e.Message + "\n"); }

private void executions()

{

//antreneaza sistemul de recunoastere

res = antrenareSistem();

if (res == false)

{

//rezulta atunci cand sistemul nu a fost antrenat

Console.WriteLine("Recognition will not be completed. System not trained. No data");

}

else

{

//porneste paralel capturarea imaginilor si inserarea in BD

t1 = new Thread(() => startCapturingProcess());

t2 = new Thread(() => startInsertingProcess());

t1.Start();

t2.Start();

}}

private void startCapturingProcess()

{

//pornire proces captura

Thread.CurrentThread.IsBackground = true;

Console.WriteLine("Incepe primul task…");

if (_cap == null)

{

try

{ _cap = new Capture(device_id); }

catch (NullReferenceException ex)

{ MessageBox.Show(ex.Message); }}

if (_cap != null)

{

//daca se porneste cu succes, se executa functia de prelucrare

_cap.ImageGrabbed += PrelFrame;

_cap.Start();

}}

private void startInsertingProcess()

{

Thread.CurrentThread.IsBackground = true;

Console.WriteLine("Incepe al doilea task..");

for (;;)

{

//se executa permanent

if (rows.Count != 0){

//daca lista de randuri nu este goala se apeleaza insereaza in BD

insertIntoDb();}}}

public bool antrenareSistem()

{

//lista in care se retin id-urile si etichetele fetelor

IdAndlabel = new List<Tuple<int, string>>();

//preia lista de imagini din BD pentru antrenare

var faces = _dataStoreAccess.CallFaces("ALL_USERS");

if (faces != null)

{

//lista in care se salveaza imaginile

var faceImages = new Image<Gray, byte>[faces.Count];

//lista in care se salveaza etichetele

var faceLabels = new int[faces.Count];

for (int i = 0; i < faces.Count; i++)

{

try

{ //preia imaginea, o converteste in grayscale si o redimensioneaza la 100×100

MemoryStream st = new MemoryStream();

st.Write(faces[i].Image, 0, faces[i].Image.Length);

Bitmap bmp = new Bitmap(st);

var faceImage = new Image<Gray, byte>(bmp);

faceImages[i] = faceImage.Resize(100, 100, Inter.Cubic);

faceLabels[i] = faces[i].UserId;

IdAndlabel.Add(new Tuple<int, string>(faces[i].UserId, faces[i].Label));}

catch (Exception EX){

Console.WriteLine(EX.Message); }}

try{

//antreneaza sistemul

_faceRec.Train(faceImages, faceLabels);}

catch (Exception ex){

MessageBox.Show(ex.Message);}

_faceRec.Save(adr_trainFile);}

return true;}

public void AppendTextBox(string text)

{

try{

if (InvokeRequired)

{

this.Invoke(new Action<string>(AppendTextBox), new object[] { text });

return;

}

txt_info.Text += text;}

catch (Exception){throw;}}

private void insertIntoDb()

{

//insereaza imagini in baza de date

OracleConnection con = new OracleConnection(connString);

try

{

//creeaza conexiunea

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "alter session set nls_date_format='DD-MM-YYYY HH:MI:SS'";

cmd.ExecuteNonQuery();

//determina ultimul id introdus in BD

cmd.CommandText = "select nvl(max(id),0) from imagesb64";

decimal lastProcessedId = (decimal)cmd.ExecuteScalar();

lastProcessedId++;

Console.WriteLine(lastProcessedId + " ");

//extrage primul element din lista si il insereaza

DbRow getRow = rows.ElementAt(0);

cmd.CommandText = "insert into imagesb64 values(:im_code, :img, :nrOfObj,TO_CHAR (SYSDATE, 'DD-MM-YYYY HH:MI:SS'), :device_id, :DetMode)";

cmd.Parameters.Add("im_code", lastProcessedId);

cmd.Parameters.Add("img", getRow.Img);

cmd.Parameters.Add("nrOfObj", getRow.Faces);

cmd.Parameters.Add("device_id", device_id);

cmd.Parameters.Add("DetMode", 1);

cmd.ExecuteNonQuery();

cmd.CommandText = "Commit";

cmd.ExecuteNonQuery();

facesDetected = 0;

//Console.WriteLine("inserted");

}

catch (Exception exc)

{

Console.WriteLine(exc.ToString());

Console.WriteLine("Database not available");

}

finally

{

con.Close();

}

//sterge din lista randul care a fost introdus

if (rows.Count > 0)

{rows.RemoveAt(0);}}

public int RecPers(Image<Gray, byte> faceIm)

{

//apeleaza functia ce returneaza numele persoanei detectate

var rez = _faceRec.Predict(faceIm.Resize(100, 100, Inter.Cubic));

return rez.Label;

}

private void FaceDetectFromWebcam()

{

//captureaza si prelucreaza frame-uri

Mat imag = new Mat();

//preia imagine grayscale de la webcam

_cap.Retrieve(imag);

//background detection

if (_detF == null)

{ _detF = new BackgroundSubtractorMOG2();}

_detF.Apply(imag, _Fmask);

Bitmap fm = new Bitmap(_Fmask.Bitmap, pbMask.Width, pbMask.Height);

pbMask.SizeMode = PictureBoxSizeMode.CenterImage;

pbMask.Image = fm;

//initializare bitmap unde se va salva imaginea prelucrata

Bitmap imgDataFinal = new Bitmap(imag.Width, imag.Height);

long timeDet;

Image<Bgr, byte> im = new Image<Bgr, byte>(imgDataFinal);

List<Rectangle> facesRez = new List<Rectangle>();

//realizeaza detectia fetelor din imagine

FaceDetection.DetectFaceModified.Detect(imag, "haarcascade_frontalface_default.xml", facesRez, out timeDet);

string nume = "";

foreach (Rectangle fR in facesRez)

{

//parcurge lista de fete si o evidentiaza

CvInvoke.Rectangle(imag, fR, new Bgr(Color.AliceBlue).MCvScalar, 2);

//selecteaza subregiunea unde se afla fata si o converteste in grayscale

Image<Gray, byte> rez = im.Copy(fR).Convert<Gray, byte>().Resize(100, 100, Emgu.CV.CvEnum.Inter.Cubic);

//creste contrastul

rez._EqualizeHist();

//afiseaza numele persoanei

string numePers = getNum(RecPers(rez));

// Console.WriteLine(username);

nume += numePers + " ";}

pbImage.Image = imag.Bitmap;

facesDetected = facesRez.Count;

imgDataFinal = imag.Bitmap;

//imaginea convertita in string base64

string insertB64 = ImageToBase64(imgDataFinal, ImageFormat.Jpeg);

string data = DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss");

//adauga in coada

DbRow row = new DbRow(insertB64, facesRez.Count, data);

if (rows.Count <= 1000)

{

Console.WriteLine(rows.Count + " imagini in lista");

rows.Add(row);

}

if (facesDetected == 0)

Console.WriteLine("Nu s-au detectat fete");

AppendTextBox("Prelucrarea a durat:" + timeDet + " \n");

nume = "";}

private string getNum(int persId)

{

foreach (var lst in IdAndlabel)

{if (lst.Item1.Equals(persId)) return lst.Item2;}

return "Unknown";}

public static string ImageToBase64(Image imag, System.Drawing.Imaging.ImageFormat format)

{

//functie ce face conversia la base64 string

using (MemoryStream ms = new MemoryStream())

{

// converteste Image in byte[]

imag.Save(ms, format);

byte[] biti = ms.ToArray();

// Converteste byte[] in String Base64

string strRez = Convert.ToBase64String(biti);

return strRez;}}

private void PrelFrame(object sender, EventArgs e)

{ FaceDetectFromWebcam();}

private void FluxInsert_FormClosing(object sender, FormClosingEventArgs e)

{

//apelata la inchiderea ferestrei

_cap.Stop();

Thread.Sleep(10);

t1.Abort();

t2.Abort();

_cap.Dispose();

this.Hide();

}

private void bStopProcess_Click(object sender, EventArgs e)

{

//apelata la apasarea butonului de oprire proces

_cap.Stop();

t1.Abort();

t2.Abort();

}

private void bToStart_Click(object sender, EventArgs e)

{

//apelata la apasarea butonului de intoarcere la start

_cap.Stop();

t1.Abort();

t2.Abort();

_cap.Dispose();

this.Hide();}}}

FluxInsertPedestrian.cs

namespace ProcressImages

{

public partial class FluxInsertPedestrian : Form

{

bool _nppOK = false;

int persDetected = 0;

//stringul de conectare

string connString = ConfigurationManager.ConnectionStrings["connStr"].ToString();

//lista care se va folosi ca o coada. se adauga imagini si se introduc asincron in baza de date

List<DbRow> rows = new List<DbRow>();

static int device_id = 0;

#region Camera Capture Variables

private Capture _capture = null; //Camera

Video_Device[] WebCams; //List containing all the camera available

#endregion

Thread t1, t2;

public FluxInsertPedestrian()

{

InitializeComponent();

getWebcamIds();

}

public FluxInsertPedestrian(int dev_id)

{

InitializeComponent();

device_id = dev_id;

getWebcamIds();

executions();

}

private void executions()

{

t1 = new Thread(() => startCapturingProcess());

t2 = new Thread(() => startInsertingProcess());

t1.Start();

t2.Start();

}

private void startCapturingProcess()

{

Thread.CurrentThread.IsBackground = true;

if (_capture == null)

{

try{

_capture = new Capture(device_id);}

catch (NullReferenceException excpt)

{ MessageBox.Show(excpt.Message); }}

if (_capture != null)

{

_capture.ImageGrabbed += ProcessFrame;

_capture.Start();

Console.WriteLine("Capture started");

}}

private void startInsertingProcess()

{

Thread.CurrentThread.IsBackground = true;

for (;;)

{

if (rows.Count != 0){

Console.WriteLine("Task 2 started. Insert into db");

insertIntoDb();}}}

private void getWebcamIds()

{

//detecteaza toate camerele web conectate la pc

DsDevice[] _SystemCamereas = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

WebCams = new Video_Device[_SystemCamereas.Length];

for (int i = 0; i < _SystemCamereas.Length; i++)

{

WebCams[i] = new Video_Device(i, _SystemCamereas[i].Name, _SystemCamereas[i].ClassID); }

OracleConnection con = new OracleConnection(connString);

List<string> video_devices_db = new List<string>();

try

{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "SELECT device_name FROM video_device ";

using (var reader = cmd.ExecuteReader())

{

while (reader.Read())

{ if (!reader.IsDBNull(0)) {

video_devices_db.Add(reader.GetString(0)); }}}

for (int w = 0; w < WebCams.Length; w++)

{

Console.WriteLine(WebCams.ElementAt(w));

if (!video_devices_db.Contains(WebCams[w].Device_Name))

{

cmd.CommandText = "insert into video_device values(:id, :name, :identifier)";

cmd.Parameters.Add("id", WebCams[w].Device_ID);

cmd.Parameters.Add("name", WebCams[w].Device_Name);

cmd.Parameters.Add("identifier", WebCams[w].Identifier.ToString());

cmd.ExecuteNonQuery();

cmd.CommandText = "Commit";

cmd.ExecuteNonQuery(); }}}

catch (Exception) { MessageBox.Show("Database not availble. Try again!"); }

finally

{ con.Close(); }}

public void AppendTextBox(string value)

{

try

{ if (InvokeRequired) {

this.Invoke(new Action<string>(AppendTextBox), new object[] { value });

return; }

txt_info.Text += value; }

catch (Exception){ throw; }}

private void insertIntoDb()

{

OracleConnection con = new OracleConnection(connString);

try

{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "alter session set nls_date_format='DD-MM-YYYY HH:MI:SS'";

cmd.ExecuteNonQuery();

cmd.CommandText = "select nvl(max(id),0) from imagesb64";

decimal lastProcessedId = (decimal)cmd.ExecuteScalar();

lastProcessedId++;

Console.WriteLine(lastProcessedId + " ");

DbRow getRow = rows.ElementAt(0);

cmd.CommandText = "insert into imagesb64 values(:im_code, :img, :nrOfObj,TO_CHAR (SYSDATE, 'DD-MM-YYYY HH:MI:SS'), :device_id, :DetMode)";

cmd.Parameters.Add("im_code", lastProcessedId);

cmd.Parameters.Add("img", getRow.Img);

cmd.Parameters.Add("nrOfObj", getRow.Faces);

cmd.Parameters.Add("device_id", device_id);

cmd.Parameters.Add("DetMode", 2);

cmd.ExecuteNonQuery();

cmd.CommandText = "Commit";

cmd.ExecuteNonQuery();

persDetected = 0;

Console.WriteLine("inserted");

}

catch (Exception exc)

{

Console.WriteLine(exc.ToString());

Console.WriteLine("Database not available");

}

finally

{ con.Close();}

if (rows.Count > 0)

{ rows.RemoveAt(0); }

AppendTextBox("imagine adaugata cu success in tabelul ImagesB64 \n"); }

private void PedestrianDetectFromWebcam()

{

Mat image = new Mat();

_capture.Retrieve(image);

//initializare bitmap unde se va salva imaginea prelucrata

Bitmap imgDataFinal;

long detectionTime;

Rectangle[] people = PedDetect. DetPietoni(image, out detectionTime);

foreach (Rectangle p in people)

{ CvInvoke.Rectangle(image, p, new Bgr(Color.Red).MCvScalar, 2); }

CapimageBox.Image = image;

imgDataFinal = image.Bitmap;

string insertB64 = ImageToBase64(imgDataFinal, ImageFormat.Jpeg);

string data = DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss");

DbRow row = new DbRow(insertB64, people.Length, data);

if (rows.Count <= 1000)

{

Console.WriteLine(rows.Count + " imagini in lista");

rows.Add(row);

}

persDetected = people.Length;

if (persDetected == 0)

Console.WriteLine("any person detected");

}

public static string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)

{

using (MemoryStream ms = new MemoryStream())

{

// Convert Image to byte[]

image.Save(ms, format);

byte[] imageBytes = ms.ToArray();

// Convert byte[] to Base64 String

string base64String = Convert.ToBase64String(imageBytes);

return base64String ;}}

private void ProcessFrame(object sender, EventArgs e)

{ PedestrianDetectFromWebcam(); }

private void bToStart_Click_1(object sender, EventArgs e)

{

_capture.Stop();

t1.Abort();

t2.Abort();

_capture.Dispose();

this.Hide();

}

private void bStopProcess_Click_1(object sender, EventArgs e)

{

t1.Abort();

t2.Abort();

_capture.Stop();

}}}

PedDetect.cs

using System;

using System.Collections.Generic;

using Emgu.CV;

using Emgu.CV.CvEnum;

using Emgu.CV.Structure;

using System.Drawing;

using System.Diagnostics;

using Emgu.CV.Util;

#if !(__IOS__ || NETFX_CORE)

using Emgu.CV.Cuda;

#endif

namespace ProcressImages

{

class PedDetect

{

public static Rectangle[] DetPietoni(IInputArray imagine, out long timpProcesare)

{

Stopwatch contor;

Rectangle[] regiuni;

using (InputArray imgIA = imagine.GetInputArray())

{

if (CudaInvoke.HasCuda)

{

using (CudaHOG descr = new CudaHOG(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8)))

{

descr.SetSVMDetector(descr.GetDefaultPeopleDetector());

contor = Stopwatch.StartNew();

using (GpuMat imgBgrGPU = new GpuMat(imagine))

using (GpuMat imgBrgaGPU = new GpuMat())

using (VectorOfRect vr = new VectorOfRect())

{

CudaInvoke.CvtColor(imgBgrGPU,imgBrgaGPU, ColorConversion.Bgr2Bgra);

descr.DetectMultiScale(imgBrgaGPU, vr);

regiuni = vr.ToArray(); }}}

else{

using (HOGDescriptor descr = new HOGDescriptor())

{

descr.SetSVMDetector(HOGDescriptor.GetDefaultPeopleDetector());

contor = Stopwatch.StartNew();

MCvObjectDetection[] rezultat = descr.DetectMultiScale(imagine);

regiuni = new Rectangle[rezultat.Length];

for (int i = 0; i < rezultat.Length; i++)

regiuni[i] = rezultat[i].Rect;

contor.Stop(); }}

timpProcesare = contor.ElapsedMilliseconds;

return regiuni; }}}}

getIm.cs

namespace ProcressImages

{

public partial class getIm : Form

{

static string connString = ConfigurationManager.ConnectionStrings["connStr"].ToString();

static OracleConnection con = new OracleConnection(connString);

public getIm()

{

InitializeComponent();

this.imageFace.SizeMode = PictureBoxSizeMode.CenterImage;

startDate.Format = DateTimePickerFormat.Custom;

startDate.CustomFormat = "dd-MM-yyyy";

endDate.Format = DateTimePickerFormat.Custom;

endDate.CustomFormat = "dd-MM-yyyy";

startTime.ShowUpDown = true;

endTime.ShowUpDown = true;

}

public static Image Base64ToImage(string base64String)

{

byte[] img= Convert.FromBase64String(base64String);

MemoryStream ms = new MemoryStream(img, 0, img.Length);

ms.Write(img, 0, img.Length);

Image imagine = Image.FromStream(ms, true);

return imagine;

}

public void getImage(int id)

{

try{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

cmd.CommandText = "select * from imagesb64 where id =" + id;

DataTable tbl = new DataTable();

tbl.Load(cmd.ExecuteReader());

DataRow row = tbl.Rows[0];

//conversie din base64 in image

string b64Data = (string)row[1];

Image img = Base64ToImage(b64Data);

byte[] b64 = Convert.FromBase64String(b64Data);

MemoryStream ms1 = new MemoryStream(b64);

Bitmap b1 = new Bitmap(ms1, false);

AppendPictBox(b1);

}

catch (Exception){ MessageBox.Show("Error while getting image from DB"); }

finally { con.Close(); }}

public void AppendPictBox(Bitmap im)

{

try{

if (InvokeRequired){

this.Invoke(new Action<Bitmap>(AppendPictBox), new object[] { im });

return;}

imageFace.Image = im; }

catch (Exception) { throw; }}

private void bShowImg_Click_1(object sender, EventArgs e)

{

try

{

con.Open();

OracleCommand cmd = new OracleCommand();

cmd.Connection = con;

string stDate = startDate.Value.ToString("dd-MM-yyyy hh:m:ss");

string eDate = endDate.Value.ToString("dd-MM-yyyy hh:m:ss");

cmd.CommandText = "alter session set nls_date_format='DD-MM-YYYY HH:MI:SS'";

cmd.ExecuteNonQuery();

Console.WriteLine(stDate);

Console.WriteLine(eDate);

string command = "SELECT id,detected,time_detect FROM imagesb64 WHERE time_detect BETWEEN TO_DATE('" + stDate + "','dd-mm-yyyy hh:mi:ss') AND TO_DATE('" + eDate + "','dd-mm-yyyy hh:mi:ss')";

if (cbFaces.Checked)

{ cmd.CommandText = command + " and detected not like '0' and det_mode=1";}

else if (cbPers.Checked)

{cmd.CommandText = command + " and detected not like '0' and det_mode=2"; }

else if (cbFaces.Checked && cbPers.Checked)

{ cmd.CommandText = command + " and detected not like '0' "; }

else

{ cmd.CommandText = "SELECT id,detected,time_detect FROM imagesb64"; }

using (OracleDataReader reader = cmd.ExecuteReader())

{

DataTable tbl = new DataTable();

tbl.Load(reader);

this.dgvRows.DataSource = tbl; } }

catch (Exception)

{ Console.WriteLine("Database not available"); }

finally

{ con.Close(); }}

private void dgvRows_CellClick_1(object sender, DataGridViewCellEventArgs e)

{

if (e.RowIndex > 0)

{

DataGridViewRow row = this.dgvRows.Rows[e.RowIndex];

if (!row.Cells[0].Value.ToString().Equals(""))

{

int index = Int32.Parse(row.Cells["id"].Value.ToString());

getImage(index);

}}}}}

Similar Posts