Un framework modulare per lo sviluppo di applicazioni di Computer
Transcript
Un framework modulare per lo sviluppo di applicazioni di Computer
Università degli Studi di Siena Facoltà di Ingegneria Corso di Laurea in Ingegneria Informatica Un framework modulare per lo sviluppo di applicazioni di Computer Vision: videosorveglianza e interazione uomo-macchina Tesi di Laurea di Yusef Maali 21 Luglio 2008 Relatori: Prof. Marco Gori Ph.D Vincenzo Di Massa Anno Accademico 2007/2008 Indice Introduzione ii 1 Rilevazione del movimento 1.1 Motion Detection . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Object Tracking . . . . . . . . . . . . . . . . . . . . . . . . . . 1 4 8 2 Costruire un framework di sperimentazione 2.1 Semplicità . . . . . . . . . . . . . . . . . . . 2.1.1 Interface Plugin . . . . . . . . . . . . 2.1.2 Component Plugin . . . . . . . . . . 2.1.3 HuMoR Plugin . . . . . . . . . . . . 2.2 Modularità . . . . . . . . . . . . . . . . . . 2.2.1 HuMoRCore Plugin . . . . . . . . . . 2.2.2 DataConsumer Plugin . . . . . . . . 2.3 Efficienza . . . . . . . . . . . . . . . . . . . 3 Esemplificazioni dell’utilizzo di HuMoR 3.1 Rimozione di oggetti da una scena . . . . 3.2 Tracciamento di oggetti in movimento . . 3.3 Rilevazione di percorsi . . . . . . . . . . 3.4 Interfaccia di controllo per giochi . . . . 3.5 Un Avatar: LIA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 14 15 16 17 18 18 19 20 . . . . . 22 22 22 22 22 22 Conclusioni 23 Bibliografia 26 Introduzione Un problema che da sempre sfida l’Intelligenza Artificiale è comprendere la semantica di una immagine. Se una macchina riuscisse a capire cosa l’uomo vede, permetterebbe di aprire una serie di scenari impensabili. Basti pensare all’interazione uomo-macchina: non si avrebbe più la necessità di limitare le possibili azioni a contesti scelti in fase progettuale con meticolosa attenzione, ma si potrebbe rendere la macchina “libera” di interpretare con grande precisione cosa accade nell’ambiente circostante e permetterle di decidere autonomamente cosa è meglio fare. Molti film cinematografici ipotizzano ormai da tempo questa realtà e sfortunatamente (o fortunatamente? ) ne siamo ben lontani, a causa anche delle difficoltà riscontrate in altri settori dell’Intelligenza Artificiale. L’interazione uomo-macchina è uno di quei task che richiede il massimo sforzo in termini teorici e al momento attuale manca parte della tecnologia necessaria per ottenere un tipo di interazione simile a quello uomo-uomo. Con HuMoR1 si è cercato di dare un piccolo contributo in questo senso, trovando il modo più efficiente per estrapolare la semantica da flussi di immagini, generalmente provenienti da telecamere. Il target preferenziale di HuMoR è inizialmente stato la videosorveglianza, in quanto estrarre concetti quali “azione lecita”, “azione non lecita” o “evento anomalo” da una ripresa video aggiunge una notevole quantità di informazioni semantiche fondamentali nel decidere se lanciare un allarme sonoro, se avvertire la sorveglianza o semplicemente se memorizzare la parte di filmato interessata dall’evento. Nella sua prima versione, HuMoR implementava interessanti concetti di Intelligenza Artificiale applicata alla Computer Vision che lo rendevano capa1 Human Motion Recognizer, il software prodotto come progetto di questa tesi. INDICE iii ce di riconoscere sequenze di azioni e catalogarle in un insieme di eventi noti. Era quindi capace di discernere da azioni proibite, come rubare un oggetto, da azioni lecite, come spostare un oggetto, raggiungendo un buon livello di affidabilità grazie all’impiego di tecniche consolidate come K-Means e reti di Kohonen. Su questa base si inserisce il mio contributo svoltosi durante il progetto di tesi, che ha riguardato principalmente la ristrutturazione di HuMoR e che lo ha portato alla sua seconda versione stabile. L’esigenza di re-ingegnerizzazione interna è nata a causa della sua struttura monolitica 2 , che rendeva difficile, spesso impossibile, introdurre miglioramenti significativi. È stata quindi fatta un’attenta pianificazione rivolta alla ricerca di una modalità che consentisse di organizzare i vari concetti in classi C++, rispettando fortemente il paradigma della programmazione ad oggetti (OOP) e preservando concetti quali semplicità, modularità ed efficienza. Il risultato è stato la nascita di un nuovo sistema, non più esclusivamente dedicato alla videosorveglianza, ma utilizzabile in qualsiasi ambito della Computer Vision, dalla sperimentazione ai sistemi di produzione. Grazie all’introduzione del concetto di plugin3 è ora possibile cambiare le componenti per la rilevazione del movimento, per il tracciamento dello stesso e per tutte le funzionalità di alto livello che aggiungono informazioni di tipo semantico al flusso video. Sono stati creati, ad esempio, dei plugin per la rilevazione di oggetti aggiunti o rimossi dalla scena, per la rilevazione del movimento avvenuto entro un certo percorso, per il conteggio di oggetti transitanti lungo la scena (con la possibilità di vincolare anche la direzione del transito), ecc. . . L’esperienza maturata durante il lavoro svolto per HuMoR ha permesso di realizzare inoltre qualcosa di decisamente diverso, ma nello stesso tempo molto simile: un interfaccia di controllo e un avatar. Inizialmente l’obiettivo primario era sfruttare le informazioni estrapolate dalle immagini per far interagire con modalità estremamente semplici la macchina e il mondo esterno. Il primo passo è stato creare un’interfaccia di controllo per videogiochi 2 Un software è monolitico quando al suo interno non c’è separazione logica e concettuale tra i vari moduli. 3 Un plugin è un modulo che può essere aggiunto ad un certo software per aggiungere funzionalità o addirittura cambiare il comportamento di funzionalità già esistenti. INDICE iv sfruttando il movimento del corpo, rilevato dalle tecnologie che HuMoR ha premesso di elaborare. Videogiochi ovviamente semplici, dove il grado di libertà del movimento consentito era solamente quello orizzontale, anche se grazie a raffinamenti successivi è stato possibile ottenere il click, permettendo cosı̀ di attivare azioni contestuali al gioco (sparare, lanciare palline, ecc. . . ). Il lavoro si era direzionato anche sul creare un sistema di puntamento analogo al mouse ma che funzionasse unicamente con il movimento della mano e delle dita (anche qui molti film cinematografici ipotizzano queste tecnologie), inglobando un altro settore dell’Intelligenza Artificiale in forte ascesa: la Gesture Recognition 4 . Il secondo passo è stato approfondire il concetto di interazione uomomacchina, studiando un avatar in grado di interagire con l’ambiente circostante. L’avatar è dotato di un volto renderizzato in real-time grazie ad un modello 3D che consente di muovere indipendentemente gli occhi, dalle sopracciglia, dalla bocca, dal volto. L’interazione, per il momento molto basilare, consiste nel seguire con gli occhi il soggetto che si muove nella scena, compensando la distorsione prospettica che si genera durante la rotazione del volto sull’asse verticale. Per cercare di avere un tipo di reazione agli eventi quanto più simile a quella umana, la macchina deve poter avere quante più informazioni possibili sul contesto in cui agisce. Assolvere questo compito significa studiare tecniche che rientrano in altri settori dell’Intelligenza Artificiale come la Face Detection e la Mood Detection, per il riconoscimento del volto e dell’umore di chi interagisce, lo Speech Recognizing e il Text to Speech, per poter avere interazioni di tipo vocale sia uomo-macchina, che macchina-uomo, l’Audio Localization, per la localizzazione spaziale delle fonti sonore, ad esempio l’utilizzatore del sistema, utile in aiuto alla localizzazione video. Tutte le funzionalità sopra esposte sono attualmente in fase di studio e implementazione, ma la loro descrizione dettagliata non rientra negli scopi di questa tesi e ne verrà dato solamente un breve accenno. 4 Branca dell’Intelligenza Artificiale rivolta all’interpretazione dei gesti umani tramite algoritmi matematici. Capitolo 1 Rilevazione del movimento L’uomo ha una naturale ed efficace propensione a rilevare il movimento che si verifica nell’ambiente circostante. Per una macchina assolvere a questo compito non è altrettanto semplice, anche se le tecniche adottate non sono concettualmente complesse. Le azioni principali eseguite per raggiungere lo scopo sono: • Acquisizione dell’immagine da una sorgente video • Adeguamento dell’immagine per le elaborazioni successive, fase nota come pre-processing • Stima dello sfondo per suddividere le parti dell’immagine che rappresentano le zone statiche e le zone in movimento • Segmentazione dell’immagine per isolare ogni singolo oggetto in movimento e rappresentarlo come entità indipendente dalle altre • Estrazione di features dall’immagine, necessarie per tutte le elaborazioni di più alto livello (semantiche), fatte in fasi successive • Tracciamento del movimento I passi sopra elencati non preoccuperebbero nessun ricercatore né alcun elaboratore se vivessimo in un mondo ideale. Sfortunatamente non è questo il caso e nel tentativo di concludere con successo questa lista di azioni, si incontrano molte difficoltà: 2 • Acquisendo un’immagine da una sorgente video i primi problemi in cui ci si imbatte sono la qualità e l’affidabilità della sorgente. Basti considerare che le singole immagini, i fotogrammi, possono non essere generati con cadenza regolare, ad esempio una sorgente in streaming via internet, obbligando il sistema ad essere robusto a questo tipo di errore. Ovviamente se i fotogrammi dovessero mancare per un periodo troppo prolungato, l’elaboratore sarebbe comunque costretto a reinizializzarsi sulle nuove immagini. Anche il problema della qualità non può essere trascurato, perché una sorgente, ad esempio una webcam economica o posizionata in modo scorretto, che non riesca a mantenere stabile il livello del colore o della luminosità, causerebbe forti variazioni locali al singolo pixel con il rischio che vengano interpretate erroneamente come movimento. • La stima dello sfondo risente molto dell’instabilità di luminosità e colore dell’immagine. L’occhio (e soprattutto la mente) umana riesce a compensare egregiamente anche forti variazioni di colore e luminosità, in virtù del fatto che viene fatta a priori un’analisi semantica. Un elaboratore non possiede questa dote e l’analisi parte dalle fondamenta dell’immagine, il pixel. Se questo subisce forti variazioni in una delle sue componenti tra un fotogramma e il successivo, lo stimatore di sfondo potrebbe essere indotto a segnalare tale pixel come movimento, anziché come sfondo. La qualità della sorgente è influenzata anche dalla risoluzione con cui l’immagine viene prodotta. Maggiore è la risoluzione, maggiore è il numero di pixel creati per ogni singolo oggetto dell’immagine, maggiore è la precisione con cui si riescono a fare analisi semantiche sull’immagine, ivi compresa la rilevazione dello sfondo. Un’immagine descritta da 320x240 pixel (76.800 pixel) sarà molto meno resistente al rumore di una descritta da 1024x768 pixel (786.432 pixel). • Il processo di segmentazione risente, anch’esso, della mancanza di qualità della sorgente. Segmentare un’immagine consiste nell’identificare zone i cui pixel sono correlati fra loro secondo un qualche criterio scelto ad-hoc, spostando il dominio di analisi dal singolo pixel a regioni di 3 pixel e permettendo, quindi, di ridurre notevolmente la mole di dati da analizzare. Partendo da un’immagine 1024x768 pixel (786.432 pixel), dopo la segmentazione, si possono avere un numero di regioni molto minore, generalmente di qualche ordine di grandezza (100-1000 regioni), arrivando ad una situazione in cui la complessità computazionale diventa sostenibile per gli attuali elaboratori. Si potrebbe osservare che si ha una notevole perdita di informazione, il che è senz’altro vero, ma il tipo di analisi successive che viene effettuato non necessita di una descrizione dettagliata dell’immagine, quanto di una descrizione più verosimilmente semantica. Risulta ovvia la constatazione che se zone ampie di pixel subiscono forti variazioni dovute al rumore, le regioni ottenute dalla segmentazione saranno diverse e più difficili da analizzare, soprattutto nel caso le si vogliano correlare tra un fotogramma e il successivo. • La scelta di quali features estrarre risulta essere particolarmente delicata, in quanto devono discriminare in modo mirato alla risoluzione del problema e devono essere il più resistenti possibile al rumore introdotto dalla sorgente. Si possono scegliere features discriminanti in base al colore, alla forma, all’area, alla posizione del baricentro o al centro di gravità dell’oggetto in movimento oppure in base a proprietà invarianti a rotazione e ridimensionamento dell’immagine. Le features che si posso estrarre da un’immagine sono generalmente molte, ma anche in questo caso si deve porre attenzione che la bontà delle features ottenute non vada a discapito della complessità computazionale complessiva. • Tracciare il movimento risulta essere un’analisi di livello più alto, in quanto va a lavorare sui dati prodotti nelle fasi precedenti. È evidente che se le features ottenute e la stima dello sfondo sono state alterate da rumore o più in generale dalla scarsa qualità della sorgente, anche in questo caso si possono ottenere risultati poco soddisfacenti. Se il dominio di applicazione risulta essere abbastanza limitato e ben strutturato nulla vieta di porre dei vincoli restrittivi sul tipo di analisi da fare, cosı̀ da ridurre a posteriori la produzione di dati errati. 1.1 Motion Detection 4 Nel seguito del presente capitolo verranno prese in esame più in dettaglio solamente le fasi di rilevazione del movimento e di tracking degli oggetti. Le altre fasi per quanto interessanti esulano dallo scopo di questa tesi. 1.1 Motion Detection Analizzare il movimento in una sequenza di immagini è un processo che coinvolge principalmente due fasi: • Rilevare lo sfondo (Background Detection) • Determinare il movimento La rilevazione dello sfondo è una fase fondamentale nell’analisi del movimento ed è anche la fase che più ha coinvolto lo sforzo dei ricercatori nell’ambito della Computer Vision. L’esperienza comune suggerisce di considerare come sfondo la parte dell’immagine che rimane immobile nel susseguirsi dei fotogrammi. Si potrebbe quindi decidere di impostare come sfondo il primo fotogramma che arriva dalla sorgente video. All’arrivo del fotogramma successivo si effettua un’operazione di confronto pixel per pixel, del tutto simile ad una sottrazione, per ottenere i pixel che sono diversi e che possono essere etichettati come movimento. Una soluzione cosı̀ strutturata per quanto semplice e veloce computazionalmente presenta dei forti limiti, in quanto si deve assumere che il primo fotogramma sarà sempre sfondo e che lo sfondo è statico, ovvero non può subire cambiamenti di alcun tipo. Impostare il primo fotogramma come sfondo può portare ad una scelta errata, basti pensare a luoghi particolarmente affollati, come una piazza, dove lo sfondo effettivo è sempre coperto da movimento oppure basti pensare alla presenza di un oggetto dentro la scena nel momento in cui viene impostato lo sfondo, ad esempio una macchina parcheggiata; quando questo si rimuoverà dalla scena la differenza con i nuovi fotogrammi, in assenza di quell’oggetto, mostrerà costantemente movimento laddove in realtà non ce n’è. La seconda restrizione produce risultati ancor più negativi, tranne che in un numero molto limitato di situazioni, in quanto è sufficiente far cambiare le condizioni di illuminazione della scena per ottenere un’immagine completamente diversa dallo 1.1 Motion Detection 5 sfondo impostato. Per chiarire meglio il concetto, si può supporre di avviare l’elaboratore la mattina, cosı̀ da impostare lo sfondo che abbia proprietà di illuminazione ben definite e di vedere cosa succede al calar della sera quando l’illuminazione diventa artificiale o totalmente assente. Ovviamente i nuovi fotogrammi risulteranno completamente diversi nella loro totalità dallo sfondo impostato. Un caso più pratico, riscontrato personalmente, è l’abilità di molte webcam moderne di compensare automaticamente la luminosità delle immagini prodotte. Per quanto questa funzionalità sia molto utile all’occhio umano per migliorarne la percezione visiva, risulta essere veramente controproducente per le analisi da effettuare in quanto l’elaboratore per ogni cambio di luminosità sarà “cieco”; tutti i pixel dell’immagine verranno etichettati come movimento, ottenendo quindi informazioni totalmente inutilizzabili. Una situazione di questo tipo può essere parzialmente isolata introducendo delle soglie (threshold ) sulla velocità di variazione della luminosità dell’intero fotogramma o dei singoli pixel, portando quindi l’elaboratore ad ignorare la variazione qualora questa superi la soglia impostata. Ovviamente una soluzione cosı̀ strutturata non può risolvere il problema dei cambi di luminosità lenti e graduali, come la transizione giorno-notte, con cui è difficoltoso impostare delle soglie adeguate senza inficiare l’analisi complessiva. Un compromesso per ovviare a queste problematiche senza incrementare eccessivamente la complessità computazionale è considerare lo sfondo non più statico ma aggiornabile secondo un qualche criterio specifico per l’applicazione. Introducendo un nuovo fattore di analisi, si introducono ovviamente nuove problematiche a cui occorre fare attenzione per non cadere in errori banali ma subdoli. Una approccio apparentemente plausibile nell’aggiornamento dello sfondo potrebbe essere di reinizializzarlo ad intervalli regolari con i nuovi fotogrammi prodotti dalla sorgente. Portando questa idea al caso limite si potrebbe pensare di restringere l’intervallo al singolo fotogramma, in modo da avere uno sfondo che si aggiorna su ogni immagine. Una soluzione di questo tipo per quanto semplice produce effetti non migliori di uno sfondo statico, in quanto soffre in parte delle stesse problematiche di quest’ultimo ma aggiunge un nuovo problema: un oggetto in movimento, rilevato in due istanti consecutivi, verrà riconosciuto come mosso solo laddove l’intersezione 1.1 Motion Detection 6 dell’oggetto nei due fotogrammi sia nulla. Semplificando il concetto se si confronta (sottrae) il primo fotogramma con il secondo, la parte dell’oggetto che verrà etichettata come movimento sarà solamente quella zona in cui l’elaboratore vede nuovi pixel dell’oggetto. Tutti i pixel in comune tra il primo e il secondo fotogramma, non rappresentando un cambiamento, verranno etichettati come zona non mossa, producendo l’indesiderato effetto di rendere totalmente inaffidabili le conclusioni. La necessità quindi di avere uno sfondo aggiornabile ha portato molti ricercatori alla produzione di soluzioni più o meno efficaci, ma con un punto in comune: la considerazione che l’alterazione dello sfondo avviene gradualmente, a meno di applicazioni specifiche. In letteratura si riescono infatti a trovare principalmente due filoni nell’approccio al problema: chi tiene in considerazione le differenze riscontrate negli ultimi fotogrammi e chi crea un modello statistico dello sfondo. Il primo approccio presenta lo svantaggio di tenere traccia di una storia troppo breve dei cambiamenti avvenuti nello sfondo e quindi essere poco accurato nel determinare eventi verificatisi a istanti di tempo più lontani. Il secondo approccio, invece, non risente di questa limitazione, ma risulta essere molto lento nel memorizzare le nuove variazioni. Il lasso di tempo necessario per l’aggiornamento del modello statistico può essere troppo ampio per rendere questa tecnica utilizzabile in un ambiente in cui gli oggetti ripresi si muovano troppo lentamente. Offre, comunque, l’indiscutibile vantaggio di essere insensibile al verificarsi di variazioni repentine (spike) sull’immagine, che non entrando a far parte dello sfondo non inficeranno le analisi successive. Offre anche la capacità di assorbire nel modello statistico oggetti che rimangono fermi per un periodo di tempo sufficientemente lungo, ad esempio una macchina che viene parcheggiata sul bordo di una strada, evento che il primo approccio avrebbe inglobato in un tempo troppo breve per la produzione di risultati attendibili (la macchina poteva essere ferma ad uno stop). L’implementazione utilizzata in HuMoR è una versione ibrida dei due metodi sopra descritti, in quanto si è cercato di sfruttarne maggiormente i vantaggi, attenuandone i difetti. Come già detto il modello statistico è quello che offre il maggior grado di accuratezza e precisione nel modellare lo sfondo, 1.1 Motion Detection 7 ma è inevitabilmente lento per aggiornarsi sui nuovi dati ricevuti. Si è proceduto quindi a creare, invece di un singolo modello, un insieme degli stessi, implementando la possibilità di scegliere quale tra quelli attualmente disponibili approssimassero meglio lo sfondo corrente. L’idea, fondamentalmente, consiste nel cominciare ad usare uno dei possibili modelli, finché questo risponda in modo adeguato alle immagini analizzate. Nel momento in cui sia necessario l’aggiornamento del modello, in quanto non è più in grado di approssimare correttamente lo sfondo corrente, invece di far partire il processo di aggiornamento, che ricordo essere lento e soprattutto irreversibile, viene scelto il modello tra quelli disponibili che meglio si adatti alla situazione corrente. Se un modello con tali caratteristiche non esiste, quello attuale viene memorizzato in uno slot vuoto e parallelamente si crea un nuovo modello da modificare secondo le richieste correnti. Gli slot dei modelli da memorizzabili sono in numero limitato ed è quindi necessaria una politica di eliminazione dei modelli che non contribuiscono più efficacemente. Si è proceduto all’introduzione di una tecnica di invecchiamento con la quale ogni modello che non viene utilizzato nel fotogramma corrente viene invecchiato; quando tutti gli slot sono pieni e si deve memorizzare un nuovo modello, si procede con l’eliminazione del modello che è più vecchio, ovvero che non è stato utilizzato per un lasso di tempo maggiore rispetto agli altri. In questo modo si possono liberare gli slot che non hanno più contribuito attivamente al funzionamento dell’algoritmo e per permettere ai nuovi modelli, che potrebbero (ma non necessariamente fanno) approssimare meglio lo sfondo, di essere allocati. Risulta chiaro che il numero di slot disponibili diventa un parametro cruciale, in quanto un numero troppo piccolo rende vana l’idea basilare dell’algoritmo implementato, mentre un numero troppo ampio aumenta inutilmente la complessità computazionale, dovendo effettuare un maggior numero di confronti tra il fotogramma attuale e tutti i modelli memorizzati, nel tentativo di trovare quello che meglio approssimi lo sfondo corrente. Si può evincere facilmente che il grosso vantaggio di un’implementazione simile consiste nell’essere robusta a variazioni sistematiche dello sfondo, come i cambi di illuminazione (una fonte luminosa che viene accesa e spenta, una finestra che viene aperta e chiusa o più banalmente la presenza di masse 1.2 Object Tracking 8 nuvolose che rendono scostante l’illuminazione solare) o come una porta che sia apre e si chiude con continuità (ad esempio un ufficio). Avendo come riferimento un solo sfondo, tutte le circostanze citate produrrebbero una lunga serie di eventi1 totalmente inutili. Con l’algoritmo implementato in HuMoR, invece, non appena lo sfondo cambia, se tale cambiamento è già stato osservato dal sistema, viene effettuato un semplice e rapido cambiamento di sfondo, senza generare tutti gli eventi del precedente caso. Si è quindi riusciti a rendere il sistema robusto a variazioni sistematiche dello sfondo. 1.2 Object Tracking Una volta che si è riusciti ad identificare quali parti dell’immagine risultano essere mosse, si procede con la fase nota come Object Tracking. Sapere che c’è movimento è di per se una informazione utile in molti contesti, ma nell’ambito della videosorveglianza2 , come in molti altri settori, si ha l’esigenza di seguire l’oggetto e quindi di tracciarne l’evoluzione del movimento avvenuto su tutti i fotogrammi in cui l’oggetto è presente, da quando entra a far parte della scena fino a quando ne esce. Si pone quindi un altro problema non banale da risolvere: come si può essere certi che un oggetto rilevato in movimento ad un certo fotogramma sia ancora lo stesso oggetto rilevato al fotogramma successivo e non sia, ad esempio, un nuovo oggetto completamente diverso? Per rispondere a questa domanda si deve fare una breve accenno sul quinto punto della lista di fasi elencati all’inizio di questo capitolo: l’estrazione di features. Una feature è una particolare sequenza di dati che identificano una certa proprietà legata all’intera immagine o ad una sua parte. Il target di interesse in questa analisi sono gli oggetti in movimento, pertanto le features generate saranno sempre in relazione alla porzione di immagine che comprende l’oggetto. Si potranno avere features selettive in base all’area (espressa in 1 Con evento si intende un’azione compiuta dal sistema in risposta alla rilevazione di movimento nella scena 2 Si ricorda al lettore che inizialmente HuMoR è stato sviluppato come progetto di videosorveglianza e come tale le tecniche usate ne rispecchiano il comportamento 1.2 Object Tracking 9 pixel) dell’oggetto, nonché in base alla forma, alla posizione del baricentro o del centro di gravità dell’oggetto stesso. Le features possono essere molteplici e possono discriminare anche sulla base di informazioni diverse da quelle puramente geometriche, ad esempio colore e luminosità, oppure sulla base di complessi algoritmi matematici, come l’algoritmo di David G. Lowe presentato in [10] estremamente potente nel localizzare alcuni punti dell’immagine invarianti a rotazione e ridimensionamento dell’immagine stessa.3 Ovviamente in un sistema che lavora in tempo reale su stream video risulta fondamentale la velocità nel calcolo delle features; si deve quindi scegliere quali possano essere utili per la risoluzione dello specifico problema e quali no. Se ad esempio si lavora su flussi di immagini in scala di grigi (in assenza quindi di colore) risulterà ovviamente inutile estrarre features sul colore. In ogni caso vale spesso la regola che più features si hanno a disposizione, meglio si riesce a stabilire la somiglianza di un certo oggetto con uno già visto ad instanti precedenti. Esistono celebri esempi di come vengano usate proficuamente features di tipo esclusivamente geometrico, come applicazioni per la compressione video Mpeg7 [1] oppure sistemi che si prefiggono obiettivi paragonabili a HuMoR come [7]. Alla base dell’estrazione di features geometriche si può riscontrare in quasi tutti gli approcci il calcolo di momenti di vario ordine come spiegato in [5]. Nell’implementazione attuale del tracker di HuMoR vengono usate principalmente features di tipo geometrico, in particolare vengono calcolate la posizione, il baricentro, il centro di gravità del blob4 e la distribuzione del colore sugli assi x e y. Si è pensato di non usare altri tipi di features in quanto il tracker non necessita di molte informazioni per riuscire a tracciare efficacemente gli oggetti presenti sulla scena; le informazioni geometriche sono sufficienti per rintracciare l’oggetto nei fotogrammi successivi, mentre 3 La tecnica implementata da Lowe è particolarmente nota per la sua potenza nel riuscire a descrivere in modo quasi univoco alcuni punti ben precisi dell’immagine, che possono poi essere ritrovati anche se l’immagine viene ruotata, ridimensionata (con perdita di informazione) e anche se l’oggetto analizzato viene parzialmente occluso da altri oggetti. 4 Un blob è una zona generalmente rettangolare che racchiude tutti i pixel identificati come movimento. 1.2 Object Tracking 10 le informazioni sulla distribuzione del colore sono utili per distinguere tra oggetti geometricamente simili. La parte finale della fase di Object Tracking, ovvero dopo aver estratto tutte le features necessarie, consiste nell’effettuare il tracking degli oggetti presenti. Per assolvere a questo compito sono stati implementati tre metodi: per votazione, con la distanza tra vettori di features e con le reti di Kohonen. Tutti i metodi citati tengono traccia degli oggetti presenti e passati della scena, utilizzando un vettore (tracklist) contenente vettori di features riferite al singolo oggetto tracciato. Per ogni oggetto identificato dal motion detector, vengono confrontate le features con quelle già presenti nella traklist; se si ha corrispondenza, si aggiungono le nuove features al vettore relativo all’oggetto nella tracklist, in quanto l’oggetto è già stato visto e se ne sta seguendo il movimento. Nel caso in cui non si ha corrispondenza, viene aggiunto un nuovo elemento nella tracklist, perché l’oggetto è appena entrato a far parte della scena e da questo momento in poi ne viene tenuta traccia. L’analisi della corrispondenza tra l’oggetto identificato nel frame corrente e quelli memorizzati nella tracklist è compito dei tre metodi sopra citati: il metodo per votazione consiste nel sottrarre due vettori di features, prenderne il valore assoluto, moltiplicarlo per un vettore di pesi e confrontare il risultato con un vettore di soglie. Se le componenti del vettore ottenuto sono inferiori delle componenti del vettore delle soglie, allora i singoli elementi del vettore ottenuto vengono sommati fra loro per ottenere il voto. Il metodo per votazione è stato poi sostituito con il metodo basato su distanza tra vettori, in quanto non necessita dell’intervento manuale per la definizione del vettore dei pesi e delle soglie. Il funzionamento di quest’ultimo metodo è del tutto simile a quello per votazione tranne che invece del voto viene usata la distanza tra i vettori di features. Al vantaggio di non dover intervenire manualmente per configurarlo, si aggiunge lo svantaggio della perdita di precisione dell’algoritmo, in quanto se il vettore delle features da confrontare contiene molti elementi, la distanza tra vettori è un parametro che non può tenere conto del tipo di variazione verificatasi: possono cambiare molte componenti oppure una sola ma in modo consistente. Si è comunque potuto osservare che questo algoritmo funziona bene nel caso di features puramente geometriche, come 1.2 Object Tracking 11 area e baricentro del blob, assumendo che gli oggetti non si muovano troppo velocemente rispetto al framerate del flusso video. Il terzo metodo si basa su approccio basato su reti neurali di tipo SOM e reti di Kohonen, dove a livello concettuale, le features presenti nella traklist attraggono le features estratte dall’immagine corrente: più forte è l’attrazione più c’è corrispondenza tra gli oggetti rappresentati dalle features in esame. Per maggiori approfondimenti si rimanda alla lettura del lavoro di Gilles Labonté [8]. Capitolo 2 Costruire un framework di sperimentazione La parte principale del contributo personale apportato a HuMoR è stata la sua ristrutturazione interna, che lo ha portato dall’essere una applicazione monolitica ad essere un framework1 estremamente flessibile per la sperimentazione di algoritmi e idee per la Computer Vision. L’esigenza di re-ingegnerizzazione interna ha dato la possibilità di pianificare attentamente quali dovessero essere le caratteristiche principali che il nuovo HuMoR avrebbe dovuto offrire. Ci siamo posti un obiettivo ambizioso: trasformare HuMoR in un’entità molto più generica che permettesse al ricercatore, non esperto di programmazione, di implementare velocemente nuovi algoritmi su una piattaforma performante. Software simili già esistono, ad esempio Matlab2 , che per quanto permetta di scrivere algoritmi molto rapidamente non permette poi di eseguirli con altrettanta velocità. Abbiamo cominciato a porre dei vincoli che la nuova piattaforma avrebbe dovuto soddisfare, cercando di prendere il meglio dalle soluzioni già esistenti e migliorare ciò che già avevamo. Siamo quindi giunti alla conclusione che se HuMoR sarebbe 1 Un framework è una struttura di supporto su cui un software può essere organizzato e progettato. HuMoR non è propriamente un framework ma un ambiente di sviluppo, poiché per quanto possa essere utilizzato come una libreria, risulta più semplice implementare le nuove funzionalità al suo interno ed eventualmente esportarne i risultati. 2 Software molto noto in ambito universitario e non solo, utilizzato principalmente per la sperimentazione in ambito matematico e affine. Per maggiori informazioni vedere [11]. 13 diventato un sistema flessibile e performante, avrebbe dovuto essere: semplice HuMoR sarebbe dovuto essere utilizzabile ma soprattutto estensibile anche da utenti non propriamente esperti di programmazione. Proponendosi come framework ovviamente avrebbe richiesto un livello minimo di conoscenze tecniche, ma questo non sarebbe dovuto essere un ostacolo per chi non fa della programmazione il proprio ambito di studio o di lavoro. Abbiamo deciso di modellare i vari moduli come dei plugin, con un’interfaccia pubblica estremamente chiara, ridotta e funzionale, per non lasciare possibili spazi ad incomprensioni o difficoltà. Come verrà mostrato in seguito (paragrafo 2.1) è possibile creare un nuovo plugin con tre file e meno di venti righe di codice sorgente. modulare Tutte le funzionalità del framework avrebbero dovuto essere implementate all’interno di moduli intercambiabili fra loro, rispettando ovviamente la compatibilità tra i moduli stessi. Sarebbe quindi stato possibile sviluppare un nuovo motion detector e sostituirlo a quello in uso per misurare poi i miglioramenti (o i peggioramenti). Come il motion detector sarebbe stato possibile sostituire velocemente anche il tracker, la sorgente video o tutti i componenti che effettuano analisi a posteriori (vedere in proposito il paragrafo 2.2.2). Nel progettare la scomposizione in moduli è stata fatta la scelta di rendere anche il core 3 di HuMoR un modulo, rendendo cosı̀ possibile alterare l’ordine che lega i vari moduli fra di essi e quindi anche il flusso logico di come i dati vengono elaborati. efficiente Progettare un framework estremamente flessibile senza offrire la possibilità di sperimentare ed eseguire gli algoritmi con rapidità sarebbe stata una contraddizione. Ambienti di lavoro con questa tipologia già esistono4 e non avrebbe avuto molto senso ripercorrere le scelte progettuali effettuate da altri software. Si è scelto di usare un linguaggio 3 Il core di un software è la sua parte principale. Generalmente senza di essa non si potrebbero avere neanche le funzionalità basilari oltre che l’interconnessione tra tutti gli altri moduli. 4 Ad esempio lo stesso Matlab che a fronte di una notevole flessibilità, offre una scarsissima velocità nel processamento dei dati. 2.1 Semplicità 14 di programmazione che offrisse sufficiente potenzialità espressiva per non privarsi della capacità di descrivere nel linguaggio stesso i concetti qui esposti (modularità, efficienza e semplicità) e che producesse binari veloci ed ottimizzati. L’attenzione è ricaduta immediatamente sul linguaggio C++, che grazie alla compatibilità quasi completa con il paradigma della programmazione ad oggetti (OOP) ha permesso di mantenere la complessità del codice sostenuta e con ottime performance globali. Oltre a tale aspetto sono state poi adottate delle tecniche di ottimizzazione del codice sorgente per evitare la ridondanza dei dati e lo spreco di risorse. Prima di concludere questa breve parte introduttiva vorrei porre l’attenzione su alcune peculiarità aggiuntive proprie di HuMoR: la scelta del linguaggio C++ ha permesso di creare un framework platfrom independent, ovvero che possa essere con pochissimo sforzo essere riadattato per funzionare su tutti i principali sistemi operativi, come Linux, Windows, MacOsx, Bsd, ecc. . . Inoltre il processo di sviluppo è avvenuto completamente su piattaforma Linux[9], utilizzando esclusivamente software libero5 come Vim[12] e Kdevelop[6], tutti rilasciati con licenze libere, come la GNU GPL[4]. Lo stesso HuMoR è stato rilasciato come software libero, con licenza GNU GPL versione 2. 2.1 Semplicità Pensando al concetto di semplicità, ci siamo posti il vincolo che per creare un nuovo modulo (da ora in avanti plugin) sarebbe servita una struttura estremamente compatta. Nell’ambito di questa struttura devono però essere risolte tutte le possibili esigenze che un plugin può avere: l’implementazione del codice operativo e la gestione di una configurazione di funzionamento. Il primo passo effettuato, quindi, è stato suddividere la struttura del plugin in due parti distinte (figura 2.1), in cui una, il Component Plugin, si occupa 5 Per una trattazione più approfondita di cosa sia il software libero l’autore della presente tesi raccomanda la lettura di “Cos’è il Software Libero?”[2]. 2.1 Semplicità 15 Figura 2.1: Struttura di un plugin dell’implementazione del codice operativo a cui è affidato l’elaborazione e la produzione di dati, mentre l’altra, l’Interface Plugin, si occupa della gestione dei parametri di funzionamento per configurare il Component Plugin nel modo voluto. L’interazione fra le due componenti avviene in maniera del tutto trasparente per il Component Plugin, che quando richiamato dal core di HuMoR effettuerà l’analisi dei dati avuti in input, mentre il lavoro “sporco” è affidato interamente all’Interface Plugin che, deve in prima istanza procurarsi una versione aggiornata dei parametri e successivamente impostare le variabili di lavoro del Component Plugin ad esso associato. Come detto, l’operazione è del tutto trasparente per il Component Plugin che si troverà le variabili di lavoro correttamente inizializzate con i valori corretti. Ovviamente questa fase deve avvenire in un momento in cui il Component Plugin non è operativo; generalmente accade quando viene creata una nuova sessione di HuMoR oppure al verificarsi di eventi esterni (come il lancio di signals6 , in particolare il segnale SIGUSR1). 2.1.1 Interface Plugin Come si vede dal listato 2.1, la classe di un’Interface Plugin è semplice: la parte pubblica ha solamente due metodi, read() e write(). Intuitivamente il primo (VERIFICARE) viene chiamato quando si vuole che il plugin legga una versione aggiornata dei parametri di funzionamento, mentre il secondo 6 Un signal, o segnale, è un evento generato dal sistema operativo o da un altro software per avvertire il destinatario del segnale del verificarsi di particolari eventi. 2.1 Semplicità 16 quando si vuole memorizzare i parametri di funzionamento correnti (può essere utile se l’algoritmo implementato aggiorna metodicamente i propri parametri di lavoro). Listing 2.1: Struttura di un Interface Plugin /∗ ∗ ∗ Generic I n t e r f a c e i n t e r f a c e ∗/ class InterfacePlugin : public Plugin { public : InterfacePlugin ( ) : component ( 0 ) {}; virtual ˜ InterfacePlugin ( ) { } ; virtual bool setup ( const std : : string& setup_data ) = 0 ; void setComponent ( ComponentPlugin ∗ o ) ; static std : : string getType ( ) { return " Interface " ; } protected : ComponentPlugin ∗ component ; friend class plugin_table ; }; La struttura dell’Interface Plugin è volutamente priva di informazioni sul tipo di supporto in cui i parametri verranno letti e memorizzati; il comportamento dipende unicamente dall’implementazione che l’utente farà all’interno dei due metodi. Si potranno avere quindi Interface Plugin che leggono e scrivono su file, piuttosto che su database, piuttosto che su socket per la comunicazione remota verso possibili repository dedicati. 2.1.2 Component Plugin La classe del Component Plugin è paradossalmente più semplice da analizzare: nel LISTATO 2 si vede chiaramente che l’interfaccia pubblica della 2.1 Semplicità 17 classe implementa un singolo metodo (VERIFICARE), run(), che sarà invocato tutte le volte che il core di HuMoR ritiene necessaria l’esecuzione della parte computativa del plugin. L’utente avrà quindi l’onere di implementare il proprio algoritmo all’interno del metodo (VERIFICARE), potendo avvalersi dell’accesso a tutte le variabili di lavoro del core di HuMoR, che si occupa principalmente della condivisione dei dati tra i vari plugin (sarà spiegato con maggior dettaglio nel paragrafo 2.2.1). 2.1.3 HuMoR Plugin Perché i due plugin, il Component e l’Interface Plugin, possano essere riconosciuti e utilizzati da HuMoR è necessaria una struttura aggiuntiva che faccia da collante alle due precedenti e che implementi tutti i metodi e le informazioni necessarie per descrivere il Component e l’Interface Plugin in esso contenuti. Quello che potremmo, quindi, chiamare HuMoR Plugin, mostrato nel LISTATO 3, è ancora più semplice da implementare, quasi automatico, in quanto l’utente deve utilizzare le due macro7 INITPLUGIN e ENDPLUGIN, che delineano l’inizio e la fine dello HuMoR Plugin, e al loro interno elencare quali classi implementano il Component Plugin e quali l’Interface Plugin, utilizzando le macro dedicate COMPONENTPLUGIN e INTERFACEPLUGIN. Una volta creato lo HuMoR Plugin è sufficiente compilarlo e copiarlo nella directory che contiene tutti gli HuMoR Plugin, in modo che al lancio di una nuova sessione di HuMoR, vengano caricati in strutture di memoria apposite e assegnati correttamente alle funzionalità descritte nelle informazioni dello HuMoR Plugin. La parte che si occupa di caricare, analizzare e istanziare ogni singolo HuMoR Plugin è mostrata nel LISTATO 4, sezione del codice che peraltro è del tutto trasparente all’utente che voglia solamente implementare il proprio algoritmo. 7 DEFINIRE COSA è UNA MACRO 2.2 Modularità 2.2 18 Modularità Formalizzato il concetto di plugin (paragrafo 2.1), si è scelto di applicare tale struttura a tutte le parti principali di HuMoR. Sono stati creati quindi un plugin di input, chiamato CameraPlugin (VERIFICARE), un plugin per l’analisi del movimento, chiamato MotionDetectionPlugin, un plugin per il tracciamento del movimento, chiamato TrackerPlugin e due categorie speciali di plugin: • HuMoRCorePlugin, deputato alla gestione del core di HuMoR e quindi alla gestione di tutto il flusso dei dati e alla loro condivisione con gli altri plugin • DataConsumerPlugin, collocato alla fine del ciclo di analisi delle immagini ottenute in input, come si può vedere nella FIGURA HO PERSO IL CONTO, ha il compito di implementare tutte le elaborazioni di più alto livello semantico, rispetto ai precedenti plugin. I DataConsumerPlugin possono essere molteplici, al contrario di quanto accade per gli altri plugin, e vengono invocati nell’esatto ordine in cui vengono elencati nei parametri di configurazione dello HuMoRCorePlugin. Per quanto sia interessante approfondire la struttura di tutti i tipi di plugin implementati, verranno analizzati in dettaglio solamente questi ultimi due, possedendo una struttura e delle funzionalità sicuramente più avanzate. 2.2.1 HuMoRCore Plugin La parte più interessante dello HuMoRCorePlugin è il metodo loop(), come si può vedere nel LISTATO X (HuMoR Daemon -¿ HuMoR Core -¿ loop()), nella parte privata dell’interfaccia. Per creare una nuova sessione di HuMoR si deve far partire un programma chiamato HuMoR Daemon, che si occupa solamente di configurare il core di HuMoR, prelevando i parametri necessari da un file di configurazione (LISTATO XX) e di far partire su di un thread parallelo l’esecuzione del core di HuMoR, che si limiterà ad invocare il metodo loop() sopra citato. Il flusso logico delle chiamate ad i vari 2.2 Modularità 19 plugin è descritto nel codice implementato in tale metodo (LISTATO X): il primo passaggio consiste nel calcolare il framerate effettivo a cui HuMoR sta lavorando ed eventualmente introdurre dei ritardi per ridurlo, se nel file di configurazione viene indicato un framerate massimo (campo delay e fps). Successivamente si procede con il prelevare dal CameraPlugin un fotogramma da analizzare ed eventualmente applicare un pre-filtro se il campo prefilter è impostato a true. Si continua invocando il MotionDetectorPlugin che restituisce una maschera di pixel dove un pixel bianco rappresenta una zona etichettata come movimento e un pixel nero rappresenta una zona etichettata come non movimento. Se il numero totale dei pixel identificati come movimento supera una certa soglia, si procede chiamando il tracker, che restituirà il vettore traklist (vedere il paragrafo 1.2) aggiornato con le features degli oggetti identificati. Infine ci sarà la chiamata a tutti i DataConsumerPlugin, nello stesso ordine in cui sono stati elencati nel campo data-consumer, e l’applicazione del ritardo per ridurre il framerate. A questo punto il ciclo può iniziare nuovamente e continuare fintanto che non si verifica un qualche condizione di stop, che generalmente provoca l’interruzione del thread del core di HuMoR. 2.2.2 DataConsumer Plugin I plugin DataConsumer hanno il compito di effettuare post-analisi su tutti i dati elaborati dal Motion Detector e dal Tracker; vengono invocati per ultimi nel flusso logico di esecuzione, come spiegato nel paragrafo 2.2.1 e dispongono di una grossa quantità di informazioni pre-elaborate. Possono essere utilizzati per effettuare processing ad un livello più alto di comprensione semantica oppure per effettuare semplici controlli sul funzionamento della pipeline esecutiva. Attualmente sono stati implementati molti plugin DataConsumer, che offrono funzionalità come disegnare bounding box intorno alla regione identificata come movimento, oppure filtrare il movimento, rilevato dal Motion Detector, sulla base di maschere fornite come parametri di funzionamento del plugin, oppure eseguire azioni personalizzabili, sempre tramite i parametri di configurazione, al verificarsi di determinati eventi. 2.3 Efficienza 20 Inoltre i vari plugin DataConsumer, essendo invocati in modo sequenziale, esattamente come sono specificati nei parametri di configurazione del core di HuMoR, possono essere concatenati opportunamente per fornire dati elaborati al plugin che segue, in maniera totalmente collaborativa, per raggiungere livelli di elaborazione anche molto complessi. Nel capitolo 3 verranno discussioni più in dettaglio alcuni plugin DataConsumer. 2.3 Efficienza Rendere efficiente un framework coinvolge principalmente due aspetti: • ottimizzare gli algoritmi maggiormente utilizzati • condividere i dati comuni senza spreco di risorse Sul primo punto si può fare ben poco in quanto è l’utente stesso a dover implementare i propri algoritmi ed è quindi affidato all’utente il compito di implementare algoritmi che siano veloci e quanto più possibile ottimizzati. Gli algoritmi attualmente presenti dentro HuMoR (Motion Detector e Tracker) hanno subito più volte operazioni di ottimizzazione e al momento attuale riescono ad avere delle buone performance. Il secondo punto invece è stato maggiormente analizzato, fino a giungere alla conclusione che il metodo migliore per non sprecare risorse, fosse passare l’intero ambiente di lavoro del core di HuMoR a tutti i plugin di tipo DataConsumer e solamente le informazioni necessarie ai plugin Camera, MotionDetector e Tracker, avendo cura di passare tutte le variabili per riferimento8 . Come si può vedere dagli snapshots presenti nel LISTATO XXX, il metodo apply() di ogni plugin prende come parametri i riferimenti delle variabili necessarie, mentre il metodo consumeData() dei plugin DataConsumer prende 8 Passare una variabile per riferimento ad una funzione, in C++, consiste nel passare l’indirizzo di memoria dove il dato è presente, al contrario del passaggio per valore che crea una copia in memoria del dato da passare alla funzione. Con dati particolarmente strutturati e complessi, il passaggio per valore causa un grosso spreco di risorse, pur garantendo l’integrità del dato originale. 2.3 Efficienza 21 come argomento puntatore const all’istanza corrente del core di HuMoR, in questo modo può accedere a tutte le elaborazioni effettuate dai precedenti plugin, ma grazie al vincolo const non ha la possibilità di modificarli. Un altro aspetto tenuto in forte considerazione, nella primissima fase progettuale, è stato di scegliere un linguaggio di programmazione espressivo e performante. La scelta è ricaduta immediatamente su C++; è un linguaggio aderente al paradigma della programmazione ad oggetti (OOP), caratteristica fondamentale nell’ottenere una bassa complessità del codice rispetto alla complessità concettuale che si deve raggiungere, e soprattutto è un linguaggio i cui compilatori, ad esempio la suite Gcc[3]9 per Linux, sono capaci di ottenere forti ottimizzazioni sul codice binario prodotto. Non è un caso se la maggioranza dei grossi software e delle librerie attualmente prodotte sfruttino proprio questo linguaggio di programmazione. 9 Implementa la versione definita nello standard ISO 1482 e parte del nuovo 0x. Capitolo 3 Esemplificazioni dell’utilizzo di HuMoR 3.1 Rimozione di oggetti da una scena 3.2 Tracciamento di oggetti in movimento 3.3 Rilevazione di percorsi 3.4 Interfaccia di controllo per giochi 3.5 Un Avatar: LIA Conclusioni Listings 2.1 Struttura di un Interface Plugin . . . . . . . . . . . . . . . . . 16 Elenco delle figure 2.1 Struttura di un plugin . . . . . . . . . . . . . . . . . . . . . . 15 Bibliografia [1] Miroslaw Bober. Mpeg-7 visual shape descriptors. 2001. [2] Spiegazione su cosa è il http://www.gnu.org/philosophy/free-sw.it.html. software libero. [3] Gcc, the gnu compiler collection. http://gcc.gnu.org/. [4] Licenza gnu gpl. http://www.gnu.org/licenses/licenses.it.html. [5] Ming-Kuei Hu. Visual pattern recognition by moment invariants. 1962. [6] Homepage dell’ide, integrated development environment, distribuito con kde. http://www.kdevelop.org/. [7] Alireza Khotanzad and Yaw Hua Hong. Invariant image recognition by zernike moments. 1990. [8] Gilles Labonté. A som neural network that reveals continuos displacement fields. 1998. [9] Spiegazione tratta da wikipedia http://it.wikipedia.org/wiki/Linux. [10] David G. Lowe. keypoints. 2004. su cosa è linux. Distinctive image features from scale-invariant [11] Sito web di the mathworks, azienda produttrice del software matlab. http://www.mathworks.com. BIBLIOGRAFIA [12] Homepage del popolare http://www.vim.org. 27 editor di testo vim, vi improved.