Matteo Dalponte Anno scolastico: 2014/2015 Sistema di riconoscimento targhe Sviluppo di un sistema di riconoscimento targhe e gestione degli accessi per una zona a traffico limitato o un parcheggio protetto da barriera automatica Matteo Dalponte Tesina di maturità Anno Scolastico 2014-2015 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Indice La mia passione per l'automazione ..............................................................................3 Il sistema: Caratteristiche principali.............................................................................5 1.1 Funzionamento Generale ........................................................................................................ 5 1.2 ANPR Specifiche tecniche ........................................................................................................ 6 Hardware e il software libero ......................................................................................7 2.1 Caratteristiche Hardware: ....................................................................................................... 7 2.2 Il software progettato: ............................................................................................................ 8 2.3 Cos'è Linux? ........................................................................................................................... 10 2.4 Il Software Libero: ................................................................................................................. 10 Approfondimento .......................................................................................................12 3.1 IP camera e protocollo MJPEG .............................................................................................. 12 3.2 Python ................................................................................................................................... 13 3.3 Gestione dati OCR ................................................................................................................. 14 Il codice sorgente .......................................................................................................16 4.1 Lettura OCR e transizione dati ............................................................................................... 16 4.2 GUI Visualizzazione streaming ............................................................................................... 22 Sitografia .....................................................................................................................29 2 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 La mia passione per l'automazione L'automazione, il controllo remoto, la gestione delle immagini e la comunicazione dei dati attraverso l'etere sono argomenti che da sempre mi affascinano; così tre anni fa ho deciso di seguire un corso extrascolastico di programmazione Arduino tenuto nei laboratori di fisica della Scuola, con l'obiettivo di imparare ad utilizzare la board Arduino. Il corso, che ho seguito per tutto l'anno, mi ha permesso di entrare nella logica della programmazione, di imparare le basi del linguaggio C e di confrontarmi con tante altre persone che condividono la stessa passione. Alla fine della terza, grazie alle competenze acquisite durante il corso, ho sviluppato e venduto due sistemi automatici di cui uno per la gestione dell'energia prodotta da una piccola centrale idroelettrica (v. foto); il sistema costruito è incaricato di misurare i parametri di corrente, potenza e tensione in ingresso al pacco batterie mantenute in carica dal generatore della turbina, visualizzarli sul un display e fungere da regolatore di carica per le batterie. Nel caso in cui la tensione superi un certo valore preimpostato, per esempio di notte quando la l'energia non viene completamente utilizzata, viene attivato un boiler che ha la duplice funzione di riportare la tensione delle batterie nel range di carica e allo stesso tempo scaldare acqua sanitaria; in questo modo tutta l'energia prodotta dall'impianto viene sfruttata a pieno. Il secondo sistema sviluppato è stato progettato per integrare sul quadro strumenti di una moto ulteriori informazioni quali il numero di giri del motore e lo stato di carica della batteria di avviamento. Per la lettura del numero di giri, senza integrare ulteriore sensoristica, ho scelto di interfacciare l'apparecchio con il sistema di accensione (che pilota il l'arco elettrico della candela), naturalmente già presente sul veicolo. Durante l'estate, poi, è nato un nuovo progetto, il cui obiettivo era quello di controllare un modellino di macchina da un computer e visualizzarne il video in diretta per il controllo a distanza. Tale progetto mi ha portato alla scoperta di nuove schede (Raspberry Pi per esempio), differenti da Arduino, e programmabili con nuovi linguaggi. Così, da autodidatta, ho imparato le basi della programmazione in Python, il funzionamento del sistema operativo GNU/Linux e i sistemi di gestione e trasmissione dati via etere (Mikrotik). Le potenzialità del controllo a distanza sono state apprezzate da un modellista che ha intravisto la possibilità di controllare droni a distanza e di visualizzarne il video in diretta ed in alta definizione. 3 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Così, assieme ad Andrea abbiamo iniziato un'avventura nel modellismo tradizionale, imparando a guidare quadricotteri e parallelamente creando modelli intelligenti in grado di "guidarsi da soli". Il progetto ci ha portato alla scoperta di tecnologie sempre più evolute e per ora siamo riusciti a creare un modello di esacottero per riprese aeree in grado di pilotarsi completamente in modo autonomo, seguendo solamente una mappa disegnata dalla stazione a terra. Per le prossime migliorie è in progetto lo studio di un sistema più evoluto di gestione del video per il controllo a distanza e la realizzazione di modelli tridimensionali di edifici e terreni filmati. Verso la metà di questo anno scolastico ci è stato proposto di prendere parte al progetto Tu Sei, in collaborazione con Confindustria, così è nata l'idea di realizzare la tesina sull'automazione di un cancello tramite un sistema di riconoscimento targhe. Il progetto in fase di sviluppo è stato presentato alla ditta Algorab di Lavis, ma il lavoro, alla fine, è stato svolto esclusivamente da me e dal mio compagno di classe Andrea. Il nostro lavoro, che ha ricevuto un'ottima valutazione dalla ditta Algorab, in data 22 maggio è stato presentato alla commissione di valutazione del progetto Tu Sei, che ha riconosciuto alla scuola una menzione speciale. In allegato la fotocopia dell'attestato di riconoscimento della commissione e la valutazione della ditta. Esacottero per riprese aeree (eliche 45 CM) Sistema di gestione della carica delle batterie e dell'utilizzo dell'energia proveniente da un piccolo impianto idroelettrico fotovoltaico stand alone 4 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Il sistema: Caratteristiche principali 1.1 Funzionamento Generale Il sistema sviluppato ha come obbiettivo principale la gestione degli accessi di veicoli in un'area riservata ad alta affluenza. Il problema è tutt'altro che banale e dipende da numerosi fattori, primi tra tutti il costo del sistema di gestione e la sicurezza degli accessi. La soluzione più utilizzata ad oggi si basa su un sistema di lettura di chiavi elettroniche, soluzione efficace e sicura, ma di costo crescente all'aumentare del numero di utenti. L'innovazione proposta dal nostro sistema prevede un riconoscimento automatico dei veicoli autorizzati tramite la lettura della targa. Il riconoscimento automatico delle targhe (ANPR Automatic number plate recognition) è una tecnologia che utilizza il riconoscimento ottico dei caratteri sulle immagini per leggere le targhe dei veicoli. Il sistema è solitamente utilizzato dalle forze dell'ordine per il controllo della velocità (Tutor), come metodo di telepedaggio per gli ingressi alle autostrade, la catalogazione dei movimenti di traffico o individui, il controllo dell'accesso a zone a traffico limitato o l'accesso a parcheggi con barriera. Il principio di funzionamento si basa sull’acquisizione tramite telecamera dell'immagine, l’elaborazione e l’ottimizzazione della stessa , il riconoscimento e la conversione in stringa del valore della targa tramite un software OCR (optical character recognition). Nel nostro caso il sistema viene posto a controllo di un ingresso parcheggio e quindi un secondo step è necessario al fine di verificare la corrispondenza della stringa fornita dall'OCR con un database, Figura1: Funzionamento generale decidere se al veicolo è consentito l'accesso e, in caso positivo, comandare il sistema di apertura. Per fare tutto ciò è stato sviluppato un software, in grado di lavorare su un hardware con potenza di calcolo medio-bassa, al fine di portare il sistema ad un buon compromesso prezzo-qualità per fornire la possibilità di installazione anche ad utenze private ad un prezzo accessibile. 5 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 1.2 ANPR Specifiche tecniche La lettura automatica del numero di targa consiste nel trasformare i pixel dell'immagine digitale in testo ASCII della targa. La tecnologia di riconoscimento ottico dei caratteri (OCR) utilizzata per ANPR è molto simile alla tecnologia che consente di acquisire documenti cartacei e trasformarli in file elettronici modificabili (software in grado di costruire documenti word da un testo acquisito a scanner). Ci sono sette algoritmi primari che il software deve eseguire per arrivare all'identificazione di una targa: Localizzazione targa - necessità di trovare e isolare la targa sull'immagine acquisita Orientamento targa e ridimensionamento - viene corretta un'eventuale inclinazione della targa e ne viene regolata la dimensione Regolazione luminosità e contrasto Segmentazione in caratteri - trova i singoli caratteri sulla targa e li divide in singoli frame Riconoscimento ottico dei caratteri Analisi Sintattica / Geometrica - viene verificato se i caratteri rispettano le specifiche per paese Eventuale media del riconoscimento su più immagini, per produrre un risultato più affidabile e sicuro Il sistema di riconoscimento non è preciso al 100%, infatti presenta alcuni limiti che possono derivare da uno o più dei seguenti fattori: Risoluzione del video scadente, solitamente dovuta al tentativo di riconoscimento di una targa troppo lontana, ma talvolta risultante dall'uso di una telecamera di bassa qualità Immagini sfocate, soprattutto se il veicolo è in movimento Scarsa illuminazione, basso contrasto o sovraesposizione, riflessione o presenza di ombre Un oscuramento della targa da parte di un oggetto, molto spesso una barra di traino o sporcizia Caratteri differenti (i caratteri delle targhe possono variare da paese a paese) Tecniche di elusione E' importante sottolineare che i sistemi di riconoscimento potrebbero portare a violazioni della normativa sulla privacy, in quanto idonei a tracciare i movimenti dei cittadini. 6 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Hardware e il software libero Figura 2: Hardware Low-Cost utilizzato 2.1 Caratteristiche Hardware: Raspberry Pi è un computer delle dimensioni di una carta di credito e del costo medio di circa 30€. Il prodotto nasce in un primo momento con lo scopo di portare nelle scuole elaboratori economici, utili per l’insegnamento dell’informatica. Grazie al grande successo riscosso presso gli istituti scolastici più aperti all’innovazione, la voce dell’esistenza di questo piccolo computer campione di versatilità si è estesa generando un commercio di centinaia di migliaia di unità e coinvolgendo i settori più disparati. Sono stati (e sono tuttora) davvero tanti gli utenti a voler scegliere RaspberryPi, dagli hacker più esperti ai giovani che Figura 3: collegamenti Hardware desiderano avvicinarsi al mondo dell’informatica, senza spendere un capitale. Dal punto di vista tecnico non sono le prestazioni che fanno gridare al miracolo, ma è la buona espandibilità dell’hardware e la sua versatilità, unita alla versatilità dei sistemi operativi Linux che supporta: sono questi i fattori che hanno portato ad una larga diffusione del prodotto. La scheda che abbiamo deciso di adottare per lo sviluppo e la messa in opera del progetto si chiama Banana Pi ed è un'evoluzione del tradizionale Raspberry, meno conosciuta, un po' più costosa, ma dalle prestazioni maggiori, fattore quest'ultimo di fondamentale importanza per il processing video e delle immagini. E' stato finora descritto l'hardware più importante del progetto, ma sono stati anche utilizzati: Arduino ed una telecamera (descrizione al punto 3.1) Inoltre è stata realizzata una scheda relay che consente di interfacciare la sensoristica già presente sul cancello (fotocellula, barriere a pressione, chiave per apertura) con Arduino. 7 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 2.2 Il software progettato: Oltre all'hardware in questo progetto gioca un ruolo fondamentale il software. Su Banana Pi è infatti necessario eseguire diversi software che devono essere coordinati e gestiti in modo simultaneo con differenti velocità di esecuzione. Tutto ciò viene gestito dal Kernel Linux, approfondito nella sezione 2.3 . A grandi linee si può dire che il sistema è così strutturato: I dati provenienti dalla telecamera su interfaccia USB vengono decodificati dal modulo di gestione delle periferiche video interno al Kernel denominato video fot linux (V4L). Questi dati decodificati vengono letti dal noto riproduttore video VLC che nel nostro caso, configurato in modo adeguato, sarà incaricato di creare il flusso streaming MJPEG (approfondimento sezione 3.1). Il flusso viene reso accessibile sulla rete locale tramite porta ethernet per la visualizzazione da remoto e allo stesso tempo verrà mandato al sistema di gestione fotogrammi e riconoscimento ottico (OCR) per la conversione in stringa del valore della targa. La stringa viene poi spedita in un "contenitore" (approfondimento sezione 3.3) che fungerà da tramite tra l'OCR ed il software di gestione database. Un software scritto in python (codice sorgente sezione 4.1) esegue il confronto tra la targa letta dall'OCR e il database e, in caso di un riscontro, gestisce l'apertura del cancello ed inserisce i dati relativi al proprietario della targa in un file di scambio. Il file di scambio e il file del database vengono resi disponibili sulla rete locale LAN tramite un server FTP, grazie al quale un qualsiasi computer autenticato nella rete potrà aggiungere o eliminare le targhe dal database e leggere il file di scambio. Su un pc della rete locale LAN viene installato un software progettato appositamente per la gestione del database e per la visualizzazione in tempo reale dello streaming(codice sorgente sez. 4.2). Di seguito alcune immagini che mostrano il software di gestione database: 8 Matteo Dalponte Tesina di Maturità Funzionamento software schematizzato in uno schema a blocchi 9 A.S: 2014/2015 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 2.3 Cos'è Linux? Linux è un sistema operativo, ovvero quell'insieme di programmi essenziali per far funzionare il computer . E' una alternativa a Windows e a MacOS, e può essere installato al loro posto (o insieme, sullo stesso computer). Più in generale Linux è il primo rappresentante del ("freesoftware", in inglese), ovvero quel software distribuito con una licenza che ne permette non solo l'utilizzo da parte di chiunque ed in qualsiasi circostanza, ma anche la modifica, la copia e l'analisi. Linux è un sistema operativo sviluppato da Linus Torvalds; in gergo Figura 4: Stemma S.O. GNU/Linux tecnico è più corretto denominare Linux come kernel, cioè cuore del sistema; attorno ad esso girano applicazioni di varia natura che sfruttano le sue potenzialità. L'idea originaria di Linus Torvalds fu quella di migliorare un kernel già esistente, Minix, creato per scopi didattici da Andrew S.Tanenbaum. La particolarità di Linux è che il progetto di sviluppo attorno al kernel è stato realizzato sia da esperti che da semplici appassionati ed hacker di tutto il mondo, grazie al coordinamento ed alla diffusione dei dati tramite Internet. Questa collaborazione ha portato alla nascita di un sistema operativo realmente in grado di essere antagonista a Windows. Il nome più corretto per indicarlo è in realtà GNU/Linux, in quanto le applicazioni fondamentali (compilatori, editor, librerie, etc.) sono state sviluppate dalla Free Software Foundation di Richard Stallman alla cui base c'è il progetto GNU (Gnu's not Unix), ovvero la realizzazione di un sistema operativo libero di cui possiamo elencare le principali caratteristiche relative alla licenza di distribuzione GPL (General Public License): codice sorgente aperto e disponibile per la lettura e la modifica; libera distribuzione, da parte di chiunque, sia del software che del relativo codice sorgente; applicazioni e prodotti derivati coperti sempre da licenza GPL (copyleft); libero utilizzo anche per fini di natura commerciale. 2.4 Il Software Libero: L'idea di software libero nasce agli inizi degli anni Ottanta, quando lo sviluppo del software cominciò a passare dalle università alle aziende (software proprietario), ponendo un pesante freno alla collaborazione che caratterizzava il lavoro di gran parte dei programmatori e dei sistemisti dell'epoca, soprattutto con i patti di 10 Figura 5: Stemma Free Software Foundation Matteo Dalponte Tesina di Maturità A.S: 2014/2015 non divulgazione che le aziende facevano firmare ai programmatori assunti. In realtà il software "commerciale" esisteva già, ma i costi elevati dell'hardware facevano sì che il business delle aziende non fosse concentrato sul software, che era considerato una parte naturale del prodotto, ed i cui file sorgente erano in genere di dominio pubblico. Con il passare del tempo il software diventò sempre più complesso e difficile da realizzare e le aziende iniziarono a non distribuire i file sorgente obbligando i propri dipendenti a non rivelare nulla per non avvantaggiare la concorrenza; inoltre con il crollo dei costi dell'hardware, lo sviluppo commerciale del software divenne un business notevole, ed il codice sorgente era divenuto un investimento prezioso che poteva, da un lato far acquisire una fetta di tale mercato in rapida crescita e dall'altro legare i propri utenti al proprio software mantenendo il segreto sui metodi utilizzati per lo sviluppo di sistemi e applicazioni. In questo modo le aziende cominciarono ad utilizzare la legge sul diritto d'autore per impedire ai concorrenti di leggere e modificare i loro prodotti, assicurandosi il controllo dei propri clienti che, senza poter vedere e modificare il codice sorgente del software, non potevano più adattarlo alle loro esigenze, ed erano così costretti ad acquistare servizi di supporto dalle aziende fornitrici. Nel 1983 Richard Stallman fondò il progetto GNU con l'intenzione di creare un sistema operativo completamente libero. Grazie alla collaborazione di molti sviluppatori volontari, all'uso di Internet per il coordinamento del progetto ed al kernel Linux di Linus Torvalds, nel 1991 nacque GNU/Linux, un clone di Unix liberamente utilizzabile, modificabile e ridistribuibile. Il movimento per il software libero si batte, quindi, per dare agli utenti di sistemi informatici la libertà che deriva dal software libero. Il software libero dà, a chi lo usa, il controllo delle proprie elaborazioni informatiche, mentre il software non libero pone chi lo usa sotto il dominio dello sviluppatore. Secondo la Free Software Foundation, un software si può definire libero solo se garantisce quattro "libertà fondamentali": Libertà 1: Libertà di eseguire il programma per qualsiasi scopo. Libertà 2: Libertà di studiare il programma e modificarlo. Libertà 3: Libertà di ridistribuire copie del programma in modo da aiutare il prossimo. Libertà 4: Libertà di migliorare il programma e di distribuirne pubblicamente i miglioramenti, in modo tale che tutta la comunità ne tragga beneficio. 11 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Approfondimento 3.1 IP camera e protocollo MJPEG Il Sistema hardware è costituito da una telecamera USB che, interfacciata con VLC come descritto nella sezione 2.2, è in grado di creare un flusso video digitale formato M-JPEG collocato in rete su protocollo HTTP e fisicamente trasportato tramite cavo di rete ethernet; sfruttando questa configurazione il sistema, a differenza della tecnologia analogica su cavo coassiale, non risente della perdita di qualità dell'immagine. La scelta del tipo di formato è stata vincolata dalla libreria incaricata di migliorare i singoli fotogrammi delle immagini contenenti la targa. Infatti per la sua struttura "grezza" il formato MJPEG non Figura 6: Speciale IP camera con visore 360° richiede processori di alto livello per comporre il flusso(nel Banana Pi) e poi per scomporlo nuovamente in singoli fotogrammi per l'analisi(nel PC di controllo), ma la velocità di trasmissione tra i due hardware richiesta dalla rete è considerevole. Il software basato sul protocollo MJPEG acquisisce dal sensore ottico singole foto (solitamente ad una velocità di 30 al secondo), le codifica nel formato JPEG per ridurne la qualità e la dimensione e poi le manda in sequenza al client, che visualizza il flusso video come una sequenza di foto. Per fare un confronto con la recentissima tecnologia H264 o MPEG2 (utilizzata per esempio nella trasmissione della tv digitale) possiamo dire che le ultime due hanno una compressione intraframe; ciò significa che se il fotogramma precedente conteneva la stessa immagine del successivo frame non viene inviata al client nessuna nuova immagine e quindi verrà mostrata l'ultima ricevuta; nel caso in cui il frame successivo cambi alcune sue parti rispetto al precedente, il protocollo h264 prevede l'invio solo delle parti modificate. In questo caso, a parità di qualità di immagine, la rete viene utilizzata anche 10 volte meno, ma sia il processore di codifica che quello di decodifica vengono utilizzati maggiormente. 12 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Figura 7: Stemma del linguaggio di programmazione Python 3.2 Python L'intero software a bordo di Banana Pi per la gestione dei caratteri digitalizzati provenienti dall'OCR, per il confronto con il database e per la gestione della comunicazione seriale con Arduino è interamente scritto in python. Analogamente è gestito con python anche il software di controllo in grado di gestire sul Pc il database delle targhe consentite e di riprodurre il video streaming. Perché proprio Python e non C, Java, Visual Basik o Perl? Python è innanzitutto un linguaggio di script pseudocompilato. Questo significa che, similmente a Perl ed a Tcl/Tk, ogni programma sorgente deve essere pseudocompilato da un interprete. L'interprete è un normale programma che va installato sulla propria macchina e si occupa di interpretare il codice sorgente e di eseguirlo. Quindi, diversamente da C++, non abbiamo una fase di compilazione che trasforma il codice sorgente in file eseguibile, ma esiste solo il sorgente che viene eseguito dall'interprete. Il fatto di essere pseudocompilato rende Python un linguaggio portabile. Una volta scritto un sorgente, esso può essere interpretato ed eseguito sulla gran parte delle piattaforme attualmente utilizzate come Apple (Mac) o PC (Microsoft Windows e GNU/Linux), disponendo semplicemente di una versione corretta dell'interprete. Python non è attualmente il linguaggio di programmazione più famoso (v. Figura 8), ma viene spesso utilizzato per creare codici sorgente destinati ad hardware tipo Raspberry Pi. Inoltre la documentazione e le librerie a disposizione sulla rete sono numerose. Figura 8: Statistica relativa ai linguaggi di programmazione più utilizzati (fonte: TIOBE Software) 13 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 3.3 Gestione dati OCR Verrà ora proposta la soluzione ad uno dei problemi riscontrati durante lo sviluppo del progetto, tutto il restante codice sorgente è, con i relativi commenti, disponibile a seguire. Il software optical character recognition (OCR), dopo aver effettuato la lettura dell'immagine, deve comunicare al software di gestione database la stringa contenente il valore della targa. Il software ocr ed il software di gestione database e accessi sono scritti in linguaggi differenti, quindi è necessario trovare un sistema comune per comunicare i dati. La soluzione è l'utilizzo delle code beanstalkd. Il sistema prevede un contenitore centrale (software beanstalkd) in cui vengono inseriti dei dati dall'alto e prelevati dal basso. In questo modo il software che legge i dati potrà leggerli quando vorrà scaricandoli dal "contenitore" e non dovrà essere sempre in ascolto. Per la gestione di questo contenitore sono presenti diverse librerie per tutti i linguaggi di programmazione: in questo modo un software scritto ad esempio in C potrà caricare i dati, mentre uno scritto in Python potrà scaricarli e, data la presenza Figura 9: Sistema di gestione code delle code beanstalkd, il software più veloce non verrà rallentato da quello più lento. Sarà poi necessario trasportare i dati in un formato standard facile da dividere e da interpretare per i diversi linguaggi di programmazione. Per la soluzione a questo secondo problema risulta particolarmente adatto il sistema JSON (JavaScript Object Notation), un formato studiato per lo scambio dei dati in applicazioni client-server. Python gestisce il sistema json come una serie di librerie e dizionari. Per capire meglio la struttura di un elemento json è necessario avere chiari i seguenti concetti: le liste in Python sono collezioni ordinate di oggetti, simili agli array di altri linguaggi di programmazione come Java. In altre parole permettono di memorizzare una sequenza di oggetti ed accedere ad essi mediante un indice. es: >>> lista=[1, 'html'] # lista con un intero e una stringa (>>> significa comando eseguito direttamente nella shell, si utilizza # in Python per aggiungere commenti ) Per recuperare un valore da una lista è sufficiente richiamare la posizione es: >>> lista[1] # indicizzazione 'html' 14 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Un dizionario invece rappresenta una collezione “non ordinata” di oggetti. Gli oggetti sono identificati univocamente da una chiave (generalmente una stringa) invece che mediante un indice numerico, come avviene nelle liste. Ogni elemento del dizionario è rappresentato da una coppia (chiave : valore), la chiave serve per accedere all’elemento e recuperare il valore. es: >>> diz={'html':1, 'HTML':2, 'HTml':3} Per recuperare un valore da un dizionario è sufficiente richiamare la posizione es: >>> diz['HTML'] #ottieni valore richiamando la chiave 2 Analizzando bene il json in uscita all'OCR possiamo vedere come i dati effettivamente siano divisi in liste e dizionari. { "uuid": "e11ecc-6aaf-47-929-9e67", "camera_id": 1, "site_id": "watchtower-hq", "img_width": 640, "img_height": 480, "epoch_time": 1402161050, "processing_time_ms": 138.669163, "results": [ { "plate": "S11FRE", "confidence": 77.130661, "matches_template": 0, "region": "", "region_confidence": 0, "coordinates": [ { "x": 218, "y": 342 }, { "x": 407, "y": 325 }, { "x": 407, "y": 413 }, { "x": 218, 15 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 "y": 431 } ], "candidates": [ { "plate": "S11FRE", "confidence": 77.130661, "matches_template": 0 }, { "plate": "S11ERE", "confidence": 75.496307, "matches_template": 0 } ] } ] } Per esempio se noi dovessimo recuperare il numero di targa dovremmo entrare nel primo dizionario e richiamare i dati tramite la chiave "results", poi recuperare i dati contenuti nella posizione 0 della lista, entrare nel nuovo dizionario e richiamare i dati tramite la chiave "plate" es: >>> json["results"][0]["plate"] 'S11FRE' # JSON è la variabile che contiene il JSON Il codice sorgente A titolo di chiarimento verranno di seguito analizzati in dettaglio due dei quattro codici sorgente da noi realizzati per il funzionamento del sistema 4.1 Lettura OCR e transizione dati Il seguente codice, scritto in Python, viene eseguito su Banana Pi e ha la funzione di leggere i dati provenienti dall'ocr, confrontarli con il database, inviare il segnale di apertura ad Arduino e scrivere un file di transizione informazioni. 16 Matteo Dalponte Tesina di Maturità 1. import serial 2. import beanstalkc 3. import json 4. import os A.S: 2014/2015 5. 6. immx=0 7. immy=0 8. buffer=0 9. valstanga = 0 10. 11. open_alprd = "/home/bananapi/openalpr/src/build/alprd --config /etc/openalpr" 12. os.system("pkill alprd") 13. os.system(open_alprd) 14. 15. 16. 17. 18. 19. 20. 21. try: ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) except: try: ser = serial.Serial('/dev/ttyUSB1', 9600, timeout=1) except: try: 22. 23. ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1) except: 24. pass 25. 26. beanstalk = beanstalkc.Connection(host='localhost', port=11300) 27. beanstalk.watch('alprd') 28. 29. while True: 30. 31. f = open('/home/bananapi/Desktop/Database.conf','r') 32. s=str(f.readlines()) 33. dati=eval(s) 34. 35. job = beanstalk.reserve() 36. jobb = job.body 37. job.delete() 38. d = json.loads(jobb) 39. 40. time = str(d["processing_time_ms"]) 41. targa = str(d["results"][0]["plate"]) 42. x1 = str(d['results'][0]['coordinates'][0]['x']) 43. y1 = str(d['results'][0]['coordinates'][0]['y']) 44. x2 = str(d['results'][0]['coordinates'][1]['x']) 45. y2 = str(d['results'][0]['coordinates'][1]['y']) 46. x3 = str(d['results'][0]['coordinates'][2]['x']) 47. y3 = str(d['results'][0]['coordinates'][2]['y']) 48. x4 = str(d['results'][0]['coordinates'][3]['x']) 49. y4 = str(d['results'][0]['coordinates'][3]['y']) 50. immx = str(d['img_width']) 51. immy = str(d['img_height']) 17 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 52. 53. 54. for num in range(0,40): 55. try: 56. try: 57. number_lines = len(dati) 58. for i in range(0, number_lines): 59. diz1 = eval(dati[i]) 60. targa_diz = diz1['targa'] 61. if (str(d['results'][0]['candidates'][num]['plate']) == targa_diz): 62. try: 63. ser.write("1") 64. except: 65. pass 66. valstanga = 1 67. nome = diz1['nome'] 68. print " Consento l'accesso! 69. 70. except: 71. pass 72. 73. except: 74. pass 75. 76. trasm = dict() 77. trasm["targa"]=nome 78. trasm["tempo"]=time 79. trasm["valstanga"]=valstanga 80. trasm["x1"]=x1 81. trasm["y1"]=y1 82. trasm["x2"]=x2 83. trasm["y2"]=y2 84. trasm["x3"]=x3 85. trasm["y3"]=y3 86. trasm["x4"]=x4 87. trasm["y4"]=y4 88. trasm["immx"]=immx 89. trasm["immy"]=immy 90. 91. f1 = open('/home/bananapi/Desktop/trasmissione.conf','w') 92. f1.write (str(trasm)) Commenti al codice: 1. import serial 2. import beanstalkc 3. import json 4. import os 18 ", time," mS ", nome Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Questa parte di codice è incaricata di importare le librerie necessarie alla corretta esecuzione del programma. Serial è la libreria che gestisce la comunicazione USB con Arduino, beanstalkc è necessario al fine di leggere il "contenitore" di cui si è parlato nella sezione 3.3, json è la libreria che decodifica i dati provenienti da beanstalkc ed infine os gestisce il controllo del terminale della macchina (è possibile eseguire comandi su shell o terminale). 1. open_alprd = "/home/bananapi/openalpr/src/build/alprd --config /etc/openalpr" 2. os.system("pkill alprd") 3. os.system(open_alprd) Ora viene assegnata alla variabile open_alprd una stringa (le stringhe in python vengono contrassegnate con le virgolette) contenente il percorso dove è collocato il software OCR. Con la seconda riga viene spedito il comando pkill alprd sul terminale delle macchina. Questo comando interrompe eventuali processi del software ocr che erano già stati aperti in precedenza (è necessario al fine di evitare che due processi di riconoscimento caratteri siano eseguiti nello stesso momento). La terza riga esegue il comando contenuto nella variabile open_alpr sul terminale della macchina. 1. try: 2. ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) 3. except: 4. 5. 6. 7. 8. 9. 10. try: ser = serial.Serial('/dev/ttyUSB1', 9600, timeout=1) except: try: ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1) except: pass Viene ora aperta la comunicazione con Arduino. E' importante inserire l'istruzione di connessione in una struttura try-except perché nel caso in cui il tentativo di connessione fallisca (per esempio USB arduino non collegata) il software non deve interrompersi per colpa dell'errore, ma deve procedere con tutti gli altri comandi ignorando il problema. E' inoltre importante fare dei tentativi di connessione su varie porte Usb visto che diversi tipi di arduino possono utilizzare diversi tipi di driver. Così se il primo ciclo try restituisce errore (o perché la USB non è collegata o perché i driver sono errati) si passa al secondo try che tenta la connessione con altri driver. I tentativi continuano fino all'ultimo except dove viene data l'istruzione di saltare il passaggio di connessione e procedere con il restante codice. La struttura della connessione è la seguente : 19 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Quando si eseguono operazioni di lettura o scrittura relative alla connessione seriale si utilizza la variabile "ser" (es: ser.read() significa "leggi i dati provenienti dalla comunicazione"). 'dev/ttyUSB' è il driver di Arduino (è come la COM in windows); 9600 è il bitrate in bit/sec; timeout=1 è il tempo (s) dopo il quale, in caso di non risposta, si interrompono i tentativi. 1. beanstalk = beanstalkc.Connection(host='localhost', port=11300) 2. beanstalk.watch('alprd') Apre il collegamento con il server "contenitore" dei dati provenienti dall'ocr. Il server si trova sulla stessa macchina (localhost) e risponde alla porta 11300. 1. while True: Tutte le istruzioni precedenti a questo comando vengono eseguite solamente all'avvio, mentre quelle a seguire saranno eseguite a ciclo continuo. 1. f = open('/home/bananapi/Desktop/Database.conf','r') 2. s=str(f.readlines()) 3. dati=eval(s) Viene aperto il file contenete il database, situato nel direttorio /home/bananapi/Desktop/Database.conf' in sola lettura (in questo caso non è necessaria la scrittura se si devono fare solamente confronti tra le targhe lette ed il database). Il file Database.conf viene poi letto e convertito in una lista (v. struttura lista sezione 3.3) dove l'indice della lista è il numero della riga e l'oggetto è il contenuto in stringa della riga. La struttura del database è: {'targa':'RK755AJ', 'nome':'Matteo Dalponte', 'password':'17846', } {'targa':'AA555AA', 'nome':'Mario Rossi', 'password':'95761', } {'targa':'AB344CA', 'nome':'Ciao ', 'password':'98264', } Dove tramite l'indice della lista si ricava la riga desiderata e poi, tramite la chiave del dizionario, si ricava a piacere il numero di targa, il nome o la password. 1. job = beanstalk.reserve() 2. jobb = job.body 3. job.delete() 4. d = json.loads(jobb) 5. 6. time = str(d["processing_time_ms"]) 7. targa = str(d["results"][0]["plate"]) 8. x1 = str(d['results'][0]['coordinates'][0]['x']) 20 Matteo Dalponte Tesina di Maturità 9. y1 = str(d['results'][0]['coordinates'][0]['y']) 10. x2 = str(d['results'][0]['coordinates'][1]['x']) 11. y2 = str(d['results'][0]['coordinates'][1]['y']) 12. x3 = str(d['results'][0]['coordinates'][2]['x']) 13. y3 = str(d['results'][0]['coordinates'][2]['y']) 14. x4 = str(d['results'][0]['coordinates'][3]['x']) 15. y4 = str(d['results'][0]['coordinates'][3]['y']) 16. immx = str(d['img_width']) 17. immy = str(d['img_height']) A.S: 2014/2015 A questo punto viene letto il "contenitore" con i dati inseriti dall'OCR e questi vengono tradotti in codice JSON (v. sezione 3.3 per spiegazione) La variabile d conterrà ora tutti i dati riportati nelle pagine 12 e 13, con l'unica differenza che sotto la chiave candidates avremo 40 tentativi di riconoscimento con diverse affidabilità (confidence). Vengono poi estrapolati da JSON (contenuto nella variabile d) i dati del tempo di processing dell'immagine della targa (time), il numero di targa con maggior probabilità (targa), gli angoli della targa (necessari poi per contornare la targa nel video streaming), la risoluzione sull'asse delle x della telecamera (immx) e quella sull'asse delle y (immy). 1. for num in range(0,40): 2. 3. try: try: 4. number_lines = len(dati) 5. for i in range(0, number_lines): 6. diz1 = eval(dati[i]) 7. targa_diz = diz1['targa'] 8. if (str(d['results'][0]['candidates'][num]['plate']) == targa_diz): 9. try: 10. ser.write("1") 11. except: 12. pass 13. valstanga = 1 14. nome = diz1['nome'] 15. print " Consento l'accesso! ", time," mS ", nome 16. 17. 18. except: 19. pass 20. 21. 22. except: pass Ora tutti i 40 tentativi di riconoscimento prodotti dall' OCR vengono confrontati con tutte le targhe contenute nel database (struttura Database a pag. 17). 21 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Nel caso in cui vi sia corrispondenza vene inviato il comando di apertura ad Arduino (ser.write("1")), viene portata ad un valore alto una variabile (ci servirà poi per colorare di rosso o di verde il contorno della targa sul video in streaming) e ricavato dal database il nome del proprietario del veicolo consentito. 1. trasm = dict() 2. trasm["targa"]=nome 3. trasm["tempo"]=time 4. trasm["valstanga"]=valstanga 5. trasm["x1"]=x1 6. trasm["y1"]=y1 7. trasm["x2"]=x2 8. trasm["y2"]=y2 9. trasm["x3"]=x3 10. trasm["y3"]=y3 11. trasm["x4"]=x4 12. trasm["y4"]=y4 13. trasm["immx"]=immx 14. trasm["immy"]=immy 15. 16. f1 = open('/home/bananapi/Desktop/trasmissione.conf','w') 17. f1.write (str(trasm)) Viene ora creato un dizionario (trasm) che conterrà tutti i dati significativi della lettura della targa ( posizione nel video, nome del proprietario del veicolo, targa consentita o meno e risoluzione della videocamera). Infine viene scritto il tutto su un file denominato trasmissione. Tale file verrà poi letto in FTP dal PC di controllo collegato tramite cavo ethernet. 4.2 GUI Visualizzazione streaming Figura 10: interfaccia grafica GUI per la visualizzazione dello streaming 22 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Il seguente codice viene eseguito sulla macchina (PC remoto) ed è una GUI (graphical user interface) necessaria per visualizzare lo streaming proveniente dall'IP camera. Il software ha inoltre il compito di delimitare la targa, in verde se è presente una corrispondenza della targa con il database, in rosso se non è presente alcuna corrispondenza (v figura 10). Il codice è scritto in python ed è stato compilato in modo tale da creare un file eseguibile (EXE) per macchina windows; in questo modo non è necessario che il computer che lo esegue sia dotato di interprete python e di tutte le librerie di funzionamento. La libreria principale per il funzionamento di tale codice è OpenCV, libreria orientata alla computer vision. E' importante spendere alcune parole per descrivere le notevoli potenzialità di tale libreria, pensata inizialmente da Intel per testare le CPU in applicazioni intensive, come ad esempio la gestione di immagini 3D, e poi rilasciata come libreria open source per la computer vision. OpenCV contiene moduli di elaborazione di immagini e video I / O, e più di 350 algoritmi di gestione dati quali: filtri di immagine, calibrazione della telecamera, riconoscimento di oggetti, analisi strutturale e molti altri. 1. import cv2 2. import urllib 3. import numpy as np 4. import ctypes 5. from ftplib import FTP 6. import time 7. 8. x1=0 9. x2=0 10. x3=0 11. x4=0 12. y1=0 13. y2=8 14. y3=0 15. y4=0 16. immx=0 17. immy=0 18. immx1=500 19. immy1=500 20. tempopresente=0 21. tempopassato=0 22. valstanga=0 23. 24. 25. 26. stream=urllib.urlopen('http://192.168.0.44:8080/video') 27. ftp = FTP('192.168.0.44', 'bananapi', 'bananapi', timeout=2) 23 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 28. 29. 30. bytes='' 31. while True: 32. tempopresente=time.time() 33. if (tempopresente-tempopassato)>0.2: 34. tempopassato=tempopresente 35. 36. try: 37. temp=open('temp.conf','w') 38. ftp.retrbinary('RETR /home/bananapi/Desktop/trasmissione.conf', temp.writelines) 39. temp.close() 40. f = open('temp.conf','r') 41. trasm=eval(f.read()) 42. 43. user32 = ctypes.windll.user32 44. Yval=int(user32.GetSystemMetrics(1)*0.66) 45. 46. rapp_trasf=((user32.GetSystemMetrics(1)*0.66)/ (eval(trasm['immy']))) 47. Xval=int((eval(trasm['immx']))*rapp_trasf) 48. 49. targa=trasm['targa'] 50. x1=int((eval(trasm['x1']))*rapp_trasf) 51. y1=int((eval(trasm['y1']))*rapp_trasf) 52. x2=int((eval(trasm['x2']))*rapp_trasf) 53. y2=int((eval(trasm['y2']))*rapp_trasf) 54. x3=int((eval(trasm['x3']))*rapp_trasf) 55. y3=int((eval(trasm['y3']))*rapp_trasf) 56. x4=int((eval(trasm['x4']))*rapp_trasf) 57. y4=int((eval(trasm['y4']))*rapp_trasf) 58. immx=int((eval(trasm['immx']))*rapp_trasf) 59. immy=int((eval(trasm['immy']))*rapp_trasf) 60. valstanga=(trasm['valstanga']) 61. 62. 63. except: pass 64. 65. 66. bytes+=stream.read(1024) 67. a = bytes.find('\xff\xd8') 68. b = bytes.find('\xff\xd9') 69. if a!=-1 and b!=-1: 70. jpg = bytes[a:b+2] 71. bytes= bytes[b+2:] 72. i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.CV_LOAD_IMAGE_COLOR) 73. 74. ridimensiona=cv2.resize(i, (Xval,Yval), fx=5, fy=5) 75. 76. pts = np.array([[x1,y1],[x2,y2],[x3,y3],[x4,y4]], np.int32) 77. 24 Matteo Dalponte 78. A.S: 2014/2015 if valstanga==0: 79. 80. Tesina di Maturità cv2.polylines(ridimensiona,[pts],True,(0,0,255),2) else: 81. cv2.polylines(ridimensiona,[pts],True,(0,255,0),2) 82. 83. cv2.putText(ridimensiona,targa, (x1,y1-5), cv2.FONT_HERSHEY_PLAIN, 1.2,(255,255,255),2) 84. cv2.putText(ridimensiona,"Q=Uscita", (0,20), cv2.FONT_HERSHEY_PLAIN, 1.2,(255,255,255),2) 85. 86. 87. cv2.imshow("Riconoscimento targhe",ridimensiona) 88. 89. 90. if cv2.waitKey(1) & 0xFF == ord('q'): 91. break 92. 93. 94. cv2.destroyAllWindows() Commento al codice: 1. import cv2 2. import urllib 3. import numpy as np 4. import ctypes 5. from ftplib import FTP 6. import time Vengono importate le librerie; open cv (cv2 ) e numpy per la gestione dei fotogrammi, urllib per l'apertura dello streaming, ctypes per ottenere informazioni utili dal sistema operativo quali per esempio la risoluzione dello schermo, ftplib per la lettura in remoto del file di trasmissione (v sez. 4.1 pag 14) e time per la gestione del tempo. 1. stream=urllib.urlopen('http://192.168.0.44:8080/video') 2. ftp = FTP('192.168.0.44', 'bananapi', 'bananapi', timeout=2) Apre il link al quale risponde il server MJPEG e inserisce nella variabile streaming tutti tutte le istruzioni per la lettura del link. Apre poi una connessione ftp criptata verso Banana Pi; a questo tentativo di connessione risponderà il server e la comunicazione servirà per scambiare dati tra il server (Banana Pi) e il PC di controllo. 1. while True: 25 Matteo Dalponte Tesina di Maturità 2. tempopresente=time.time() 3. if (tempopresente-tempopassato)>0.2: 4. A.S: 2014/2015 tempopassato=tempopresente Nel ciclo infinito while True andiamo ora ad assegnare alla variabile tempopresente il valore in secondi del tempo trascorso dall'accensione della macchina al momento in cui si richiama la funzione time.time(). Ogni 0,2 secondi viene rieseguito il ciclo IF Questo accorgimento è un sistema rudimentale, ma in questo caso efficace al fine di non rallentare due processi che richiedono diverse velocità alla macchina. Infatti la lettura tramite FTP del file trasmissione.conf è nettamente più lenta della lettura e visualizzazione del flusso video MJPEG quindi, se la lettura FTP venisse fatta ad ogni ciclo macchina la visualizzazione dello streaming risulterebbe a scatti e in ritardo rispetto alla realtà. Con questo accorgimento le lettura FTP viene eseguita solamente ogni 200 millisecondi, mentre la gestione video viene fatta a ciclo continuo senza interruzioni. 1. try: 2. temp=open('temp.conf','w') 3. ftp.retrbinary('RETR /home/bananapi/Desktop/trasmissione.conf', temp.writelines) 4. temp.close() 5. f = open('temp.conf','r') 6. trasm=eval(f.read()) Con le righe 2,3,4 viene trasferito il file trasmissione.conf, situato su Banana Pi, alla macchina dove viene eseguito il seguente codice. Il file appena creato viene letto, vengono estratti i dati, viene convertito il contenuto in dizionario (da file di testo si ricavano solo stringhe) e assegnato alla variabile trasm. Il seguente file è temporaneo visto che al ciclo successivo viene riscritto e riletto. 7. user32 = ctypes.windll.user32 8. Yval=int(user32.GetSystemMetrics(1)*0.66) 9. rapp_trasf=((user32.GetSystemMetrics(1)*0.66)/(eval(trasm['immy']))) 10. Xval=int((eval(trasm['immx']))*rapp_trasf) 11. 12. targa=trasm['targa'] 13. x1=int((eval(trasm['x1']))*rapp_trasf) 14. y1=int((eval(trasm['y1']))*rapp_trasf) 15. x2=int((eval(trasm['x2']))*rapp_trasf) 16. y2=int((eval(trasm['y2']))*rapp_trasf) 17. x3=int((eval(trasm['x3']))*rapp_trasf) 18. y3=int((eval(trasm['y3']))*rapp_trasf) 19. x4=int((eval(trasm['x4']))*rapp_trasf) 26 Matteo Dalponte Tesina di Maturità 20. y4=int((eval(trasm['y4']))*rapp_trasf) 21. immx=int((eval(trasm['immx']))*rapp_trasf) 22. immy=int((eval(trasm['immy']))*rapp_trasf) 23. valstanga=(trasm['valstanga']) A.S: 2014/2015 24. 25. except: 26. pass L'interfaccia grafica (GUI) e quindi anche il video streaming visualizzato devono avere delle dimensioni tali da non essere troppo grandi per lo schermo del PC e allo stesso tempo mantenere le stesse proporzioni del video inviato dalla telecamera per evitare di deformare l'immagine. Si è deciso di occupare 2/3 dello schermo in altezza per cui, una volta applicata questa misura, la lunghezza della finestra dovrà variare in base al rapporto del video sorgente. 27. Yval=int(user32.GetSystemMetrics(1)*0.66) Con il seguente comando inseriamo nella variabile Yval il valore della dimensione in pixel dello schermo in altezza moltiplicandolo per 2/3 (0,66) 28. rapp_trasf=((user32.GetSystemMetrics(1)*0.66)/(eval(trasm['immy']))) Il valore di Yval viene ora diviso per l'altezza in pixel dell''immagine della telecamera che avevamo ricavato dall'ocr su Banana Pi e poi inserito nel file trasmissione.conf. In questo modo il rapporto tra le grandezze dello schermo e della telecamera costituisce un rapporto di trasformazione che utilizzeremo per adattare la posizione del contorno targa nel piano di visualizzazione. 1. bytes+=stream.read(1024) 2. a = bytes.find('\xff\xd8') 3. b = bytes.find('\xff\xd9') 4. if a!=-1 and b!=-1: 5. jpg = bytes[a:b+2] 6. bytes= bytes[b+2:] 7. i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.CV_LOAD_IMAGE_COLOR) 8. 9. ridimensiona=cv2.resize(i, (Xval,Yval), fx=5, fy=5) 10. 11. pts = np.array([[x1,y1],[x2,y2],[x3,y3],[x4,y4]], np.int32) 12. 13. if valstanga==0: 14. 15. 16. cv2.polylines(ridimensiona,[pts],True,(0,0,255),2) else: cv2.polylines(ridimensiona,[pts],True,(0,255,0),2) 17. 27 Matteo Dalponte 18. Tesina di Maturità A.S: 2014/2015 cv2.putText(ridimensiona,targa, (x1,y1-5), cv2.FONT_HERSHEY_PLAIN, 1.2,(255,255,255),2) 19. cv2.putText(ridimensiona,"Q=Uscita", (0,20), cv2.FONT_HERSHEY_PLAIN, 1.2,(255,255,255),2) 20. 21. 22. cv2.imshow("Riconoscimento targhe",ridimensiona) 23. 24. 25. 26. if cv2.waitKey(1) & 0xFF == ord('q'): break 27. 28. 29. cv2.destroyAllWindows() Vengono inseriti tutti i bit provenienti dalla lettura della pagina di streaming nella variabile bytes, vengono selezionati solamente i bit che contengono il fotogramma del video e vengono ricombinati per crearne un'immagine dalla funzione cv2.imdecode. L'immagine viene poi ridimensionata con i criteri scelti e descritti nella pagina precedente e viene creato un poligono che avrà come estremi i punti di limite della targa riportati dall'OCR nel JSON (v sez. 3.3 pag. 13). Questo poligono verrà colorato in rosso o in verde in base al valore della variabile valstanga e, assieme al numero di targa o proprietario del veicolo, verrà poi stampato a monitor sopra l'immagine proveniente dall'IP camera. 28 Matteo Dalponte Tesina di Maturità A.S: 2014/2015 Sitografia http://www.anpr.net/ http://www.police.uk/information-and-advice/automatic-number-plate-recognition/ http://en.wikipedia.org/wiki/Automatic_number_plate_recognition http://www.raspberrypi.org http://www.linux.it/linux http://it.wikipedia.org/wiki/Software_libero http://www.softwarelibero.it/ https://gnu.org/ http://it.wikipedia.org/wiki/MPEG-4 http://en.wikipedia.org/wiki/Motion_JPEG https://github.com/openalpr/openalpr/wiki/OpenALPR-Design http://www.python.it/doc/intro/ http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html http://www.html.it/pag/15614/dizionari/ http://www.html.it/pag/15613/liste/ https://github.com/openalpr/openalpr/wiki/OpenALPR-Daemon-%28alprd%29 http://opencv.org/ 29