UNIVERSITÀ DEGLI STUDI DI SIENA FACOLTÀ DI INGEGNERIA CORSO DI LAUREA SPECIALISTICA IN INGEGNERIA INFORMATICA Progettazione di un sistema di videosorveglianza basato sull'interpretazione automatica di scene: Integrazione del software "HuMoR" come elemento di "GStreamer". Relatore: Prof. Marco Gori Correlatore: Ing. Vincenzo Di Massa ANNO ACCADEMICO 2005-2006 Tesi di Laurea di: Andrea Paolessi Sommario Abstract p.1 Capitolo 1 - GStreamer p.3 • • • • • • Cosa è GStreamer Gli Elementi Proprietà degli elementi Alcuni strumenti di GStreamer p.11 La Pipeline gst-editor gst-launch Conclusioni Capitolo 2 - HuMoR • • • • • • Cosa è HuMoR Acquisizione delle immagini p.9 p.11 p.15 p.17 p.18 p.18 p.18 Preprocessing delle immagini p.19 Segmentazione p.22 Stima del background Estrazione di features Tracking • • • • p.7 p.8 • • p.4 I Pads • • p.3 Tracking basato su votazioni Tracking basato su reti neurali – Kohonen Networks Tracking basato sulla distanza tra vettori di features Classificazione p.20 p.24 p.27 p.29 p.30 p.31 p.31 • • Interpretazione Analisi p.32 p.33 Capitolo 3 - HuMoR come elemento di GStreamer p.34 • • • • • Definizione dei pad di ingresso dell'elemento humor p.35 Controllo della configurazione dell'elemento humor p.41 Conclusioni p.50 Passaggio del flusso dei dati ad HuMoR I pad di uscita dell'elemento humor Capitolo 4 - L'elemento DataDemuxer • Formato dei dati in uscita dall'elemento humor p.36 p.46 p.52 p.53 • Definizione dei pad di ingresso dell'elemento DataDemuxer • Controllo dell'esportazione delle informazioni • • I pad di uscita dell'elemento DataDemuxer Conclusioni p.58 p.59 p.63 p.66 Capitolo 5 - Definizione delle pipelines su GStreamer • • • • • Sintassi del comando gst-launch Un semplice visualizzatore: "focus" Acquisizione di filmati con "cam2avi" Estrazione delle features con "avi2hum" p.67 p.69 p.71 p.73 p.78 Estrazione multipla di features da un set di esempi con"set2hum" p.82 Capitolo 6 - Utilizzo delle pipelines • • • • Connessione della videocamera tramite xplug Raccolta degli esempi per la creazione del learning set Addestramento di HuMoR Riconoscimento di azioni p.84 p.84 p.85 p.88 p.92 Capitolo 7 - Estensione delle potenzialità di HuMoR p.95 Bibliografia p.96 Abstract L'installazione di un sistema di sorveglianza in un ambiente reale prevede una iniziale progettazione effettuata al fine di individuare e risolvere una serie di problematiche di fondamentale importanza tipiche del contesto di utilizzo: lo scopo da raggiungere deve essere la copertura di tutte le zone da sottoporre a sorveglianza e la certezza che qualsiasi azione significativa venga rilevata e segnalata. Tali requisiti implicano la possibilità di scalare ed adattare il sistema di base in modo da renderlo in grado di gestire all'occorrenza, ad esempio, sia una singola stanza che un intero edificio in tutta la sua estensione ed eterogeneità dei vari ambienti da controllare. La analisi degli ambienti e la progettazione del sistema devono essere pertanto finalizzati a renderlo effettivamente funzionale ed affidabile, indipendentemente dalla verificata efficacia che esso ha garantito in altre situazioni. Un software di interpretazione automatica di scene valido, come HuMoR (HUman MOtion Recognizer), sviluppato per lavorare in un ambiente "monodimensionale" (una stanza sorvegliata, con una videocamera ed un pc) ha pertanto bisogno di essere adattato all'interno di una più grande struttura in modo da consentire la sua applicazione ed il suo utilizzo in un ambiente in cui potrebbe essere necessario sorvegliare più locali tramite una o più videocamere per ciascuno, controllando e gestendo in maniera parallela e centralizzata la situazione. 1 Da questa esigenza è nata l'idea di estendere le funzionalità di HuMoR in modo da poterlo utilizzare videosorveglianza completo. per costruire un sistema di L'ambiente ideale per il raggiungimento di tali obiettivi si è trovato in GStreamer, un framework che permette la gestione e la elaborazione di generici flussi dati attraverso l'uso e l'interconnessione di elementi di base, in grado di processare in maniera specializzata i dati ad essi forniti. La sua natura modulare, ricca di elementi predefiniti per la comune gestione di flussi video, rende possibile la creazione di sistemi di controllo ed analisi di scene in maniera scalabile e decentralizzata, facilitando sensibilmente il raggiungimento dell'obiettivo prefissato. Uno dei principali sforzi compiuti in questo elaborato è stato pertanto quello di integrare HuMoR nel framework, in modo da poter utilizzare le sue potenzialità all'interno di una struttura modulare ed estendibile con facilità. Si rende così possibile la creazione e la progettazione di un sistema di sorveglianza che, dopo una fase iniziale di valutazione dell'ambiente e di addestramento (come previsto da HuMoR), sia in grado di classificare azioni, segnalandole e compiendo le opportune operazioni conseguenti al rilevamento. 2 Capitolo 1 - GStreamer Cosa è GStreamer GStreamer è definito come un framework per la gestione di flussi multimediali di dati che permette, tramite l'uso di elementi collegati opportunamente tra loro, il controllo e l'elaborazione di tali dati in maniera versatile ed ampiamente adattabile ad ogni casistica. Lo scopo di GStreamer è di permettere la gestione di flussi di dati di qualsiasi tipo (audio, video, binario, testo, metadati...) definendo insiemi strutturati (detti pipelines) di elementi, ciascuno adibito ad un particolare tipo di elaborazione sull'input ricevuto. L'interfaccia per la definizione delle pipelines utilizza una sintassi mirata alla progettazione di uno schema di elementi connessi tra loro, con la struttura tipica di un diagramma a blocchi; ciò permette di definire rapidamente una pipeline complessa e adattabile ad ogni particolare tipo di casistica nell'ambito dell'elaborazione dei dati con estrema efficacia e senza particolari nozioni di programmazione di basso livello. La piattaforma di sviluppo che GStreamer mette a disposizione consente quindi di creare applicativi multimediali di elevata complessità con estrema facilità e velocità, pur mantenendo intatti i gradi di libertà ed estendibilità propri di un linguaggio di programmazione; in ogni caso sono disponibili librerie che permettono l'inclusione e l'uso in ambiente C/C++ di primitive per l'utilizzo delle funzionalità e per la creazione di pipelines in modo da poter sfruttare le potenzialità offerte da GStreamer anche nei propri applicativi. 3 Gli Elementi Le unità elaborative di base utilizzabili all'interno di questa struttura di interconnessione e di gestione di flussi di dati sono gli elementi della pipeline, blocchi caratterizzati da uno o più ingressi/uscite (rispettivamente sink e source pads) a cui mandare in input e da cui ottenere in output il flusso di dati opportunamente elaborato a seconda delle caratteristiche dell'elemento stesso. Gli elementi, a seconda del tipo di elaborazione che applicano al flusso di dati passatogli, vengono classificati localizzandoli in una categoria 4 opportuna all'interno di una struttura ad albero: vi sono ad esempio una serie di categorie radice, come "codec", "sink", "source", "visualization", "generic", etc. che si possono suddividere attraverso altre sottocategorie, fino ad arrivare alle foglie della struttura, gli elementi stessi. Ad esempio, un encoder DV, utilizzabile per la compressione video di un filmato, viene posizionato sotto la categoria dei codec, nelle sottostutture (elencandole scendendo dalla radice verso le foglie dell'albero) encoder e video. Un elemento terminale per l'output di un flusso su file (filesink) sarà invece posto su sink->file. 5 In particolare, i sinks ed i sources, rispettivamente consumatori e produttori di flussi di dati vengono distinti da tutte le altre categorie, le quali contengono tutte elementi in grado di elaborare i dati da cui vengono attraversati, classificabili quindi in un unico tipo: i filters. 6 Proprietà degli elementi Gli elementi di GStreamer non sono solamente semplici "black box" in grado di elaborare staticamente il flusso dati che li attraversa; il modo in cui l'elaborazione ha luogo ed altre caratteristiche sono configurabili liberamente dall'utente attraverso le cosiddette proprietà dell'elemento (nella misura in cui esse vengano messe a disposizione dall'elemento stesso). Queste si comportano come veri e propri parametri di configurazione da specificare, possono sia modificare l'elaborazione del flusso che attivare una qualsiasi caratteristica dell'elemento; il loro inserimento può sia essere obbligatorio che non. Ad esempio, per un filesrc è necessario specificare il nome del file da cui estrarre il flusso di dati da fornire in uscita, tramite la sua proprietà location. Si vedrà successivamente che sul plugin che implementa HuMoR sarà possibile attivare o disattivare una finestra di visualizzazione dei risultati dell'analisi tramite la proprietà view, opzione il cui inserimento non è necessario, poichè nel caso in cui non venga specificata assumerà un valore di default impostato su OFF. 7 I Pads I Pads, come già anticipato in precedenza, non sono altro che i connettori che permettono la negoziazione del flusso di dati e delle connessioni tra gli elementi; essi hanno il controllo sui dati, poichè su di essi si stabilisce il tipo o i tipi di flusso che può passare una volta che il collegamento viene effettuato, restringendo quindi il passaggio ai soli tipi consentiti e permettendo inoltre il collegamento solo con altri pad che siano compatibili, in grado quindi di gestire gli stessi tipi di dati. Un elemento può avere uno o più source pads attraverso i quali produce dati in uscita e analogamente uno o più sink pads, con i quali accetta dati in ingresso; mentre i filter elements possono avere elementi di entrambi i tipi, solo source o sink pads sono presenti nei source o sink elements rispettivamente. Nell'immagine, tre esempi (partendo da sinistra) di source, sink e filter element con relativi pad (i rettangoli rosa presenti all'interno degli elementi). 8 La Pipeline L'interconnessione degli elementi permette di definire una struttura di elaborazione di un flusso di dati chiamata pipeline, ampiamente adattabile ad ogni necessità o tipologia di problema. Nell'immagine è riportato un semplice esempio di una pipeline in grado di decodificare e riprodurre un file audio compresso in formato ogg/vorbis. Il primo elemento source si occupa di recuperare i dati di ingresso da un file, dopodichè il flusso viene processato da un demuxer ogg e da un decodificatore vorbis ed infine inviato in riproduzione ad una periferica di output audio tramite l'elemento sink element "alsasink". 9 In figura è riportato un'altro esempio di pipeline, creata utilizzando l'editor grafico gst-editor, il quale permette di effettuare questa operazione con estrema semplicità, selezionando gli elementi da inserire e collegandoli opportunamente tra loro, a formare una specie di diagramma a blocchi. Nell'esempio in questione il primo elemento è un source di un pattern video per scopi di test, collegato ad ximagesink, un sink di visualizzazione che si occupa di trasferire su una finestra di output video (visualizzata in figura in primo piano rispetto a quella dell'editor) i dati trasferiti nel suo pad di ingresso. 10 Alcuni strumenti di GStreamer Procediamo adesso con l'esposizione delle funzionalità fornite da parte degli strumenti messi a disposizione da GStreamer per vari utilizzi, analizzando in particolare quelli che sono stati indispensabili per lo sviluppo delle pipelines e dei plugins creati. gst-editor Nell'esempio precedente, in cui viene definita una semplice pipeline dimostrativa, si è già introdotto un semplice approccio che consente di costruire ed eseguire pipelines di GStreamer in maniera intuitiva e veloce tramite un editor grafico: gst-editor. Tale applicativo permette di prendere gli elementi e sistemarli su una finestra, in modo da poterli collegare opportunamente; in questo modo viene definito un diagramma a blocchi di immediata comprensione, che costituirà la pipeline da eseguire. L'interfaccia utente dell'editor si presenta alla sua apertura con tre finestre: la principale è l'area vera e propria di lavoro specifica di una pipeline; al suo interno possono essere inseriti e collegati gli elementi e successivamente, a lavoro ultimato, si può lanciare l'elaborazione per verificare il funzionamento o utilizzare la pipeline creata direttamente. 11 E' possibile caricare pipelines già esistenti o salvarle; il formato che viene utilizzato per tale scopo è l'xml. Gli elementi, organizzati secondo la gerarchia di categorie e sottocategorie esposta in precedenza, sono selezionabili per l'inserimento sulla pipeline comodamente dalla finestra utility palette, dalla quale è possibile anche eseguire una rapida ricerca full text per individuare con facilità un elemento, oltre che impostarne il livello di debug in fase di esecuzione. 12 L'ultima finestra, chiamata properties, serve (come il nome stesso suggerisce) per visualizzare ed editare le proprietà disponibili di un oggetto selezionato sull'area di lavoro; nell'immagine seguente si possono vedere e modificare opportunamente tutti i parametri dell'elemento dvdec, un decoder di filmati in formato dv. 13 E' inoltre presente un secondo tab pads che contiene la lista di tutti i source e sink pads dell'oggetto, visualizzandone inoltre capabilities, ovvero i tipi di dati supportati da ognuno di essi. tutte le 14 gst-launch Il comando principale per l'esecuzione di una pipeline da shell è gst- launch, con il quale risulta possibile con estrema semplicità definire una pipeline utilizzando una sintassi di facile ed immediata comprensione, ma di grande versatilità. E' sufficiente elencare gli elementi da utilizzare nella stessa sequenza in cui il flusso dei dati deve essere elaborato, semplicemente separando i loro nomi con un punto esclamativo ed uno spazio prima e dopo del nome, ad esempio: ... elemento1 ! elemento2 ... Eventuali proprietà possono essere impostate specificandone il nome ed immettendo il valore da assegnare a seguito: ... elemento1 proprietà1=valore1 ! elemento2 ... Nella fattispecie , la sintassi è la seguente: Element1 [Property1=Value1][ ! Element2 [Property2=Value2]]...[ ! ElementN [PropertyN=ValueN]] Un semplice esempio di pipeline, che implementa lo stesso precedentemente esposto riguardante l'output del video di test, è il seguente: videotestsrc ! ximagesink Se si prova a lanciare tale pipeline tramite il comando gst-launch (gst- launch videotestsrc ! ximagesink), si potrà visualizzare ovviamente 15 lo stesso output che si era ottenuto definendola e lanciandola tramite l'editor. La possibilità di creare ed eseguire pipelines direttamente da riga di comando ci è stata particolarmente utile, come si vedrà nei capitoli successivi, per creare scripts parametrizzati, in grado di eseguire una serie di operazioni che fanno utilizzo di HuMoR tramite GStreamer. 16 Conclusioni Da ciò si può chiaramente capire come la possibilità di rendere disponibili le funzionalità offerte da HuMoR in un framework così versatile sia di grande interesse ed utilità: a tale scopo è stato necessario convertire HuMoR da un applicativo stand-alone in un vero e proprio elemento da utilizzare all'interno di GStreamer. In questo modo quindi si rende infatti possibile l'utilizzo di HuMoR in qualsiasi contesto (ad esempio la sua integrazione in un qualsiasi riproduttore video basato su GStreamer), la semplificata estensione delle sue funzionalità (in particolare si è rilevato utile costruire un framework finalizzato a raccogliere efficientemente e rapidamente un set di esempi e di features ottenute dall'elaborazione degli stessi) e, non meno importante, la scalabilità dell'intero sistema di videosorveglianza; risulta infatti non solo possibile, ma anche semplice decentrare, tramite l'uso del framework il lavoro di analisi delle scene in microaree in cui una singola istanza di HuMoR processa il video acquisito da una videocamera, fornendo dei risultati che poi vengono convogliati e utilizzati da settori specializzati per il raccoglimento e l'aggregazione dei dati così ottenuti. 17 Capitolo 2 - HuMoR Cosa è HuMoR Il nucleo su cui si basa il complesso sistema di videosorveglianza da progettare, come già accennato nell'introduzione, è il software HuMoR; tale applicativo è stato progettato per rendere possibile il riconoscimento di azioni compiute in particolare da soggetti umani all'interno di scene riprese tramite un a videocamera; da ciò deriva l'acronimo, il quale sta per HUman MOtion Recognizer. Le scene vengono filmate e classificate, decidendo a quale tipologia appartengono e scegliendo tra un insieme di scene note, secondo quanto appreso durante una fase di addestramento effettuata a priori. L'analisi e l'elaborazione dei risultati di HuMoR è suddivisibile in diverse fasi eseguite in successione, le quali sono costituite dai 9 stadi di elaborazione esposti di seguito. Acquisizione delle immagini Nella fase iniziale HuMoR acquisisce il video da analizzare, proveniente da telecamere di rete, come una sequenza di immagini fornite da un server web, e lo trasferisce all'elaboratore. L’impiego di Internet Camera, ovvero di telecamere provviste di connessione ethernet (802.3) o wireless (802.11), consente sfruttare la grande diffusione di questi protocolli rendendo il sistema utilizzabile su qualunque pc dotato di scheda di rete e soprattutto indipendente dal sistema operativo. 18 Un importante requisito per la valutazione delle telecamere da usare è il framerate, ossia il numero di immagini che sono in grado di fornire al secondo. Considerando l’impiego principalmente rivolto al riconoscimento di comportamenti umani e all’interpretazione di scene, un valore di 20 frames al secondo consente di ottenere buoni risultati anche in situazioni delicate come il movimento degli arti di una persona. Alcune telecamere hanno un web-server integrato e non presentano particolari problemi, mentre altre possono utilizzare applet java per visualizzare i video all’interno del browser; in questi casi occorre interfacciarsi opportunamente con tali dispositivi in modo da consentire a HuMoR di recuperare ed elaborare le immagini acquisite. Preprocessing delle immagini Il passo successivo è quello di effettuare il preprocessing delle immagini ricevute in modo da eliminare potenziali fonti di disturbo dovute, ad esempio, a livelli di dettaglio eccessivi ai fini del riconoscimento di scene; in questo modo le immagini vengono adattate alle esigenze dei componenti successivi, regolando opportunamente alcuni parametri (luminosità, contrasto etc.) la cui corretta impostazione risulta fondamentale per una corretta analisi successiva. A seconda del tipo di azione da interpretare, infatti, alcuni particolari possono essere tranquillamente trascurati. Ad esempio un livello di dettaglio che arrivasse ad apprezzare le caratteristiche di un volto umano potrebbe essere assolutamente superfluo (anzi, probabilmente deleterio) se l’obiettivo prefissato è quello di capire se una persona sta entrando in una zona riservata. In questo caso si può tranquillamente applicare un filtro gaussiano per livellare l’immagine stessa e, di conseguenza, ridurne il rumore 19 focalizzando l'analisi del software esclusivamente sui particolari significativi della scena. Stima del background Il passo successivo separa parti che sono considerate come sfondo immobile da altre che si muovono; viene rilevato lo spostamento di oggetti rispetto alla parte dell'immagine che viene stimata come facente parte dello sfondo o comunque rispetto ad una o più immagini di riferimento. La stima del background è uno dei punti critici del sistema che si scontra con problematiche decisamente complesse in ambiente esterno: basti pensare all’ombra che si muove nell’arco della giornata che può essere interpretata come un oggetto in movimento; ma anche in ambienti indoor sono presenti non poche insidie, quali ad esempio l’ instabilità della luce artificiale, la presenza di oggetti in movimento spurii, generati da fenomeni di riflessione, oppure oggetti che si muovono lentamente e che possono venire parzialmente inglobati nello sfondo. Quest’ultima osservazione introduce direttamente l’elemento centrale della stima del background, ovvero la mediazione necessaria fra due fondamentali requisiti: lo sfondo deve essere aggiornato a ritmi sufficientemente rapidi da rilevare tempestivamente il movimento di un oggetto, ma non tali da tenere in considerazione soltanto la parte più recente dell’evoluzione della scena. In altre parole il sistema dovrebbe idealmente avere memoria di ciò che è avvenuto in precedenza nella scena ed allo stesso tempo essere pronto a reagire rapidamente alle sue variazioni. Risulta quindi necessario stabilire un criterio efficace per la stima del background. In letteratura si utilizzano tipicamente due approcci 20 differenti: nel primo si tengono in considerazione le differenze riscontrate negli ultimi due fotogrammi, nel secondo si crea un modello statistico dello sfondo. Il difetto del primo approccio è quello di tenere conto solo della parte più recente dell'evoluzione della scena; l'altro metodo, invece, non consente di accorgersi rapidamente di un cambiamento: è necessario un lasso di tempo consistente affinché il modello statistico dello sfondo cambi sostanzialmente. Nell’algoritmo utilizzato in HuMoR si cerca di sfruttare i vantaggi di entrambi: anziché creare un unico modello complesso per lo sfondo se ne creano molti. In questo modo, sebbene i singoli modelli vengano aggiornati lentamente, una volta creati si passa da uno all'altro con grande rapidità. In particolare, ogni pixel viene considerato come sfondo solo quando rimane stabile per un numero sufficiente di frames. Inoltre, per aumentare l’efficienza della stima si possono memorizzare più “versioni” di sfondo, in modo da rispondere efficacemente a brusche differenze generali della scena. Un caso tipico è quello dell’improvvisa variazione della luminosità dovuta all’accensione o allo spegnimento della luce in un ambiente chiuso, magari senza alcun oggetto in movimento. Situazioni di questo genere partono da uno sfondo stabile seguito da una brusca variazione della scena in cui, in prima analisi, si verifica un generale “movimento” di tutti i pixels fra due immagini consecutive. La seconda di esse acquisisce però ben presto una sua nuova stabilità e quindi diventa un nuovo candidato a background. Se si evita di sovrascrivere il primo sfondo con il nuovo, ma semplicemente si tiene in memoria un set di sfondi validi, la successiva variazione inversa (luce che viene riportata nella condizione iniziale) 21 verrà immediatamente considerata come uno sfondo valido senza dover aspettare la ri-stabilizzazione dei pixels. Per migliorare ulteriormente l’efficacia della stima le varie immagini di riferimento vengono progressivamente invecchiate in modo da eliminare gradualmente quelle che non risultano essere più riutilizzate con il passare del tempo. Segmentazione Al termine del processo di stima del background siamo in grado di stabilire, per ognuno dei pixel dell’immagine acquisita, se appartiene allo sfondo o meno; possiamo quindi definire l’insieme dei pixel che si sono “mossi” rispetto alla collezione di sfondi al fine di tentare di estrarre delle sotto-immagini contenenti ognuna solo un corpo in movimento. Un sistema di rilevamento di movimento potrebbe fermarsi qua, ma per essere in grado di distinguere comportamenti diversi occorre andare oltre e stabilire l’entità e la tipologia del movimento appena rilevato, osservandone l’evoluzione e attribuendo ad esso, in definitiva, un senso compiuto. Il primo passo in questa direzione è cercare di raggruppare i pixel che appartengono ad uno stesso segmentazione dell’immagine. oggetto, ovvero effettuare la Lavorare a livello di pixel significa imbattersi ben presto in un numero ragguardevole di confronti: in un’immagine di 160x120 = 19200 pixels è necessario effettuare 8*n confronti con i pixels adiacenti (con n numero di pixels), quindi 153600 confronti ad ogni frame. Da questa semplice analisi si conclude immediatamente che, per limitare la complessità computazionale di qualunque algoritmo che tratti confronti tra immagini, è necessario superare il concetto di pixel, 22 nella fattispecie estendendolo a quello di box, ovvero una regione quadrangolare di pixel contigui, ad esempio 10x10. Ai boxes verranno associati direttamente il numero dei rispettivi pixel cambiati rispetto all’immagine di riferimento, pertanto si potranno raggruppare i boxes contigui aventi un numero di pixel cambiati superiore ad una certa soglia. In questo modo si ottengono due immediati vantaggi. Il primo è che il numero dei confronti da effettuare per determinare se 2 boxes adiacenti vanno considerati parte dello stesso oggetto decresce sensibilmente, essendo il numero dei box molto inferiore al numero dei pixel totali; nell’immagine di 160x120 con boxes di 10x10 i confronti da effettuare ad ogni frame passano a 1536, ovvero 2 ordini di grandezza in meno. Il secondo è che, ragionando in termini di boxes, si riduce il rischio di incorrere nella suddivisione di uno stesso oggetto in movimento in più frammenti che verrebbero considerati, a tutti gli effetti, oggetti diversi: se un oggetto venisse spezzato con un tratto di pixel di larghezza inferiore al lato del box, verrebbe infatti automaticamente ricollegato, poichè l’ampiezza del box ricoprirebbe la porzione di separazione, mantenendola invece unita. Tale considerazione indurrebbe inoltre, se non ricorressimo ai boxes, ad effettuare confronti fra pixels non immediatamente contigui, ma anche più distanti con un conseguente ulteriore incremento di complessità. Come diretta conseguenza di quanto detto, è facile rilevare che il difetto maggiore di questo approccio risieda nel fatto che per distinguere due oggetti è necessario che essi siano ad almeno un box di distanza. 23 Estrazione di features Una volta individuati e raggruppati opportunamente gli oggetti che si stanno muovendo, l’obiettivo diventa quello di seguirli in modo da determinarne il comportamento; a tale scopo si cerca pertanto di trasformare i dati relativi ai corpi in una forma “sintetica” adatta a successive elaborazioni. Le tecniche con cui è possibile tracciare con efficacia il movimento di un oggetto sulla scena, si basano sull’analisi di determinate features estratte dalle immagini con particolare riferimento all’area e al baricentro dei blob, oltre a determinate informazioni circa la distribuzione spaziale dei colori. Sebbene tali informazioni consentano di individuare e seguire un oggetto, per cercare di apprendere e riconoscere comportamenti e, in ultima analisi, interpretare la scena in esame, sono necessarie altre tipologie di dati. In particolare dovremmo estrarre informazioni poco sensibili a variazioni di posizione o di area, ma al tempo stesso dovremo prestare particolare attenzione alla forma del blob, in modo da poter fare considerazioni sulla “postura” del soggetto piuttosto che sulla posizione. Inoltre è fondamentale riuscire a garantire l’invarianza rispetto ai colori, evitando così che la stessa azione, compiuta da persone vestite in maniera differente o con diverso colore di capelli, generi features differenti. Ciò di cui abbiamo bisogno è dunque una serie di misure geometriche dei vari blob in esame le quali, analogamente ad approcci riscontrabili in altre applicazioni come la compressione video, hanno alla base il calcolo dei momenti di vario ordine. Di conseguenza abbiamo a che fare con una duplice tipologia di features: la prima, basata su caratteristiche prevalentemente geometriche, verrà usata per identificare la postura e quindi fornire 24 informazioni sull’azione svolta; la seconda essendo più sensibile alle variazioni cromatiche, sarà necessaria a distinguere l’oggetto in movimento dal background ed il successivo tracking. In particolare per la classificazione degli oggetti vengono usate le ltiGeometricFeatures, disponibili con la libreria LTI-Lib, elencate a titolo di esempio di seguito: Feature Group 0 (COG means "center of gravity"). feat[0] (area). = "areasize" = (T,F) number of enclosed pixels feat[1] = "bordersize" = (T,F) number of border pixels. feat[2] = "xcog" = x-component of COG. feat[3] = "ycog" = y-component of COG. feat[4] = "xmin" = smallest x coordinate. feat[5] = "xmax" = largest x coordinate. feat[6] = "ymin" = smallest y coordinate. feat[7] = "ymax" = largest y coordinate. feat[26]= "compactness" = (T,R,S,F) compactness = (4PI*area /(bordersize)^2), ranges from 0 to 1. Feature Group 1 [16]. feat[8] = "m02" = (T) central moment m02. feat[9] = "m03" = (T) central moment m03. feat[10]= "m11" = (T) central moment m11. feat[11]= "m12" = (T) central moment m12. feat[12]= "m20" = (T) central moment m20. feat[13]= "m21" = (T) central moment m21. feat[14]= "m30" = (T) central moment m30. feat[22]= "j1" = (T,R,F) inertia parallel to main axis. feat[23]= "j2" = (T,R,F) inertia orthogonal to main axis. feat[24]= "orientation" = (T,S) orientation of main axis. range: -90 to 90 degrees. feat[25]= "eccentricity"= (T,R,S,F) eccentricity, ranges from 0 (=circle) to 1 (=line). 25 Feature Group 2. These are the first 7 moment invariants as described in [27] feat[15]= "hu1" = (T,R,S,F) moment invariant 1. feat[16]= "hu2" = (T,R,S,F) moment invariant 2. feat[17]= "hu3" = (T,R,S,F) moment invariant 3. feat[18]= "hu4" = (T,R,S,F) moment invariant 4. feat[19]= "hu5" = (T,R,S,F) moment invariant 5. feat[20]= "hu6" = (T,R,S,F) moment invariant 6. feat[21]= "hu7" = (T,R,S) moment invariant 7. Feature Group 4. All the following distances are relative to the COG. feat[27] = "rmin" = (T,R,F) minimum distance to border. feat[28]= "rmax" = (T,R,F) maximum distance to border. feat[29]= "rmean" = (T,R,F) mean distance to border. feat[30]= "dleft" = (T,R -90°to+90°) leftmost distance to main axis. feat[31]= "dright" = (T,R -90°to+90°) rightmost distance to main axis. feat[32]= "dfront" = (T,R -90°to+90°) frontmost distance to COG, along main axis. feat[33]= "drear" = (T,R -90°to+90°) rearmost distance to COG, along main axis. Queste features vengono utilizzate tutte tranne la 2 (x-component of COG) e la 3 (y-component of COG) per rendere gli oggetti indipendenti dalla posizione che occupano nella scena. Le features necessarie al tracker si calcolano nel seguente modo: 26 /* I valori RGB sono normalizzati tra 0 e 1 */ xdist_r = xdist_r + ( i - x ) * red; xdist_g = xdist_g + ( i - x ) * green; xdist_b = xdist_b + ( i - x ) * blue; ydist_r = ydist_r + ( j - y ) * red; ydist_g = ydist_g + ( j - y ) * green; ydist_b = ydist_b + ( j - y ) * blue; xrel_q = ( i - x ) * ( i - x ); yrel_q = ( j - y ) * ( j - y ); tr = ( xrel_q + yrel_q ) * red; tg = ( xrel_q + yrel_q ) * green; tb = ( xrel_q + yrel_q ) * blue; moment_r = moment_r + tr; moment_g = moment_g + tg; moment_b = moment_b + tb; Come si vede dal codice vengono calcolati le distribuzioni lungo l'asse X, lungo l'asse Y e il momento del secondo ordine. Ad ogni pixel viene dato un peso che è pari al suo valore di rosso, di verde e di blu, infatti abbiamo tre valori per ogni features calcolata, uno per ogni colore. Tracking Il tracking è il processo che ha il compito di individuare il moto di ogni corpo, analizzandolo in fotogrammi differenti; viene rilevato e seguito lo spostamento di un oggetto in movimento (blob) nella scena, tracciando l’evolversi della sua posizione nel tempo. Per stabilire se un determinato oggetto si sta muovendo, è necessario analizzare i blob rilevati in due fotogrammi consecutivi e determinare 27 se sono stati effettivamente prodotti dallo stesso oggetto nonostante le diversa posizione. L’obbiettivo principale consiste quindi nel definire se due blob siano somiglianti al punto di rappresentare uno stesso oggetto reale. Già da una prima analisi si evince la necessità di scartare il confronto a livello di pixel, sia per la diversa angolazione da cui una telecamera fissa riprende un oggetto che si muove, sia per la naturale deformazione di determinati oggetti in movimento come persone o animali. Altre complicazioni possono sopraggiungere nei casi in cui un oggetto cromaticamente simile allo sfondo può venire suddiviso in più parti oppure più oggetti si sovrappongano e vengano quindi riuniti in un unico blob. L'analisi effettuata dal tracker integrato in HuMoR è principalmente focalizzata sul primo aspetto, ovvero evitare che un oggetto in movimento venga spezzato, riunendo i vari blob che la segmentazione può aver prodotto a partire da un unico oggetto. In particolare si rende necessario tenere traccia di ciascuno degli oggetti presenti nella scena al variare del tempo. L'informazione su un singolo oggetto già individuato in passato, estratta frame per frame, viene memorizzata all'interno di una lista di vettori, i quali risultano dunque composti dalle sequenze di features estratte dai blob individuati dal segmentatore (tale lista è chiamata track). Le tracks di tutti questi oggetti vengono memorizzate in una lista di tracks, la tracklist. 28 Per decidere se gli oggetti presenti in un nuovo fotogramma sono già stati osservati, le features di ogni suo blob vengono confrontate con quelle presenti nelle varie tracks della tracklist. Se il confronto ha esito positivo per una particolare coppia di blob e track (se quindi l'oggetto corrispondente è già stato tracciato nei fotogrammi precedenti) si aggiungono le features del blob in esame in coda a quelle della sua track. Se invece nessuna track genera corrispondenza con il blob in esame e quindi se l'oggetto compare nella scena per la prima volta, è necessario aggiungere una nuova track alla tracklist. Risulta dunque evidente che il funzionamento del tracker sarà fortemente caratterizzato dalle modalità con cui si effettuano i confronti tra le features dei nuovi blob e quelle presenti nella tracklist e, in ultima analisi, da quando un oggetto osservato deve essere considerato l’evoluzione temporale di un altro oggetto già considerato in precedenza e dunque collegato logicamente ad esso. Ognuna di queste tipologie di comparazione ha dei vantaggi particolari che la rendono utile in determinate condizioni o per evidenziare specifiche condizioni, ma nessuna può essere considerata migliore delle altre in termini assoluti. Entrando più nel dettaglio sono state implementate tre diverse modalità di tracking: 1. Tracking basato su votazioni Il primo si basa su un sistema di votazioni: in sostanza, vengono calcolate le features per ogni oggetto nell'immagine attuale e ognuna viene confrontata con le features degli oggetti tracciati fino a quel momento. Ad ogni features confrontata viene assegnato un voto di 29 “somiglianza” ed al termine questi voti vengono sommati; ad ogni oggetto viene associato l'oggetto che ha ricevuto la votazione più alta, se questa è maggiore della soglia minima. I parametri usati per i confronti sono frutto di un lungo lavoro di settaggio pressoché manuale, inoltre tali valori sono decisamente dipendenti dalla scena osservata. Lo spostamento della telecamera da un ambiente all'altro comporta la necessità di calcolare nuovamente tali parametri. Tali variazioni sono dovute alle differenze di segmentazione degli oggetti causate da diverse condizioni ambientali.Le prestazioni di questo algoritmo sono risultate soddisfacenti solo dopo aver accuratamente tarato tutti i parametri, ma questo è un difetto che, di fatto, lo rende inutilizzabile. 2. Tracking basato su reti neurali – Kohonen Networks Questo sistema di tracking prende spunto dal lavoro realizzato da Gilles Labonté per lo studio della dinamica dei fluidi; la soluzione proposta consisteva nell'utilizzare delle Self-Organizing Map (SOM), ovvero reti neurali in grado di apprendere in modo non supervisionato. Queste reti sono ispirate alle Khonen Networks, dalle quali si differenziano principalmente per il diverso modo in cui vengono aggiornati i pesi dei neuroni. Il vantaggio di questo algoritmo è quello di essere molto efficace in presenza di molti oggetti, purtroppo funziona peggio degli altri quando nella scena non viene rilevato troppo movimento. Altre situazioni critiche sono rappresentate da oggetti che variano repentinamente la propria postura, ad esempio una persona che ruota bruscamente su se stessa: il tracker non è in grado di tracciare la scena con continuità. 30 3. Tracking basato sulla distanza tra vettori di features Il sistema basato su distanza tra i vettori è un caso particolare del sistema basato su votazione: si tratta semplicemente di utilizzare una qualche forma di distanza in sostituzione del voto per decidere quanto un vettore sia simile ad un altro. Se il vantaggio di questo sistema consiste nel non avere bisogno di impostazioni manuali, lo svantaggio è che non funziona bene se i vettori hanno numerosi elementi, principalmente a causa del fatto che dalla distanza non si può risalire a che tipo di variazione si sia verificata: non si riesce a capire infatti se sono cambiate molte componenti in maniera lieve oppure se ne è cambiata una sola ma in modo molto evidente. Per limitare tale effetto indesiderato si è pensato di determinare un ristretto sottoinsieme di features con cui descrivere l’oggetto. Questo tipo di tracker riesce a tracciare la brusca variazione di forma di un oggetto, situazione invece critica per il tracker basato su SOM. Se la segmentazione non ha un buon livello di qualità, presenta però problemi di varia natura, specialmente in casi di cattiva illuminazione e conseguente bassa qualità delle immagini acquisite. Classificazione In questa fase il movimento rilevato viene sintetizzato in una sequenza di simboli da utilizzare per il confronto con altre sequenze note. Dopo avere tracciato gli oggetti in movimento sulla scena ed avere generato una sequenza di features che rappresentano l'oggetto nei vari frames, bisogna passare ad un livello di astrazione ancora più elevato, creando una sequenza di simboli che rappresenti una particolare azione, a partire dalle features estratte. 31 La definizione dei simboli viene effettuata con l'uso di K-Means, un algoritmo di clustering non supervisionato che raggruppa un insieme di dati, sotto forma di vettori, in sottoinsiemi di elementi “simili” rappresentati da un unico vettore leader, detto centroide. Nel nostro caso ad ogni oggetto tracciato viene assegnato il simbolo, ovvero un semplice numero intero, identificativo del cluster a cui appartiene il vettore delle features che lo rappresenta. La classificazione analizza dunque la sequenza delle immagini, più in particolare la sequenza delle “forme” o posture assunte dall'oggetto che si muove sulla scena in ogni frame, associando ad ognuna di esse un simbolo. Di conseguenza il classificatore usato “osservando” un gran numero di vettori di features, è in grado di raggruppare quelli simili associandoli ad un unico simbolo; al termine otterremo quindi un set di sequenze di tali simboli, ognuna comportamento. delle quali rappresenta un particolare Interpretazione In questo step viene interpretata la scena ripresa dalla telecamera; Per raggiungere questo obiettivo, l’idea di fondo dell’intero progetto HuMoR è quella di determinare un modello di Markov della scena stessa e confrontarlo con altri modelli ottenuti in una precedente fase in cui sono state presentate al sistema altre scene a titolo di esempio. Tali azioni devono venire catalogate e classificate da un operatore, permettendo al sistema di generare sequenze di riferimento da confrontare in seguito con quelle originate da nuove scene. Vediamo dunque nel dettaglio cosa sono e come vengono generati tali sequenze a partire dalle scene osservate, sia quelle di esempio che 32 quelle da interpretare, per poterle successivamente confrontare tra loro. HuMoR fa uso di una batteria di Hidden Markov Models (HMM), una variante dei modelli Markoviani in cui gli stati sono non ben definibili (nascosti): ogni HMM viene addestrato per riconoscere un particolare comportamento; ovvero ogni tipologia di azione che vogliamo identificare in seguito viene fatta “vedere” al sistema in diverse versioni in modo da poter costruire un modello tipo da associare ad essa. L'intera batteria di modelli analizza, una volta addestrata, le nuove sequenze e fra tutti i modelli sceglie quello che ha la probabilità più alta di aver generato la sequenza vista; scegliere un modello significa quindi scegliere tra i comportamenti appresi durante il training quello che è più simile alla scena appena osservata. Analisi Infine, riconosciuta un’azione ed associata ad uno dei comportamenti noti, il passo successivo e finale è semplicemente quello di decidere se o quale contromisura intraprendere. Qui le possibilità sono molteplici e dipendono strettamente dal contesto in cui viene impiegato il sistema. Potrebbe essere necessario attivare un allarme, oppure registrare soltanto il fatto rilevato o magari attivare un altro dispositivo di sicurezza. 33 Capitolo 3 - HuMoR come elemento di GStreamer Gli elementi che di base sono già diponibili per l'uso all'interno di una pipeline non sono gli unici; in un framework progettato per garantire l'estendibilità e la elaborazione di un qualsiasi tipo di flusso dati ciò sarebbe estremamente limitante e riduttivo. Ciò che ci si aspetterebbe è qundi la possibilità di introdurre nuovi elementi progettati appositamente per uno specifico tipo di elaborazione. Naturalmente questa problematica viene risolta in GStreamer introducendo la possibilità di estendere la gamma di elementi disponibili tramite l'inclusione di plugins, ovvero blocchi di codice caricabili all'interno del framework, solitamente in forma di oggetto condiviso o libreria collegata dinamicamente. Di conseguenza la creazione di un nuovo elemento implica il suo incapsulamento all'interno di una struttura predefinita, imposta dallo standard stabilito per la definizione di un generico plugin. 34 Definizione dei pad di ingresso dell'elemento humor In prima istanza è quindi stato necessario creare un elemento in grado di accettare in ingresso un flusso di dati video conformi allo standard utilizzato da HuMoR ed elaborarli di conseguenza come se venissero passati all'applicazione stand-alone. A tale scopo è stato dotato l'elemento "humor" di un pad di ingresso, configurandolo in modo che esso sia in grado di ammettere come tipo di dati solamente un flusso generico raw di video in spazio di colori rgb ("x-raw-rgb"). In questo modo è stato possibile rendere disponibile all'elemento i dati in ingresso nel corretto formato. 35 Passaggio del flusso dei dati ad HuMoR Il prossimo passo consiste nell'elaborazione dei dati passati secondo le funzionalità disponibili in HuMoR per ottenere gli outputs di interesse, ovvero le features risultanti, contenenti le informazioni relative agli oggetti rilevati e al tracking di essi. E' a questo punto in cui si va a realizzare veramente il collegamento tra GStreamer e HuMoR: le funzioni di quest'ultimo infatti sono rese disponibili sul plugin attraverso l'integrazione di una apposita libreria (libgsthumor.la) al suo interno; è da adesso possibile utilizzare le funzioni offerte da HuMoR direttamente dal codice del plugin. Ma come passare i dati da analizzare ad un applicativo inizialmente progettato per ottenere un input definito su una interfaccia utente? Innanzitutto è stato necessario individuare nel codice in quale punto i dati in ingresso, a prescindere dal tipo di sorgente (videocamera Panasonic, videocamera DLink, sorgente diretta di dati su host locale tramite xplug1), vengono trattati come un flusso video standard per poi essere elaborati. Dopo aver verificato che ciò avviene all'interno del modulo netcam di HuMoR, è stato necessario definire una nuova funzione apposita che, prendendo come argomento un puntatore alla locazione di memoria contenente i dati video in ingresso (ottenuti da un input definito sulla pipeline di GStreamer), sia in grado di fornirli al modulo stesso per l'elaborazione. E' con questa funzione che è possibile, richiamandola dal codice dell'elemento, scambiare dati tra l'elemento stesso del framework per fornirli direttamente al motore di elaborazione di HuMoR, bypassando i dati forniti dall'interfaccia utente. xplug: un applicativo scritto in Python che permette di ottenere da un 1 IP specificato (una generica IP camera) una sequenza di immagini e renderle disponibili sull'host locale. 36 Questa modifica al modo in cui le immagini in ingresso ad HuMoR vengono acquisite richiede però un ulteriore intervento al fine di garantire un trasferimento corretto ed integro dei dati da analizzare. Bisogna infatti tenere in considerazione che la funzione che recupera e fornisce a HuMoR le immagini da analizzare non viene più richiamata dall'interno di uno stesso applicativo come in precedenza, ma da un plugin, ovvero una applicazione separata, che utilizza le funzioni di HuMoR rese disponibili da una libreria esterna. In tale situazione bisogna tener presente che non necessariamente l'importazione dei dati da analizzare da parte del plugin e l'analisi vera e propria effettuata con HuMoR avvengono nella giusta sequenza. Trattandosi infatti a tutti gli effetti di due processi separati, l'esecuzione di uno rimane indipendente dall'altro. In questa situazione è possibile che HuMoR importi dati per l'analisi quando ancora questi non siano stati aggiornati dal plugin o, peggio ancora, quando questi siano in fase di aggiornamento. D'altro canto è possibile anche che il plugin richiami la funzione per aggiornare i dati su HuMoR mentre questi stanno per essere elaborati o addirittura prima che l'elaborazione stessa avesse luogo, causando la perdita di frames nell'analisi. Il diagramma seguente schematizza la situazione appena esposta: 37 Nel caso analizzato si possono notare gli effetti derivanti dall'assenza di sincronia nello scambio dati tra il plugin di GStreamer e l'applicazione HuMoR: per i primi 3 secondi il plugin è stato in grado di fornire ad intervalli regolari di 1 secondo tre fotogrammi acquisiti dalla videocamera; al quarto secondo è avvenuto un ritardo nella trasmissione (congestione o interruzione temporanea della rete, elaborazione ritardata del plugin, etc.) che ha causato la non disponibilità di un fotogramma aggiornato in quell'istante; successivamente la trasmissione è ripresa regolarmente ad intervalli costanti di 1 secondo. Parallelamente, l'applicativo ha effettuato le operazioni di prelievo dei frame disponibili sul plugin in maniera completamente svincolata dal fatto che fossero o no disponibili ed aggiornati; il risultato ottenuto ci fa vedere che, se per i primi 3 secondi HuMoR ha correttamente acquisito le immagini disponibili (assumendo i tempi di elaborazione siano stati tali da permettergli di processare esattamente un frame al secondo con regolarità), al quarto secondo non era presente un frame aggiornato per causa del ritardo nella trasmissione operato dal plugin. Da ciò deriva la ricezione erronea dello stesso frame precedentemente trasmesso; di conseguenza nella fase di elaborazione sarà processato due volte lo stesso frame e la mancata ricezione di quello corretto ed aggiornato, corrispondente al quarto secondo, parallelamente la perdita dello stesso in tale fase. comporterà Inoltre, se l'elaborazione per il quinto secondo è ripresa correttamente, al sesto è avvenuto un ritardo (ancora una volta per normale traffico di rete, per un ritardo nell'analisi operata da HuMoR o altro) a causa del quale l'importazione del frame da parte dell'applicativo non è avvenuta; tale frame verrà quindi perduto, sovrascritto a livello del plugin dal successivo acquisito al settimo secondo; si è verificata 38 pertanto nuovamente una incoerenza di tipo diverso, ma ugualmente dannosa, per cui la sequenza di frame vista dalla videocamera e quella ricevuta dall'applicazione non corrispondono. Ci si immagina immediatamente come, in assenza di un sistema di sincronizzazione sia totalmente non garantita la coerenza di ciò che viene elaborato e di conseguenza l'affidabilità del sistema. Per risolvere il problema appena esposto si rende quindi necessaria l'introduzione di un controllo sul flusso di esecuzione dei due processi in modo da rendere mutuamente esclusivo l'accesso alle variabili interessate per lo scambio delle immagini tra il plugin e HuMoR. Tale obiettivo viene raggiunto introducendo alcune nuove variabili: Una variabile di tipo conditional ("cond"), introdotta per il controllo dell'esecuzione dei processi, ed una boolean ("imageBufferReady") utilizzata per impostare e verificare lo stato di ulteriori variabili buffer; queste ultime sono state definite per consentire lo scambio dei dati relativi ai frame acquisiti. Tali variabili vengono utilizzate nel codice in corrispondenza dei punti in cui la scrittura o la lettura del buffer delle immagini ha luogo: innanzitutto, quando viene richiamata la funzione dal plugin per trasferire i dati su HuMoR, si richiama un lock per consentire la scrittura del buffer senza collisioni; terminato il trasferimento si sblocca l'esecuzione e si imposta a true la imageBufferReady, in modo da segnalare che il buffer è pronto e contiene i dati aggiornati. Alla parte del codice in cui si leggono i dati dal buffer viene anteposto un ciclo while che non fa altro che attendere che proprio la variabile imageBufferReady sia true, segnalando quindi che il buffer è aggiornato e contiene i nuovi dati in ingresso. Viene quindi effettuato un lock per consentire la lettura del buffer e viene aggiornata la 39 imageBufferReady a false per indicare che i dati nel buffer sono adesso obsoleti e necessitano di un aggiornamento prima di essere riletti. Nell'esempio esposto precedentemente si otterrebbe l'effetto di mettere in pausa opportunamente il plugin o l'applicazione quando opportuno, facendo in modo che l'uno attenda che l'elaborazione dei frame dell'altro sia terminata e che sia possibile scrivere/leggere sul buffer di scambio in maniera controllata e tale da garantire la corretta sequenza e l'assenza di perdita di frames. Lo schema seguente visualizza la situazione appena esposta: 40 Controllo della configurazione dell'elemento humor L'interfaccia grafica di HuMoR come applicazione permette di impostare una serie vastissima di proprietà adibite a vari tipi di configurazione; la scelta della videocamera usata per l'acquisizione del video, il modello di tracker da utilizzare, l'attivazione della applicazione del filtro Gaussiano sull'immagine di ingresso o altri tipi di filtro, la visualizzazione del video in ingresso con la contornazione degli oggetti e l'evidenziazione delle linee di tracking ad esempio, per citare solo alcuni tra i più importanti parametri utilizzati. Di seguito sono riportate configurazione di HuMoR. alcune schermate dell'interfaccia di 41 Ovviamente, dato che l'interfaccia grafica di controllo non sarà più disponibile una volta integrata l'applicazione all'interno di un elemento del framework, si pone il problema di permettere il controllo della configurazione in fase di definizione di una pipeline che faccia uso dell'elemento humor. La soluzione ci viene offerta dalla possibilità di definire sugli elementi di GStreamer delle proprietà utilizzate per configurare gli elementi direttamente a livello della pipeline; l'uso di tali oggetti consiste 42 semplicemente nel richiamare il nome della proprietà che si vuole impostare, seguito dal valore che deve essere assegnato ad essa; ad esempio, ammettendo di aver attivato quella che controlla la visualizzazione del video elaborato, con la contornazione degli oggetti rilevati e la linea di tracking di essi, ed aver impostato per essa il nome "view", per inserire all'interno di una pipeline l'elemento humor con tale proprietà attivata sarà sufficiente utilizzare la seguente sintassi: ... ! humor view=TRUE ! ... Nella finestra delle properties di GSTeditor, sarà possibile controllare le proprietà a disposizione in maniera visuale: L'attivazione di una nuova proprietà di un elemento avviene attraverso direttive richieste dallo standard stabilito per la creazione di un plugin: innanzitutto è necessario specificare in testa al codice le propietà che faranno parte dell'argomento, definendole nella funzione _class_init(), ed in seguito si può opzionalmente dichiarare e definire le due funzioni _get_property() e _set_property() che l'elemento può implementare. A queste funzioni verrà inviata una notifica nel caso in cui venga cambiato o richiesto il valore di una proprietà, in modo che possano di conseguenza fornire il valore o 43 eseguire l'azione richiesta internamente il suo valore. da questa proprietà per cambiare Nel nostro caso, per attivare la proprietà view, è stato necessario enumerarla tra gli argomenti utilizzati, all'inizio del codice: enum { ARG_0, ARG_VIEW, }; In fase di inizializzazione della classe del plugin ( gst_humor_class_init), si è dovuto installare la proprietà, definendone alcune caratteristiche, tipo il nome, la descrizione etc. g_object_class_install_property g_param_spec_boolean ( ( gobject_class, "view", "View", video", FALSE, G_PARAM_READWRITE ) ); ARG_VIEW, "Show output Infine sono state definite le due funzioni _get_property() e _set_property(), il cui compito consiste rispettivamente nell'impostare o leggere la proprietà view dall'applicativo, con il quale si interagisce tramite la variabile filter; in entrambe le funzioni, le operazioni sulla proprietà vengono gestite dal costrutto switch...case, in corrispondenza del caso in cui l'argomento prop_id assume valore ARG_VIEW. static void gst_humor_set_property ( GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec ) { GstHumor * filter; g_return_if_fail ( GST_IS_HUMOR ( object ) ); filter = GST_HUMOR ( object ); switch ( prop_id ) { case ARG_VIEW: { 44 filter->h_props.view = g_value_get_boolean ( value ); break; } default: { G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, prop_id, pspec ); break; } } if ( filter->netcam ) netcamSetProperties( filter->netcam, &filter->h_props ); } static void gst_humor_get_property ( GObject *object, guint prop_id, GValue *value, GParamSpec *pspec ) { GstHumor * filter; g_return_if_fail ( GST_IS_HUMOR ( object ) ); filter = GST_HUMOR ( object ); switch ( prop_id ) { case ARG_VIEW: { g_value_set_boolean ( value, filter->h_props.view ); break; } default: { G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, prop_id, pspec ); break; } } } Come si vede dal codice, per rendere attiva su HuMoR l'impostazione della proprietà definita sulla pipeline, viene richiamata una variabile che veniva in precedenza controllata dall'interfaccia dell'applicazione, al fine di modificarla opportunamente in corrispondenza del valore assegnato alla proprietà dell'elemento; in maniera analoga a quanto succedeva in precedenza per lo scambio del flusso dei dati in input, questa variabile è presente nel codice di HuMoR ed è accessibile dal plugin in virtù dell'inclusione della libreria libgsthumor.la. 45 I pad di uscita dell'elemento humor L'ultimo passo da compiere per completare la definizione dell'elemento consiste nel definire le uscite che esso deve fornire in seguito alla elaborazione dei dati. Senz'altro i dati più di interesse prodotti dall'elaborazione di HuMoR sono le features risultanti che caratterizzano gli oggetti individuati e il tracking del loro movimento; questi dati sono particolarmente rilevanti essendo le informazioni principali su cui si baseranno le successive fasi di learning e di classificazione. Risulta altresì utile fornire in uscita il flusso video che è stato processato al fine di conservarne una copia nel caso in cui si abbia necessità di riprocessare nuovamente il flusso a posteriori su HuMoR, o semplicemente si voglia verificare di persona l'evento avvenuto. L'elemento humor è stato perciò dotato di due source pads, su uno dei quali viene semplicemente riversato il flusso video in ingresso; su tale pad il tipo di dati ammesso, analogamente al pad di ingresso, è un generico flusso "x-raw-rgb". Per quanto riguarda l'altro pad, la situazione risulta più complessa: le informazioni da restituire riguardanti le features degli oggetti e del tracking sono presenti in HuMoR sotto forma di array mutidimensionali; il problema che si presenta consiste quindi nel trovare un modo per trasferire questi dati da HuMoR all'elemento che lo incorpora e dall'elemento ad un flusso di uscita su un source pad in maniera efficiente e tenendo ben presente l'utilizzo finale di questi dati. In accordo con gli sviluppatori di HuMoR è statto deciso che il formato più adeguato per l'esportazione delle features sarebbe stato su un file di testo, formattato in modo da definire un raggruppamento dei dati che rispetti il seguente ordinamento: frame video analizzato, tipo di 46 features analizzato (features dell'oggetto o del tracking) ed oggetto analizzato; in corrispondeza di ogni oggetto riportato segue una lista di features (separata da punti e virgola) facenti parti dell'oggetto in considerazione. Con questo formato si rende possibile il parsing a posteriori del file per recuperare le features ed utilizzarle nelle fasi successive di learning e classificazione. Stabilito questo standard rimane da decidere quali devono essere i passaggi, partendo dalle informazioni contenute negli array, per arrivare a trasferire i dati sul file. Questa problematica, apparentemente di facile risoluzione, richiede particolare attenzione a due aspetti: 1. in primo luogo bisogna considerare che queste informazioni non dovranno essere inviate direttamente sul file di testo in questione, ma dovranno prima passare attraverso il pad di uscita dell'elemento humor e dovranno essere trattate pertanto come un flusso di dati; la ragione di ciò risiede nel fatto che, essendo humor un elemento di GStreamer, risulta necessario che qualsiasi suo input o output venga gestito in maniera standard, conforme ad un qualsiasi altro elemento; in conseguenza a ciò, qualsiasi uscita o ingresso deve essere trattata come flusso di dati ed essendo humor un elemento filtro (non un source o un sink), non può che accettare o fornire in uscita dati provenienti o diretti verso un altro pad di un altro elemento della pipeline. La fase finale in cui i dati saranno scritti su un file di testo sarà effettuata da un elemento apposito (un sink per l'appunto) in grado di accettare un flusso di dati e redirigerlo scrivendolo sul filesystem su un opportuno percorso specificato (ovviamente GStreamer è già dotato di tale elemento sink, il filesink). 47 La scelta di imporre questo standard proviene essenzialmente dalla necessità di garantire la scalabilità e la standardizzazione proprie di GStreamer, requisiti essenziali e punti di forza del framework stesso, grazie ai quali si riesce a garantire la applicabilità dei suoi filtri in qualsiasi ambito, oltre alla facile e veloce estendibilità delle funzionalità dei filtri stessi. 2. il secondo aspetto è in parte collegato al primo, in quanto permette di mantenere ed ampliare le caratteristiche di scalabilità e generalizzazione considerazioni e le dell'output modifiche dell'elemento, da applicare anche se stavolta le non riguardano la conformità con standard richiesti da GStreamer; si consideri infatti il caso in cui, in una successiva versione di HuMoR (si tenga conto che tuttora il progetto è in fase di ampliazione e sviluppo) vengano introdotte nuove features o ulteriori dati di interesse per le analisi successive: se l'elemento si occupasse semplicemente di recuperare i dati dagli array e fornirli in uscita staticamente in un formato predefinito, sarebbe necessario modificare anche la parte del plugin che si occupa della formattazione per includere anche la nuova informazione introdotta. Inoltre il requisito di trasferire le informazioni come flusso di dati imposto dal primo punto tende a far prestare attenzione ad un aspetto in particolare: i dati vanno estratti dagli array di provenienza e posti in un flusso di uscita in un formato tale da permettere il loro recupero e la loro aggiunta nel file di testo, in modo che siano opportunamente formattati. Queste considerazioni ci fanno capire il motivo per cui sia stato necessario progettare un sistema per impacchettare i dati ottenuti dagli 48 array delle features in blocchi di dati strutturati in maniera tale da essere indipendenti dalle features che ne fanno parte. In seguito a questa decisione riguardante la gestione dei dati, si è creduto opportuno definire un ulteriore elemento sul plugin, appositamente adibito alla gestione del flusso dei dati delle features in uscita, in modo da rendere humor svincolato dalle informazioni sulle features richieste in uscita; tale elemento, chiamato DataDemuxer, è stato progettato in modo da prendere in ingresso il flusso dei dati informativi in uscita da humor in un formato stabilito e restituire solo i dati di interesse (mediante una selezione effettuata tramite una proprietà dell'elemento) su un flusso redirezionabile su un file di testo. I dettagli sull'implementazione di tale elemento e sullo standard stabilito per i blocchi di dati contenenti le features in uscita da humor saranno trattati DataDemuxer). in seguito (v. Capitolo 4 - L'elemento Sul pad di uscita rimanente dell'elemento vengono inviati pertanto i dati restituiti da una funzione definita su HuMoR (ancora una volta operante in maniera analoga a quelle descritte per lo scambio dati con il pad di ingresso e per le proprietà dell'elemento) che si occupa di recuperare i dati informativi e impacchettarli opportunamente. 49 Conclusioni La progettazione dell'elemento è a questo punto completa: non rimane che compilare il codice del plugin in forma di libreria (libgsthumor.la) per poterlo utilizzare con GStreamer. Naturalmente, per poter far riconoscere il nuovo plugin, è necessario apportare le necessarie modifiche al framework per permettere di includerlo ed utilizzarlo. Ciò può essere ottenuto in più modi: • è possibile registrare il plugin in modo che venga definitivamente incluso tra i plugin caricati all'avvio da GStreamer (e quindi risulti sempre disponibile ad ogni utilizzo del framework) attraverso la funzione • gst-register. si può caricare dinamicamente ad ogni utilizzo di GStreamer il plugin attraverso la direttiva --gst-plugin-load="percorso_al_plugin" specificandola ad esempio come argomento del comando gstlaunch (che permette di eseguire una pipeline direttamente specificata da linea di comando) o gst-editor (che lancia l'editor visuale di pipelines in xml e ne permette l'esecuzione). Considerando che il plugin è tuttora in continua fase di sviluppo e messa a punto è stato scelto di utilizzare il secondo metodo, in quanto rende possibile l'uso dell'ultima versione del plugin ad ogni utilizzo del framework, evitando di rieseguirne la registrazione ad ogni modifica e ricompilazione del codice. La pipeline è adesso in grado di riconoscere correttamente e far funzionare l'elemento humor a tutti gli effetti, ma rimane ancora un passo da compiere per poterlo davvero utilizzare in pratica: è 50 necessario collegare il suo source pad adibito a fornire le features come uscita ad un elemento in grado di riconoscere il flusso e formattarlo in un file di testo per poter rendere disponibili tutti i risultati dell'elaborazione; senza di esso avremmo un elemento a tutti gli effetti funzionante ma che fornisce uno degli outputs in un formato non intelligibile. 51 Capitolo 4 - L'elemento DataDemuxer Lo scopo di questo elemento consiste, come detto in precedenza, nella conversione di un generico flusso di dati in uscita dall'elemento humor, contenenti i risultati dell'elaborazione (features di vario tipo ed eventualmente altre informazioni) in dati formattati destinati all'output su file di testo. Funzione di questo elemento è anche la gestione di quali informazioni andare a trasferire sull'uscita: è infatti noto da precedenti considerazioni come risulta necessario sia decidere quali features o proprietà si desidera avere disponibili per una successiva analisi (training e classificazione); è plausibile che si desideri impostare un formato differente in cui compaiano non tutte le informazioni, ma solo quelle necessarie all'analisi, come è altrettanto possibile che un successivo sviluppo di HuMoR stesso imponga la presenza di nuove features o informazioni precedentemente non esistenti, che richiedano una modifica al formato del file di testo risultante per far si che esse vengano incluse. L'implementazione di queste funzionalità all'interno di un plugin richiede innanzitutto, analogamente al caso precedente in cui abbiamo analizzato l'inclusione delle funzionalità di HuMoR in un elemento, che venga predisposto un sink pad opportunamente impostato per accettare correttamente i dati in ingresso da processare. Per comprendere a fondo come è strutturato il formato di tale flusso e le modalità con cui è necessario procedere per elaborarlo correttamente, al fine di estrarne le informazioni richieste, è importante a questo punto analizzare a fondo come le informazioni vengono impacchettate al momento immediatamente precedente in cui vengono immesse nel pad di uscita dell'elemento humor. 52 Formato dei dati in uscita dall'elemento humor Come già anticipato in precedenza, il formato con cui i dai vengono forniti in uscita da humor è strutturato in modo da consentire che le informazioni, indipendentemente dalla loro natura o da quante e quali esse siano, vengano impacchettate e inserite in un flusso generico di dati in modo da essere ricevute dall'elemento DataDemuxer. Ciò implica che la modalità con cui si vanno ad impacchettare le informazioni e la struttura stessa dei dati impacchettati rimangano svincolate dalla loro natura e quantità, bensí consentano di aggiungere o rimuovere al flusso qualsiasi dato a prescindere dal suo tipo e dalle sue dimensioni in qualsiasi momento. Tale requisito può essere soddisfatto progettando una struttura dati autoesplicativa, ovvero contenente al suo interno non solo le informazioni da scambiare tra gli elementi, ma anche un overhead di intestazioni che permettano di ricostruire a posteriori la struttura stessa del flusso trasferito. In questo modo si rende possibile l'inserimento o la eliminazione di qualsiasi informazione, essendo la struttura dinamicamente riadattabile alle variazioni in virtù delle intestazioni, le quali vengono scritte sul flusso nel momento stesso in cui i dati vengono impacchettati e sono variabili in dipendenza dei dati stessi. E' importante che in fase di definizione di tale formato di esportazione si tenga conto di un altro aspetto fondamentale: si è detto essere funzione principale dell'elemento DataDemuxer l'esportazione dei dati provenienti da humor in un formato testuale opportunamente formattato; si è detto anche come fosse importante dare l'opportunità di selezionare quali tra le informazioni presenti sul flusso si avesse necessità di inviare all'output finale. 53 Risulta evidente che la selezione che verrà effettuata dall'elemento DataDemuxer dovrà avvenire secondo una modalità che tenga conto della variabilità dei dati presenti sul flusso ed andrà progettata di conseguenza in modo da poter gestire correttamente tale variabilità; inoltre la struttura di un flusso di dati non staticamente organizzato dovrà essere progettata in modo da consentire che la selezione delle informazioni da estrarre possa essere effettuata, implicando che, sia la progettazione del selettore dell'elemento che dello standard di impacchettamento correlatamente. dei dati avvenga contemporaneamente e Nel rispetto di tali prerequisiti, la struttura dati è stata definita innanzitutto stabilendo la presenza di una intestazione contenente informazioni generiche sul pacchetto dati inviato: Come prima informazione viene aggiunta la versione dello standard del formato dei dati utilizzato, seguita dal secondo campo contenente il numero di elementi informativi che sono contenuti nel pacchetto. Tali elementi possono pertanto essere presenti in un numero variabile nel pacchetto; essi sono i veri e propri contenitori delle informazioni prodotte da humor e sono caratterizzati da una sottostruttura organizzata secondo lo standard che si può schematizzare con il seguente diagramma: 54 Ogni elemento è costituito da 4 campi, 3 caratterizzati da una dimensione fissa, e l'ultimo invece da dimensione variabile. Il primo ed il secondo campo contengono due stringhe che rappresentano il nome ed il tipo (intero, stringa, array, etc.) dell'elemento esportato rispettivamente, il terzo un intero che rappresenta la dimensione in bit dei dati contenuti nell'elemento; infine il quarto campo contiene i dati veri e propri dell'elemento ed è caratterizzato da una dimensione variabile in funzione della quantità dei dati esportati con quell'elemento; tale dimensione è quella indicata nel terzo elemento. Si noti come tutte le informazioni necessarie per la ricostruzione della struttura originaria dei dati siano presenti: il secondo campo 55 dell'intestazione ci permette di venire a conoscenza di quanti elementi sono contenuti nel pacchetto; di conseguenza risulta facile, scorrendo un elemento per volta letto dal flusso dei dati, estrarli ad uno ad uno fino ad arrivare alla fine del pacchetto, sapendo esattamente quanti sono e quindi quando fermarsi nell'estrazione. Inoltre, avendo a disposizione per ciascun elemento un campo (il terzo, preceduto da due campi a dimensione fissa, quindi inequivocabilmente localizzabile) contenente la dimensione occupata, risulta possibile risalire con esattezza a conoscere quanta memoria è stata allocata per il quarto campo variabile dell'elemento, quindi delimitare con esattezza il termine dei dati relativi ad ogni singolo elemento. Questa organizzazione articolata secondo una sequenza di elementi permette inoltre di rispettare il requisito di correlazione con il selettore di informazioni da estrarre presente nel DataDemuxer: le informazioni provenienti da humor vengono infatti impacchettate ciascuna in un elemento, seguendo sempre la stessa sequenza; pertanto, su due pacchetti differenti saranno sempre contenute (a meno che non venga aggiornato lo standard con cui il pacchetto viene assemblato, evento comunque verificabile dall'intestazione del pacchetto) due occorrenze della stessa informazione in due elementi posti nella stessa posizione nel pacchetto; ad esempio prendendo da due pacchetti il terzo elemento presente sarà possibile ottenere l'array contenente le features del primo oggetto rilevato da HuMoR. In virtù di questa proprietà risulta banale progettare un selettore su DataDemuxer in grado di estrarre le informazioni desiderate, semplicemente facendo in modo che la scelta avvenga in base alla posizione dell'elemento da estrarre. 56 Nel caso in cui lo standard dei pacchetti venisse modificato, sarebbe possibile estrarre la stessa informazione richiamando sul selettore una posizione diversa ove recuperare lo stesso elemento; ciò può essere fatto anche in fase di esecuzione della pipeline su GStreamer, senza modificare una riga di codice del plugin, rispettando i requisiti di adattabilità e scalabilità esposti in precedenza. In conclusione, i dati in uscita da humor vengono impacchettati in maniera conforme questo formato; al momento in cui i pad degli elementi vengono collegati e lo scambio dei dati avviene, il sink pad di DataDemuxer riceverà tale flusso in ingresso; sarà questo pertanto il formato del flusso di dati che l'elemento dovrà riconoscere ed elaborare. 57 Definizione dei pad di ingresso dell'elemento DataDemuxer Il primo passo per la costruzione dell'elemento DataDemuxer è stato quindi quello di definire un sink pad per permettere l'input dei dati provenienti da humor. Tale pad è in grado di accettare dati nel formato appena definito e di collegarsi con il source pad corrispondente di humor, ovvero quello abilitato per fornire le informazioni ottenute dall'elaborazione. I dati così ottenuti vengono copiati in una locazione di memoria in attesa di essere elaborati ed opportunamente estratti dalla struttura pacchettizzata. 58 Controllo dell'esportazione delle informazioni Si è già spiegato come, in virtù della struttura con cui i dati vengono esportati, sia possibile estrarre ogni singolo elemento informativo presente sul flusso attraverso una selezione basata sulla posizione sequenziale degli elementi, ovvero come sia possibile recuperare la stessa categoria di informazione accedendo all'elemento alla stessa kesima posizione nel pacchetto. Grazie a questo tipo di struttura è possibile progettare un selettore che permetta di scegliere quali elementi recuperare basandosi sulla loro posizione all'interno del pacchetto: se, ad esempio, tenendo presente che le features degli oggetti sono recuperabili estraendo il quarto elemento nel pacchetto e le features del tracking estraendo il quinto, è sufficiente impostare il selettore in modo che recuperi sempre il quarto ed il quinto elemento dal pacchetto dati per ottenere le informazioni desiderate relativamente ad ogni pacchetto ricevuto. Resta quindi da definire un metodo di interfacciamento con l'utente che permetta di impostare le proprietà del DataDemuxer in modo da specificare le posizioni degli elementi da estrarre. 59 Requisito primario da considerare in fase di progettazione del selettore è che lo stesso consenta, in caso di variazione dello standard dei pacchetti ricevuti (aggiunta, rimozione o modifica di uno o più elementi), di consentire di adattare la selezione al nuovo standard senza necessità di modificare la struttura del selettore stesso e di una conseguente ricompilazione del plugin. Al fine di rispettare questi requisiti, una possibile soluzione può essere quella di codificare in binario la posizione degli elementi da estrarre, in modo da assegnare ad ogni bit di tale numero un elemento e con esso attivare, in base al suo valore, l'esportazione o lo scarto dello stesso. La scelta, pertanto, degli elementi in sequenza da attivare viene rappresentata da un numero binario: se ad esempio volessimo esportare il primo, il terzo ed il quarto elemento da un pacchetto contenente 5 elementi, il numero che rappresenterebbe questa scelta sarebbe 01101. Si noti come, rispetto ad eventuali variazioni dello standard dei pacchetti ed alla modalità con cui le informazioni in esso contenute vengono organizzate, tale sistema rimane completamente non condizionato, in quanto codifica la posizione degli elementi da attivare; è sufficiente pertanto prestare attenzione alla nuova posizione degli elementi, spostando conseguentemente le cifre in maniera opportuna, al fine di attivare correttamente quelle relative agli elementi desiderati, adesso presenti in una differente posizione. Risulta necessario, se l'aumento del numero di elementi lo richiede, estendere o ridurre eventualmente l'ordine di grandezza del numero binario utilizzato. Inoltre è possibile convertire tale cifra in decimale in modo da ottenere una rappresentazione più compatta degli elementi da estrarre per fornire una interfaccia migliore; risulta così possibile all'utente finale 60 selezionare gli elementi semplicemente immettendo una cifra decimale ottenuta dalla conversione della cifra binaria che codifica la posizione degli elementi da esportare, in maniera analoga allo standard utilizzato negli ambienti relativamente ai operativi permessi Unix/Linux di con il comando lettura/scrittura/esecuzione chmod, di un file/directory. Nel caso trattato nell'esempio precedente il numero in questione sarebbe pertanto 13. Stabilito il tipo di interfacciamento da utilizzare per il controllo della selezione degli elementi, rimane da dotare DataDemuxer della relativa proprietà, utilizzabile in seguito da una pipeline di GStreamer. Analogamente a quanto fatto per l'elemento humor, è necessario inserire nel codice le opportune direttive e primitive per l'attivazione della proprietà, chiamata "select"; inoltre sono state definite funzioni per la conversione in binario del numero immesso e l'estrazione delle informazioni in dipendenza di tale valore. In particolare, la seconda funzione introdotta estrae dal flusso dati in ingresso gli elementi selezionati nel Demuxer, parsandoli ad uno ad uno dalla struttura dati e passando in output solo quelli scelti tramite il selettore. Tale processo avviene in maniera inversa a quando accade in fase di impacchettamento delle informazioni all'interno dell'elemento humor: viene innanzitutto estratto l'header del pacchetto contenente le informazioni sulla versione dello standard del pacchetto ed il numero degli elementi in esso presenti; successivamente ha inizio il parsing del pacchetto elemento per elemento. Vengono estratte le informazioni sul nome, il tipo e la dimensione dei dati presenti nell'elemento, dopodichè, ottenuta tale informazione, se dal valore immesso nel selettore risultava che questo elemento fosse selezionato per l'esportazione, vengono estratti ed inviati in output i dati relativi ad 61 esso; l'estrazione si interrompe in corrispondenza del termine del blocco dati dell'elemento (posizione conosciuta dal campo estratto dall'header dell'elemento, contenente le informazioni sulle dimensioni dei dati). Nel caso in cui l'elemento non risulti invece contrassegnato per l'esportazione i dati vengono ignorati ed il parsing prosegue dopo la fine dell'elemento considerato, prendendo quindi in esame il prossimo in successione. 62 I pad di uscita dell'elemento DataDemuxer I dati che in questo modo vengono estratti vanno opportunamente formattati e convertiti in un formato opportuno che consenta di generare un flusso in uscita, reindirizzabile su un normale file di testo. Ogni blocco informativo contenuto in ciascun elemento viene pertanto accodato in una stringa che sarà a sua volta successivamente copiata sul pad di uscita dell'elemento. In virtù della continuità dello streaming dei dati, gli elementi sarebbero esportati in successione delimitazione che uno dopo l'altro, senza una precisa permetta di differenziarli e distinguerne l'appartenenza all'uno o all'altro frame elaborato da humor. E' per questo motivo che è stato anteposto, in fase di esportazione, un delimitatore che rappresenta l'inizio dei dati relativi ad un particolare frame; come informazione disambiguante e discernente viene inserito come componente del delimitatore un timestamp che indica l'ora in cui l'accodamento dell'informazione ha luogo. Un esempio di una stringa così costruita è il seguente: ---Frame 1626211271839548--ove il timestamp rappresenta il tempo in secondi trascorso da una data di riferimento (00:00:00 UTC, January 1, 1970) alla data di sistema corrente. Stabilito questo riferimento, la stringa così creata viene accodata alla stringa di esportazione, seguita dalle stringhe contenenti i dati dell'elemento selezionati per l'esportazione, tutti appartenenti allo stesso frame cui l'intestazione fa riferimento; queste stringhe sono costruite accodando le informazioni opportunamente in base al tipo dei dati estratto. 63 Di seguito è riportato un esempio della stringa così costituita, relativa ad un frame: ---Frame 1626211271839548--changedPixels int 4 261 FeaturesTrain str 480 Obj#: 0 0,114754; 164; 138,77; 281,378; 1,14713; 1,71488; -0,385184; 32,0051; 88,7257; 0,983825; 0,0379895; -0,029015; 0,00783974; 0,00111344; 0,000316232; -0,543903; -1,0715; 1,84148; 1, 90829; 3,78318; 1,37803; 9,74444; 1,10378; 16,0804; Obj#: 1 0,114754; 164; 138,77; 281,378; 1,14713; 1,71488; -0,385184; 32,0051; 88,7257; 0,983825; 0,0379895; -0,029015; 0,00783974; 0,00111344; 0,000316232; -0,543903; -1,0715; 1,84148; 1, 90829; 3,78318; 1,37803; 9,74444; 1,10378; 16,0804; FeaturesTrackTrain str 214 ObjTrack#: 0 6,62157; 5,79922; 5,57922; 67,1322; 59,62; 59,9886; 31,9327; 27,9294; 25,9422; 229; 117; 47; ObjTrack#: 1 6,62157; 5,79922; 5,57922; 67,1322; 59,62; 59,9886; 31,9327; 27,9294; 25,9422; 229; 117; 47; Una volta esaurite le informazioni contenute nel pacchetto (che corrispondono all'elaborazione di un frame), la stringa così costruita viene inviata al pad di uscita dell'elemento di GStreamer. 64 Tale pad è stato ovviamente preimpostato per l'handling di dati di tipo testuale, in modo da consentire, tramite il collegamento con un elemento filesink, il redirect su file dell'uscita ottenuta. 65 Conclusioni Analogamente al caso dell'elemento precedentemente trattato, il plugin viene compilato ed utilizzato su una qualunque pipeline di GStreamer includendolo dinamicamente tramite l'istruzione --gst-pluginload, scelta derivante dalle stesse motivazioni esposte per il plugin relativo a HuMoR. 66 Capitolo 5 - Definizione delle pipelines su GStreamer Con la creazione di questi nuovi plugins è finalmente possibile definire delle pipelines che possano sfruttare le potenzialità di HuMoR in maniera facile e versatile. Come già detto, definendo una pipeline è possibile controllare, modificare ed analizzare un qualsiasi flusso di dati, consentendo pertanto di trovare soluzioni per una vasta gamma di casistiche; ciò che serve ai nostri scopi è uno strumento che sia in grado di elaborare una sequenza video proveniente da una videocamera, filtrandola attraverso HuMoR al fine di analizzarla ed estrarre le features relative agli oggetti ed al tracking degli stessi. Le modalità per definire una pipeline sono molteplici: è possibilie specificarne una e lanciarla in esecuzione direttamente da una unica riga di comando tramite il comando gst-launch, oppure creando un opportuno file xml contenente l'intera struttura della pipeline, eseguibile tramite gst-xmllaunch; esiste inoltre il comodo ed intuitivo tool per la creazione di pipelines tramite interfaccia grafica esposto in precedenza, gst-editor: tramite esso è possibile inserire gli elementi che servono, reperendoli da un comodo browser di elementi (catalogati gerarchicamente in una struttura da albero organizzata secondo le loro categorie o sottocategorie di appartenenza), in un foglio di lavoro rappresentante la pipeline; è sufficiente in seguito collegare opportunamente tra loro i pad degli elementi e lanciare in esecuzione la pipeline così creata. Viene data inoltre la possibilità di salvare o caricare la pipeline in formato xml. Per il nostro lavoro è stato necessario sia utilizzare l'editor grafico che il launcher da riga di comando, essendo entrambi due approcci utili e 67 complementari tra loro per la definizione, la prova, la modifica, il salvataggio e l'utilizzo finale della pipeline. Se da un lato infatti l'editor grafico risulta comodissimo per il facile reperimento degli elementi desiderati, il linking dei pad (si è immediatamente in grado di verificare se ci sono problemi, come ad esempio se due pad supportano tipi di dati non compatibili tra loro), il salvataggio ed un primo testing, il launcher risulta indispensabile per la creazione finale di scripts parametrizzabili che usino pipelines e per una più approfondita sessione di testing. 68 Sintassi del comando gst-launch La sintassi per definire una pipeline tramite il comando gst-launch è estremamente semplice ed immediata: è sufficiente elencare la sequenza degli elementi desiderati, separandoli con un punto esclamativo, nell'ordine con il quale si vuole che il processing del flusso dei dati abbia luogo. Se si desidera cambiare il valore di una proprietà di un elemento è sufficiente postporre, al nome dell'elemento, il nome della proprietà ed assegnargli il valore desiderato; ad esempio filesrc location=start.avi assegna alla proprietà location dell'elemento filesrc il valore start. avi, facendo in modo che l'elemento processi il file start.avi e restituisca in uscita un flusso contenente i dati relativi ad esso. Vediamo ad esempio con quale semplicità si riesce a definire un riproduttore di file avi: gst-launch-0.8 name=spider ! xvimagesink } filesrc { queue location=start.avi ! osssink } { spider ! spider. ! queue ! La pipeline è composta da un elemento filesrc che, come spiegato nell'esempio precedente, accede al file "start.avi" e ne estrae i dati per immetterli nel flusso elaborato da GStreamer. spider invece è un autoplugger che si occupa di collegare tramite gli elementi opportuni le sink e le sources al quale sono state collegate. Nella fattispecie gli elementi da sostituire per far funzionare la pipeline sono essenzialmente un demuxer avi e i decodificatori audio e video per ottenere i due flussi corrispondenti da mandare in riproduzione alle 69 rispettive uscite. Una pipeline corrispondente potrebbe essere la seguente: gst-launch-0.8 filesrc location=start.avi avidemux ! name=demuxer ! { queue ! mad ! osssink } { demuxer. ! queue ! ffdec_mpeg4 ! xvimagesink } In primo luogo il flusso viene processato tramite avidemux, il quale si occupa della separazione del video avi in ingresso in due flussi separati audio e video; tali flussi vengono parallelamente processati con gli opportuni decoder, mad per l'audio e ffdec_mpeg4 per il video, dopodichè vengono xvimagesink, inviati alle rispettive uscite, osssink e le quali non fanno altro che prendere il flusso decodificato (quindi adesso in formato raw) e mandarlo nel relativo dispositivo di output per la riproduzione sincrona audio/video. Questo esempio ci mostra con quale rapidità si riesce ad ottenere un semplice riproduttore audio/video con GStreamer; è anche facile capire che tale pipeline può essere espansa con altri elementi in grado di effettuare le più variegate operazioni sul flusso, in modo da estendere le potenzialità e le funzionalità del player, fornendo un alto grado di scalabilità ed estendibilità. 70 Un semplice visualizzatore: "focus" Andiamo adesso ad analizzare un altro semplice script, creato con lo scopo di ottenere un banale visualizzatore del flusso di immagini ripreso da una videocamera di rete, utile per verificare che l'inquadratura comprenda l'area di visualizzazione desiderata e per la corretta messa a fuoco dell'obiettivo. Questo script risulterà particolarmente utile per predisporre correttamente la videocamera prima di procedere con l'utilizzo di HuMoR vero e proprio. Il codice contenuto nello script è il seguente: #!/bin/sh SCRIPTS_PATH="/home/gas/Tesi/scripts" GSTLIBS_PATH ="/home/gas/Tesi/svn/humor/trunk/humor/debug/src/gst-plugin" PIPELINE="urlsrc url=http://localhost:8000/IMAGE.JPG ! jpegdec ! ffmpegcolorspace ! ximagesink" sh $SCRIPTS_PATH/xpluglaunch.sh & gst-launch-0.8 --gst-plugin-load=$GSTLIBS_PATH/libgsturlsrc. la $PIPELINE ps xww | grep "xplug" | cut -c1-5 | xargs -i kill {} 2> /dev/null Lo script è inizialmente costituito dalla definizione di tre variabili: GSTLIBS_PATH, che conterrà il percorso assoluto delle librerie contenenti i plugin aggiuntivi da includere in GStreamer, PIPELINE, nella quale sarà contenuta ovviamente la pipeline da lanciare in esecuzione e SCRIPTS_PATH, contenente il percorso di tutti gli scripts. Come prima operazione, viene lanciato uno script secondario in background per l'attivazione di xplug (vedi Cap. Connessione della videocamera tramite xplug), in modo da rendere disponibili le 71 immagini catturate dalla videocamera sull'host locale per poterle poi passare in ingresso alla pipeline. Il passaggio della pipeline come argomento del comando gst-launch è preceduto da una opzione , la --gst-plugin-load, che permette di importare la libreria libgsturlsrc.la; in questo modo si aggiunge agli elementi disponibili su GStreamer il primo elemento utilizzato nella pipeline, urlsrc. Tale elemento non fa altro che immettere nella pipeline un flusso di dati proveniente da un URL specificato (nel nostro caso il file IMAGE.JPG ottenuto da localhost sulla porta 8000, ovvero il socket utilizzato da xplug per fornire le immagini ottenute dalla videocamera). Successivamente la sequenza di immagini immessa nel flusso viene processata tramite jpegdec, che si occupa della decodifica delle immagini in ingresso in formato jpg; il flusso di immagini così convertito in formato raw viene filtrato tramite ffmpegcolorspace, un elemento che premette di convertire lo spazio di colori utilizzato nella sequenza in modo da essere compatibile con l'elemento successivo, l'ximagesink, un sink video che invia il flusso ad una finestra di visualizzazione di output video. Infine, quando l'esecuzione della pipeline è terminata, lo script procede con la sua ultima istruzione, adibita alla terminazione dell'esecuzione di xplug, in modo da interrompere l'acquisizione delle immagini dalla videocamera, liberando risorse di rete e l'occupazione della CPU sull'host locale. 72 Acquisizione di filmati con "cam2avi" Una delle caratteristiche più interessanti di HuMoR consiste nella capacità di riconoscere e classificare le azioni compiute in una sequenza video; ma per essere in grado di riconoscere una azione è necessaria una precedente fase di learning, in cui alcune sequenze contenenti la stessa azione ripetuta più volte vengono passate ad HuMoR, segnalando l'appartenenza di esse ad una stessa classe di comportamento. HuMoR come applicazione stand-alone permette di fare learning su una sequenza video ripresa in diretta da una videocamera; pertanto è necessario, nella fase di addestramento relativa ad una particolare azione, ripetere la stessa azione lanciando il learning con HuMoR per un numero sufficiente di volte. Questa operazione risulta in determinate situazioni non solo poco pratica ed imprecisa (è possibile che una azione non avvenga nella maniera desiderata per qualche errore o disturbo), ma non consente di conservare una traccia delle azioni effettuate, ovvero una copia degli esempi, riutilizzabili per verifiche a posteriori o per una nuova fase di learning. Da queste considerazioni nasce l'esigenza di organizzare l'apprendimento in maniera tale da poter agevolmente creare un learning set, avendo eventualmente la possibilità di ripetere un esempio non andato a buon fine per un qualsiasi errore in maniera rapida e semplice, tendendo inoltre ben presente l'obbiettivo di separare la fase di raccolta degli esempi da quella di analisi di HuMoR (learning vero e proprio). Il compito di "cam2avi" mira a soddisfare la prima fase, in cui gli esempi devono essere ripresi e salvati opportunamente su file, organizzandoli in modo da costituire un learning set ordinato e 73 facilmente rianalizzabile tramite HuMoR in un secondo momento per l'estrazione delle features. Vediamone il codice: #!/bin/sh if [ $# -ne 2 ] ; then echo "cam2avi: Errore negli argomenti" echo "Sintassi: cam2avi ESEMPIO ISTANZA" exit 0 fi SCRIPTS_PATH="/home/gas/Tesi/scripts" GSTLIBS_PATH ="/home/gas/Tesi/svn/humor/trunk/humor/debug/src/gst-plugin" LEARNINGSET_PATH="/home/gas/Tesi/esempi" PIPELINE="urlsrc url=http://localhost:8000/IMAGE.JPG ! jpegdec ! videorate ! video/x-raw-yuv,framerate=25.0 ! avimux ! filesink location=$LEARNINGSET_PATH/$1/video/$2.avi" sh $SCRIPTS_PATH/xpluglaunch_silent.sh >/dev/null & mkdir $LEARNINGSET_PATH/$1 mkdir $LEARNINGSET_PATH/$1/features mkdir $LEARNINGSET_PATH/$1/video gst-launch-0.8 --gst-plugin-load=$GSTLIBS_PATH/libgsturlsrc. la $PIPELINE ps xww | grep "xplug" | cut -c1-5 | xargs -i kill {} 2> /dev/null Questo script richiede l'immissione di due argomenti, come si può notare dalle prime righe del codice, con le quali si controlla che il numero dei parametri inseriti insieme al comando siano esattamente uguali a 2; in caso contrario, l'esecuzione dello script viene terminata restituendo in output un messaggio di errore che riporta inoltre la corretta sintassi del comando. Se il numero degli argomenti passati è corretto, lo script procede nella sua esecuzione lanciando xplug, come nel caso precedente di focus. Il primo argomento richiesto sarà utilizzato come nome da assegnare alla categoria dell'esempio, ed il secondo corrisponderà invece all'istanza dell'esempio ripresa dalla specifica esecuzione dello script, 74 informazioni che vengono utilizzate per una ordinata organizzazione del learning set; partendo da una directory radice specificata nella variabile LEARNINGSET_PATH, viene creata una struttura costituita da una sottodirectory principale relativa all'esempio in esame (prima istruzione mkdir), ovviamente etichettata con il primo argomento passato allo script, ovvero il nome scelto per l'esempio. Le due successive mkdir non fanno che creare due sottodirectory di quella appena creata, features che sarà adibita ovviamente a contenere le informazioni risultanti dall'analisi successivamente effettuata tramite HuMoR, e video sulla quale saranno immagazzinate le sequenze video catturate tramite questo stesso script. La struttura appena esposta può essere più semplicemente compresa tramite la seguente immagine, che mostra l'albero di directory che costituisce un learning set. Nel caso specifico, la struttura è costituita da una serie di esempi ( enter_diath, enter_fox, exit_diath etc.), ed in particolare si può osservare la sottostruttura interna del primo che contiene le due directory video e features, in cui sono memorizzati rispettivamente 5 filmati corrispondenti a 5 istanze dell'esempio in esame ed i relativi file di testo prodotti dall'analisi di HuMoR. 75 La pipeline è essenzialmente identica nella sua parte iniziale a quella definita con lo script focus, ad esclusione dell'ultimo elemento ximagesink, adesso non più presente; questa prima parte si occupa come nel caso precedente di recuperare una sequenza di immagini da un URL specificato e convertirla in un flusso video impostandone il corretto spazio di colori. Il video stavolta però viene successivamente processato tramite una serie di elementi in modo da salvarlo in formato avi nella opportuna 76 locazione in precedenza prestabilita (sottodirectory video all'interno della directory relativa all'esempio). Per prima cosa vengono impostate alcune proprietà del flusso video per garantirne una corretta rielaborazione e riproduzione a posteriori; di ciò si occupa l'elemento videorate, il quale permette di abilitare la definizione e la modifica del framerate sul flusso dati processato. Come successivo elemento della pipeline è presente infatti video/x-raw-yuv, framerate=25.0, tramite il quale si specifica esplicitamente che quello che viene processato è un flusso video raw non compresso su spazio di colori yuv e con un framerate di 25 frame al secondo. A questo punto è possibile salvare il video su un file tramite filesink, specificando, con la proprietà location, come percorso di destinazione la directory video dell'esempio e come nome da assegnare al file il secondo argomento passato dell'esempio) con estensione avi. allo script (numero dell'istanza 77 Estrazione delle features con "avi2hum" Adesso che il learning set è disponibile per essere analizzato, risulta necessario definire una pipeline in grado di processare i filmati ottenuti per estrarne le informazioni relative alle features in essi rilevate. Questo è esattamente il compito che spetta al plugin di HuMoR, precedentemente descritto. Riportiamo di seguito il codice di avi2hum, lo script che prende in ingresso un filmato del learning set e ne estrae le features che lo caratterizzano, generando un file di testo opportunamente formattato: #!/bin/sh if [ $# -ne 3 ] ; then echo "avi2hum: Errore negli argomenti" echo "Sintassi: avi2hum ESEMPIO FILE_ISTANZA SUFFISSO" exit 0 fi VIDEOFILE=$2 SRC_PATH="/home/gas/Tesi/svn/humor/trunk/humor/debug/src" GSTLIBS_PATH=$SRC_PATH"/gst-plugin" LEARNINGSET_PATH="/home/gas/Tesi/esempi" PIPELINE="filesrc location=$LEARNINGSET_PATH/$1/video/$2 ! avidemux ! ffcolorspace ! humor name=hum view=TRUE hum.src1 ! DataDemuxer select=7 ! filesink location=$LEARNINGSET_PATH/$1/features/${VIDEOFILE%.avi}-$3. txt" export LD_LIBRARY_PATH=$SRC_PATH/LibHumor/.libs/: $SRC_PATH/Thread/.libs/ gst-launch-0.8 --gst-debug-level=1 --gst-pluginload=$GSTLIBS_PATH/libgsthumor.la --gst-plugin- load=$GSTLIBS_PATH/libgsturlsrc.la --gst-plugin- load=$GSTLIBS_PATH/libgstDataDemuxer.la $PIPELINE 78 Come primo argomento viene passato il nome della classe di esempi dalla quale recuperare il file video dell'istanza da elaborare, specificata come secondo argomento. Il terzo, invece, verrà utilizzato per aggiungere un suffisso al nome del file delle features, ottenuto dall'elaborazione della pipeline, allo scopo di distinguere la sessione di analisi effettuata da HuMoR; questa funzionalità è stata introdotta allo scopo di consentire la distinzione di una pluralità di analisi effettuate, ottenendo features diverse a partire dallo stesso filmato, in base a differenti configurazioni con cui l'elaborazione ha luogo. In tal modo risulta possibile effettuare il tuning dei parametri utilizzati, il confronto tra diversi algoritmi di tracking e di segmentazione, ed ogni altra modifica alla configurazione di HuMoR che vada ad influire sul risultato dell'analisi producendo un risultato differente nell'estrazione delle features. Addirittura risulta potenzialmente possibile testare una qualunque futura estensione, misurandone l'efficacia e consentendo la comparazione con sistemi già in uso tramite il semplice confronto di più file di features, distinguibili dal suffisso che ne identifica la appartenenza ad una particolare sessione avviata con una specifica configurazione di HuMoR. Analogamente allo script precedente, viene preliminarmente effettuato un controllo sul numero di argomenti passato: in questo caso è necessario che siano esattamente i tre appena descritti. Successivamente vengono definite alcune variabili, ovvero VIDEOFILE che contiene il nome del file video dell'istanza da elaborare, SRC_PATH e GSTLIBS_PATH le quali servono a risalire ai percorsi contenenti le librerie dei plugin di HuMoR, urlsrc e DataDemuxer, LEARNINGSET_PATH che contiene il percorso radice dove è memorizzato l'intero learning set, ed infine la pipeline vera e propria. 79 A differenza dei due script precedenti il primo elemento recupera il flusso dati in ingresso da un file, invece che dalla videocamera; il componente utilizzato per tale scopo è un filesrc, elemento in grado di aprire un file in lettura e trasferire il suo contenuto il un flusso di elaborazione di GStreamer. La sua proprietà location indica il percorso del filesystem da cui recuperare i dati, che nel nostro caso sono localizzati all'interno del learning set, nella sottodirectory video dell'esempio specificato (passato come primo argomento) ed il file aprire è esattamente il video dell'istanza dell'esempio passato come secondo parametro. Il formato con cui i video sono salvati da cam2avi è, come già specificato, avi non compresso; risulta pertanto necessario preprocessare il flusso tramite il demuxer avidemux ed in seguito convertire adeguatamente lo spazio di colori del flusso video tramite ffcolorspace, in modo da ottenere dei dati video standard e compatibili con il pad di ingresso del prossimo elemento: humor. A questo punto ha luogo l'analisi e l'estrazione delle features vera e propria, i cui risultati vengono forniti in output sul primo source pad e passati all'elemento DataDemuxer, che come già spiegato nei capitoli precedenti si occupa di rielaborare i risultati dell'analisi e riportarli in un formato opportuno; su humor la proprietà view=TRUE attiva la finestra di visualizzazione dell'elemento, con la quale si può verificare visivamente gli oggetti rilevati ed il tracking, i quali vengono disegnati direttamente sul video elaborato, oltre alla finestra che evidenzia i pixel differenti rilevati tra un frame e l'altro. La proprietà select di DataDemuxer specifica invece quali informazioni, tra quelle fornite come risultato dell'analisi, devono essere esportate in uscita dall'elemento. 80 Questo flusso dati viene infine passato all'elemento filesink che provvederà a scrivere su un file di testo l'output formattato opportunamente, contenente le features. Analogamente a filesrc, la proprietà location specifica il percorso su cui il file sarà scritto; in questo caso la destinazione sarà ubicata sempre all'interno del learning set nella sottodirectory relativa allo stesso esempio specificato come primo parametro, ma a differenza del video in origine, il file di testo finale verrà salvato nella sottocartella features. Il suo nome sarà determinato dal nome del file video (senza estensione ".avi"), seguito dal suffisso specificato come terzo argomento. Infine è presente un export per aggiungere alla variabile di sistema LD_LIBRARY_PATH i percorsi opportuni per utilizzare le librerie dei plugin aggiunti a GStreamer. L'ultima riga non fa altro che lanciare la pipeline caricando opportunamente i plugin richiesti per l'uso degli elementi aggiuntivi con GStreamer. 81 Estrazione multipla di features da un set di esempi con "set2hum" Lo script appena esposto permette di eseguire l'elaborazione di HuMoR su un unico file video acquisito, relativo ad una particolare istanza di un esempio. Tuttavia, in fase di raccolta degli esempi è sicuramente più conveniente acquisire una serie di istanze relative ad un esempio. Per una serie di fattori, come ad esempio l'illuminazione della scena soggetta a variazioni con il tempo, l'esecuzione ripetuta della stessa azione, la durata, i tempi di inizio e fine scena ed altri, è sicuramente più conveniente eseguire le istanze di uno stesso esempio ripetutamente, senza interruzioni ed nella maniera più agevole e veloce possibile. Da questa esigenza deriva che, dopo una serie di acquisizioni tramite cam2avi, ci si trova con un certo numero di filmati relativi ad uno stesso esempio da elaborare tramite avi2hum per estrarne le relative features. Per questo risulta estremamente utile definire uno script in grado di processare tutte le istanze da analizzare semplicemente specificando l'esempio al quale sono relative. set2hum è stato creato in modo da eseguire l'estrazione delle features su tutti i filmati relativi ad una serie di esempi con un unico comando. Il codice è il seguente: #!/bin/sh LEARNINGSET_PATH="/home/gas/Tesi/esempi" SCRIPTS_PATH="/home/gas/Tesi/scripts" if [ $# -le 1 ] ; then echo "set2hum: Errore negli argomenti." echo "Sintassi: set2hum SUFFISSO ESEMPIO1 [| ESEMPIO2 ... | ESEMPION]" exit 0 fi SUFFEAT=$1; shift 82 for ESEMPIO in $* do cd $LEARNINGSET_PATH/${ESEMPIO}/video for ISTANZA in *; do $SCRIPTS_PATH/avi2hum ${ESEMPIO} $ISTANZA $SUFFEAT done done Dopo aver settato le due variabili LEARNINGSET_PATH e SCRIPTS_PATH, che contengono rispettivamente il percorso del learning set e quello degli script (ove reperire avi2hum), viene effettuato un controllo sugli argomenti per garantire che vengano passati nel numero minimo richiesto; devono essere almeno 2, ovvero il suffisso identificativo dell'analisi ed uno o più esempi da processare, consentendo il processing non solo di tutte le istanze di un esempio ma di un intero set enumerato in una unica linea di comando. Lo script procede con un ciclo while in cui viene processato ad uno ad uno ogni esempio elencato negli argomenti: per ciascuno ne viene analizzata la relativa directory dei file video, e per ogni istanza trovata in essa viene eseguito avi2hum opportunamente, in modo da estrarne le features, che verranno salvate nell'opportuno percorso con il suffisso specificato. 83 Capitolo 6 - Utilizzo delle pipelines Connessione della videocamera tramite xplug Come si è visto da esempi precedenti, per l'acquisizione del video filmato tramite una videocamera di rete è necessario utilizzare uno script in Python, sviluppato per trasferire le immagini in locale sulla macchina dove HuMoR è in esecuzione. Il compito di tale script consiste essenzialmente nel connettersi ad un ip specificato e nel reperire da esso le immagini messe a disposizione dalla videocamera. Ottenute le immagini, esse vengono rese disponibili in locale su una porta specifica, tramite un mini-server web, in modo da consentire il recupero di tali immagini tramite una semplice richiesta http. 84 Raccolta degli esempi per la creazione del learning set La fase di apprendimento risulta particolarmente importante per il corretto funzionamento del classificatore markoviano implementato su HuMoR per il riconoscimento di comportamenti rilevati nelle scene analizzate. Il funzionamento del classificatore infatti consiste essenzialmente nell'analizzare la sequenza di immagini osservata, in cui è presente una persona che sta compiendo una azione, e confrontarla con sequenze prese dal learning set, al fine di trovare la più simile e classificarla quindi opportunamente. Ne deriva ovviamente che un set di esempi mal generato può condurre ad una classificazione delle azioni non attendibile; è pertanto necessario che gli esempi filmati siano privi per quanto possibile di rumore e siano corrispondenti ad una ben precisa azione, la quale deve essere quindi eseguita e ripresa in maniera inequivocabile (non confusa con altre azioni spurie presenti nel filmato e caratterizzata da una fine ed un inizio ben definito, non comprendenti altre eventuali azioni precedenti e successive, ad esempio) e distinguibile da altre azioni simili ma da associare ad altre classi di comportamento. Gli scripts appena esposti, creati appositamente per soddisfare a questi scopi e per creare e gestire rapidamente ed agevolmente il learning set, ci sono di grande aiuto per tale obbiettivo. In primo luogo risulta non trascurabile prestare attenzione all'inquadratura della videocamera, per far si che la ripresa venga effettuata nella maniera più adeguata per garantire che le azioni filmate siano più facilmente ed inequivocabilmente distinguibili e dettagliate. Questa operazione, seppur apparentemente banale, ha una importanza rilevante poichè una inquadratura ottimale, implicando una migliore distinguibilità e qualità delle azioni riprese, consente un conseguente 85 miglioramento sensibile anche nella classificazione di esse, dato che vengono facilitate classificatore. ed ottimizzate le condizioni di lavoro del Una buona inquadratura deve tener conto di numerosi aspetti: tra i più importanti ricade ad esempio la posizione della telecamera stessa, da scegliere in modo che l'azione possa essere ripresa chiaramente e ogni sua caratteristica possa essere altrettanto chiaramente osservata; ovviamente, se ad esempio si deve riconoscere se un interruttore viene acceso o spento, è opportuno che la telecamera sia posizionata in modo che questo non sia coperto dalla persona che lo accende nel momento in cui l'azione ha luogo. Inoltre è importante che la posizione di ripresa sia scelta in modo che effetti indesiderati, quali ombre, riflessi ed oggetti in movimento non coinvolti nell'azione siano ridotti al minimo; tale risultato si ottiene solitamente facendo attenzione a scegliere una posizione tale che eviti la presenza di specchi, pareti riflettenti nell'area di ripresa e che la luce sia il più uniformemente distribuita, di intensità adeguata e non variabile durante l'azione stessa o due successive azioni. L'individuazione di una posizione ottimale della videocamera che consideri la verifica delle condizioni appena esposte e di molte altre da considerare, viene resa possibile e facilitata con l'uso di focus, il quale permettendo una visione in tempo reale di ciò che viene ripreso dalla videocamera consente una scelta opportuna e l'immediata visualizzazione di eventuali problemi presenti, oltre alla principale ed originaria funzione di messa a fuoco dell'obiettivo, da cui ne deriva il nome. 86 Una volta posizionata la videocamera tutto è pronto per l'acquisizione video delle azioni che caratterizzeranno gli esempi. Tramite cam2avi come già detto, si riesce ad acquisire una singola istanza di un esempio e salvarla su disco. Si procede a questo punto scegliendo una azione particolare da effettuare che caratterizzerà un singolo esempio del learning set. Tale azione dovrà essere ripetuta e filmata per un numero n desiderato di volte e ad ogni ripetizione corrisponderà una istanza salvata di un particolare esempio. Con l'uso di cam2avi l'aquisizione ripetuta di n filmati dello stesso esempio è particolarmente agevole, dato che basta rilanciare la stessa riga di comando, incrementando di volta in volta solamente il numero che identifica l'istanza. L'ultima fase della creazione del learning set prevede l'analisi tramite HuMoR dei video acquisiti, operazione efficientemente effettuata tramite set2hum, il quale ci permette di lanciare l'elaborazione su uno o più esempi in una volta sola, ottenendo così un set di esempi video ed i relativi file contenenti le features estratte. E' comunque possibile, all'occorrenza, riprocessare una singola istanza tramite avi2hum (ad esempio nel caso in cui si voglia preventivamente analizzare il comportamento dell'elaborazione di HuMoR su un solo filmato prima di procedere con la acquisizione di un set di n istanze). E' a questo punto completata la creazione del learning set e risulta possibile procedere con la fase di apprendimento, in cui vengono forniti a HuMoR gli esempi che saranno utilizzati per l'addestramento del sistema. 87 Addestramento di HuMoR Una volta raccolti tutti gli esempi, si procede con l'addestramento del sistema in modo che ad ogni esempio venga associata una particolare azione, la quale successivamente, quando verrà analizzato un esempio simile (mai visto prima dal sistema) in cui viene effettuata la stessa azione, HuMoR dovrà essere in grado di classificare correttamente. L'addestramento viene suddiviso in tre fasi distinte: 1. Inizialmente si analizzano gli esempi da apprendere, raccogliendo i dati relativi a ciascuno di essi ed associando tali dati ad una azione ben precisa da riconoscere in seguito (ad es., "accensione interruttore"); 2. I dati di ogni singola azione vengono processati applicando l'algoritmo di "k-means", con lo scopo di raggruppare le features simili degli esempi considerati in simboli; 3. Sulla base di questi gruppi di features viene costruito un HMM (Hidden Markov Model) in modo da modellare l'azione ed essere in grado successivamente di associare una particolare osservazione successiva ad uno dei modelli creati, classificandolo con la relativa azione. Questi 3 passi venivano originariamente richiamati, uno ad uno, tramite una opportuna scheda presente nell'interfaccia grafica del software. Nell'implementazione che utilizza GStreamer, come per il resto delle esigenze, è stato possibile richiamare le fasi di training predisponendo opportune proprietà nell'elemento HuMoR, in modo da attivare l'apprendimento sulle sequenze passate. 88 Le proprietà in questione sono train, hmm e act: La prima specifica se attivare il training sull'esempio passato, in modo da eseguire il primo passo dell'apprendimento; se tale proprietà viene attivata (impostando ad 1 il suo valore), risulta necessario specificare anche act, una stringa che rappresenta il nome da assegnare all'azione compiuta nell'esempio in input, ovvero il nome del file su cui salvare i dati risultanti dall'analisi effettuata. Infine la proprietà hmm, se attivata, rende possibile l'utilizzo di HuMoR in modalità di riconoscimento, al fine di classificare l'esempio analizzato e restituire l'azione a cui più assomiglia. Partendo dallo script avi2hum, che si occupava di processare un filmato per estrarne le features, si è costruito avi2humTrain, naturale evoluzione del precedente, inclusivo delle modifiche oppportune utili ad attivare l'apprendimento passando i parametri opportuni, oltre che a richiamare il software nella modalità di riconoscimento discussa in seguito. Analizziamone il codice: #!/bin/bash if [ $# -lt 5 ] ; then echo "avi2humTrain: Errore negli argomenti" echo "Sintassi: avi2humTrain ESEMPIO FILE_ISTANZA SUFFISSO TRAIN_ACTIVATION HMM_ACTIVATION" exit 0 fi VIDEOFILE=$2 SRC_PATH="/home/gas/Tesi/git/HuMoR/humor/debug/src" GSTLIBS_PATH=$SRC_PATH"/gst-plugin" LEARNINGSET_PATH="/home/gas/Tesi/esempi" PIPELINE="filesrc location=$LEARNINGSET_PATH/$1/video/$2 ! avidemux ! ffcolorspace ! humor name=hum view=TRUE train=$4 hmm=$5" if [ $4 -eq 1 ] ; then PIPELINE=$PIPELINE" act=$1$2" fi export LD_LIBRARY_PATH=$SRC_PATH/LibHumor/.libs/: $SRC_PATH/Thread/.libs/ 89 gst-launch-0.8 --gst-debug-level=1 --gst-pluginload=$GSTLIBS_PATH/libgsthumor.la --gst-pluginload=$GSTLIBS_PATH/libgsturlsrc.la --gst-pluginload=$GSTLIBS_PATH/libgstDataDemuxer.la $PIPELINE Come si può chiaramente notare, poco viene aggiunto al codice di avi2hum; lo script viene semplicemente adattato per rendere possibile l'attivazione delle nuove proprietà introdotte nell'elemento HuMoR per rendere possibile l'attivazione dell'addestramento e del riconoscimento. In particolare i due nuovi parametri TRAIN_ACTIVATION e HMM_ACTIVATION rendono possibile l'attivazione delle rispettive nuove proprietà train e hmm. Si noti solamente che nel caso in cui il training viene attivato, alla proprietà act viene assegnato in automatico, come denominazione per riconoscere l'azione compiuta, il nome dell'esempio passato, concatenato con l'istanza analizzata, in modo da evitare di inserirne ogni volta uno a mano (il nome dell'esempio è solitamente rappresentativo dell'azione compiuta). Questo script può essere utilizzato quindi, attivando il training tramite il parametro TRAIN_ACTIVATION, in primo luogo, per eseguire il primo step dell'addestramento previsto dall'interfaccia grafica; il risultato che si otterrà è quindi un file relativo alla specifica istanza di un esempio, il quale conterrà i dati su cui fare learning, modellandoli con gli HMM, corrispondenti a quella azione ben precisa. Nell'immagine, alcuni frame di un esempio utilizzato nella fase di apprendimento, ove si può notare la contornazione ed il tracking degli oggetti in movimento rilevati: 90 Questa operazione va ripetuta per tutte le istanze di tutti gli esempi che vanno a costituire il learning set, dopodichè risulta possibile eseguire i successivi passi (secondo e terzo) dell'addestramento, in modo da ottenere un modello HMM in grado di rappresentare le azioni osservate e di permettere la classificazione di azioni simili presenti in esempi nuovi. 91 Riconoscimento di azioni Lo script avi2humTrain stesso può essere utilizzato, attivando il parametro HMM_ACTIVATION, per effettuare il riconoscimento, passando come argomento un esempio mai visto prima, allo scopo di trovare una tra le azioni apprese in fase di learning che più assomiglia a quella compiuta in esso. Lanciando lo script in questa modalità, viene lanciata una pipeline di GStreamer che processa il video in ingresso tramite HuMoR, attivando il modulo che si occupa di classificare la sequenza vista a quella più somigliante scelta dagli esempi modellati con HMM. Come output verrà restituito il modello che più si avvicina al video processato e con quale probabilità tale modello rappresenta davvero l'azione compiuta. Provando ad esempio a processare tramite lo script una istanza di una nuova azione mai vista prima, tramite il seguente comando, avi2humTrain umbr_stole_fox 05.avi 01 0 1 si otterrebbe un risultato del genere: la pipeline viene mandata in esecuzione e l'elemento humor viene utilizzato per analizzare l'istanza passata da riga di comando, ottenendo un output su finestra ove viene visualizzato il consueto tracking degli oggetti rilevati. 92 Quando il moto degli oggetti in movimento finisce di essere tracciato (l'azione è finita), viene restituito su output testuale, come accennato in precedenza, il modello che più si avvicina all'azione analizzata: Come si nota dall'output su shell, il modello che meglio ha approssimato la sequenza vista corrisponde ad "umbr_stole_diath", corrispondente alla stessa azione effettuata da un altro soggetto e passata in fase di learning. 93 Eseguendo la stessa pipeline su un altro esempio, ove viene compiuta una differente azione (una persona che esce dalla porta principale dell'edificio), si ottiene il seguente risultato: Anche in questo caso si può notare che l'azione, compiuta da un'altra persona in fase di learning, è stata correttamente classificata. 94 Capitolo 7 - Estensione delle potenzialità di HuMoR I risultati ottenuti da questo lavoro di integrazione di HuMoR all'interno di un framework ci dà finalmente la possibilità di procedere con la progettazione di un sistema di sorveglianza estendibile in vasta scala, come prefissato negli obiettivi iniziali. Risulta possibile da adesso proseguire nella creazione di un sistema di videosorveglianza non solo flessibile e scalabile, ma basato su l'utilizzo di macchine ed apparati low-cost, come possono essere semplici pc, infrastrutture di rete standard (Ethernet) e videocamere di rete; è immediatamente riscontrabile che i costi sono di sicuro nettamente inferiori rispetto ad un sistema di videosorveglianza specializzato e proprietario, costretto per sua natura a ricorrere ad apparecchiature ed infrastrutture create ad hoc per esso, pertanto sicuramente più costose rispetto a materiale standardizzato, usato per mille altri scopi e pertanto ampiamente disponibile sul mercato. La progettazione del sistema richiede inoltre un attento studio degli ambienti ed una fase di messa a punto finale; una precisa calibrazione degli apparati e della struttuta permette di ottenere un rilevamento affidabile al massimo, requisito sicuramente critico per quanto riguarda la videosorveglianza. Come esposto in precedenza, l'uso del software per una fase di learning e di verifica è stato semplificato significativamente tramite l'intoduzione di script appositi, studiati opportunamente per questi scopi. Tramite questi strumenti risulta possibile valutare e scegliere un’ implementazione piuttosto che un’altra, affinchè tutti i parametri necessari per garantire che le prestazioni dell’intero sistema siano ottimizzate, vengano messi a punto. 95 Bibliografia [1]: The GStreamer team, GSTreamer, 2006, http://gstreamer. freedesktop.org/ [2]: Canonical Ltd., Ubuntu, 2006, http://www.ubuntu.com/ [3]: KDE.org, K Desktop Environment, 2004, http://www.kde.org [4]: Daniel Stenberg, libcurl, 2004, http://curl.haxx.se/libcurl/ [5]: Trolltech, Librerie QT, 2004, http://www.trolltech.com [6]: Innocenzo Russo, Modelli di Apprendimento Markoviani per l'Interpretazione Automatica di Scene, Università degli Studi di Siena [7]: Vincenzo Di Massa, Rilievo di Features da Sequenze di Immagini ai Fini dell’interpretazione Automatica di Scene, Università degli Studi di Siena [8]: Chair of Technical Computer Science, RWTH Aachen University, LTI-Lib, 2003, http://ltilib.sf.net [9]: Stuart J. Russell, Peter Norvig, "Intelligenza Artificiale", PrenticeHall, Englewood Cliffs 1995. [10]: doxygen.org, Doxygen, 2004, http://www.doxygen.org/index. html [11]: KDevelop, http://www.kdevelop.org 96 [12]: Stefano Volpini, Progettazione di un sistema di videosorveglianza basato sull'interpretazione automatica di scene: Sviluppi ed estensione del progetto HuMoR, Università degli Studi di Siena [13]: Gianluca Mazzei, Progettazione di un sistema di videosorveglianza basato sull'interpretazione automatica di scene: Struttura e dispositivi, Università degli Studi di Siena 97 Questa Tesi è stata scritta utilizzando: • AbiWord (©1998-2006, The AbiSource Community, http://www. • Dia (©1998-2006, The Free Software Foundation and the abisource.com), un elaboratore di testi OpenSource; authors, http://www.gnome.org/projects/dia/), un programma per il disegno di diagrammi strutturati; • VIM (Bram Moolenaar et al., http://www.vim.org), un editor di • GIMP, the GNU Image Manipulation Program (Peter Mattis e testi; Spencer Kimball, http://www.gimp.org/), il programma GNU per la gestione delle immagini; • OpenOffice.org (Sun Microsystems, http://www.openoffice.org), una suite per ufficio multipiattaforma e multilingua open-source 98
© Copyright 2024 Paperzz