SCam Introducere SCam (Security Camera) este o camera de securitate wireless inteligenta. Aceasta detecteaza persoanele care apar in stream-ul video… [609246]
SCam Introducere SCam (Security Camera) este o camera de securitate wireless inteligenta. Aceasta detecteaza persoanele care apar in stream-ul video si le recunoaste. Are o interfata web intuitiva si usor de folosit. Aceasta este creata pentru persoanele care doresc o securitate sporita a locuintei sau altor locuri personale. Poate fi folosita in orice loc pentru a gestiona persoanele care intra intr-o anumita incinta. SCam este o alegere potrivita intrucat stream-ul video are o intarziere foarte mica impotriva faptului ca acesta este prelucrat la nivelul server-ului. Scurt istoric Primele sisteme de securitate video necesitau moitorizare constanta deoarece nu existau modalitati de stocare a informatiei. Dezvoltarea sistemelor de stocare de tip reel-to-reel (Figure 1) a facut posibila inregistrarea video. Aceste sisteme necesitau schimbarea benzilor magnetice manuala, care era costisitoare și din punct de vedere al timpului consumat cât și pe plan financiar. Din cauza acestor neajunsuri, supravegherea video nu a fost larg răspândită. Tehnologia VCR a apărut în anii 1970, facilitand inregistrarea și stergerea informației, astfel supravegherea video devenind mai populara. În anii 90’ a fost dezvoltata multiplexarea, permitand mai multor camere sa inregistrze în același timp cât și tehnologia time lapse și inregistrarea bazata pe mișcare. Acestea au dus la costuri mai mici, care eventual a dus la o creștere al numarului de sisteme CCTV folosite. Camerele de securitate wirless sunt camere ce transmit un semnal video și audio către un receptor wireless printr-o banda radio. „Wireless” se refera la modul de transmitere al datelor audio video.
Camerele de securitate câștiga popularitate printre consumatorii moderni date fiind costurile mici de instalare și optiunile de montare mult mai diverse decât în cazul camerelor clasice cu fir. În plus fata de nivelul scăzut al dificultatii de folosire și amplasarea convenienta camerele de securitate wireless permit și un faciliteaza accesul la semnalul video de la distanța. Camerele de securitate pot fi folosite în diverse domenii: monitorizarea animalelor dintr-o ograda, identificarea suspectilor de către poliție, monitorizarea proceselor industriale, monitorizarea traficului, sporirea sigurantei în mijloacele de transport în comun, monitorizarea angajatilor sau a elevilor etc. În ceea ce privește calitatea semnalului, acestea funcționează cel mai bine când exista un drum liber de la camera la receiver întrucât semnalul wireless poate fi blocat de anumite suprafețe.
Descriere Tehnologii Folosite Aplicatia foloseste tehnologii noi, de actualitate, pentru a asigura o experienta fara intreruperi. Pe parte software aplicatia are la baza multiple servere scrise in Python ce se folosesc de microframework-uri pentru un timp de raspuns cat mai mic. De asemenea aceasta are o interfata web dezvoltata in Angular, un framework pentru dezvoltare de aplicatii si site-uri web creat de Google. Partea hardware consta in dispozitive cu un cost mic, si anume: Raspberry PI, o camera si un ventilator. A n g u l a r Angular este o platforma și un framework pentru construirea aplicatiilor în HTML și TypeScript. Angular este scris în TypeScript. Acesta implementeaza functionalitati de baza și optionale ca un set de librarii TypeScript care sunt importate în aplicatiile create de utilizatori. Angular este bazat pe obiecte numite NgModules, care furnizeaza un context de compilare pentru componente. NgModules colecteaza codul în seturi functionale; o aplicație Angular este definita de un set de NgModules. O aplicație are cel puțin un modul root care permite bootstrapping, și are deobicei mai multe caracteristici modulare. Componentele definesc view-uri, care sunt seturi de elemente afisate pe ecran pe care Angular le poate modifica și alege în funcție de logica programului. Componentele folosesc de asemenea servicii, care furnizeaza anumite functionalitati care nu sunt direct legate de view-uri. Atât componentele cât și serviciile sunt clase cu decoratori care le specifica tipul și furnizeaza metadate care ii indica Angular-ului cum sa le folosească. TypeScript TypeScript este un limbaj de programare open-source dezvoltat și menținut de Microsoft. Este un superset sintactic strict al limbajului de programare Javascript, adăugându-i tipare statice opționale. Este dezvoltat pentru crearea aplicațiilor de mari dimensiuni și se transformă în cod JavaScript. Întrucât
Typescript este un superset al programului Javascript, programele JavaScript existente sunt programe TypeScript valide. Acesta poate fi folosit atât pentru crearea programelor pentru server cât și pentru client. Compilatorul TypeScript este scris tot in TypeScript compilat în JavaScript. TypeScript a fost lansat in Octombrie 2012 dupa doi ani de dezvoltare la Microsoft. În iulie 2014 echipa Microsoft a anunțat un nou compilator TypeScript, susținând că ar atinge viteze de procesare de 5 ori mai mari decât compilatorul anterior. În același timp codul sursă care era publicat pe CodePlex a fost mutat pe GitHub. Pe 22 Octombrie 2016, a fost lansat TypeScript 2.0; introducea mai multe funcții noi, incuzând posibilitatea pentru programatori de a împiedica, opțional, alocarea valorii null variabilelor, denumită uneori ca fiind greșeala de un miliard de dolari. Unul dintre motivele pentru care a fost creat este deficiența JavaScriptului când vine vorba de proiecte voluminoase atât in cadrul Microsoft cât si printre clienții acestora. Un alt motiv este complexitatea codului JavaScript care a dus la nevoia pentru o unealtă care să ușureze dezvoltarea componentelor în limbaj. HTML Hyper Text Markup Language este limbajul markup standard pentru documentele afișate în browsere. Poate fi asistat de tehnologii cum ar fi Cascading Style Sheet și limbaje de scripting cum ar fi Javascript. SASS SASS este un limbaj de scripting preprocesor care este interpretat sau compilat în CSS. SASS constă in doua sintaxe. Sintaxa mai nouă, SCSS (Sassy CSS), folosește format de bloc asemanătoare CSS-ului. Folosește acoalade pentru a separa blocurile de cod și punct și virgulă pentru a separa liniile din interiorul unui bloc. SASS este cea mai matură, stabilă, si puternică extensie CSS din lume. Acesta este compatibil cu toate versiunile CSS, problemele de compatibilitate fiind nule. Python Python este un limbaj de programare interpretat, high-level. A fost creat de Guido van Rossum si a fost lansat in 1991. Python este cunoscut pentru accentul pus pe lizibilitatea codului prin folosirea
de spatii. Limbajul permite programatorilor sa scrie cod clar și logic pentru proiecte de dimensiuni mai mici. Filozofia de baza a limbajului este descrisa în documentul The Zen of Python: •Frumos este mai bine decât urât •Explicit este mai bine decât implicit •Simplu este mai bine decât complex •Complex este mai bine decât complicat •Lizibilitatea contează În loc sa aibă toate functionalitatile în baza, Python a fost construit să fie extensibil. Aceasta modularitate compacta a făcut limbajul popular ca mijloc de adaugare a interfetelor programabile la aplicatiile existente. Motion JPEG Motion JPEG sau MJPEG este un format de compresie in care fiecare frame al unui video este comprimat separat sub forma de JPEG. Deși a fost dezvoltat pentru aplicații PC multimedia, MJPEG este folosit de device-uri de captură video cum ar fi: camere digitale, camere IP, și camere WEB. Spre deosebire de alte formaturi care permit atașarea unui strat audio, MJPEG nu include audio, deoarece este o concatenare de imagini. Eșantionul avantajelor rezultate din operarea cu acest format de compresie este vast. Este simplu de implementat si tolerează mișcări rapide în stream-ul video, pe când alte formate de compresie pot suferi degradări majore în ceea ce privește calitatea imaginii. Pe lângă acestea, suportul pentru acest tip de compresie este larg raspândit, fiind acceptat de toate browserele cât si de consolele de jocuri video. Un alt avantaj sunt cerințele minime necesare pentru procesarea imaginii de către calculator.
JSON JSON (JavaScript Object Notation) este un format de fișier care folosește text ușor de citit de către utilzator pentru transmiterea datelor. Este foarte des folosit pentru comunicare asincronă între browser și server, fiind un înlocuitor pentru XML în unele sisteme de tip AJAX. Este un format de date independent de limbaj. A fost derivat din JavaScript, dar din 2017, multe limbaje de programare includ cod pentru parsarea și generarea de fișiere JSON. JSON a apărut din nevoia de un protocol de comunicare în timp real apartid între browser si server fără a folosi un plugin de browser cum ar fi Flash sau Java applets, metode dominante in anii 2000. JSON a fost lansat prima dată ca fiind un subset strict al limbajului de programare JavaScript și ECMAScript. Raspberry PI Raspberry Pi este un computer dezvoltat pe o singura placa de circuit (Figure 2), creat în Marea Britanei cu scopul de a promova predarea infomaticii de baza în școli și în tari în stare de dezvoltare, însă acestea au început să fie folosite în multe proiecte datorită puterii de procesare mare date fiind dimensiunile acestuia care faciliteaza amplasarea lui în multe locatii greu accesibile altor coputere mai voluminoase. Acesta poate rula diverse sisteme de operare însă cel mai des folosit este Raspbian ce are la baza linux și este dezvoltat special pentru placutele de dezvoltare Raspberry PI. S S H SSH (Secure Shell) este un protocol de rețea criptografic pentru operarea serviciilor de rețea pe o rețea nesigură în siguranță. Cele mai tipice aplicări includ logarea pe linia de comandă și rularea comenzilor la distanță dar orice serviciu de rețea poate fi securizat folosind SSH. SSH furnizează un canal securizat într-o rețea nesigură folosind o arhitectură de tip client-server conectând un client SSH la un server SSH. Portul TCP standard pentru SSH este 22. SSH este folosit în general pentru accesarea sistemelor de tip UNIX, dar poate fi folosit și pe sisteme ce folosesc Windows ca sistem de operare.
SSH a fost creat ca un înlocuitor pentru Telnet și pentru protocoale de tip remote shell nesecurizate cum ar fi protocoalele Berkeley login, rlogin, rsh și rexec. Acele protocoale trimit informații folosind text simplu, făcându-le susceptibile la intercepție prin intermediul analizei de pachete. Encripția folosită de SSH este destinată să ofere confidențialitatea si integritatea datelor într-o rețea nesecurizată, cum ar fi Internetul, deși fișierele dezvăluite de Edward Snowden indică faptul că NSA (National Security Agency) pot decripta uneori SSH-ul, permițându-le să citească conținutul sesiunilor SSH.
Descrierea aplicației În mare aplicația consta in stream-ul video trimis de Raspberry Pi care este procesat pe server de către cele două componente principale și anume object tracker și face recognition. Frame-urile procesate sunt apoi trimise catre interfața web prin intermediul unui API. Stream-ul video Pentru stream-ul video am folosit placuța de dezvoltare Raspberry Pi deoarece costul si dimensiunile acesteia sunt foarte scazute. Pentru înregistrarea video am conectat deasemenea si o cameră de 5MP care are capacitatea de a înregistra la o calitate de 1080p. După testarea stream-ului pe durate indelungate, am observat că placuța se supraîncălzea. Pentru combaterea acestui inconvenient am adăugat un radiator la procesorul Raspberry Pi-ului. Întrucât acest lucru nu a fost de ajuns, am decis să conectez și un ventilator. Pentru a atinge acest scop a fost necesară studierea schema pinilor GPIO (Figure 3). Am conectat ventiloatorul la două cabluri mamă-mamă și la conectorii de 5V și Ground ai plăcuței. Din acest lucru au rezultat evitarea supraîncălzirii cât și o îmbunătățire a vitezei de procesare a Raspberry Pi-ului. Am testat de asemenea prelucrarea video-ului si pe Raspberry însă procesele necesitau mai multă putere de procesare decat putea acesta oferi. Astfel am decis integrarea unui server pentru procesarea video-ului si pentru rularea server-ului interfeței web. Object tracker Object tracking este procesul de: •preluare a unui set inițial de obiecte detectate •crearea unui ID unic pentru fiecare dintre detecții
•și de urmarire a fiecărui obiect in timp ce se mișcă in frame-urile unui video, păstrând ID-ul unic atribuit la început Algoritmul de urmarire al centroizilor este un proces alcatuit din mai multe etape. Pas 1. Algoritmul presupune că există un set de coordonate (x, y) ale casetelor de încadrare la fiecare frame. Aceste casete de încadrare sunt generate de un detector de obiecte de tip Haar Cascade. Odată ce avem coordonatele trebuie să calculăm ”centroidul” sau coordonatele (x, y) ale casetei de încadrare. Deoarece acestea sunt setul inițial de coordonate prezentate algoritmului, le vom atribui ID-uri unice. Pas 2. Pentru fiecare frame în stream-ul video vom aplica pasul 1. Însă în loc să le atribuim ID-uri unice, trebuie sa determinăm dacă putem asocia noii centroizi cu cei vechi. Pentru a realiza acest process, calculăm distanța Euclideană între fiecare pereche de centroizi existenți. Pas 3. Prima presupunere a algoritmului de tracking este că un obiect dat se va mișca între frame-uri consecutive, dar distanța între centroizii frame-urilor Ft și Ft+1 va fi mai mică decât toate celelalte distanțe între obiecte. Pas 4. În cazul în care sunt mai multe detecții decât obiecte care sunt urmarite, trebuie să înregistrăm obiectul nou. (Figure 4) •I se atribuie un ID •Salvăm centroid-ul obiectului Putem apoi să ne întoarcem la pasul 2 si sa repetăm pașii principali pentru fiecare frame din video. Pas 5. Orice algoritm de tracking trebuie să aibă abilitatea de a gestiona situația în care un obiect a fost pierdut, a dispărut sau a părăsit câmpul de vizualizare. În algoritmul implementat, obiectele vechi sunt șterse când nu mai pot fi atribuite niciunui obiect existent pentru un anumit număr de frame-uri. (Figure 5) Am ales să implementez acest algoritm deoarece recunoașterea unei fețe la fiecare frame ar fi fost foarte costisitoare din punct de vedere al timpului de execuție și al resurselor folosite. Cu ajutorul acestui algoritm putem rula algoritmul de recunoaștere facială doar pe un singur frame, apoi acesta
urmând sa fie urmarit de algoritmul de tracking. O altă alterare practică a algoritmului de tracking este adăugarea unui alt pas. Pas 6. Algoritmul de face recognition este posibil sa nu poată recunoaște o persoană detectată dacă aceasta este pozitionată la un anumit unghi de camera în mometul în care algoritmul încearcă să recunoască persoana prezentă în frame. Așadar, este necesară repetarea pasului de recunoaștere a persoanei pe parcursul a mai multor frame-uri, până când ajungem la un număr de frame-uri prestabilit, sau persoana este într-un final recunoscută. În cazul în care persoana nu este recunoscută, algoritmul îi atribuie numele generic ”Individ”. (Figure 5) Face recognition Pentru recunoașterea facială am folosit biblioteca face_recognition care se folosește de caracteristicele feței persoanelor pentru a recunoaște o față dintr-o având în memorie o singură poză a persoanei. Recunoașterea unei persoane necesită mai multe etape. Pas 1. Fiecare imagine a persoanelor cunoscute trebuie preluată de pe disc. Numele fiecărei imagini trebuie să conțină numele persoanei reprezentată (ex. Marius_0.jpg). Pas 2. Numele persoanei reprezentata in imagine este extras din denumirea imaginii, si salvata intr-un vector. Acest vector este folosit pentru identificarea persoanelor. Pas 3. Pentru a mări vitea de procesare a imaginilor si pentru minimizarea imaginilor necesare pentru a recunoaște o persoană, biblioteca folosită nu efectuează calcule pe imagini. Acesta recunoaște ochii si gura persoanei detectată. Acesta memorează relația geometrică dintre acestea și astfel reușește să recunoască persoana cu ajutorul unei singure imagini intr-un timp foarte scurt, greu de sesizat. F l a s k s e r v e r Object tracker-ul si algormitmul de face rocognition sunt folosite de flask server. Scoopul principal al server-ului este cel de a trimite stream-ul video catre interfata web. Am implementat acest server deoarece trimiterea stream-ului din corpul algoritmilor precedenti nu era practica.
Acest lucru a implicat modificarea object tracker-ului. Am transpus algoritmul in interiorul unei clase cu o singura funcție care returnează frame-urile procesate ale clipului. Frame-urile sunt transmise interfetei web in momentul când acesta le cere. Server-ul conține 3 rute principale: video_feed, start_recording, stop_recording. Ruta are rolul de a indica API-ului ce funcție să execute. Dacă interfața accesează ruta video_feed, API-ul instanțiază un obiect object tracker și se folosește de o buclă infinită pentru a servi frame-urile stream-ului video. Stream-ul trimis este de tip MJPEG (Motion JPEG). Ruta start_recording este asociată funcției de începere a înregistrării stream-ului video. Am abordat această metodă deoarece de cele mai multe ori nu este necesară inrgistrarea evenimentelor și este eficienta din punctul de vedere al memoriei. De asemenea s-a folosit o baza de date de tip JSON. Flask crează un thread nou pentru fiecare accesare în parte, așadar folosirea unei variabile pentru stocarea stării înregistrării nu a fost posibilă. Stocarea acestei variabile în format JSON a fost cea mai rapidă metodă pentru crearea unei memorii comune între thread-urile create de Flask. Ruta stop_recording este asociată funcției de oprire a înregistrării stream-ului video. Odată accesat, acest endpoint schimbă starea înregistrării în fișierul de configurare JSON, comunicând thread-ului ce se ocupă cu livrarea stream-ului video încetarea înregistrării. După încetarea înregistrării, frame-urile salvate sunt procesate de către clasa ProcessVideo care crează un fișier video și îl salvează pe disc. Videoclipurile salvate pot fi accesate prin intermediul interfeței grafice, si descărcate pe orice dispozitiv. Interfața Web Interfața a fost dezvoltată cu ajutorul framework-ului Angular. Acest framework permite creearea unei aplicații web ce poate fi transformată cu un efort minim într-o aplicație cross-platorform, ce poate rula pe aproape orice dispozitiv. A fost aleasă păstrarea aplicației în forma web
pentru un acces mai ușor și mai rapid de pe orice dispozitiv care poate accesa rețeaua locală a incintei unde este amplasat serverul. Dezvoltarea cu ajutorul Angular-ului se realizează în principal cu ajutorul limbajului de programare TypeScript pentru backend și HTML si SASS pentru interfață. Dezvoltarea interfeței în HTML conferă un avantaj întrucât amplasarea elementelor în pagină nu este restrânsă de nimic. Aplicația UI este compusă din două componente principale: main și gallery. Componenta main afișează stream-ul procesat MJPEG ce incadrează fețele detectate cu un dreptunghi de culoare verde. Numele persoanelor recunoscute sunt de asemenea atașate de dreptunghi. Pentru a ușura observarea fețelor, în partea de jos a ecranului, există un container, în care sunt prezentate persoanele detectate, împreună cu numele acestora, dacă au fost recunoscute, sau cu denumirea generică Individ. Persoanele extrase sunt actualizate în timp real. Componenta gallery se ocupă cu afișarea videoclipurilor înregistrate de user. Descrierea procesului de dezvoltare al aplicației Primul pas în dezvoltarea aplicației a fost crearea script-ului de object tracking. În realizarea acestuia a fost folosit un model pre-antrenată să detecteze fețele persoanelor. Modelul a fost antrenat folosind framework-ul pentru deep learning CAFFE (Convolutional Architecture for Fast Feature Embedding). Caffe suportă multe arhitecturi diferite de deep learning antrenate pentru clasificarea imaginilor și recunoaștere de imagini. A fost folosită libraria OpenCv pentru detectarea fețelor. OpenCV (Open source Coputer Vision) este o bibliotecă de funcții de programare care vizează în principal ideea de computer vision în timp real. Primul script creat a fost centroidtracker.py care are scopul de a calcula distanțele euclidiene dintre centrul fețelor detectate, pentru a determina cu succes diferența între obiectele detectate.
centroidtracker.py
În primele linii importăm pachetele și modulele necesare. Constructorul clasei CentroidTracker acceptă un singur parametru și anume numărul de frame-uri consecutive pentru care un obiect trebuie să fie dispărut pentru a-l șterge din tracker. Constructorul conține 4 variabile: •nextObjectID — acesta este contorul folosit pentru a atribui ID-uri unice fiecărui obiect •objects — este un dicționar care folosește ca și cheie ID-ul obiectului și coordonatele centroid-ului ca valoare. •disappeared — asemănător cu variabila objects, și aceasta folosește pe post de cheie ID-ul obiectului, ca valoare folosind numărul de frame-uri consecutive pentru care un anumit obiect a dispărut. •maxDisappeared — numărul de frame-uri consecutive pentru care un obiect dispărut este păstrat
Funcția register este responsabilă pentru adăugarea noilor obiecte la tracker. Aceata acceptă un centroid și îl adaugă la dicționarul objects folosind următorul ID disponibil. Numărul frame-urilor pentru care un obiect a dispărut este inițializat cu 0 și se incrementează variabila
nextObjectID astfel încât în momentul apariției unui obiect nou, acestuia îi va fi atribuit un ID unic. Pe același principiu funcționează și funcția deregister. Funcția acceptă un singur parametru, și anume identificatorul unic al obiectului care trebuie șters. Operația de ștergere înseamnă eliminarea obiectului din dicționarul de obiecte urmărite și din cel ce contorizează frame-urile pentru care a dispărut un obiect.
Funcția update primește ca parametru o listă de dreptunghiuri ce încadrează fețele detectate de către model. Formatul dreptunghiurilor este de forma: (startX, startY, endX, endY). Dacă nu există obiecte detectate, algoritmul iterează prin lista de obiecte urmărite și pentru fiecare incrementăm numărul de frame-uri în care obiectul nu mai este detectat. Totodată acesta verifică dacă vreun obiect a ajuns la numărul maxim de frame-uri în care îi este permis să fie nedetectat înainte să fie șters și îl sterge dacă este cazul. Deoarece nu mai există obiecte, algoritmul returna lista obiectelor urmărite.
În cazul în care avem obiecte detectate, inițializăm un vector în care se vor reține centrele fiecărui dreptunghi. Apoi algoritmul trece prin toate dreptunghiurile primite și calculează centrul fiecăruia.
Dacă nu există niciun obiect urmărit, algoritmul înregistrează fiecare obiect nou. În caz contrar, acesta actualizează pozițiile centroizilor curenți bazat pe locația centroidului care minimizează distanța Euclidiană dintre acestea. Scopul algoritmului este acela de a urmări obiectele și de a păstra ID-urile corecte — acest scop este îndeplinit prin calcularea distanței Euclidiene între toate perechile de centroizi, urmată de atribuirea ID-ului obiectului care minimizează distanța Euclidiană. Algoritmul preia ID-urile obiectelor si coordonatele centroizilor acestora în variabilele objectIDs și objectCentroids apoi calculăm distanța dintre fiecare pereche de centroizi existenți și cei noi.
Pentru a executa operația de împerechere trebuie să găsim cea mai mică valoare din fiecare rând și să le sortăm după valorile minime. Un proces asemănator este aplicat și coloanelor; sunt găsite cele mai mici valori din coloană și apoi sortate după ordinea rândurilor. Scopul este acela de a avea cele mai mici valor în fața listei.
În Figura (()), agoritmul inițializează două seturi pentru a determina ce rânduri și ce coloane au fost deja folosite. Pentru a actualiza centroizii algoritmul iterează prin combinații de forma (row, col). Dacă rândul sau coloana au fost folosite, le ignoră si continuă, altfel înseamnă ca algoritmul a găsit un centroid care are cea mai mică distanță către un centroid existent și nu a fost împerecheat cu niciun alt centroid. În aceasta situație actualizăm obiectul.
Algoritmul determina in continuare centroizii care nu au fost încă examinați și îi stochează în variabilele unusedRows și unusedCols.
Ultimele verificări se ocupă de obiectele care au fost pierdute sau care au dispărut. Dacă numărul de centroizi este mai mare decât numărul de centroizi primiți, algoritmul verifică ce obiecte au fost pierdute și incrementează counter-ul de frame-uri în care obiectul nu a mai fost detectat, iar in cazul în care obiectul a trecut numărul de frame-uri maxime în care ii este permis să dispară acesta este șters. Totodată dacă există obiecte noi acestea sunt înregistrate.
object_tracker.py Pentru ca algoritmul de tracking să funcționeze, a fost necesară implementarea unui algoritm pentru procesarea stream-ului video. Inițial script-ul a fost construit să proceseze imaginile primite de la camera principală a laptop-ului, ulterior fiind urmărită alterarea acestuia pentru procesarea stream-ului primit de la Raspberry Pi. În interiorul acestui script este procesat stream-ul video primite de la camera video, este instanțiat obiectul CentroidTracker ce face referire la clasa descrisă in scriptul anterior și folosit pentru urmărirea fețelor din stream-ul video și afișarea rezultatelor care include casete de încadrare și anotații cu ID-urile corespunzătoare suprapuse pe frame-uri.
În primul rând sunt specificate modulele și bibliotecile folosite, împreună cu clasa creată CentroidTracker.
În figura (()) este instanțiat CentroidTracker-ul. Din acesta este folosi funcția update() deoarece gestionează automat obiectele. De asemenea sunt inițializate variabilele H și W (dimensiunile ferestrei) cu None (echivalentul variabilei null din alte limbaje). Este încărcat detectorul de fețe de pe disc folosind modului dnn al bibliotecii OpenCV .
Algoritmul crează o buclă infinită folosită pentru procesarea frame-urilor stream-ului video. Fiecare frame este redimensionat la o lățime fixă păstrând aspectul imaginii. Apoi frame-ul este trecut prin obiectul CNN pentru a obține predicțiile și pozițiile obiectelor. Apoi sunt prelucrate detecțiile.
Se iterează prin toate detecțiile, și în momentul și sunt procesate doar cele care trec de un anumit prag în ceea ce privește încrederea că există o detecție validă. Pentru fiecare dintre aceste detecții sunt calculate dreptunghiurile de încadrare și sunt adăugate la lista rects, și sunt desenate pe frame.
Funcția ct.update() se ocupă de calcularea pozițiilor obiectelor. Apoi algoritmul afișează centroid-ul sub formă de cerc umplut și sub acesta ID-ul unic al persoanei. În momentul acesta se poate observa daca algoritmul urmărește corect persoanele detectate prin asocierea corectă a identificatorilor unici. Este afișat stream-ul video procesat până în momentul în care user-ul apasă tasta q, moment în care programul iese din bucla infinită și execută operații de curățare. Chiar dacă centroid tracker-ul funcționează in acest moment, există două dezavantaje majore: •Pentru detectoare foarte rapide, cum este în cazul acesta haarcascade, rularea detectorului la fiecare frame nu este o problemă, însă pentru detectoare mai scumpe din punct de vedere computațional •Al doilea dezavantaj ține de presupunerea că centroizii vor fi apropiați unul de celalalt. Această presupunere este adevarată de obicei, însă trebuie ținut cont de faptul că este reprezentată o lume tridimensională în frame-uri bidimensionale. Problema poate apărea în momentul în care obiectele se suprapun și teoretic poate apărea situația interschimbării identificatorilor unici. Această problemă nu este specifică acestui tip de tracking ci este destul de comună și în algoritmi de tracking mai avansați. Totuși această problemă este mai pronunțată în algoritmul bazat pe centroizi întrucât se bazează în totalitate pe distanțe euclidiene. Programare Raspberry Pi Odată completat acest pas, a fost începută documentarea cu privire la opțiunile pentru transmiterea stream-ului video de pe Raspberry Pi. Opțiunile sunt vaste însă majoritatea au un dezavantaj major. Întârzierea minimă este principala caracteristică în alegerea unei metode potrivite pentru stream-ul video. Înainte de alegerea unei metode potrivite pentru transmiterea stream-ului a fost necesară instalarea corectă a camerei. Raspberry Pi are un conector special pentru camera video. Camera video trebuie manevrată cu grijă deoarece poate fi deteriorată de încărcături electrostatice, așadar este ca orice atinge
camera să se asigure că este descărcat static, lucru ușor de realizat prin atingerea unui obiect împământat. Camera este ușor de instalat, fiind necesară doar conectarea acesteia la portul special al Raspberry Pi-ului.
După conectarea la Raspberry Pi, îl putem aprinde conectând-ul la curent. După accesarea consolei se poate accesa meniul de configurare a Raspberry Pi-ului rulând comanda sudo raspi-config. Accesând meniul Interfaces și apoi Enable Camera se activează camera video, singurul pas rămas fiind restartarea Raspberry-ului. De asemenea, urmând o serie de pași asemănători a fost activat și serverul SSH. A fost folosită din nou comanda sudo raspi-config, după care a fost selectat meniul Intefacing Options și SSH. O altă metodă de actiare a SSH-ului pe un Raspberry Pi este adăugarea unui fișier gol denumit ssh , fără nicio extensie, pe partiția boot a cardului SD. Dacă acest fișier este găsit, serviciul SSH este activat și fișierul ssh este șters. După restartarea Raspberry-ului camera a fost pregătită pentru testare. Testarea camerei a fost realizată prin rularea comenzii raspistill -o cam.jpg care a dus la fotografierea unui cadru și salvarea acestuia în fișierul cam.jpg. Inițial, captura era răstrurnată însă acest lucru este ușor de remediat folosind optiunile -vf (vertical flip) și -hf (horizontal flip) a comenzii raspistill.
Prima încercare de trimitere a stream-ului video a fost folosirea comenzii nc (netcat) împreună cu comanda raspivid. Netcat este o utilitate pentru networking ce se ocupă cu citirea din conexiuni și trimiterea către conexiuni folosind TCP sau UDP. Comanda folosită pentru trimiterea stream-ului este raspivid -w 640 -h 480 -t 60000 -o – | nc 192.168.1.7 8000. A fost folosită comanda raspivid pentru începerea stream-ului iar comanda netcat pentru a trimite stream-ul primit prin pipe către server care, în exemplul de față, are IP-ul 192.168.1.7 și folosește port-ul 8000 pentru primirea stream-ului video. Rezultatul acestui test nu a fost satisfăcator, întrucât stream-ul nu ținea cont de câte frame-uri primește pe secundă, din acest aspect rezultând un stream haotic, care accelera în fiecare secundă. A doua încercare a fost stabilizarea stream-ului video folosind FFMPEG. FFMPEG este un proiect open-source ce constă într-un set vast de biblioteci și programe pentru manipularea stream-
urilor video, audio și alte stream-uri de tip multimedia. Folosind ffmpeg pe partea de server, s-a reușit stabilizarea stream-ului. Însă durata videoclipului a devenit direct proporțională cu întarzierea acestuia. A treia, și ultima încercare, a constat în crearea unui script care să se ocupe cu trimiterea stream-ului video folosind socket-uri. Prima parte a script-ului constă în importarea modulelor necesare. De asemenea, folosind biblioteca zmq, script-ul se conectează folosindu-se de socket-uri la un port TCP de pe server. În continuare se inițializează camera. După inițializarea camerei, se pornește o buclă infinită, in interiorul căreia este procesat fiecare frame. Procesarea frame-ului constă în primul rând în redimensionarea acesteia. Inainte de redimensionare, camera fiind capabilă de înregistrare full HD, frame-ul are o dimensiune foarte mare, ceea ce într-un final duce la mărirea necesității de putere de procesare. Deoarece imaginea este întoarsă la înainte de procesare, în momentul codificării imaginii, aceasta este îndreptată. Urmează criptarea frame-ului cu algoritmul base64, pentru micșorarea dimensiunii imaginii trimise. Codificarea în base64 nu este intensivă din punctul de vedere al procesării și nici din punctul de vedere al timpului, procesul invers având aceleași avantaje. Această metoda s-a dovedit a fi cea mai bună din punctul de vedere al întârzierii, întrucât nici după testarea pe perioade lungi de timp, stream-ul nu avea întârzieri perceptibile, acestea măsurând aproximativ 150 de milisecunde. Preluarea stream-ului video Pe partea server-ului, odată cu transmiterea cu succes al stream-ului video, a apărut nevoia de a modifica script-ului de object tracking pentru a prelua stream-ul video trimis de Raspberry în locul celui preluat de pe camera web a laptop-ului. Ideea de baza a preluării stream-ului video este ascultarea pe un port prestabilit pentru primirea stream-ului si introducerea lui în algoritm. Pentru a atinge acest scop, a fost folosită aceeași librărie pentru gestionarea conexiunii prin intermediul socket-urilor precum în cazul script-ului de transmitere a stream-ului video și anume zmq.
Pentru a prelua stream-ul video trimis de Raspberry Pi, server-ul trebuie sa asculte pe portul prestabilit. Realizarea acestui pas implică folosirea bibliotecii zmq pentru crearea conexiunii.
Următorul pas este preluarea efectivă a fiecărui frame în interiorul buclei infinite din cadrul script-ului object_tracker.py. Frame-urile sunt preulate în forma lor criptată, primul pas fiind decriptarea acestora. După decodarea imaginii copiem imaginea în variabila frame pentru a nu altera algoritmul rămas. Face Recognition După preluarea cu succes a stream-ului video de la Raspberry Pi, următorul pas în dezvoltarea aplicației a fost implementarea algoritmului de face recognition. În realizarea acestui pas a fost folosită biblioteca face_recognition. Această bibliotecă reușește să recunoască o persoană având în memorie o singură poză a acelei persoane. Pe lângă această bibliotecă a fost necesară colectarea pozelor persoanelor care se dorește a fi recunoscute. Pozele trebuie denumite în funcție de numele persoanelor (ex. Lavinia_0.jpg). Pentru a mări acuratețea bibliotecii, au fost folosite mai multe poze ale aceleiași persoane, denumirea fiecărei poze consistând din două părți desparțite prin “_“ și anume: numele
persoanei și index-ul imaginii ce aparține acelei persoane. Index-ul este folosit pentru a avea posibilitatea denumirii unei poze cu același nume al unei persoane în timp ce numele fișierului rămâne unic. Recunoașterea persoanelor depinde de mai multe criterii cum ar fi unghiul în care este surprinsă persoana și lumina prezentă în cameră. Având mai multe imagini în care este prezentă aceeași persoană, surprinsă în mai multe ipostaze, este mărită acuratețea algoritmului.
Primul pas în realizarea recunoașterii faciale este încărcarea imaginilor persoanelor cunoscute. Deoarece preluăm toate fișierele din interiorul unui folder prestabilit, este necesară filtrarea acestora după anumite extensii. Variabila known_image_extensions este folosită pentru a stoca extensiile fișierelor ce sunt admise. În acest caz sunt procesate doar fișierele de tip JPG, JPEG sau PNG. Fiecărui fișier valid îi este extras numele, și procesat pentru a afla numele persoanei prezentă în imagine, care sunt apoi stocate în variabila known_face_names. Variabila known_face_encodings conține date despre toate caracteristicile fețelor persoanelor din imaginile încărcate. Următorul pas a fost recunoașterea propriuzisă a persoanelor din frame. Pentru a îndeplini acest scop, au fost folosite pentru a decupa persoanele din frame, dreptunghiurile de încadrare generate de object tracker. face_encodings = [] self.face_names = [] known_image_extensions = ["jpg", "jpeg", "png"] self.known_face_names = [] for (dirpath, dirnames, filenames) in walk("known_persons"): for image in filenames: if(image.split(".")[-1] in known_image_extensions): known_face_encodings.append(face_recognition.face_encodings(face_recognition. load_image_file(“known_persons/"+image))[0]) self.known_face_names.append(image.split(".")[0].split("_")[0])
Odată decupate, fiecare imagine este codificată în același mod în care sunt codificate și imaginile cu persoanele cunoscute. Pentru fiecare imagine codificată, este verficată egalitatea cu imaginile codificate cunoscute. Dacă este găsită o egalitate, înseamnă că a fost găsită o persoană cunsocută și setăm variabila name să reflecte acest lucru, stocând în aceasta numele persoanei recunoscute. În caz contrar, înseamnă că nu a fost găsită o persoană cunoscută și i se atribuie denumirea Individ. face_locations = face_recognition.face_locations(face_to_detect) face_encodings = face_recognition.face_encodings(face_to_detect, face_locations) for face_encoding in face_encodings: register_unknown = False matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding) name = "Individ" if True in matches: first_match_index = matches.index(True) name = self.known_face_names[first_match_index]
try: (objects, names) = self.ct.update(rects, frame) print(names) i = len(self.face_names) – 1 for (objectID, centroid) in objects.items(): try: text = names[objectID] except: text = "Unknown {}".format(objectID) i -= 1 cv2.putText(frame, text, (centroid[0] – 10, centroid[1] – 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1) cv2.imwrite("tmp/frame.jpg", frame) return (open("tmp/frame.jpg", 'rb').read(), [], "") except Exception as e: print(KeyError(e)) cv2.imwrite("tmp/frame.jpg", frame) return (open("tmp/frame.jpg", 'rb').read(), [], "")
În acest moment algoritmul complet funcționa bine în momentul când exista o singură persoană în cadru. Însă dacă singura persoană dispare din cadru, aplicația întâmpină o eroare ce cauzează oprirea forțată a acesteia. Aceasta problemă ținea de o eroare în logica de programare. Clasa CentroidTracker presupunea eronat faptul că în fiecare frame trebuie să existe cel puțin o persoană. Acest comportament a fost corectat prin integrarea codului într-un bloc try…except. De asemenea, în momentul în care intrau mai multe persoane în cadru, programul nu se comporta normal, acesta amestecând numele persoanelor; acestea deși erau corecte, nu erau atribuite persoanei corecte. Un alt impediment a fost faptul că acest proces era reluat pentru fiecare frame în parte, lucru deloc optim. Așadar a fost decisă implementarea acestui algoritm în script-ul centroidtracker.py deoarece, în teorie, implementarea acestuia în cadrul funcției register() ar fi însemnat rularea acesteia o singura dată pentru fiecare persoană în parte, lucru ce ar fi avut un impact pozitiv asupra optimalității programului. Testarea după modificarea algoritmului a produs efecte pozitive. Persoanelor li se atribuia numele corect și programul era vizibil mai rapid din punctul de vedere al procesării. Totuși recunoașterea persoanelor a devenit rară. Persoanele erau trimise către algoritmul de recunoaștere imediat ce erau recunoscute, acest lucru implicând fețe parțiale care nu puteau fi recunoscute de algoritm. Astfel a fost decisă introducerea unei noi variabile in clasa CentroidTracker. Această variabilă are ca scop setarea unui eșantion pentru numărul de frame-uri consecutive, pentru care algoritmul încearcă recunoașterea unei persoane. def __init__(self, known_face_names, known_face_encodings, maxDisappeared=10, maxFaceChecks=60): self.nextObjectID = 0 self.objects = OrderedDict() self.disappeared = OrderedDict() self.names = OrderedDict() self.known_face_names = known_face_names self.known_face_encodings = known_face_encodings self.maxDisappeared = maxDisappeared self.maxFaceChecks = maxFaceChecks self.faceChecks = OrderedDict()
Constructorul clasei CentroidTracker a fost modificat pentru a accepta ca parametri, numele persoanelor cunoscute în variabila known_face_names și varianta codificată a acestora în variabila known_face_encodings. De asemenea, sunt inițializate mai multe variabile: •names — folosește identificatorul unic al obiectelor pe post de cheie și numele persoanei pentru valoare; •maxFaceChecks — numărul maxim de frame-uri consecutive pentru care algoritmul încearcă recunoașterea unei anumite persoane; •faceChecks — folosește asemănător variabilei names, identificatorul unic al obiectelor pe post de cheie iar pentru valoare acesta stochează numărul de frame-uri pentru care s-a reîncercat recunoașterea numelui unei persoane. Funcția register() a fost modificată de asemenea pentru a recunoaște persoanele nou apărute. Algoritmul funcționează în același mod, însă numele sunt corelate cu identificatorul unic al fiecărui obiect.
În funcția update() verificăm pentru frame-ul curent, au fost verificate toate persoanele. În cazul în care găsim o persoană care nu a fost identificată anterior, și nu a trecut de numărul maxim de identificări încercate, rulăm algoritmul de recunoaștere facială, acesta având același comportament ca cel din funcția register(). if self.names[objectID] == "Individ" and self.faceChecks[objectID] != self.maxFaceChecks : . . . f o r f a c e _ e n c o d i n g i n f a c e _ e n c o d i n g s : register_unknown = False matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding) name = "Individ" if True in matches: first_match_index = matches.index(True) name = self.known_face_names[first_match_index] self.names[objectID] = name print("registering", name) if register_unknown:
Interfața grafică Interfața grafică este o aplicație ce primește date de la API-uri și afișează date corespunzătoare. Pentru a afișa datele corespunzătoare a fost necesară transmiterea frame-urilor procesate către aplicația Angular. Pentru preluarea datelor a devenit necesară crearea unor API-uri care odată accesate să poată comunica interfeței datele necesare. flask_streamer.py Primul API dezvoltat a fost flask_streamer.py care este construit pentru transmiterea frame-urilor procesate către aplicația de interfață. Pentru a transmite frame-urile generate de către script-ul object_tracker.py acesta a suferit transformarea într-o clasă. Utilitatea transformării script-ului în clasă este faptul că poate fi instanțiată în orice script cu ușurință.
Transformarea script-ului de urmărire în clasă a fost un proces simplu, primul pas a fost transformarea parametrilor în variabile publice a clasei nou formate, împreună cu toate variabilele ce sunt folosite în interiorul funcției update(). Funcția update() a fost redenumită, pentru a oglindi noua funcție, în get_frame(). Noua funcție retunrează frame-ul procesat și salvat într-un folder temporar. După reproiectarea script-ului, a fost începută crearea API-ului principal. Acesta folosește librăria Flask pentru management-ul conexiunilor REST (Representational State Transfer) care este un stil arhitectural de software care defineste un set de constrângeri ce sunt folosite pentru crearea serviciilor Web. self.prototxt = prototxt self.model = model self.confidence = confidence
from flask import Flask, render_template, Response, jsonify, g from object_tracker import ObjectTracker import json import time app = Flask(__name__)
Prima parte a API-ului se ocupă cu importarea modulelor necesare printre care se numără și noua clasă ObjectTracker, împreună cu inițializarea applicației flask.
Funcția gen() primește ca parametru un obiect de tip ObjectTracker și conține o buclă infinită în interiorul căreia este apelată funcția get_frame() din interiorul variabile primită ca parametru. Ultima linie din această funcție întoarce frame-ul, care este pregătit de transmitere.
Este creată ruta ‘/video_feed’ care execută funcția video_feed(). Această funcție constă într-o singură instrucțiune return care întoarce un răspuns care conține imaginea obținută din funcția gen() căreia i se pasează ca parametru un obiect instanțiat de tip ObjectTracker. Instanțierea obiectului înseamnă implicit rularea constructorului acestuia, ceea ce implică pornirea stream-ului video. Proiectul interfeței a fost creat folosind Angular CLI. Instalarea acestuia a fost realizată executând comanda npm install -g @angular/cli. Proiectul este generat folosind comanda ng new SCam –style=scss –routing, care generează automat fișierele proiectului. Opțiunea –style indică Angular-ului tipul de stylesheet pe care să îl folosească. Implicit Angular generează fișiere de tip CSS însă a fost folosit SASS pentru funcționalitățile mai avansate oferite de acesta. Opțiunea –routing indică faptul că se dorește folosirea capabilității de rutare a Angular-ului. def gen(object_tracker): while True: frame = object_tracker.get_frame() yield (b'–frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/video_feed') def video_feed(): return Response(gen(ObjectTracker()), mimetype='multipart/x-mixed-replace; boundary=frame')
Componentele principale ale proiectului sunt: •app/ — conține fișierele componentelor în care este definită logica aplicației; •assets/ — conține imagini si alte fișiere care trebuie copiate așa cum sunt când este executat procesul de build; •index.html — pagina HTML principală care este oferită cuiva care vizitează site-ul sau aplicația; •main.ts — punctul principal de intrare al aplicației; •styles.scss — stilurile prezente în acest fișier sunt aplicate la nivel global în aplicație; •app/app.component.ts — definește logica componentei de bază a aplicației; •app/app.component.html — definește template-ul HTML asociat componentei de bază; •app/app.component.scss — definește fișierul care se ocupă cu stilul componentei de bază; •app/app.module.ts — definește modulul de bază, numit AppModule, care instruiește Angular-ul despre modul în care să asambleze aplicația. Totodată acesta este fișierul în care sunt importate toate modulele aplicației. Modulul principal al aplicației constă într-un stream video preluat de pe server. Crearea acestei componente a fost realizată utilizând comanda ng g c main. Această comandă generează fișierele necesare unei componente, o numește “main” și o adaugă declară în interiorul fișierului app.module.ts. Fiecare componentă generată este compusă dintr-un fișier HTML care este template-ul componentei, ceea ce înseamnă că se ocupă cu elementele grafice prezente în interiorul componentei, un fișier SCSS care se ocupă cu stilurile componentei și o componentă TypeScript care se ocupă cu logica din spatele componentei. Componenta main conține stream-ul video procesat, acesta fiind atributul fundamental al componentei. Stream-ul este preluat în interiorul componentei HTML prin intermediul unei componente img. Componenta img accesează API-ul preluând fiecare frame-ul curent trimis de acesta. <img class="camera-feed" src="http://localhost:5000/video_feed">
Acest tip de stream multimedia se numește Motion JPEG sau MJPEG. Componenta accesează API-ul pentru a primi o imagine. Imaginea trimisă de API însă este schimbată constant, ceea ce dă iluzia de video. În acest mod este creat un stream video de tip MJPEG. Unul dintre avantajele unui stream de acest tip este faptul că există un eșantion larg de modificări ce pot fi aduse, numărul acestora fiind echivalent numărului de modificări ce pot fi aduse unei imagini în cadrul unei pagini HTML. Printre acestea se numără: redimensionare, cropare, aplicarea filtrelor etc. Următorul pas în aplicația de interfață a fost adăugarea unui container care să conțină fețele persoanelor detectate, pentru o detectare mai ușoară a persoanelor recunoscute cât și a celor necunoscute.
Pentru a realiza acest lucru a fost necesară modificarea ușoară a scriptului centroidtracker.py. Pentru fiecare persoană acesta salvează în format JPG, porțiunea frame-ului în care fața acesteia este încadrată. A fost adăugată o nouă variabilă publică și anume imageNames care stochează numele imaginii pentru fiecare persoană recunoscută și folosește pe post de cheie identificatorul unic al obiectului înregistrat. s e l f . i m a g e N a m e s [ s e l f . n e x t O b j e c t I D ] = " " (startX, startY, endX, endY) = rect.astype("int") cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 2) face_to_detect = frame[startY:endY, startX:endX] rgb_frame = face_to_detect face_locations = face_recognition.face_locations(face_to_detect) face_encodings = face_recognition.face_encodings(face_to_detect, face_locations) image_name =str(uuid.uuid4()) + ".jpg" image_path = "recognized_persons/" + image_name cv2.imwrite(image_path, face_to_detect) self.imageNames[self.nextObjectID] = image_name
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: SCam Introducere SCam (Security Camera) este o camera de securitate wireless inteligenta. Aceasta detecteaza persoanele care apar in stream-ul video… [609246] (ID: 609246)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
