Paolessi Andrea - Università degli Studi di Siena

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