Recunoașterea plăcuțelor de înmatriculare și alertare prin e -mail [616839]

Universitatea POLITEHNICA din Bucure s, ti
Facultatea de Electroni ca˘, Teleco municat, ii s, i Tehnologia Informa t, iei
Recunoașterea plăcuțelor de înmatriculare și alertare prin e -mail
Lucrare de licență
Prezen tat˘ă ca cerin t, ˘a parțial˘a pentru obt, inerea titlului
de Inginer
ˆın domeniul Electronică , Telecomunicații și Tehnologia Informației
progra mul de studii Calculatoare si tehnologia informației
Condu c˘atori s, tiint, ific Absolv ent
As.Ing. Va lentin-Adrian N IȚĂ Teodor -Mihai Dumitru
Con
f.Dr.Ing. Razvan -Alexandru VULPE
Anul 2019

Declar a¸tie de onestitate academi c˘a
Prin preze nta declar c˘a lucrarea cu titlul Recunoașterea plăcuțelor de înmatriculare și alertare prin e –
mail, preze ntat˘a ˆın cadrul Facult ˘a¸tii de Electronic ˘a, Telecom unica¸tii ¸si Tehnologia Inform a¸tiei a
Universit˘a¸tii “Politeh nica” din Bu- cure¸sti ca cerin¸t˘a par¸tial˘a pentru ob¸tinerea titlului de Inginer ˆın
domeniul Inginerie Electroni c˘a
¸si Telecom unica¸tii/ Calculatoare ¸si Tehnologia Inform a¸tiei, progra mul de studii Calculatoare si tehnologia
Informației este scris˘a de mine ¸si nu a mai fost prezentat˘a niciodat˘a la o facultate sau instit u¸tie de ˆınv˘a¸t˘amˆant
superior din ¸tar˘a sau str˘ain˘atate. Declar c˘a toate sursele utilizate, inclusiv cele de pe Internet, sunt indicate
ˆın lucrare, ca referin¸te bibliogra fice. Fragme ntele de text din alte surse, repro duse exact, chiar ¸si ˆın traducere
proprie din alt˘a limb˘a, sunt scrise ˆıntre ghilimele ¸si fac referi n¸ta˘ la surs˘a. Refor mularea ˆın cuvin te proprii
a textelor scrise de c˘atre al¸ti autori face referi n¸ta˘ la surs˘a. ˆIn¸teleg c˘a plagiatul constituie infrac¸tiune ¸si se
sanc¸tioneaz ˘a conform legilor ˆın vigoare.
Declar c˘a toate rezultatele simul˘arilor, experimen telor ¸si m˘asur˘atorilor pe care le prezint ca fiind f˘acute
de mine, precum ¸si metodele prin care au fost ob¸tinute, sunt reale ¸si provin din respectiv ele simul˘ari,
experimen te ¸si m˘asur˘atori. ˆIn¸teleg c˘a falsificarea datelor ¸si rezultatelor constituie fraud ˘a ¸si se
sanc¸tioneaz ˘a conform regulame ntelor ˆın vigoare.
Bucure ¸sti, Septembrie 2019.
Absol vent: Teodor -Mihai Dumitru
………………………

Cuprins
Lista figurilor …………………………………………………………………………………………………………. 8
Lista acronimelor ………………………………………………………………………………………………. 10
1.Introducere ……………………………………………………………………………………………………….12
1.1 Motivaț ie ……………………………………………………………………………………………….. 12
1.2 Context ……………………………………………………………………………………………………12
1.3 Problema …………………………………………………………………………………………………13
1.4 Obiectivele lucrării …………………………………………………………………………………..13
1.5 Soluția propusă ………………………………………………………………………………………..13
1.6 Competitori pe piață …………………………………………………………………………………14
1.7 Conținutul lucrării ……………………………………………………………………………………14
1.8 Contribuțiile personale ale autorului …………………………………………………………..14
2. D ispozitive , tehnologii utilizate și noțiuni teoretice ……………………………………………..16
2.1 Dispozitive folosite ………………………………………………………………………………….16
2.2 Limbajul de programare Python …………………………………………………………………17
2.3 Module Python folosite …………………………………………………………………………….19
2.3.a Procesare de imagini ………………………………………………………………………19
2.3.b Inteligență artificială ……………………………………………………………………… 22
2.3.c Server -client …………………………………………………………………………………. 25
2.3.d CSV……………………………………………………………………………………………… 26
2.3.e Poștă electronică ……………………………………………………………………………..26
3.Detalii de implementare ……………………………………………………………………………………..29
3.1 Organizarea codului aplicației ……………………………………………………………………..29
3.2 Recunoașterea plăcuței de înmatriculare ……………………………………………………….29

3.3 Comunicarea dintre client și server …………………………………………………….34
3.4 Salvarea rezultatelor în CSV și interpretarea datelor …………………………… 35
3.5 Când se face alertarea ………………………………………………………………………35
3.6 Notificarea prin e -mail ……………………………………………………………………..36
4.Concluzii …………………………………………………………………………………………………..39
4.1 Dezvoltarea aplic ației ……………………………………………………………………….39
4.2 Ce am învățat …………………………………………………………………………………..39
4.3 Probleme întâmpinate ………………………………………………………………………..40
4.4 Idei de îmbunătățire …………………………………………………………………………..40
Bibliografie ……………………………………………………………………………………………………. 41
Anexe ……………………………………………………………………………………………………………..42

8 Lista figurilor
Figura 2.1 Raspberry PI si camera raspberry
Figura 2.3.b Ramurile inteligenței artificiale
Figura 3.1 Flow- ul aplica ției
Figura 3.2.1 Imaginea în nuanțe de gri și binarizarea acesteia
Figura 3.2.2 Detecția plăcuței de înmatriculare
Figura 3.2.3 Segmentarea caracterelor
Figura 3.2.3 Folder -ul de antrenament
Figura 3.2.4 Cross Validation Result
Figura 3.2.5 Rezultatul recunoa șterii
Figura 3.3.1 Protocolul de trimitere a pozelor
Figura 3.5 Notificare e- mail
Figura 3.6 Form atul e -mai-ului

9

10
Lista acronimelor

IA – Inteligență artificială
CP – computer vision
ALPR – automatic licence plate recognition
CCA – Connected Component Analysis
IA – Artificial Intelligence
ML – Machine Learning
SVM – Mașina cu vectori suport (Support Vector Machine)
SVC – Support Vector Classifier
CSV – Comma Separated Values
MIME – Multipurpose Internet Mail Extensions
LPR – License Plate Recognition
CCA – Connected component analysis
K-NN – K – nearest neighbour

11

12
Capitolul 1
Introducere

Acest proiect constă în monitorizarea traficului, mai precis proiectul reprezintă o măsură de
siguranță de care șoferii pot beneficia, aceștia primesc o notificare prin e- mail dacă sunt urmăriți și dacă
dacă da, de cine si de cât timp. Proiectul are la baz ă machine learning pentru recunoașterea plăcuțelor de
înmatriculare.

1.1 Motivație

Inteligența artificială este in continuă expansiune și ușor -ușor va fi folosită peste tot, de asemenea
este o ramură interesantă care îmbină programarea cu algoritmi matematici. Motivația a venit din dorința
de a face un proiect care implică inteligență artificială și ce altă metodă mai bună este, decât conceperea
unei de idei de computer vision și învelirea acesteia sub formă de aplicație completă cu notific ări, bază de
date, server etc.

1.1.a Necesitatea inteligenței artificiale
Lumea este in continuă evoluție, oamenii fac o treabă foarte bună la ajutorul acestei evoluții, dar
lumea in care trăim aparține digitalizării, accentul in dezvoltare se pune pe d ispozitive electronice,
programare, realizarea roboților care să aibă caracteristici umane, mai exact dorința este ca oamenii să nu mai depună efort , iar totul să fie controlat electronic. Astfel inteligența artificială are un cuvânt mare de
spus, doar ple când de la ideea că oamenii fac greșeli, ne putem da seama de plusul adus de aceasta noua
ramură.
Această perioadă temporală este una care aparține interconectării, și cum știm ca inteligența
artificială are nevoie de seturi de antrenare, interconectarea u mană este fix ceea ce are nevoie ca ea să se
dezvolte.
In cadrul acestui proiect se folosește doar pentru recunoașterea caracterelor, antrenarea făcându -se
cu cifrele și literele alfabetului român, dar se poate folosi și mai mult, se poate face o legătura ca baza de
date de la poliție, iar daca un număr este receptat si se regăsește prin dosarele poliției se poate trimite direct o alertă la poliție, toate datele reprezintă o antrenare pentru un divers sistem inteligent.

1.2 Context

În ultimii ani, o ramură care a fost dezvoltată cu ajutorul IA este cea de computer vision, acesta
este un domeniu științific interdisciplinar care tratează modul în care calculatoarele pot fi făcute pentru a
obține o înțelegere la nivel înalt din imagini sau videoclipuri digitale. Din perspectiva ingineriei, acesta
încearcă să automatizeze sarcinile pe care sistemul vizual uman le poate face. [1]
Sarcinile CP includ metode de achiziție, procesare, analiză și înțelegere a imaginilor digitale și
extragerea d atelor de dimensiuni foarte mari din lumea reală pentru a produce informații numerice sau
simbolice, de exemplu sub formă de decizii. Înțelegerea în acest context înseamnă transformarea
imaginilor vizuale (intrarea retinei) în descrieri ale lumii care pot interfața cu alte procese de gândire și
pot provoca acțiuni adecvate. Această înțelegere a imaginii poate fi văzută ca dezagregarea informațiilor
simbolice din datele imaginii folosind modele construite cu ajutorul geometriei, fizicii, statisticilor si
teoriei învățării. [1]
Ca disciplină științifică, CP este preocupată de teoria din spatele sistemelor artificiale care extrag
informații din imagini. Datele de imagine pot lua mai multe forme, cum ar fi secvențe video, vizualizări

13
de la mai multe camere sau d ate multidimensionale de la un scaner medical. Fiind o disciplina
tehnologică, CP încearcă să aplice teoriile și modelele sale pentru construcția sistemelor CP. [1]
Subdomeniile CP includ reconstrucția scenei, detectarea evenimentelor, urmărirea videoclipu rilor,
recunoașterea obiectele, estimarea pozelor 3D, învățarea, indexarea, estimarea mișcării și restaurarea
imaginii. [1]
În condițiile acestea in care se pune accent pe IA, inclusiv CP, iar piața cere sisteme cât mai
inteligente, este o variantă foarte bună ca in aplicațiile ce se realizează sa includem și IA, deoarece
rezultatele au procentaj de acuratețe mult mai mare.
Pe piață mai există sisteme care se ocupă cu recunoașterea plăcuțelor de înmatriculare, unul dintre
cele mai cunoscute este openalpr, acest serviciu se promovează prin alertarea în momentul în care o
plăcuță de înmatriculare este văzută de camerele tale de securitate, aceștia oferă date precum numărul
mașinii, numele brand- ului plus alte servicii, problema fiind că este disponibil doar pe teritoriul Americii.
Din acest motiv am creat această aplicație, dar care evident nu are infrastructura pe care o are
OpenALPR .

1.3 Problema

După cum am precizat mai sus, astfel de recunoașteri de plăcuțe de înmatriculare au mai fost
dezvoltate, de -a lungul timpului, dar nu au fost învelite sub forma unei aplicații complexe, sau dacă da, nu
pe teritoriul României.
Problema pe care o rezolvă acest proiect este siguranța la volan, când te afli într -o mașină te poți
simți sau nu in siguranță, este mai multe pericole, dar în principiu sunt două mari, pericolul ca șoferul să
comită o eroare sau ca un alt participant la trafic să comită o eroare, in cazul de față sistemul ne ajută sa ne ocupăm de ceilalți participanți la trafic, mai exact cei care doresc sa facă probleme dinadins.
Sunt aproximativ 8 milioane de mașini in România, acest lucru înseamnă ca unul din doi români
deține un autoturism, cum foarte bine știm nu putem ține o evidență a oamenilor, ce fac, unde se duc, cu cine, ar fi și imoral, dar gestionarea a 8 milioane de autoturisme, cu interes de siguranță a șoferilor, ar trebui să fie bine venit.
De asemenea se pot semna contracte cu poliția , iar astfel se garantează o măsură de siguranță în
plus, la fel cum există smart home și dacă cineva dorește să intre prin efracție o alertă este trimisă la poliție,
la fel se poate realiza si pentru acest proiect.

1.4 Obiectivele lucrării

Lucrarea de față caută să ofere șoferilor siguranța in cazul urmării, aceasta este necesară persoanelor
publice, cunoscute, artiști sau a căror viață poate fi pusă in pericol.
În vederea proiectării aplicației s -a pornit de la o idee actuală și beneficiile pe care le poate aduce,
ideea a fost cea de recunoaștere a obiectelor folosind algoritmi de inteligență artificială, astfel am ajuns la recunoașterea literelor și cifrelor. Și unde sunt întâlnite mereu litere și cifre ? În trafic.
Obiectivul a fost ca recunoașterea să se facă cât mai rapid și mai precis, iar tot procesul de
recunoaștere și alertare să fie aproape instant.

1.5 Soluția propusă

Soluția propusă de către acest proiect este împărțită în două, pe de -o parte avem aplicația care
rulează pe server și aplicația care rulează pe client, mai precis clientul este un Raspberry Pi, pe care s -a
montat o cameră raspberry, clientul trimite consta nt poze la server pentru analiză, server -ul se ocupă de

14
partea de recunoaștere și alertare.
Alerta înseamnă de fapt ca un număr de înmatriculare să fie prezent in baza de date de mai multe
ori, iar declanșarea alarmei este dependentă de o condiție de timp, in mesajul de alertă este prezent numărul
plăcuței de înmatriculare, dar și când a mai fost prezentă mașina respectivă, d etaliile l egate de partea
software sunt prezente in Capitolul 4 .

1.6 Competitori pe piață

Proiectul are la baza recunoașterea plăcuței de înmatriculare, acest termen se mai numește și
Automatic Licence Plate Recognition, acest termen este reg ăsit și în OpenALPR, acesta este un serviciu
pe teritoriul Statelor Unite ale Americii, ei oferă tot ce oferă și acest proiect, doar că din păcate serviciul
lor este doar pe teritoriul americii.
OpenALPR are multe avantaje, unul dintre ele constă în faptul că este open source, de asemenea ei
au contract cu statul și îți pot oferi mai multe informații despre mașina respectivă.
Dar există totuși o diferență majoră între acest proiect si ceea ce face OpenALPR, cum ar fi scopul,
scopul la proiectul creat de mine este acela de alertare în cazul unei urmăriri, în schimb la OpenALPR este mai mult pentru gestionarea traficului.

1.7 Conținutul lucrării

Dezvoltarea proiectului s -a realizat în mai multe etape, în care s -au adăugat diferite funcționalități
de la o etapă la alta, iar in final rafinarea codului.
Următorul capitol, Dispozitive, tehnologii utilizate și noțiuni teoretice, cuprinde dispozitivele
necesare pentru realizarea acestui proiect, tehnologiile care s -au folosit, la bază este doar limbajul python
împreună cu diferite module si evident breviar teoretic pentru fiecare subcapitol în parte.
În capitolul 3 , denumit Detalii de implementare, este prezentat flow -ul aplicației și f iecare etapă
luată în parte , studiată si explicată in detaliu, împreună cu exemple.
Capitolul 4 este cel de Concluzii, aici sunt menționate problemele întâmpinate, idei de îmbunătățire
și ce am învățat din realizarea acestui proiect.

1.8 Contribuțiile personale ale autorului

Contribuțiile personale pot fi regăsite pe tot parcursul proiectului, dacă este ceva ce nu îmi aparține
in totalitate este algoritmul de recunoaștere, deoarece am stat să caut și să testez diverși algoritmi de clasificare supervizați, urmând ca să aleg pe cel pe care îl consider optim, iar ca să înțeleg tot procesul, care are în spate mai multe etape, cum ar fi recunoaștere de plăcuță, recu noaștere de caractere, predicție,
antrenarea setului de date etc. necesită o înțelegere aprofundată asupra domeniului, așa ca nu îmi asum tot creditul pentru partea de recunoaștere, sunt bucăți luate din diverse locuri și îmbunătățite.
O enumerare a principalelor contribuții personale aduse in realizarea acestei lucrări sunt:
– Identificarea plăcuței de înmatriculare
– Identificarea caracterelor
– Setul de antrenare
– Realizarea trimiterii cadrelor către server folosind server -client
– Salvarea datelor intr -un CSV
– Alertarea, care constă in trimiterea unui e -mail la destinator
– Calcularea timpului de când a fost receptat respectivul număr de înmatriculare

15

16
Capitolul 2
Dispozitive, tehnologii utilizate si noțiuni teoretice

Aplicația de față este scrisă in totalitate in limbajul Python, un limbaj orientat pe obiect, care
interacționează cu un CSV (Comma Separated Values), acesta fiind un fișier text care conține o listă de
date.
Limbajul Python este unul dintre cele mai cău tate limbaje de programare, motivul fiind că este un
limbaj ușor de înțeles și care dispune de foarte multe framework -uri și biblioteci. Acesta este un limbaj
mai încet, dacă îl comparăm cu C/C++, dar acest lucru este evident, deoarece C este un limbaj low -level,
iar python un limbaj high- level. În cele ce urmează vor fi prezentate toate necesarele realizării acestui
proiect.

2.1 Dispozitive folosite

Pentru realizarea acestui proiect au fost utilizate următoarele dispozitive :
– Un Raspberry Pi 3 Model B
– O camera Raspberry v1.3 de 5MP
– Un server (un calculator)

Figura 2.1 Raspberry PI si camera raspberry

Raspberry Pi este o serie de SBC ( Single -board computer ) de dimensiunile unui card de credit . Este
inspirat de BBC Micro și produs în UK de către Raspberry Pi Foundation . Scopul a fost acela de a crea un
dispozitiv cu costuri reduse care să îmbunătățească abilitățile de programare și înțelegerea hardware la
nivel preuni versitar . [2]
Raspberry Pi este mai lent decât un laptop sau PC, dar care poate oferi majoritatea aplicațiilor
acestora precum conecta re la internet , procesare de text, redare de conținut video/ audio, jocuri video, la un
nivel de consum redus de energie. [2]
În plus, Raspberry Pi are o caracteristică specială pe care computerele nu o folosesc: port generic
de intrare/ieșire (General -Purpose Input/Output )(GPIO). Acesta oferă posibilitatea de a conecta diverse
componente electronice specifice sistemelor înglobate : senzori , butoane, ecran LCD , relee, și crearea de
noi proiecte electronice. [2]
În cazul de față a fost folosit un Raspberry Pi 3 Model B, acesta este cu 50 -60% mai rapid decât

17
versiunea anterioară și de 10 ori mai rapid decât versiunea originală, acest raspberry model 3 dispune de
1Gb de memorie RAM, bluetooth, wi -fi, 40 pini GPIO ș i altele.
Sistemul de operare care rulează pe un Raspberry PI este Raspbian, acest sistem de operar e se poate
downloada pe site -ul oficial al celor de la Raspberry si instala pe un SD Card care se montează in slot -ul
oferit de Raspberry Pi.
S-a ales folosirea unei camere dedicate pentru Raspberry, mai precis o pi cameră versiunea 1.3 de 5
megapixeli, această camera este cea mai bună variantă pentru proiectul nostru, deoarece spre deosebire de celelalte camere, web camera, care sunt prin USB, camera raspberry dispune de un port special. Pe placa
Raspberry sunt regăsite mai multe porturi, precum :
– 4 porturi USB
– port Ethernet
– port Jack (audio)
– slot pentru SD Card
– port HDMI
– port pentru alimentare
– port de Display
– port de Camera, acesta fiind dedicat pentru camerele picamera

Avantajul î n a folosi această cameră raspberry este faptul că se găsesc tutoriale oficiale pentru a te
învăța cum să le folosești și instalezi, practic sunt ghiduri complete pentru utilizatorii dornici de a lucra cu plăci raspberry si module deriviate din aceasta.
Motivul pentru care am folosit rasp berry, a fost acela de dimensiune si capabilitatea de a oferi
prelucrări pe imagine utilizând camerele lor, personal am folosit raspberry -ul doar pentru a trimite poze
către server urmând ca acesta din urmă să facă recunoașterea de obiecte.
Server -ul in momentul de față este reprezentat de calculatorul meu personal, evident nu aș putea să
pun in aplicare proiectul, în sensul de a oferi serviciul meu publicului extins, deoarece PC -ul meu nu ar fi
capabil să se descurce cu atâtea request -uri, fiind în c ele din urmă suprasolicitat.
Pentru o variantă de test în care există un singur utilizator, PC -ul este foarte bun și nu au fost erori
sau întârzieri, procesarea făcându- se repede.

2.2
Limbajul python

Python este un limbaj de programare interpretat, d e nivel înalt. [3]
A fost creat de Guido van Rossum și lansat pentru prima dată în 1991, filozofia de design a
limbajului Python subliniază lizibilitatea codurilor prin utilizarea notabilă a indentării . Construcțiile sale
de limbaj și abordarea orientată pe obiect e urmăresc să îi ajute pe programatori să scrie cod logic, clar,
pentru proiecte mici și mari. [3]
Python este un limbaj de programare dinamic și folosește garbage collector, precum si Java.
Acceptă mai multe paradigme de programare, inclusiv programare procedurală, orientată pe obiecte și
funcțional ă. Python este adesea descris ca un limbaj "baterii incluse", datorită bibliotecii sale standard
complete . [3]
Python a fost conceput la sfârșitul a nilor 1980 ca un succesor al limbajului ABC. Python 2.0, lansat
în 2000, a introdus funcții precum ,,list comprehensions,, și un sistem de colectare a gunoiului capabil să
colecteze cicluri de referință, denumit ,,garbage collector,,. Python 3.0, lansat î n 2008, a fost o revizuire
majoră a limbajului care nu este complet compatibil cu întoarcerea și mult codul Python 2 nu rulează nemodificat pe Python 3. Din cauza îngrijorării cu privire la cantitatea de cod scrisă pentru Python 2, suport pentru Python 2.7 (ultima versiune din seria 2.x) a fost extinsă până în 2020. Dezvoltatorul de limbaj Guido
van Rossum a asumat responsabilitatea exclusivă a proiectului până în iulie 2018, dar acum împărtășește conducerea sa ca membru al unui consiliu de cinci persoane.
[3]

18
Interpreții Python sunt disponibili pentru multe sisteme de operare. O comunitate globală de
programatori dezvoltă și menține CPython, o implementare de referință open source. O organizație non-
profit, Python Software Foundation, gestionează și direcț ionează resurse pentru dezvoltarea Python și
CPython. [3]
S-a ales folosirea acestui limbaj de programare deoarece este unul dintre cele mai căutate limbaje
de programare pe piața mondială, python, împreună cu JavaScript, de asemenea fiindcă acesta este u n
limbaj high -level, îl face astfel și un limbaj de programare de viitor, motivul fiind crearea calculatoarelor
cuantice.
Unul din dezavantajele acestui limbaj este viteza, deoarece folosește multe biblioteci și multe
apelări de funcții, este un limbaj care nu are un timp așa bun de execuție, dar acest dezavantaj va fi remediat prin utilizarea calculatoarelor cuantice, în momentul scrierii acestei lucrări este deja creat calculatorul cuantic, iar acesta ca limbaj de programare folosește python, deoarece vi teza nu mai este relevanta, ea fiind
deja multe prea mare.
Pentru această lucrare limbajul python este cea mai bună variantă, deoarece pune la dispoziție multe
module dedicate prelucrării pe imagini și module pentru IA.

19
2.3 Module python și noțiuni teoretice
În acest subcapitol vom discuta despre modulele python folosite și mai exact ce s- a folosit din ele ,
împreună cu un scurt breviar teoretic.
Pentru realizarea acestui proiect au fost necesare mai multe module python, primele cu care vom
începe sunt cele de procesare de imagini.

2.3.a Procesare de imagini

Prelucrarea digitală a imaginilor înseamnă procesarea imaginii digitale cu ajutorul unui computer
digital. De asemenea, putem spune că este o utilizare a algoritmilor comput aționali , pentru a obține o
imagine îmbunătățită, fie pentru a extrage informații utile. [4]

Prelucrarea imaginilor include în principal următoarele etape:
1. Importarea imaginii prin instrumente de achiziție de imagini;
2. Analizarea și manipularea imaginii;
3. Ouput -ul în care rezultatul poate fi o imagine modificată sau un raport care se bazează pe analiza imaginii
respective.

Ce este o imagine? O imagine este definită ca o funcție bidimensională, F (x, y), unde x și y sunt coordonate spațiale,
iar amplitudinea F la orice pereche de coordonate (x, y) se numește intensitatea acelei imagini la acea punct. Când valorile x, y și amplitudinea lui F sunt finite, o numim imagine digitală.
Cu alte cuvinte, o imagine poate fi definită printr -un tablou bidimensional special aranjat în rânduri și
coloane.
Imaginea digitală este compusă dintr -un număr finit de elemente, fiecare dintre ele având o valoare
particulară într- o anumită locație. Aceste elemente sunt denumite elemente de imagine, elemente de
imagine și pixeli. Un pixel este cel mai utilizat pentru a indica elementele de o imagine digitală.

Scikit -image

Scikit -image este o colecție de algoritmi pentru procesarea pe imagini, acesta este gratis și poate fi
folosit de oricine, pentru folos irea unui astfel de modul, este necesar un mediu de dezvoltare virtual și
instalarea acestuia.
Proiectul de față a fost realizat folosind ca mediu de dezvoltare PyCharm si a fost scris folosind
python 2.7, astfel pentru instalarea modulelor trebuie urmați pașii următori:
Pentru PyCharm:
– se creează un proiect nou: File -> New Project -> Introducere nume – > Ok
– se creeaza un fișier .py: În tab -ul din stânga apare proiectul : Click dreapta pe numele proi ectului
-> New – > Python File -> Introducere nume -> Ok
– abia acum începe procesul pentru adăugarea unui nou modul: File -> Settings ->
Project:,,Nume,, – > Project Interpreter – > alt+insert – > Se scrie in searchbar numele modulului,
ex: scikit -image – > click st ânga pe el – > Install Package

Aceasta este metoda de instalare a unui modul folosind PyCharm, pe o distribuție linux instalarea
unui modul se face folosind terminalul :
Pip install scikit -image
Sau
Pip3 install scikit- image, dacă se folosește python 3.

20
Importări din scikit -image:

a) Imread , acea stă funcție este necesară când se dorește citirea unei imagini. În această funcție
avem ca parametrii numele imaginii și valoarea boolean pentru transformarea imaginii din RGB
in nuanțe de gri.

from skimage.io import imread

carImage = imread (,,nume -imagine,, , as_grey = True)

b) Threshold_otsu , aceasta este o funcție care calculează valoarea de prag folosind metoda otsu.
Thresholding

Pragul este utilizat pentru a crea o imagine binară dintr -o imagine în tonuri de gri. Este cel mai
simplu mod de a segmenta obiectele dintr -un fundal.

Algoritmii de prag implementa ți în scikit -image pot fi separa ți în două categorii: [5]

– Bazați pe histogramă . Se folosește histograma intensității pixelilor și se fac anumite ipoteze cu
privire la propri etățile acestei histograme (de exemplu, bimodal). [5]
– Local. Pentru a procesa un pixel, se folosesc doar pixeli vecini. Acești algoritmi necesită adesea mai mult timp de calcul. [5]
Încercăm să minimizăm aria de sub histograma unei regiuni, care pe baza p ragului calculat va fi
atribuită celeilalte regiuni. Vom considera aceste două regiuni ca două grupuri.[6]
– Pragul va fi ales astfel ca cele două grupuri să fie cât mai “strânse”, minimizând astfel
suprapunerea. – O măsură a omogenității grupului este varianța. Un grup cu omogenitate mare are o varianță mică,
un grup cu omogenitate mică are varianță mare.
Pentru orice prag, varianța totală a imaginii este suma dintre varianța intra -clasă, și varianța dintre
clase. [6]
-Deoarece varianța totală este constantă, independentă de pragul t, efectul alegerii pragului este
doar de a schimba ponderea celor două varianțe.
-Problema minimizării varianței intra -clasă este astfel echivalentă cu problema maximizării
varianței dintre clase.
-Acest lucru se poate face recursiv. [6]
Metoda lui Otsu de calculare a pragului calculează un prag optim, prin maximizarea varianței între
două clase de pixeli, care sunt separate de prag.
Am ales această metodă de calcul al pragului deoarece părea prin încercate să dea cele mai bune
rezultate.
Acest pas, cel de calculare al pragului este unul foarte important, deoarece noi vom binariza
imaginea ca să vedem mai bine caracterele pe un fundal, deci noi vom avea 2 elemente, caracterul si fundalul, astfel metoda de alege re a pragului este una foarte importantă.

from skimage.filters import threshold_otsu
threshold_value = threshold_otsu(gray_car_image)

21
c) Measure.label , etichetează regiunile conectate ale unui integer array
2 pixeli sunt conectați atunci când sunt vecini și au aceeași valoare. În 2 -D aceștia pot fi vecini fie
sub formă de 4V, adică ,, + ,, , fie 8V, in care se includ și colțurile.

from skimage import measure

label_image = measure.label(binary_car_image)

d) Measure.regionprops

Măsoară proprietățile regiunilor de imagine etichetate, deci această funcție se poate aplica numai
după ce s -a aplicat funcția de measure.label.
Apelarea metodei regionprops pe imaginea etichetată va returna o listă cu t oate regiunile, precum
și cu proprietățile lor, cum ar fi caseta de delimitare(bbox), eticheteta etc. Pixelii aparținând căsuței de
delimitare sunt la intervalul pe jumătate deschis [min_row ; max_row) ș i [min_col; max_col].
În programul nostru am avut nevoie de regionprops si bbox, deoarece aveam nevoie să vedem
locația plăcuței pe o imagine, respectiv, identificarea caracterelor în plăcuță.

from skimage.measure import regionprops
……………………….. for region in regionprops(label_image) :
min_row, min_col, max_row, max_col = region.bbox
În capitolul 3 se va explica pe larg folosirea acestor funcții, dar pe scurt am avut nevoie de bbox,
pentru a face comparație intre regiunile găsite si dimensiunile unei plăcuțe(dimensiune dată de noi, adică dimensiunea unei plăci reale).
După determinarea regiunilor am folosit patches.Rectangle , pentru a desena un chenar roșu,
motivul fiind de a vizualiza într- un plot regiunile identificate , de asemenea am folosit si matplotlib.pyplot ,
pentru a afișa desenul.

import matplotlib.patches as p atches
import matplotlib.pyplot as plt

e) Skimage.transform.resize , redimensionează imaginea pentru a se potrivi cu o anumită
dimensiune

Efectuează interpolarea imaginilor de dimensiuni mari sau dimensiuni mici.
from skimage.transform import resize
roi = license_plate[y0:y1, x0:x1]

resized_char = resize(roi, (20, 20)) # pentru caractere

Motivul pentru care am folosit resize, este acela că am folosit un algoritm de clasificare
supervizat la partea de recunoaștere , iar antrenarea am făcut -o cu caractere de dimensiuni 20×20.

22
2.3.b Inteligență artificială

Inteligența artificială (IA) este o ramură a informaticii cu ajutorul căreia se încearcă construirea
de mașini inteligente și modelarea inteligenței umane.
Învățare mașină , denumită și machi ne learning (ML) este o ramură din știința inteligenței
artificiale care urmărește să le confere mașinilor capacitatea de a „învăța”. Acest lucru se realizează prin
utilizarea unor algoritmi care identifică modele pe baza datelor primite, astfel încât mașinile să poată lua
decizii și să facă predicții, adică să devină „inteligente”. În acest fel nu mai este necesar ca mașina să fie
programată în mod specific pentru fiecare acțiune în parte .[8]
Învățarea automată a devenit unul dintre cele mai importante subiecte în cadrul organizațiilor de
dezvoltare care caută modalități inovatoare de a utiliza activele de date pentru a ajuta compania să câștige
un nou nivel de înțelegere. Cu modelele adecvate de învățare automată, organizațiile au capacitatea de a prezice continuu schimbări în afaceri, astfel încât ei sunt cei mai în măsură să prezică ce urmează. Deoarece datele sunt în mod constant adăugate, modelele de învățare automată se asigură că soluția este actualizat ă
constant. Dacă se utiliz ează cele mai potrivite și în continuă schimbare sursele de date în contextul învățării
automate, există ocazia de prezic ere a viitorului . [7]
Învățarea automată este o formă de AI care permite unui sistem să învețe de la date, mai degrabă
decât prin programare explicită. În orice caz, învățarea automată nu este un proces simplu.
Învățarea mașină folosește o varietate de algoritmi care iterativ învață din date pentru a se
îmbunătăți, descrie date și prezice output -ul. Deoarece algoritmii antrenează seturi de date , este posibil să
se producă modele mai precise pe baza acestor date. Un model de învățare automat ă este rezultatul generat
atunci când vă antrenați algoritmul mașină de învățare cu setul de date. După antrena re, când oferiți un
model cu un input , vi se va da un output . De exemplu, algoritmul predictiv va crea un model predictiv.
Apoi când furnizați mo delul predictiv cu date, veți primi o predicție bazată pe datele care au instruit
modelul. Învățarea automată este esențială pentru crearea modelelor de analiză. [7]

Este posibil ă interacționarea user -ilor cu aplicații de învățare automată fără ca aceștia să realizeze.
De exempl u vizitarea unui site de comerț electronic și când aceștia încep să vizualiz eze produse și să
citească recenzii, probabil că le va fi prezentat produse similare care l i se pot părea interesante. Aceste
recomandări nu sunt hard codate de dezvoltatori. Sugestiile sunt oferite site- ului printr -un model de
învățare automată. Modelul ia istoricul de navigare împreună cu browsing- ul și achizițion ează date userilor
pentru a le prezenta alte produse similare pe care poate ar dori să l e cump ere. [7]
Învățarea profundă , denumită și deep learning este o ramură din știința învățare mașină care
reprezintă cel mai avansat domeniu al inteligenței artificiale. Ea are principalul obiectiv să le ofere mașinilor posibilitatea să învețe și să gând ească cât mai asemănător oamenilor. [8]

23 I
maginea de mai jos ilustrează perfect relația dintre inteligența artificială, învățare mașină și
învățarea profundă.
F
igura 2.3.b Ramurile inteligenței artificiale [8]
P
rogresele înregistrate în acest domeniu (inteligență artificială) al informaticii au generat dezbateri
aprinse cu privire la amenințarea pe care inteligența artificială o reprezintă la adresă omenirii, indiferent
că vorbim de o amenințare fizică (există voci care susțin că omenirea poate fi exterminată de roboții
înzestrați cu inteligență artificială) sau economică (pentru prevenirea acestui pericol s -a propus venitul
universal de bază, care în prezent este în curs de testare în anumite țări). [8]
Metodele de învățare mașină permit eliminarea necesității programării mașinii pentru fiecare
acțiune dintr -o listă de posi bilități și se stabilește cum ar trebui să reacționeze inteligența mașinii la fiecare
dintre acestea. [8]
Metodele actuale de învățare mașină devin din ce în ce mai sofisticate, acestea fiind integrate într –
o serie de aplicații medicale complexe cum ar fi analiza genomului, într -un efort de prevenire a bolilor,
diagnosticarea depresiei pe baza unor modele de vorbire sau identificarea persoanelor cu tendințe de sinucidere. [8]
Învățarea profundă necesită o arhitectură complexă care imită rețelele neuronale ale creierului uman
pentru a da sens unor modele chiar și atunci când lipsesc detalii, datele disponibile sunt insuficiente sau atunci când acestea pot crea confuzie. Deși posibilitățile oferite de învățarea profundă sunt vaste, cerințele sale sunt pe măsu ră: avem nevoie de multe date și de o putere de calcul extraordinară. [8]

24 În aplicație, pentru recunoașterea literelor este folosit algoritmul de învățare mașină numit Mașina
cu vectori suport, SVM.
SVM
Mași
na cu vectori suport este o tehnică modernă și performantă de clasificare supervizată.
Clasificatorul SVM face parte din categoria mașinilor cu nuclee dispersate(sparse kernel machine). Este
considerat cazul cu două clase, metoda fiind generalizată apo i relativ simplu pentru ipoteza multiplă(mai
mult de două clase). Dacă se consideră un set de antrenare constituit dintr -o mulșime de N vectori n –
dimensionali 12{ , ,…, }, , 1,n
Ni xx x x i N ∈= și setul de etichete corespunzătoare
12{ , ,…, }, { 1,1 }, 1,Ni T tt t t i N= ∈− = , atunci SVM caută să determine un hiperplan în spațiul n -dimensional
care să fie optimal din punct de vedere al ,,margin,,- ului dintre cele două clase de date.
Se pleacă de la problema clasificării liniare in două clase, care revine la a deduce funcția y(x).
() ()tyx w x b φ= +
,
unde ()xφ este nucleul care definește o transformare într -un spațiu de caracteristici convenabil
ales. Semnul funcției y(x) conferă clasa.
P
utem avea date liniar separabile, SVM ,,hard margin,, sau date neseparabile liniar, SVM ,,soft
margin,,. [9]
SVM se poate ex tinde de la cazul analizării clasificării binare la cazul de tehnici de clasificare
multi- clasă. O metodă simplă SVM multi -clasă este metoda ,,one- to-one,,. La clasificarea unui vector,
clasa câștigătoare este clasa care are cele mai multe voturi. Un vot este obșinut în urma câștigării unui ,,duel,, one -to-one. De exemplu, pentru o problemă de clasificare cu M=5 clase este nevoie să se efectueze
M(M- 1)/2=25 de ,,dueluri,,. Efortul computațional crește cu numărul M de clase.
Funcții nucleu (kernel) uzuale : [9]
1.Liniară:
(, ) ;tKer x y xy =
2.G
aussiană: 2
2 ( , ) exp( );Ker x y = −γ || − || xy
3.E
uclidiană :1 ( , ) exp( );Ker x y = −γ || − || xy
4.P
olinomială : ( , ) exp( 1) ;tyKer x y xy = +
Mai
există algoritmi de clasificare, cum ar fi K -NN pe care l -am testat, dar SVM a avut acuratețe
mai bună.
Scikit -learn
Scikit -learn este un modul pentru folosirea învățării mașină folosind python, acest modul este
simplu și eficient, și mai ales ușor de folosit.
În cadrul acestui proiect a fost folosit pentru a antrena setul de date, de a face cross- validantion, de
a implementa SVM, de a rula o singură data setul de antrenare și programul este pregătit de predicție, încărcarea setului antrenat de litere pentru a f i folosit in predicție.
Instalarea acestui modul se face in felul următor :
pip install scikit -learn

25
Sklearn.svm.SVC

svc_model = SVC(kernel=’linear’, probability=True)

kernel – Specifică tipul de kernel care trebuie utilizat in algoritm. Acesta trebuie să fie unul ,,liniar,, , ,,poli,,
,,rbf,, , ,,sigmoid,, , ,,precomputed,,. Dacă nu este dat, se va folosi ,,rbf,,.
Probability – este un boolean și este pentru activarea estimării de probabilitate.
SVM -ul pentru mine a dat rezultate bune și astfel am ales să îl utilizez, dar acest lucru nu
înseamnă că este cel mai bun.

Sklearn.model_selection.cross_val_score

Evaluează un scor folosind cross -validation.

Accuracy_result = cross_val_score(mode, trai n_data, train_label, cv=num_of_fold)
Mode este reprezintat de svc_model.
Train_data de image_data
Train_label este target_data.
Cv este numărul de fold -uri.

Sklearn.externals.joblib

Joblib a fost folosit pentru optimizare, fișierul de antrenare ar fi rulat la fiecare input, acest lucru
ar însemna un timp de rulare mare. Rostul joblib- ului este de a salva antrenarea, care trebuie făcută o
singură dată, adică la început și a o încărca pentru predicție.
Joblib.dump(svc_model, save_directory+ ’/svc.pk1’ )
,iar în fișierul de predicție :
Model = joblib.load(model_dir)

2.3.c Server- client

În cadrul acestui proiect recunoașterea se face live, iar camera este montată pe raspberry, acesta
fiind clientul, iar procesarea sau mai bine zis toată aplicația rulează pe server, în cazul de față serverul este
calculatorul personal.
Evident, tot proiectul s -ar fi putut realiza de pe raspberry, dar nu ar fi eficient, deoarece raspberry-
ul nu are putere de procesare mare și nu poate stoca multă informație, astfel s- a ales ca procesarea să se
facă pe server.
Problema care s -a pus a fost că aplicația trebuie să primească un input, acest input este imaginea
pe care trebuie să se facă recunoașterea. Astfel că s-a scris un fișier de client de aici [10], mai exact din
documentația de la picamera.

26
Modulul picamera , oferă beneficiile de a folosi funcții deja scrise pentru camera raspberry -ului,
cum ar fi setarea rezoluției, momentul când să se facă poză etc.
Modulurile care au contribuit la partea de trimitere a imaginilor către server sunt io, socket,
struct, time . Au fost necesare conversii in bytes și de aflare a dimensiunii imaginii ca să fie trimise stream –
uri peste o rețea.
Iar pe partea de server, a mai fost necesar modulul PIL , care se ocupă tot cu prelucrări de imagini,
mai exact s- a importat PIL.image, care practic a convertit șirul de bytes într -o imagine, urmând ca apoi să
fie convertită pentru interpretarea cu scikit -image , adică utilizarea de np. array.

2.3.d CSV

CSV (valori separate prin virgulă ) este un tip special de fișier care se poate crea sau edita folosind
diverse limbaje de programare, Excel etc. În loc să se stocheze informații in coloane, fișierele CSV
stochează informațiile separate prin virgule. Atunci când textul și numerele sunt salvate într -un fișier CSV,
este simplă mutarea de la un program la altul.
În proiectul curent a fost folosit p ython pentru crearea si editarea fișierului CSV. Acest lucru s -a
făcut utilizând modulul denumit csv.
import csv
with open(„dates_file.csv”) as csvfile: # Deschiderea fișierului csv
csvreader = csv.reader(csvfile, delimiter=’,’) # Citirea valorilor din fișierul csv
csvwriter = csv.writer(dates_file, delimiter= ’,’, quotechar=’”’, # Scrierea
quoting=csv.QUOTE_MINIMAL )

Exemplu de scriere in fișierul csv :
csvwriter.writerow([‘Numarul_placutei’, ‘an’, ‘luna’, ‘zi’ , ‘ora’, ‘minut’])

2.3.e Poștă electronic ă

E-mailul sau poșta electronica desemneazză sisteme pentru transmiterea sau primirea de mesaje,
prin Internet.
În cadrul acestui proiect a fost necesară o alertă către user, aceasta a fost implementată folosind e –
mail.
Multipurpose Internet Mail Extensions (MIME) este un standard de Internet care extinde formatul
de e-mail pentru suportul:
– Textului în altele seturi de caractere decât ASCII
– A atașamentelor care nu sunt text : imagini, video e tc.
– A corpurilor de mesaje cu mai multe părți
Etc.

Crearea de istanță obiect mesaj :
from email.mime.multipart import MIMEMultipart
msg = MIMEMultipart()

27

Atașarea mesajului la istanța obiect :
from email.mime.text import MIMEText
msg.attach( MIMEText(message, ‚plain’))
Crearea conexiunii serverului:
import smtplib
server = smtplib.SMTP(smtphost)

28

29
Capitolul 3
Detalii de implementare

3.1 Organizarea codului aplicației

Flow -ul aplicației este cel din desenul următor :

Figura 3.1 Flow -ul aplicaț iei

Pentru o înțelegere mai bună asupra proiectului se vor analiza fișierele in ordinea următoare :

– Recunoașterea plăcuței de înmatriculare
– Comunicarea dintre client si server
– Salvarea rezultatelor în CSV și interpretarea datelor
– Când se face alertarea
– Notificarea prin e -mail

3.2 Recunoașterea plăcuței de înmatriculare

Recunoașterea plăcuțelor de înmatriculare a fost realizată folosind procesarea de imagini și
inteligență artificială. LPR a fost realizată în 3 etape :

30
1. Detectarea plăcuței
Aceasta este etapa în care se află dacă există in imaginea noastră o plăcuță, iar dacă există
se determină poziția acesteia. Această etapă a fost realizată folosind ca input o imagine, urmând ca
apoi să se facă realizarea comunicării dintre raspberry și server.

2. Segmentarea caracterelor
Dacă prima etapă a fost realizată cu succes înseamnă că avem o nouă imagine, aceasta fiind
reprezentată de plăcuța de înmatriculare. Acuma caracterele de pe plăcuță vor fi mapate și
segmentate în imagini individuale.

3. Recunoașterea caracterelor
În această etapă vor fi identificate caracterele segmentate, în această etapă este folosită
învățarea mașină.

Detecția plăcuței

Odată ce a fost instalat modulul scikit -image, se poate începe detecția plăcuței, pentru această etapă
avem ca input o imagine, care poate să aibă sau nu un număr de înmatriculare. Dacă există o plăcuță trebuie să îi determinăm poziția acesteia. Pentru acest lucru trebuie să convertim imaginea originală în 256 nuanțe de gri.
Acum avem o imagine cu 256 nuanțe de gri, iar pe noi ne inter esează plăcuța de înmatriculare, noi
știm o plăcuță de înmatriculare este sub forma unui dreptunghi, de dimensiuni constante, iar fondul este alb, iar literele și cifrele sunt negre, deci trebuie sa binarizăm imaginea, ca să avem doar negru și alb la
valor ile pixelilor.
Citirea imaginii, convertirea în nuanțe de gri și binarizarea acesteia sunt regăsite in fișierul denumit
localization.py.
Citirea imaginii și convertirea in griuri:
car_image = imread(“nume.jpg”, as_grey=True)

Binarizarea a fost făcut ă comparând valoarea fiecărui pixel cu valoarea de prag a imaginii gri, iar
dacă valoarea unui pixel este mai mare această valoare va lua o valoare de Boolean, iar cum binarizarea înseamnă 0 si 1, 0 fiind negru, iar 1 fiind alb se realizează binarizarea imagi nii.
Pentru calcularea valorii de prag a fost ales algoritmul otsu, deoarece din documentația scikit -learn
[11], reiese că este o metodă buna pentru cazurile cu plăcuțe de înmatriculare. Tot în documentație găsim
cum se face binarizarea cu metoda otsu .
threshold_value = threshold_otsu(gray_car_image)
binary_car_image = gray_car_image > threshold_value

31

Figura 3.2.1 Imaginea în nuanțe de gri și binarizarea acesteia

Acum trebuie să identificam plăcuța, ca să realizăm acest lucru trebuie să identificăm toate regiunile
conectate din imagine folosind conceptul de “Connected component analysis” . Practic CCA ne ajută la
gruparea și etichetarea regiunilor conectate din plan. Un pixel este considerat conectat cu altul atunci când
ambii au aceeași valoare și sunt adiacenți între ei.
Folosim funcția measure.label pentru a mapa toate regiunile conectate din imaginea binară și
pentru a le eticheta.
label_image = measure.label(binary_car_image)
Deoarece vom avea multe regiuni care nu conțin plăcuța de înmatriculare trebuie sa facem niște
definiri pentru plăcuța de înmatriculare, știm că are forma dreptunghiulară, este de dimensiuni 520×110
mm și evident ca lățimea este mai mare decât înălțimea. D eci putem să ne folosim de aceste informații ca
să predefinim noi dimensiunea regiunilor care trebuiesc găsite. Eu am ales valorile următoare pentru raportul lungimii și lățimii la imaginea completă :
plate_dimensions = (0.05*label_image.shape[0], 0.3*label_image.shape[0],
0.15*label_image.shape[1], 0.8*label_image.shape[1])
min_height, max_height, min_width, max_width = plate_dimensions
Am extras aceste valori deoarece dorim să le comparăm cu regiunile găsite și astfel să m ai filtrăm
din multitudinea de regiuni, iar dacă corespund cerințelor vom desena un dreptunghi roșu pentru a vizualiza
rezultatele și vom salva coordonatele regiunilor găsite.
rectBorder = patches.Rectangle((min_col, min_row), max_col -min_col, max_row -min_row,
edgecolor="red", linewidth=2, fill=False)
ax1.add_patch(rectBorder)
Noi nu vom reuși să eliminăm toate regiunile nepotrivite, dar vom reuși să mai filtrăm din ele.
Dar ne vom descurca de această problemă la partea de recu noaștere, deoarece vom pune condiții pe

32
numărul de caractere dintr -o plăcuță.
Figura 3.2.2 Detecția plăcuței de înmatriculare
Segmentarea caracterelor
În această etapă vom mapa toate caracterele din plăcuța de înmatricualare, pentru a realiza acest
lucru trebuie să procedăm ca în etapa precedentă, adică să folosim CCA, dacă în etapa trecută am dat
dimensiunile plăcuței raportate la imaginea mare, acuma vom introduce dimensiunea unui caracter raportat
la plăcuța de înmatriculare.
În poza precedentă se p oate observa chenarul roșu, practic acesta este noua noastră imagine și
trebuie să vedem cât ocupă un caracter, raportat la chenar . Varianta aleasă de mine:

character_dimensions = (0.35*license_plate.shape[0], 0.60*license_plate.shape[0],
0.05*license_plate.shape[1], 0.15*license_plate.shape[1])
min_height, max_height, min_width, max_width = character_dimensions

Și vom extrage din nou valorile și vom face comparație cu regiunile identificate de sistem, iar la
final fiecare caracter va fi evidențiat cu un chenar roșu.
Până acum seamănă ca etapa trecută, dar vom mai face un lucru, vom reajusta dimensiunea
caracterelor la 20×20, deoarece la partea de recunoaștere folosim machine learning, iar sistemul a fost
antrenat cu caractere de dimensiuni 20 x20.

Figura 3.2.3 Segmentarea caracterelor

33
Recunoașterea caracterelor

În această etapă vom folosi machine learning, învățarea mașină este împărțită in 2 categorii învățare
supervizată și învățare nesupervizată, în proiectul curent este folosită învățarea supervizată, mai exact
SVM, acest lucru înseamnă că îi punem sistemului la dispoziție un set de antrenare, adică îi spunem cum arată fiecare cifră și fiecare literă. Noi astfel vom antrena sistemul și acesta poate să facă predicție.
Noi în acest moment avem caracterele din poza de mai sus, pe dimensiune 20×20, eu am creat un
folder în cadrul proiectului, în care se află diferite foldere sub forma :
Figura 3.2.3 Folder -ul de antrenament
În fiecare subfolder se află 10 poze de dimensi uni 20×20 în care este prezentă litera sau cifra
respectivă.
În fișierul de antrenare, numit machine_train.py vom introduce de la tastatură fiecare literă, vom citi pentru
fiecare caracter, cele 10 imagini din subfolderul respectiv, vom face și cross vali dation ca să măsurăm acuratețea
modelului, iar final antrenarea sistemului folosind SVM.
cross_validation(svc_model, 4, image_data, target_data)
Acel 4 reprezintă faptul că facem 40fold cross validation, iar acesta va divide setul de date in 4 și
vom folosi ¼ din acesta pentru testare și restul de ¾ pentru antrenare.
Figura 3.2.4 Cross Validation Result
Se observă un rezultat foarte bun, acest fișier trebuie rulat o singură dată, deoarece nu este nevoie
să facem antrenări de mai multe ori, pentru că vom avea un timp de așteptare mare.
Acum că am antrenat modelul, mai trebuie doar să facem predicția caracterelor pe care le -am
segmentat la etapa 2.
În fișierul prediction.py are loc predicția, aici ne vom folosi de modelul antrenat și fișierul
segmentat, vom parcurge fiecare regiune din plăcuță, adică fiecare caracter, iar la final vom pune o condiție pe lungimea listei unde se regăsesc caracterele, adică dacă au fost recunoscute peste 6 si sub 8 caractere, vom printa acest rezultat.
În acest moment am obținut rezultatul final de recunoaștere :

Figura 3.2.5 Rezultatul recunoaș terii

34
3.3 Comunicarea dintre client și server

În cazul acestui proiect clientul este raspberryul, iar serverul este computerul personal, care are o
putere mult mai mare.
Tot proiectul s -ar fi putut realiza pe raspberry, dar ar fi fost mult pentru puterile sale, astfel am decis
ca raspberryul să facă poze si să le trimită prin internet, deschizând o conexiune cu serverul și astfel să
trimită stream -uri de date, care odată a junșe la destinație să fie refăcu te din bytes într -o imagine.
Avem două scripturi: un server care ascultă o conexiune de la Raspberry Pi și un client care rulează
pe Raspberry Pi și trimite un flux continuu de imagini către server. Vom folosi un protocol foarte simplu
pentru comunicare: mai întâi lungimea imaginii va fi trimisă ca un număr întreg pe 32 de biți, apoi va fi
urmată de octeții de date ale imaginii. Dacă lungimea este 0, aceasta indică faptul că conexiunea ar trebui
să fie închisă, deoarece nu vor mai apărea imagini. Acest protocol este ilustrat mai jos:

Figura 3.3.1 Protocolul de trimitere a pozelor [12]

Scriptul client rulează pe raspberry , acesta conectează clientul socket la Numele_serverului:8000.
client_socket = socket.socket()
client_socket.connect(('my_server', 8000))
Creem un fișier obiect din conexiune, accesăm camera PI îi setăm rezolutia și setăm timpul de pauză
dintre capturi. S e reține momentul de pornire și se construiește un stream pentru reținerea temporară a
datelor imaginii, aș putea să scriu direct la conexiune, dar în acest caz doresc să aflu mai întâi dimensiunea
fiecărei capturi pentru a păstra protocolul simplu.
Vom s crie lungimea capturei în stream și o trimitem, folosind funcția flush pentru a ne asigura că
se va trimite, iar trimiterea se face folosind connection.wire. Am pus și un timp de rulare pentru închiderea
programului ca să nu ruleze la infinit, evident ace astă valoare este doar pentru testare. Vom face reset la
stream pentru următoare captură folosind stream.seek(0) și stream.trunctate() . Urmează închiderea
conexiunii scriind valoarea 0 pentru alertarea de terminare.

start = time.time()
stream = io.BytesIO()
for foo in camera.capture_continuous(stream, 'jpeg'):
connection.write(struct.pack('<L', stream.tell()))
connection.flush()
stream.seek(0)
connect ion.write(stream.read())
if time.time() – start > 50:
break
stream.seek(0)
stream.truncate()
connection.write(struct.pack('<L', 0))

Scriptul server depinde de modulul Pillow pentru citirea imaginilor. Se pornește o ascultare de
socket pentru conexiunile pe 0.0.0.0:8000, adică pentru toate interfețele:
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 8000))

35
server_socket.listen(0)
Se acceptă conexiunea și se citește lungimea imaginii ca 32 -bit unsigned int, iar dacă lungimea este
0 se va închide loop- ul în care rulează conexiunea.
Construiesc o variabila care să țină datele imaginii image_stream = io.BytesIO() și citesc datele
imaginii din conexiune folosind image_stream.write(connection.read(image_len)) . Imaginea am
deschis -o folosind modulul Pillow , urm ând să o transform sub formă de array, ca să o pot interpreta ca
imagine.

image = Image.open(image_st ream)
img = np.array(image)
Am modificat fișierul localizatio.py, cel în care încarc imagina și fac binarizarea, am introdus tot
conținutul într- o funcție, care este apelată la sfârșitului scriptului de server :
localization(img )
Pentru rularea acestui proiect, mai întâi se va rula scriptul server, care practic va alimentat modulul
de recunoaștere creat mai devreme, iar după se rulează scriptul client, cel de pe raspberry, care face capturi și le trimite către server. Din acest punct, am început să editez fișierele de recunoaștere, incluzând
conținutul pentru fiecare in parte într -o funcție, motivul a fost acela de scriere mai curată și evidențiere a
flow-ului programului.

3.4 Salvarea rezultatelor in CSV și interpretarea datel or

Până în acest moment am realizat partea de recunoaștere și partea de client -server, deci acum
trebuie ca fiecare rezultat al recunoașterii să fie salvat, împreună cu momentul apariției. Avem nevoie și
de momentul apariției, deoarece pe baza acestei valori vom face alertarea.
În fișierul de predicție după ce am aflat rezultatul, adică string -ul cu numărul plăcuței de
înmatriculare voi deschide un fișier CSV și voi salva plăcuța de înmatriculare și momentul de timp la care a fost vazută :
with open('dates_file.csv', mode='a') as dates_file:
time_writer = csv.writer(dates_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
time_writer.writerow([rightplate_string, nr_sec_acum.acum()])
Func ția acum() din fișierul de nr_sec_acum returnează un float care reprezintă cât a trecut de la
1 Jan 1970, până în secunda respectivă :
def acum():
dt = datetime.datetime.now()
print type(time.mktime(dt.timetuple())) return time.mktime(dt.timetuple())

3.5 Când se face alertarea

Odată ce avem plăcuța de înmatriculare si momentul apariției sale, putem începe alertarea, eu m –
am gândit ca alertarea să se facă dacă au trecut 15 minute de când un număr a fost salvat î n CSV, iar în
acel moment ar trebui ca șoferul să acționeze.
Partea de alertare conține regăsirea acelei plăcuțe în csv și face diferență între timpi , iar dacă au
trecut 15 minute se va trimite un e -mail în care se anunță că proprietarul este urmărit și i se va mai arăta
dacă acel număr a mai apărut și înainte.

36

Figura 3.5 Notificare e -mail
3.6 Notificarea prin e -mail
După cum am spus notificarea se va face prin a face o diferență de timpi, iar aceste valori sunt in
csv, iar pentru acest lucru trebuie să citesc valorile din csv, astfel am făcut o funcție in fișierul readCsv.py
numită findPlateInDb(plate), unde se citesc liniile din Csv și se caută dacă plăcuț a care trebuie adăugată in
csv este deja prezentă, iar dacă da acest string se va trimite către funcția shouldAlert.
def findPlateInDb(plate):
with open('dates_file.csv') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
… // pa rtea de verificare dacă numărul era în csv
Funcția de alertare este cea care este apelată în fișierul prediction.py, ea primind ca parametru
numărul plăcuței de înmatriculare care a fost adăugat în csv (recunoscută de programul nostru). Rezultatul funcție i findPlateInDb este salvat într- o listă , aceste valori sunt sortate, iar după se verifică dacă timpul
acestora este mai mare decât 15 minute:
def shouldAlert(plate):
foundPlates = findPlateInDb(plate)
foundPlates.sort()
print foundPlates
acum = nr_sec_acum.acum()
for placuta in foundPlates:
if(plate == placuta.plateNumber and acum – float(placuta.date) > 900000) :
… // partea de trimitere a e -mail-ului
Pentru partea de trimitere e -mail, a fost folosită bibliote ca smtplib și mime, pentru înțelegere mai
ușoară am scris sub formă de pași. # Pas 1 – Crearea instanței denumita mesaj
msg = MIMEMultipart()

# Pas 2 – Crearea body -ului, iar funcția afișare_timp conține o prelucrare a unei valori mari în
milisecunde, sub format de săptămâni, zile, ore, minute secunde.

37
message = 'Esti urmarit! \n Masina: ' + plate + ' a fost vazuta si acum ' +
afisare_timp.display_time(int(acum – float(placuta.date)))

# Pas 3 – Declarare credențialelor SMTP
password = "parolalpr"
username = "test.licentav1@gmail.com" smtphost = "smtp.gmail.com:587"
# Pas 4 – Declararea elementelor mesajului
msg['From'] = "test.licentav1@g mail.com"
msg['To'] = "teo.dumitru96@yahoo.com"
msg['Subject'] = plate
# Pas 5 – Adăgarea body -ului la instanță
msg.attach(MIMEText(message, 'plain'))
# Pas 6 – Crearea conexiunii serverului
server = smtplib.SMTP(smtphost)
# Pas 7 – Transmites peste TLS ca să criptăm
server.starttls()
# Pas 8 – Autentificarea cu serverul
server.login(username, password)

# Pas 9 – Trimiterea e -mail- ului
server.sendmail(msg['From'], msg['To'], msg.as_string())
# Pas 10 – Deconectare
server.quit()
# Pas 11 –
print "Successfully sent email message to %s:" % (msg['To'])
Acum proiectul este terminat și se verifică rezult atul:

Figura 3.6 Formatul email -ului

38

39
Capitolul 4
Concluzii

4.1 Dezvoltarea aplicației
În acest subcapitol o să discut despre cum se poate crea o aplicație, după urma căreia se poate
beneficia, pentru că totul a pornit de la ideea de a face o recunoaștere a caracterelor, mai precis litere și
cifre, am ales această idee deoarece la facultate am făcut recunoaștere de forme folosind inteligență
artificială, așa că m -am decis să folosesc o temă de computer vision.
M-am gândit cum pot dezvolta această idee și am ajuns la plăcuțele de înmatriculare, evident sunt
foarte multe variante, recunoașterea caracterelor este folosită în multe alte locuri, dar eu am ales această
variantă deoarece voiam sa îl leg cumva cu raspberry Pi. După m-am gândit care ar fi rostul să fac
recunoașterea plăcuțelor și am ajuns la ideea de a alerta șoferii, în cazul în care aceștia sunt urmăriți, mi s-a părut o idee foarte buna, deoarece în ultimul timp s -a dezvoltat mult partea de siguranță a șoferilor, iar
proiectul meu se poate implementa pe camera care este deja existentă, cea de marșarier.
Am început prin a face recunoașterea plăcuțelor de înmatriculare primind ca input imagini și am
decis să folosesc învățarea mașină, mai exact SVM, eu mai implementasem K -NN și știam că acest
algoritm nu ar fi atât de bun pentru ceea ce am eu nevoie, deoarece timpul de rulare este cam mare.
Am făcut recunoașterea și am decis ca pasul următor să fie cel de client -server și am căutat metode
de extragere cadre din live și a le da spre procesare, dar am ales să se facă poze la un interval de timp
anume, după ce am realizat acest pas am trecut mai departe, adică la salvarea rezultatelor într -un csv, ca
mai apoi să le verific și a da spre alertare numerele de înmatriculare ca re au mai apărut în fișierul nostru
csv.

4.2 Ce am învățat

Din acest proiect cel mai important lucru pe care l- am învățat este cum să poți începe un start -up
sau mai bine zis, cum să ajungi de la o simplă idee la o aplicație care să aducă profit.
Am dobândit în facultate cunoștințe de prelucrări de imagini, inteligență artificială, baze de date,
programare și am decis să îmbin ceea ce știu plus altele și să re alizez un proiect mai stufos.
Al 2-lea cel mai important lucru învățat este practica, faptul că am făcut o aplicație completă, am
învățat să folosesc un algoritm de clasificare pentru recunoașterea caracterelor, să lucrez cu raspberry pi, să fac o conexiune client -server, să manevrez fișier csv și trimiterea unei alerte. Pe l ângă acestea, am învățat
si cum să trimit sms, doar ca erau probleme cu anumite rețele de telefonie din țară și astfel nu toți utilizatorii
ar fi putut beneficia.
Al alt lucru învățat este aprofundarea celui mai important skill al unui programator, căutare a pe
internet, unul dintre cele mai importante skill -uri pe care trebuie să le aibă un programator este cătarea pe
internet, nu le poți știi pe toate și nici nu trebuie, trebuie învățat ce să cauți și cum să cauți, foarte multe informații sunt date in documentațiile online de la fiecare modul python, de acolo am învățat cum se folosesc funcțiile respective și ce fac.
De asemenea, am învățat că schimbarea pe parcurs este un lucru bun, inițial am dorit alterare prin
notificare de aplicație dedicată, dar am renunțat la acest lucru, am dorit alertare pe telefon, dar au fost
probleme cu serviciile de telefonie mobilă. Mai apar idei pe parcurs și de multe ori se poate întâmpla ca
schimbarea să fie bun ă, nu poți vedea de la bun început cum vei ajunge la rezultatul final.

40
4.3 Probleme întâmpinate

Pe parcursul acestui proiect am întâmpinat destule probleme, dar cu o abordare diferită și debugging
am reușit să finalizez proiectul. În partea de recunoaștere nu am întâmpinat probleme, doar la valoarea de
prag, când am testa t proiectul cu camera raspberry pi, am pus telefonul cu o imagine a unei plăcuțe de
înmatriculare și nu recunoștea, dar după am ajutat pragul și a mers, problema era ca imaginea de pe telefon
nu este interpretată la fel de bine ca o imagine normală.
La par tea de conexiune client -server, am avut dificultăți cu modul în care camera făcea poze, ea
face poze la un interval constant de timp, iar această metodă nu e cea mai bună.
Am avut erori la importări, la un moment dat aveam un cycle flow și când am făcut de bugging, am
văzut că rămânea blocat într -un fișier, așa că am scris toate programele în funcții și le- am apelat în ordinea
flow-ului și problema a fost remediată.
Inițial voiam să fac alertare printr- o aplicație dedicată android, dar asta necesita anumite servicii și
erau mai multe aspecte de luat în considerare, cum ar fi aplicația rulează in background sau nu, conexiunea
se face wireless sau bluetooth, la momentul respectiv părea mai mult ca și cum mă complicam, așa că am
decis să fac cu alertare prin sms, dar alertarea nu mergea pe toate numerele de telefon, așa că am ales ca variantă finală alertarea prin e -mail.

4.4 Idei de îmbunătățire

O problemă sau un task poate fi rezolvat prin mai multe metode, nu există o metodă unică, uneori
prima variantă de rezolvare a unui task este cea mai eficientă, dar nu mereu, mai ales cu cât task -ul este
unul mai amplu. Ca proiectul de față, acesta cuprinde mai multe fișiere și sunt sute de linii de cod, pe parcurs mi -au venit idei mai bune, dar asta însemna să schimb iar din gândire sau să ridic nivelul de
dificultate și cu cât ridic nivelul cu atât se întârzie mai mult data de finalizare a aplicației.
Firmele când lansează un produs, îl doresc cât mai repede pe piață, cum este și la telefoane, apar
mereu unele mai bune și toți trebuie să țină pasul, ei nu trebuie să întârzie cu produsul, iar uneori se întâmplă să nu fie testate destul și apar probleme, mai exact este o balanță care trebuie să stea în echilibru, eu am ales să implementez o metodă bună și rapidă, ca mai apoi dacă doresc să dezvolt în continuare
această idee să o eficientizez.
În acest proiect, camera raspberry este setată să facă poze la un intervalul constant de secunde,
această metodă poate fi îmbunătățită dacă fac o recunoaștere de obiecte, mai precis camera să recunoască
mașini, și când recunoaște o mașină în acel moment să facă captură și să o trimită la server.
O altă idee este folosirea unei baze de date, nu a unui csv, deoarece o bază de date este securizată
și toate aplicațiile folosesc baze de da te, nu fișiere csv.
Altă idee este paralelizarea programului.
Partea de alertare poate fi îmbunătățită, utilizatorul să primească mai multe informații legate de
mașina care îl urmărește, o evidență a tuturor aparițiilor.
Antrenarea pentru învățarea mașină, aceasta poate fi îmbunătățită, adică antrenată cu mai multe
modele de litere și cifre.

41

Bibliografie

https://en.wikipedia.org/wiki/Computer_vision [1
]
Sean McManus and Mike Cook. Raspberry Pi for Dummies 3rd edition , 2017 [2]
Richard L. Halterman . Fundamentals of Python programming, August, 2019 [3]
Rafael C. Gonzalez, Richard E. Woods. Digital Image Processing 4
th edition, Pearson March, 2017
[4]

https://scikit –
image.org/docs/stable/auto_examples/applications/plot_thresholding.html#sphx -glr-
auto -examples -applications -plot-thresholding -py [5]

G. X.Ritter, J.N. Wilson, Handbook of computer vision algorithms in image algebra – 2nd ed, 2001
CRC Press: Cap. 4.7. Threshold Selection by Maximizing Between- Class Variance. [6]

Judith Hurwitz, Daniel Kirsch, Machine Learning for dummies IBM limited edition [7]

https://www.stiintaonline.ro/inteligenta- artificiala- ce-este-si-cum -functioneaza/ [8]

Victor Neagoe, Rețele neurale pentru explorarea datelor, Matrix Rom Bucur ești 2018[9]
https://picamera.readthedocs.io/en/release -1.9/recipes1.html#capturing -to-a-network –
stream [10]

https://scikit –
image.org/docs/stable/auto_examples/segmentation/plot_thresholding.html#sphx -glr-
auto -examples -segmentation -plot-thresholding -py [11]
https://picamera.readthedocs.io/en/release -1.9/_images/image_protocol.svg [12
]

ANEXE
# clientv2.py
import ioimport socketimport structimport timeimport picamera
# Connect a client socket to my_server:8000 (change my_server to the
# hostname of your server)client_socket = socket.socket()client_socket.connect(('DESKTOP-6BSGTE3', 8000))
# Make a file-like object out of the connection
connection = client_socket.makefile('wb')try: with picamera.PiCamera() as camera: camera.resolution = (1800, 1800) # Start a preview and let the camera warm up for 2 seconds #camera.start_preview() time.sleep(3)
# Note the start time and construct a stream to hold image data
# temporarily (we could write it directly to connection but in this # case we want to find out the size of each capture first to keep # our protocol simple) start = time.time() stream = io.BytesIO() for foo in camera.capture_continuous(stream, 'jpeg'): # Write the length of the capture to the stream and flush to # ensure it actually gets sent connection.write(struct.pack('<L', stream.tell())) connection.flush() # Rewind the stream and send the image data over the wire stream.seek(0) connection.write(stream.read()) time.sleep(5) # pauza dintre poze # If we've been capturing for more than 30 seconds, quit if time.time() – start > 50: break # Reset the stream for the next capture stream.seek(0) stream.truncate() # Write a length of zero to the stream to signal we're done connection.write(struct.pack('<L', 0))finally: connection.close() client_socket.close()

#server.py
import localizationimport os # operating systemfrom sklearn.externals import joblibimport ioimport socketimport os # operating systemimport structimport numpy as npimport timefrom PIL import Imagefrom sklearn.externals import joblibfrom skimage.color import rgb2grayfrom localization import localizationimport matplotlib.pyplot as pltimport scipy.misc
# Start a socket listening for connections on 0.0.0.0:8000 (0.0.0.0 means
# all interfaces)server_socket = socket.socket()#server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server_socket.bind(('0.0.0.0', 8000))
server_socket.listen(0)
# Accept a single connection and make a file-like object out of it
connection = server_socket.accept()[0].makefile('rb')#print rulare()#rulare()try: while True: # Read the length of the image as a 32-bit unsigned int. If the # length is zero, quit the loop image_len = struct.unpack('<L', connection.read(struct.calcsize('<L')))[0] if not image_len: break # Construct a stream to hold the image data and read the image # data from the connection image_stream = io.BytesIO() image_stream.write(connection.read(image_len)) # Rewind the stream, open it as an image with PIL and do some # processing on it image_stream.seek(0) print 'Image stream is {}'.format(image_stream) image = Image.open(image_stream) img = np.array(image) # acesta e jpg-ul #gray_img = rgb2gray(img)
print('Image has %dx%d pixels in size' % image.size)
image.verify() print('Image is verified') localization(img)

#print(image)
#print(img) #print(rgb2gray(img))
#grayscale = rgb2gray(img) # de bagat imread aci @@@@@@@@@@@@@@@
#scipy.misc.toimage(img, cmin=0.0, cmax=255.0).save('img{counter:03d}.jpg')finally: connection.close() server_socket.close()

#localization.py
from skimage.io import imreadfrom skimage.filters import threshold_otsuimport matplotlib.pyplot as pltimport cca2import server from skimage.color import rgb2gray
#car_image = imread("nume.jpg", as_grey=True) # @@@
def localization(car_image):
# it should be a 2 dimensional array
car_image = img # img e imaginea primita car_image = rgb2gray(car_image) #print(car_image)
print(car_image.shape) # the next line is not compulsory however, a grey scale pixel
# in skimage ranges between 0 & 1. multiplying it with 255 # will make it range between 0 & 255 (something we can relate better with
gray_car_image = car_image * 255
fig, (ax1, ax2) = plt.subplots(1, 2) ax1.imshow(gray_car_image, cmap="gray") threshold_value = threshold_otsu(gray_car_image) binary_car_image = gray_car_image > threshold_value #+ 30 print 'The threshold value for grayscale is {}'.format(threshold_value) ax2.imshow(binary_car_image, cmap="gray") plt.show() cca2.f_cca2(gray_car_image, binary_car_image)
localization(car_image)

#cca2.py
from skimage import measurefrom skimage.measure import regionpropsimport matplotlib.pyplot as pltimport matplotlib.patches as patchesimport segmentation
def f_cca2(gray_car_image, binary_car_image):
#localization.local(localization.car_image) # this gets all the connected regions and groups them together label_image = measure.label(binary_car_image)
# getting the maximum width, height and minimum width and height that a license plate can be
plate_dimensions = (0.05*label_image.shape[0], 0.3*label_image.shape[0], 0.15*label_image.shape[1], 0.8*label_image.shape[1]) # asta este pt dimensiunea placute raportata la marimea imaginii min_height, max_height, min_width, max_width = plate_dimensions plate_objects_cordinates = [] plate_like_objects = [] fig, (ax1) = plt.subplots(1) ax1.imshow(gray_car_image, cmap="gray")
# regionprops creates a list of properties of all the labelled regions
for region in regionprops(label_image): if region.area < 50: #if the region is so small then it's likely not a license plate continue
# the bounding box coordinates
min_row, min_col, max_row, max_col = region.bbox region_height = max_row – min_row region_width = max_col – min_col # ensuring that the region identified satisfies the condition of a typical license plate if region_height >= min_height and region_height <= max_height and region_width >= min_width and region_width <= max_width and region_width > region_height: plate_like_objects.append(binary_car_image[min_row:max_row,
min_col:max_col])
plate_objects_cordinates.append((min_row, min_col,
max_row, max_col))
rectBorder = patches.Rectangle((min_col, min_row), max_col-min_col, max_row-min_row, edgecolor="red", linewidth=2, fill=False) ax1.add_patch(rectBorder) # let's draw a red rectangle over those regions plt.show() segmentation.f_segm(plate_like_objects)

#segmentation.py
import numpy as np
from skimage.transform import resize
from skimage import measure
from skimage.measure import regionprops
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import prediction
def f_segm(plate_like_objects):
# on the image I'm using, the headlamps were categorized as a license plate # because their shapes were similar # for now I'll just use the plate_like_objects[2] since I know that's the # license plate. We'll fix this later
# The invert was done so as to convert the black pixel to white pixel and vice versa license_plates = [] for i in range(len(plate_like_objects)):
column_list = [] ok = False license_plate = np.invert(plate_like_objects[i]) # [0] -> adica primul cadran , // se pot depista mai multe drept. labelled_plate = measure.label(license_plate)
fig, ax1 = plt.subplots(1)
ax1.imshow(license_plate, cmap="gray")
# the next two lines is based on the assumptions that the width of
# a license plate should be between 3% and 30 % of the license plate,
# and height should be between 55% and 99%
# this will eliminate some
character_dimensions = (0.55*license_plate.shape[0], 0.99*license_plate.shape[0], 0.03*license_plate.shape[1],
0.3*license_plate.shape[1])
min_height, max_height, min_width, max_width = character_dimensions
characters = []
counter=0 for regions in regionprops(labelled_plate): y0, x0, y1, x1 = regions.bbox region_height = y1 – y0 region_width = x1 – x0
if region_height > min_height and region_height < max_height and region_width > min_width and region_width
< max_width: roi = license_plate[y0:y1, x0:x1]
# draw a red bordered rectangle over the character.
rect_border = patches.Rectangle((x0, y0), x1 – x0, y1 – y0, edgecolor="red",
linewidth=2, fill=False)
ax1.add_patch(rect_border)
# resize the characters to 20X20 and then append each character into the characters list
resized_char = resize(roi, (20, 20)) characters.append(resized_char)

# this is just to keep track of the arrangement of the characters
column_list.append(x0) #if len(column_list) >= 6:
# print len(column_list)# ok = True
license_plates.append(characters) prediction.f_predict(license_plates, column_list) #if len(column_list)>6: # in column_list sunt pastrate nr de drept rosii in jurul caracterelor, adica 6 dreptuhiuri mici plt.show() #DE COMENTAT CA SA NU DESCHIDA PE TOATE

#machine_train.py
import osimport numpy as npfrom sklearn.svm import SVCfrom sklearn.model_selection import cross_val_scorefrom sklearn.externals import joblibfrom skimage.io import imreadfrom skimage.filters import threshold_otsu
letters = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]
def read_training_data(training_directory):
image_data = [] target_data = [] for each_letter in letters: for each in range(10): image_path = os.path.join(training_directory, each_letter, each_letter + '_' + str(each) + '.jpg') # read each image of each character img_details = imread(image_path, as_grey=True) # converts each character image to binary image binary_image = img_details < threshold_otsu(img_details) # the 2D array of each image is flattened because the machine learning # classifier requires that each sample is a 1D array # therefore the 20*20 image becomes 1*400 # in machine learning terms that's 400 features with each pixel # representing a feature flat_bin_image = binary_image.reshape(-1) image_data.append(flat_bin_image) target_data.append(each_letter)
return (np.array(image_data), np.array(target_data))
def cross_validation(model, num_of_fold, train_data, train_label):
# this uses the concept of cross validation to measure the accuracy # of a model, the num_of_fold determines the type of validation # e.g if num_of_fold is 4, then we are performing a 4-fold cross validation # it will divide the dataset into 4 and use 1/4 of it for testing # and the remaining 3/4 for the training accuracy_result = cross_val_score(model, train_data, train_label,
cv=num_of_fold)
print("Cross Validation Result for ", str(num_of_fold), " -fold")
print(accuracy_result * 100)
current_dir = os.path.dirname(os.path.realpath(__file__))

training_dataset_dir = os.path.join(current_dir, 'train') # in folderul train se afla literele de test.
image_data, target_data = read_training_data(training_dataset_dir)
# the kernel can be 'linear', 'poly' or 'rbf'
# the probability was set to True so as to show# how sure the model is of it's predictionsvc_model = SVC(kernel='linear', probability=True)
cross_validation(svc_model, 4, image_data, target_data)# let's train the model with all the input data
svc_model.fit(image_data, target_data)
# we will use the joblib module to persist the model
# into files. This means that the next time we need to# predict, we don't need to train the model againsave_directory = os.path.join(current_dir, 'models/svc/')if not os.path.exists(save_directory): os.makedirs(save_directory)joblib.dump(svc_model, save_directory+'/svc.pkl')

#prediction.py
from sklearn.externals import joblibfrom datetime import datetimeimport csvimport os # operating systemimport readCsvimport nr_sec_acum
def f_predict(license_plates, column_list):
# load the model
current_dir = os.path.dirname(os.path.realpath(__file__)) model_dir = os.path.join(current_dir, 'models/svc/svc.pkl') model = joblib.load(model_dir)
if True: # segmentation.ok:
classification_result = [] for license_plate in license_plates: for each_character in license_plate: # converts it to a 1D array each_character = each_character.reshape(1, -1); result = model.predict(each_character) classification_result.append(result)
if len(classification_result) == 0:
continue print "classification result: " print(classification_result)
plate_string = ''
for eachPredict in classification_result: plate_string += eachPredict[0]
print "plate string result: // not in order "
print(plate_string) # NR DE INMATRICULARE NEARANJAT
# it's possible the characters are wrongly arranged
# since that's a possibility, the column_list will be # used to sort the letters in the right order
column_list_copy = column_list[:]
column_list.sort() rightplate_string = ''
for each in column_list:
rightplate_string += plate_string[column_list_copy.index(each)]
print len(rightplate_string)
if len(rightplate_string) >= 6 and len(rightplate_string) <= 8:
print "plate string result: "

print(rightplate_string) # NR DE INMATRICULARE BUN
#moment = datetime.now()
with open('dates_file.csv', mode='a') as dates_file:
time_writer = csv.writer(dates_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)time_writer.writerow([rightplate_string, nr_sec_acum.acum()])#time_writer.writerow(['Numarul placutei', 'an', 'luna', 'zi', 'ora', 'minut'])
#moment.utcfromtimestamp()
#time_writer.writerow([rightplate_string, moment.year, moment.month, moment.day, moment.hour,# moment.minute])
readCsv.shouldAlert(rightplate_string) # Len = 0, trebe modificari

import csv
import nr_sec_acumimport afisare_timpfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMETextimport smtplib
class PlateRegistration:
plateNumber = "" date = 0
date= 0#mai e placuta asta in baza de date? Daca da, la ce minute a fost gasita?
def findPlateInDb(plate): with open('dates_file.csv') as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') line_count = 0
platesFound = [] for row in csv_reader:
if line_count == 0: line_count += 1 else: if row[0]:
if row[0] == plate:
new_plate = PlateRegistration()new_plate.plateNumber = row[0]new_plate.date = row[1]platesFound.append(new_plate)
# print(f'\t{row[0]} works in the {row[1]} department, and was born in {row[2]}.')
line_count += 1 print platesFound #print type(platesFound[0]) # instanta return platesFound
# trebuie sa arat alerta sau nu?
def shouldAlert(plate): foundPlates = findPlateInDb(plate)
foundPlates.sort() print foundPlates acum = nr_sec_acum.acum() for placuta in foundPlates:
if(plate == placuta.plateNumber and acum – float(placuta.date) > 900000) : # Step 2 – Create message object instance

msg = MIMEMultipart()
# Step 3 – Create message body
message = 'Esti urmarit!\n Masina: ' + plate + ' a fost vazuta si acum ' + afisare_timp.display_time(int(acum –
float(placuta.date)))
# Step 4 – Declare SMTP credentials
password = "parolalpr" username = "test.licentav1@gmail.com" smtphost = "smtp.gmail.com:587"
# Step 5 – Declare message elements
msg['From'] = "test.licentav1@gmail.com" msg['To'] = "teo.dumitru96@yahoo.com" msg['Subject'] = plate
# Step 6 – Add the message body to the object instance
msg.attach(MIMEText(message, 'plain'))
# Step 7 – Create the server connection
server = smtplib.SMTP(smtphost)
# Step 8 – Switch the connection over to TLS encryption
server.starttls()
# Step 9 – Authenticate with the server
server.login(username, password)
# Step 10 – Send the message
server.sendmail(msg['From'], msg['To'], msg.as_string())
# Step 11 – Disconnect
server.quit()
# Step 12 –
print "Successfully sent email message to %s:" % (msg['To'])
#print 'Esti urmarit de masina: ' + plate + ' de ' + afisare_timp.display_time(int(acum – float(placuta.date)))
#print acum #print float(placuta.date) return 'Esti urmarit de masina: ' + plate + ' de ' + afisare_timp.display_time(int(acum – float(placuta.date)))

#nr_sec_acum.py
import datetimeimport time
def acum():
dt = datetime.datetime.now() print type(time.mktime(dt.timetuple())) return time.mktime(dt.timetuple())

#afisare_timp.py
intervals = ( ('saptamani', 604800), # 60 * 60 * 24 * 7 ('zile', 86400), # 60 * 60 * 24 ('ore', 3600), # 60 * 60 ('minute', 60), ('secunde', 1), )
def display_time(seconds, granularity=2):
result = []
for name, count in intervals:
value = seconds // count if value: seconds -= value * count if value == 1: name = name.rstrip('s') result.append("{} {}".format(value, name)) print ', '.join(result[:granularity]) return ', '.join(result[:granularity])

Similar Posts