recognizing hand gestures using webcams

24
UNIVERSIT ` A DEGLI STUDI DI TRENTO Facolt` a di Science Matematiche Fisiche e Naturali Corso di Laurea in Informatica Principi di Computer Graphics Riconoscere i gesti della mano tramite WebCam Studenti: Massimiliano Bernab` e Andrea Gastaldello Anno accademico 2004–2005

Upload: graphitech

Post on 05-Dec-2014

948 views

Category:

Education


0 download

DESCRIPTION

Design and implementation of a new pointing system through the use of webcams and hand-tracking.

TRANSCRIPT

Page 1: Recognizing Hand Gestures using WebCams

UNIVERSITA DEGLI STUDI DI TRENTO

Facolta di Science Matematiche Fisiche e Naturali

Corso di Laurea in Informatica

Principi di Computer Graphics

Riconoscere i gesti della mano tramiteWebCam

Studenti:

Massimiliano Bernabe

Andrea Gastaldello

Anno accademico 2004–2005

Page 2: Recognizing Hand Gestures using WebCams

1 Descrizione del problema

Il problema che ci e stato sottoposto consistema nella ideazione e realiz-zazione di un nuovo sistema di puntamento che, utilizzando due webcam,potesse seguire il movimento della mano riproducendolo nel mondo 3D. Ilsistema avrebbe dovuto anche riconoscere alcuni comandi dati con partico-lari posizioni della mano. Ci si prefiggeva l’obbiettivo di giungere ad averedue macchine distinte per la cattura del movimento e la visualizzazione delmondo 3D.

2 Stato dell’Arte

Al momento in cui ci siamo applicati al problema esistevano gia diversimetodi per il riconoscimento della gestualita della mano.Infatti erano giastate realizzate varie tipologie di guanti, con sensori piu o meno precisi peril riconoscimento della posizione delle dita. Erano stati ideati, per esempio,alcuni metodi per il riconoscimento della gestualita della mano in un’im-magine, con algoritmi piu o meno sofisticati che si basano sul confronto diimmagini. Interessante e il progetto che, nel 1997, ha consentito di arrivareal risultato di implementare un algoritmo per il riconoscimento della ges-tualita basato sulla forma della mano in 2 dimensioni. Il sistema ottenutopermetteva di riprodurre movimenti della mano naturali riducendo il ru-more dovuto allo sfondo. Questi sistemi sono il frutto di ricerche che hannodiversi campi di applicazione,ad esempio potrebbero sviluppare un sistemadi riconoscimento del linguaggio dei segni al servizio di persone con pro-blemi di handicap oppure potrebbero facilitare la comunicazione tra uomoe macchina. Queste sono le precedenti esperienze nel settore specifico di cuiabbiamo trovato notizie, ma sicuramente esistono progetti simili.

3 Installazione

Il software e un’estensione della libreria OpenTracker e le sue funzioni visono incluse. Per l’installazione c’e bisogno di alcune librerie di supportoche sono le seguenti:

ACE The Adaptive Communication Environment, a wrapper library forsystem services.

ArToolKit ARToolKit applications allow virtual imagery to be superim-posed over live video of the real world.Although this appears magicalit is not. The secret is in the black squares used as tracking markers.

Coin3D-2 Is an OpenGL based, retained mode 3D graphics renderinglibrary.

1

Page 3: Recognizing Hand Gestures using WebCams

OpenCV Means Intel Open Source Computer Vision Library. It is a collec-tion of C functions and few C++ classes that implement some popularalgorithms of Image Processing and Computer Vision.

Xerces A validating XML parser library for C++ and Java.

Dopo aver installato queste librerie, per ciascuna di esse si deve aggiun-gere una variabile d’ambiente che contiene il percorso nel quale si trova lalibreria. Riporto un esempio di come settare le variabili:

ACEROOT=C:\Graphics\ACE - Per ACE

ARTOOLKITROOT=C:\Graphics\ArToolKit - Per ArToolKit

COIN3DDI=C:\Graphics\Coin3D-2 - Per Coin3D-2

OPENCVROOT=C:\Graphics\OpenCV - Per OpenCV

XERCESCROOT=C:\Graphics\Xerces - Per Xerces

Devo anche aggiungere una variabile d’ambiente per OpenTracker chesi chiama OTROOT alla quale devo assegnare “il path dove copio il miosoftware”\opentraker

Poi devo assegnare alla variabile d’ambiente PATH tutte le variabilesopra citate seguite da “\bin”.

Ora il sistema e installato e devo procedere con la configurazione me-diante i due file XML come e spiegato nel paragrafo 4.1.

Il passo successivo all’installazione del software e il posizionamento dellewebcam. Per illustrare la posizione delle camere rispetto alla mano imma-giniamo un piano cartesiano: sull’asse delle ascisse disponiamo una web-cam, in grado quindi di riprendere dall’alto la mano, mentre la seconda ladisponiamo sull’asse delle ordinate, quindi in grado di riprendere la manolateralmente. Mentre le due camere risultano in uno spazio tridimensionalefisse e poste sullo stesso piano verticale, la mano si muove liberamente.

2

Page 4: Recognizing Hand Gestures using WebCams

4 Come si usa

Per eseguire il sistema devono essere eseguite entrambi le sue parti:

• il sistema di Tracking

• il mondo 3D

Entrambe le parti hanno una file di configurazione XML che contiene isettaggi dei software, vedremo in seguito come questi file sono fatti. Unavolta compilato il file di configurazione le due parti del software possonoessere eseguite da linea di comando passando come parametro proprio il filedi configurazione.

Esempio:

> cvsample MyHandTracker.xml

> world3d MyWorld3d.xml

Per il tracker c’e una sessione di configurazione da fare in cui si devonosettare i punti della mano in entrambe le finestre che vengono presentate,come in figura 1. Si deve settare la porzione di immagine che rappresenta og-

Figura 1: Schermata di configurazione del sistama di tracking

ni dito e il retro della mano. Per passare da una parte all’altra della mano sideve premere un tasto tra 1 e 6 e successivamente, evidenziando la porzionedi immagine corrispondente alla parte di mano che si intende settare, comeconferma appare una elisse che circoscrive la sezione. Dopo aver configuratotutti punti della mano premendo il tasto ’s’ si avvia il tracker e la comuni-cazione con il software che modella il mondo 3D. E anche possobile tornarealla fase di configurazione in un secondo momento premendo il tasto ’c’.

3

Page 5: Recognizing Hand Gestures using WebCams

Segue la sequenza completa di tasti utilizzati.

1 Retro della mano.

2 Pollice.

3 Indice.

4 Medio.

5 Anulare.

6 Mignolo.

s Fine della configurazione.

c Configurazione.

ESC Termine del programma.

4.1 File di configurazione

Le due parti del sistema necessitano di un file di configurazione. Iniziamocon l’esaminare il file di configurazione del sistema di tracking.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE OpenTracker SYSTEM "myClient.dtd">

<?xml-stylesheet type="text/xsl" href=".\myClient.xsl"?>

<OpenTracker>

<configuration>

<ConsoleConfig interval="1" headerline="webCam Tracker"/>

<MyHandConfig />

</configuration>

<ConsoleSink comment="MyHand Source 1" active="on" DEF="netSource1">

<NetworkSink name="myHandClient" number="0" multicast-address="localhost" port="12346">

<MyHandSource frontalCamPos="20 15 52"

lateralCamPos="80.0 15.0 10.0"

frontalPlane="42 30"

lateralPlane="30 20"

frontalMovie="./video/VideoSopra3.avi"

lateralMovie="./video/VideoLato3.avi"/>

</NetworkSink>

</ConsoleSink>

</OpenTracker>

Figura 2: Esempio di file di configurazione per il sistema di tracking.

Come possiamo notare nella figura 2, che rappresenta un esempio di filedi configurazione per il sistema di tracking, esso ha una sola sezione nellaquale viene settato l’indirizzo e la porta al quale spedire i dati e all’inter-no del tag MyHandSource i parametri di configurazione delle webcam e glieventuali nomi dei file dei filmati. Gli attributi obbligatori frontalCamPos

e lateralCamPos, rappresentano la posizione della camera orizzontale e ver-ticale rispetto al punto d’origine(vedi figura 3), frontalPlane e lateralPlane,

4

Page 6: Recognizing Hand Gestures using WebCams

rappresentano, invece, le dimensioni del piano reale di riferimento orizzon-tale e verticale. Gli altri due attributi frontalMovie e latgeralMovie nonsono obbligatori e rappresentano il nome del file contenente rispettivamentela ripresa dall’alto e quella laterale.

Figura 3: Posizionamento delle camere rispetto all’origine

Vediamo ora come e fatto il file di configurazione del Mondo 3D. Comepossiamo notare nella figura 4, che rappresenta un esempio di file di config-urazione per il mondo 3D, esso e diviso in tre parti principali evidenziate daun asterisco:

1. l’indirizzo dell’host e la porta sulla quale riceve i dati;

2. il nome della callback che viene richiamata quando arriva un pachetto;

3. la definizione di un comando: assegnando un nome e la configurazionedella mano che lo rappresenta, settandone il range valido di aperturadelle dita. Sono stati implementati i seguenti comandi:

CommandSelect Seleziona l’oggetto nelle vicinanze.

CommandMove Sposta l’oggetto nelle vicinanze.

CommandRGBChange Cambia il colore dell’oggetto nelle vicinanze.

CommandScaleChange Cambia la scala dell’oggetto nelle vicinanze.

5

Page 7: Recognizing Hand Gestures using WebCams

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE OpenTracker SYSTEM "myWorld3d.dtd">

<?xml-stylesheet type="text/xsl" href=".\myWorld3d.xsl"?>

<!-- multicas addr: mettere indirizzo di world3d -->

<OpenTracker>

<configuration> <ConsoleConfig interval="1" headerline="World3D"/> </configuration>

<!-- sorgente dei dati per il palmo della mano e delle dita -->

<ConsoleSink comment="ALL Hand Source" active="off" DEF="incomingDataSource">

[*] <NetworkSource number="0" multicast-address="1.1.1.12" port="12346"/>

</ConsoleSink>

<!-- binding tra callback e sorgente dei dati -->

<ConsoleSink comment="Hand Reference Node" active="off">

[*] <Callback name="IncomingDataSourceCB"> <Ref USE="incomingDataSource"/> </Callback>

</ConsoleSink>

<ConsoleSink comment="Hand Command Select" active="off">

[*] <HandCommand name="CommandSelect"

thumbRateMin="0.0" thumbRateMax="0.4"

indexfingerRateMin="0.0" indexfingerRateMax="0.4"

middlefingerRateMin="0.0" middlefingerRateMax="0.4"

ringfingerRateMin="0.0" ringfingerRateMax="0.4"

littlefingerRateMin="0.0" littlefingerRateMax="0.4" />

</ConsoleSink>

</OpenTracker>

Figura 4: Esempio di file di configurazione per il mondo 3D.

6

Page 8: Recognizing Hand Gestures using WebCams

5 Architettura

Il sistema e facilmente divisibile in due parti:

• la prima che utilizzando OpenCV cattura da WebCam i filmati e seguela mano calcolandone la posizione e i movimenti;

• la seconda che rappresenta la mano nel mondo 3D e la fa muoverebasandosi sui dati che riceve.

Per far si che le due parti comunichino abbiamo utilizzato Opentracker, unparticolare middleware che permette di far colloquiare due applicazioni sep-arate. Esso si basa su una architettura sviluppata per essere una flessibilesoluzione per l’implementazione dei processi legati al tracking dei disposi-tivi di imput ed al flusso di dati utilizzati da ambientazioni virtuali. Questoha permesso all’applicazione di funzionare tramite un flusso di dati gener-ici attraverso la rete e di essere in modo nativo trasparente alla stessa. Eanche possibile stabilire nuovi tipi di interazioni mediante la configurazioneo l’estensione di file XML. Le nuove classi di OpenTracker, implementateper i nostri scopi, leggono i dati provenienti dalle WebCam, effettuano lenecessarie trasformazioni, e inviano i dati cosı modificati verso l’altra ap-plicazione che utilizzando OpenTracker leggera i dati e li memorizzera inuna classe che rappresenta lo stato della mano, questa classe verra poi us-ata dall’applicazione stessa che rappresenta la mano del mondo 3D.Comerappresentato nella figura 5. L’estensione della libreria di OpenTracker e

Figura 5: Architettura del Sistema

avvenuta espandendo l’architettura ad albero per la gestione del flusso datiproveniente dal dispositivo di tracking con un modulo ad-hoc per il nostrodispositivo.

7

Page 9: Recognizing Hand Gestures using WebCams

5.1 Nuovo sistema di tracking

Per il nuovo sistema di tracking abbiamo esteso la libreria di OpenTrackercon un nuovo modulo. Questo modulo rappresenta un nodo nell’albero eviene configurato tramite XML. Come mostra la figura 6 il modulo e di-viso in diverse classi: MyHandModule, che implementa nella loro strutturale funzioni necessarie alla costruzione della struttura dati, al filtraggio edalla formattazione dei valori per il passaggio successivo verso la classe cor-rispondente, incaricata di fungere da contenitore dei dati, MyHandSource.Ci sono anche delle altre classi di supporto (MyHand, My3dPoint, MyPoint)alle quali il modulo delega di tracciare la mano nel mondo reale.

Figura 6: Architettura del Sistema

8

Page 10: Recognizing Hand Gestures using WebCams

5.2 Architettura di World3d

L’albero di scena del mondo virtuale puo essere scomposto in tre parti:

1. gli assi

2. gli oggetti di scena

3. la mano

Organizzati come in figura 7. Gli assi sono costituiti da 3 cilindri di diversocolore orientati opportunamente. Il soparator wSep contiene la parte delmondo virtuale sulla quale si puo agire: la mano e gli oggetti.

Figura 7: L’albero di scena

Il Separator worldSeparator isola gli oggetti sui quali l’utente puo com-piere operazioni: una sfera un cono e un cubo, ognuno con un colore carat-teristico e una matrice di rototraslazione che ne indica la posizione e l’ori-entamento (Figura 8).

La mano che rappresenta nel mondo virtuale la mano dell’utente e a suavolta composta da 7 parti(Figura 9):

• il palmo, limitato da palmSeparator;

• cinque dita, ognuna limitata da un separator;

• una sfera, che serve per individuare le collisioni tra la mano e gli altrioggetti, la loro selezionatura.

9

Page 11: Recognizing Hand Gestures using WebCams

Figura 8: L’albero di scena per gli oggetti del mondo virtuale

Figura 9: L’albero di scena per la mano

Le parti di scene graph che identifacano ogni dito e il palmo della manohanno una struttura identica (Figura 10): viene applicato un nodo SoColorad un ShapeKit. Il ShapeKit contine un nodo Complexity mediante il qualesi puo variare la precisione nel renderizzare le strutture, la matrice dei puntidi controllo e una superficie NURBS.

Figura 10: L’albero di scena per il palmo

10

Page 12: Recognizing Hand Gestures using WebCams

6 Problemi e soluzioni

6.1 Trovare le coordinate di un punto nello spazio reale

Per trovare un punto nello spazio reale sono state utilizzate due funzioni eduna struttura dati apposita. Si calcolano due rette, ciascuna passante peruna camera e per il punto che si prende in esame. Successivamento vieneidentificato il punto su ogni retta piu vicino all’altra retta, poi si calcola ilpunto medio tra i due punti.

Da file di configurazione vengono lette la posizione delle due telecamerenello spazio reale e le dimensioni del piano di riferimento. Le due telecameredevono essere localizzate in una posizione azzimutale rispetto al piano diriferimento.

Chiamando la funzione calculateRealPos() si individuano i due puntisulle rette piu vicini e se ne calcola il punto medio.

struct myLine3d_s

{

int coordGen;

bool IsFixed[3];

float FirstPoint[3];

float Coef[3];

float SecondPoint[3];

};

typedef struct myLine3d_s myLine3d;

La struttura racchiude tutti i dati utili per valutare una retta nello spazioutilizzando un parametro di riferimento:

int coordGen e la coordinata che genera le altre: 0=x 1=y 2=z;

bool IsFixed e un vettore che tiene traccia se ogni coordianta varia infunzione della coordinata generatrice oppure e fissa;

float FirstPoint il vettore dei primi spostamenti: in 0:x, 1:y, 2:z;

float Coef vettore dei coefficenti

float SecondPoint il vettore dei secondi spostamenti: in 0:x, 1:y, 2:z.

La formula che viene utilizzata per il calcolo della posizione di ogni puntosu una retta data e la seguente (nel caso di coordinata generatrice z):

input : val p(x, y, z) c(x, y, z)l = Xc − Xp

m = Yc − Yp

n = Zc − Zp

output =

((

(val − Zp) l

n+ Xp

)

,

(

(val − Zp)m

n+ Yp

)

, val

)

11

Page 13: Recognizing Hand Gestures using WebCams

Questa formula viene chiamata per le coordinate non fisse, all’internodel metodo evaluate della classe My3dPoint.

void My3dPoint::evaluate(myLine3d line, float val, float pos[3])

{

for(int i=0;i<3;i++)

{

if(i==line.coordGen){pos[i]=val;}

else{

if(line.IsFixed[i]){

pos[i]=line.SecondPoint[i];

}else{

pos[i]=(val-line.FirstPoint[line.coordGen])*

line.Coef[i]+line.SecondPoint[i];

}

}

}

}

Per assegnare correttamente i campi della struttura MyLine3d si devechiamare la funzione buildLine3d della classe My3dPoint. Questa funzioneha come parametri 2 punti dello spazio reale, uno dato dalla posizione dellatelecamera, l’altro e dato dalla proiezione del punto reale sul piano indi-viduato dalla camera. La struttura MyLine3d e stata ideata in modo chesia possibile scorrere sulla retta determinata dai due punti, variando unparametro e la funzione buildLine costruisce la struttura in modo da gestireanche le linee parallele ad uno qualsiasi degli assi.

bool My3dPoint::buildLine3d(myLine3d & line, float CameraPos[3],float PointPos[3])

{

if(0.1f > fabs(CameraPos[coord_X]-PointPos[coord_X])){

if(0.1f > fabs(CameraPos[coord_Y]-PointPos[coord_Y])){

if(0.1f > fabs(CameraPos[coord_Z]-PointPos[coord_Z])){

cout<< "ERRORE, punti troppo vicini" <<endl;return false;

}else{

cout<<"eq Retta: z=k"<<endl;

line.coordGen=coord_Z;

line.IsFixed[coord_X]=true;

line.IsFixed[coord_Y]=true;

line.IsFixed[coord_Z]=false;

line.Coef[coord_X]=0.0f;

line.Coef[coord_Y]=0.0f;

line.Coef[coord_Z]=1.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=0.0f;

line.FirstPoint[coord_Z]=0.0f;

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=PointPos[coord_Y];

line.SecondPoint[coord_Z]=0.0f;

}

}else{

if(0.1f > fabs(CameraPos[coord_Z]-PointPos[coord_Z])){

cout<<"eq Retta: y=k"<<endl;

line.coordGen=coord_Y;

line.IsFixed[coord_X]=true;

line.IsFixed[coord_Y]=false;

line.IsFixed[coord_Z]=true;

line.Coef[coord_X]=0.0f;

12

Page 14: Recognizing Hand Gestures using WebCams

line.Coef[coord_Y]=1.0f;

line.Coef[coord_Z]=0.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=0.0f;

line.FirstPoint[coord_Z]=0.0f;

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=0.0f;

line.SecondPoint[coord_Z]=PointPos[coord_Z];

}else{

cout<<"eq Retta: ay + bz +c =0"<<endl;

line.coordGen=coord_Z;

line.IsFixed[coord_X]=true;

line.IsFixed[coord_Y]=false;

line.IsFixed[coord_Z]=false;

line.Coef[coord_X]=0.0f;

line.Coef[coord_Y]=(CameraPos[coord_Y]-PointPos[coord_Y])

/(CameraPos[coord_Z]-PointPos[coord_Z]);

line.Coef[coord_Z]=1.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=0.0f;

line.FirstPoint[coord_Z]=PointPos[coord_Z];

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=PointPos[coord_Y];

line.SecondPoint[coord_Z]=0.0;

}

}

}else{

if(0.1f > fabs(CameraPos[coord_Y]-PointPos[coord_Y])){

if(0.1f > fabs(CameraPos[coord_Z]-PointPos[coord_Z])){

cout<<"eq Retta x=k"<<endl;

line.coordGen=coord_X;

line.IsFixed[coord_X]=false;

line.IsFixed[coord_Y]=true;

line.IsFixed[coord_Z]=true;

line.Coef[coord_X]=1.0f;

line.Coef[coord_Y]=0.0f;

line.Coef[coord_Z]=0.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=0.0f;

line.FirstPoint[coord_Z]=0.0f;

line.SecondPoint[coord_X];

line.SecondPoint[coord_Y]=PointPos[coord_Y];

line.SecondPoint[coord_Z]=PointPos[coord_Z];

}else{

cout<<"eq Retta ax + bz + c =0"<<endl;

line.coordGen=coord_Z;

line.IsFixed[coord_X]=false;

line.IsFixed[coord_Y]=true;

line.IsFixed[coord_Z]=false;

line.Coef[coord_X]=(CameraPos[coord_X]-PointPos[coord_X])

/(CameraPos[coord_Z]-PointPos[coord_Z]);

line.Coef[coord_Y]=0.0f;

line.Coef[coord_Z]=1.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=0.0f;

line.FirstPoint[coord_Z]=PointPos[coord_Z];

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=PointPos[coord_Y];

line.SecondPoint[coord_Z]=0.0;

}

}else{

if(0.1f > fabs(CameraPos[coord_Z]-PointPos[coord_Z])){

13

Page 15: Recognizing Hand Gestures using WebCams

cout<<"eq Retta ax + by + c = 0"<<endl;

line.coordGen=coord_Y;

line.IsFixed[coord_X]=false;

line.IsFixed[coord_Y]=false;

line.IsFixed[coord_Z]=true;

line.Coef[coord_X]=(CameraPos[coord_X]-PointPos[coord_X])

/(CameraPos[coord_Y]-PointPos[coord_Y]);

line.Coef[coord_Y]=1.0;

line.Coef[coord_Z]=0.0f;

line.FirstPoint[coord_X]=0.0f;

line.FirstPoint[coord_Y]=PointPos[coord_Y];

line.FirstPoint[coord_Z]=0.0f;

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=0.0f;

line.SecondPoint[coord_Z]=PointPos[coord_Z];

}else{

cout<<"eq Retta ax + by + cz + d = 0"<<endl;

line.coordGen=coord_Z;

line.IsFixed[coord_X]=false;

line.IsFixed[coord_Y]=false;

line.IsFixed[coord_Z]=false;

line.Coef[coord_X]=(CameraPos[coord_X]-PointPos[coord_X])

/(CameraPos[coord_Z]-PointPos[coord_Z]);

line.Coef[coord_Y]=(CameraPos[coord_Y]-PointPos[coord_Y])

/(CameraPos[coord_Z]-PointPos[coord_Z]);

line.Coef[coord_Z]=1.0f;

line.FirstPoint[coord_X];

line.FirstPoint[coord_Y];

line.FirstPoint[coord_Z]=PointPos[coord_Z];

line.SecondPoint[coord_X]=PointPos[coord_X];

line.SecondPoint[coord_Y]=PointPos[coord_Y];

line.SecondPoint[coord_Z]=0.0f;

}

}

}

return true;

}

Infine la funzione calculateRealPos individua l’intersezione tra le duerette e, nel caso in cui le rette non si incrocino, trova il punto medio tra ipunti piu vicini sulle rette.

6.2 Costruire un dito e farlo piegare

Un dito della mano visualizzata nel mondo 3d e formato da una NURBS,la quale viene determinata in funzione di una matrice di punti di controlloe da due vettori di nodi, uno per le posizioni orizzontali della superficie el’altro per le posizioni verticali della superficie.

Nel scene graph ogni dito e delimitato da un SoSeparator e il sottoalberocontiene un colore e un shapekit che identifica la superficie (Figura 11).

I nodi vengono inseriti nell’albero di scena mediante la funzione buildfin-ger.

SoSeparator * buildFinger(const float red,const float green,const float blue,

const char controlPointsName[]){

const int UPOINTS = 7;

14

Page 16: Recognizing Hand Gestures using WebCams

Figura 11: L’albero di scena per un dito

const int VPOINTS = 5;

float fingerKnotsU[UPOINTS + 4] = {0,0,0,0,1,2,3,4,4,4,4};

float fingerKnotsV[VPOINTS + 4] = {0,0,0,0,1,2,2,2,2};

SoSeparator * fingerSeparator = new SoSeparator;

SoShapeKit * finger = new SoShapeKit;

SoBaseColor *color=new SoBaseColor();

color->rgb.setValue(red,green,blue);

SoComplexity *complexity = new SoComplexity;

complexity->value = 1.0f; //0=veloce, 1=complesso

SoCoordinate3 *controlPts = new SoCoordinate3;

controlPts->setName(controlPointsName);

SoNurbsSurface *surface = new SoNurbsSurface;

surface->numUControlPoints = UPOINTS;

surface->numVControlPoints = VPOINTS;

surface->uKnotVector.setValues(0, (UPOINTS+4), fingerKnotsU);

surface->vKnotVector.setValues(0, (VPOINTS+4), fingerKnotsV);

finger->setPart("complexity",complexity);

finger->setPart("coordinate3",controlPts);

finger->setPart("shape",surface);

fingerSeparator->addChild(color);

fingerSeparator->addChild(finger);

return fingerSeparator;

Per costruire la matrice dei punti di controllo di un dito sono necessarisoltanto 3 parametri: la posizione nel mondo 3d dell’inizio del dito, l’angolorispetto all’asse X e un valore compreso tra 0 e 1 che indica di quanto il ditoe piegato.

Vengono calcolati 3 livelli di framework per individuare i punti di con-trollo:

• Il framework di livello 1 connette gli estremi di falange, falangina efalangetta. Al variare del parametro di apertura i punti del frameworkdi livello 1 si spostano su circonferenze che hanno come centro il puntoprecedente.

15

Page 17: Recognizing Hand Gestures using WebCams

• Il framework di livello 2 viene determinato partendo dal framework dilivello 1. I punti si muovono su una circonferenza che ha come centro ilpunto di livello 1 e come raggio 1. La circonferenza e orientata secondol’orientamento del dito. Al variare del parametro di apertura i puntisi muovono sulla circonferenza.

• Il framework di livello 3 viene determinato utilizzando come base ilframework di livello 2. I punti di terzo livello sono posizionati lat-eralmente rispetto ai punti di secondo livello. Essi si muovono su unacirconferenza posta su un piano parallelo alla circonferenza su cui sispostano i punti di secondo livello.

I dati cosı calcolati vengono assegnati nella posizione corretta della ma-trice dei punti di controllo.

Figura 12: Particolare delle dita

const float red componente in rosso del colore del dito - parametro;

const float green componente in verde del colore del dito - parametro;

const float blue componente in blu del colore del dito - parametro;

const char controlPointsName vettore che contiene il nome del nododei controlPoints della NURBS - parametro;

16

Page 18: Recognizing Hand Gestures using WebCams

Figura 13: Particolare della mano

const int UPOINTS numero dei punti di controllo orizzontali - locale;

const int VPOINTS numero dei punti di controllo verticali - locale;

float fingerKnotsU vettore dei nodi orizzontali - locale;

float fingerKnotsV vettore dei nodi verticali - locale;

Inizialmente si creano i vettori dei nodi in verticale e in orizzontale, ilvalore di ogni nodo in relazione agli altri indica quanto vicino si desiderache passi la curva rispetto al punto di controllo. I primi 4 nodi e gli ultimi4 vengono settati allo stesso valore in quanto si desidera che la curva inizi etermini proprio nel punto di controllo.

SoSeparator * modifyFinger (SoSeparator * fingerSeparator,

float startPoint[3],

float rotation,

float openrate,

const char controlPointsName[]){

float rAngle=rotation;

float oAngle = (openrate) * M_PI / 2;

float frameworkL1[3],frameworkL2[3],frameworkL3[3];

float toAngle;

const int UPOINTS = 7;

const int VPOINTS = 5;

SoNode * tmpNode = fingerSeparator->getByName(controlPointsName);

SoCoordinate3 * fingerControlPoints;

17

Page 19: Recognizing Hand Gestures using WebCams

if(tmpNode->getTypeId() == SoCoordinate3::getClassTypeId()){

fingerControlPoints = (SoCoordinate3*) tmpNode;

}else{

cout<<"ERRORE: Finger Control Points non trovati"<<endl;

return fingerSeparator;

}

int i;

for(i=0;i<4;i++)

{

//parte variabilie

if(i==0){

toAngle = M_PI *3/2 + oAngle;

frameworkL1[0]=startPoint[0];

frameworkL1[1]=startPoint[1];

frameworkL1[2]=startPoint[2];

}else if (i==1) {

toAngle = M_PI *3/2 + oAngle*1.5;

frameworkL1[0]+=sin(oAngle)*5;

frameworkL1[1]+=cos(oAngle)*cos(rAngle)*5;

frameworkL1[2]+=cos(oAngle)*sin(rAngle)*5;

}else if (i==2) {

oAngle += (openrate) * M_PI / 2;

toAngle = M_PI *3/2 + oAngle*1.25;

frameworkL1[0]+=sin(oAngle)*4;

frameworkL1[1]+=cos(oAngle)*cos(rAngle)*4;

frameworkL1[2]+=cos(oAngle)*sin(rAngle)*4;}

else {

oAngle += (openrate) * M_PI / 2;

toAngle = M_PI *3/2 + oAngle;

frameworkL1[0]+=sin(oAngle)*3;

frameworkL1[1]+=cos(oAngle)*cos(rAngle)*3;

frameworkL1[2]+=cos(oAngle)*sin(rAngle)*3;}

//parte fissa

frameworkL2[0]=frameworkL1[0]+sin(toAngle);

frameworkL2[1]=frameworkL1[1]+cos(toAngle)*cos(rAngle);

frameworkL2[2]=frameworkL1[2]+cos(toAngle)*sin(rAngle);

fingerControlPoints->point.set1Value((UPOINTS*i+0),frameworkL2);

frameworkL3[0]=frameworkL2[0];

frameworkL3[1]=frameworkL2[1]+cos(rAngle+M_PI/2);

frameworkL3[2]=frameworkL2[2]+sin(rAngle+M_PI/2);

fingerControlPoints->point.set1Value((UPOINTS*i+1),frameworkL3);

frameworkL3[0]=frameworkL2[0];

frameworkL3[1]=frameworkL2[1]+cos(rAngle-M_PI/2);

frameworkL3[2]=frameworkL2[2]+sin(rAngle-M_PI/2);

fingerControlPoints->point.set1Value((UPOINTS*i+5),frameworkL3);

frameworkL2[0]=frameworkL1[0]+sin(toAngle+M_PI);

frameworkL2[1]=frameworkL1[1]+cos(toAngle+M_PI)*cos(rAngle);

frameworkL2[2]=frameworkL1[2]+cos(toAngle+M_PI)*sin(rAngle);

fingerControlPoints->point.set1Value((UPOINTS*i+3),frameworkL2);

frameworkL3[0]=frameworkL2[0];

frameworkL3[1]=frameworkL2[1]+cos(rAngle+M_PI/2);

frameworkL3[2]=frameworkL2[2]+sin(rAngle+M_PI/2);

fingerControlPoints->point.set1Value((UPOINTS*i+2),frameworkL3);

frameworkL3[0]=frameworkL2[0];

frameworkL3[1]=frameworkL2[1]+cos(rAngle-M_PI/2);

frameworkL3[2]=frameworkL2[2]+sin(rAngle-M_PI/2);

fingerControlPoints->point.set1Value((UPOINTS*i+4),frameworkL3);

frameworkL2[0]=frameworkL1[0]+sin(toAngle);

frameworkL2[1]=frameworkL1[1]+cos(toAngle)*cos(rAngle);

frameworkL2[2]=frameworkL1[2]+cos(toAngle)*sin(rAngle);

fingerControlPoints->point.set1Value((UPOINTS*i+6),frameworkL2);

18

Page 20: Recognizing Hand Gestures using WebCams

}

if(i==4){

for(int k=0;k<7;k++){

fingerControlPoints->point.set1Value((UPOINTS*i+k),frameworkL1);

}

}

return fingerSeparator;

}

SoSeparator * fingerSeparator separator del dito - parametro;

float startPoint vettore che racchiude le coordinate del punto da doveinizia il dito - parametro;

float rotation angolo di rotazione del dito in radianti - parametro;

float openrate indica il grado di apertura del dito, se il dito e comple-tamente chiuso il parametro varra 0.0, mentre, se e completamenteaperto, varra 1.0 - parametro;

char controlPointsName vettore che contiene il nome del nodo dei con-trolPoints della NURBS - parametro;

float rAngle angolo di rotazione del dito rispetto all’origine del dito stesso- locale;

oAngle angolo di apertura, in radianti - locale;

frameworkL1 scheletro di guida di livello 1 per punti di controllo - locale;

frameworkL2 scheletro di guida di livello 2 per punti di controllo - locale;

frameworkL3 scheletro di guida di livello 3 per punti di controllo - locale;

toAngle angolo di apertua temporaneo - locale

const int UPOINTS numero dei punti di controllo orizzontali - locale;

const int VPOINTS numero dei punti di controllo verticali - locale;

Inizialmente si recupera dall’albero di scena la matrice dei punti dicontrollo, e poi si controlla che il cast sia corretto.

Ogni frame di livello 1 e spostato dal precedente dipendentemente dal-l’angolo di rotazione del dito e dall’angolo di apertura del dito. I frame dilivello 2 e di livello 3 vengono immessi nella matrice dei punti di controllinella posizione opportuna.

Per chiudere il dito nella parte terminale vengono settati tutti i punti dicontrollo al framework di livello 1.

Infine la funzione ritorna il separator del dito modificato.

19

Page 21: Recognizing Hand Gestures using WebCams

6.3 Riconoscere un comando

Modificando le impostazione del file di configurazione e possibile customiz-zare i comandi che vengono impartiti al software. Basta inserire un tagHandCommand impostando gli attributi

• name

• thumbRateMin

• thumbRateMax

• indexfingerRateMin

• indexfingerRateMax

• middlefingerRateMin

• middlefingerRateMax

• ringfingerRateMin

• ringfingerRateMax

• littlefingerRateMin

• littlefingerRateMax.

L’attributo name indica il nome del comando, i comandi supportatisono CommandSelect, CommandMove, CommandRGBChange e Command-ScaleChange, mentre, tramite gli altri attributi, e possibile modificare ilrange valido di apertura di ogni dito.

Le configurazioni lette nel file di input vengono memorizzate in un’is-tanza della classe HandStatus. Chiamando poi il metodo testCommand() epossibile riconoscere se la conformazione della mano e compatibile con uncomando.

6.4 Spostamento e rotazione della mano

Per applicare delle rotazioni e delle traslazioni alla rappresentazione dellamano nel mondo 3d si e scelto di inserire nell’albero di scena, prima dellarappresentazione della mano, un nodo SoMatrixTransform. Ogni qualvoltala posizione o la rotazione della mano cambia, mediante le funzioni cange-HandPosition() e cangeHandRotation(), la matrice di rototraslazione vienescomposta, modificata in modo opportuno e successivamente ricomposta.

La classe handStatus e rappresentativa dello stato della mano. In essavengono memorizzate le aperture delle dita,la posizione della mano e la suarotazione. Vengono anche memorizzati i comandi che la mano puo compiere.

20

Page 22: Recognizing Hand Gestures using WebCams

Tramite chiamata al metodo insertCommand() e possibile inserire un nuovocomando, mentre chiamando testCommand, si controlla se la conformazionedella mano e compatibile con un comando, se il test e positivo viene ritornatoil nome del comando, in caso contrario ritorna una stringa vuota.

class handStatus

{

private:

int nCommand;

SbString cmdName[30];

float vecCommand[30][10];

public:

handStatus();

virtual ~handStatus();

float handPosition[3];

float angle;

float deltaAngle;

float thumbRate;

float indexfingerRate;

float middlefingerRate;

float ringfingerRate;

float littlefingerRate;

void setData(const State & event);

bool insertCommand(const char name[50],

float thumbRateMin,

float thumbRateMax,

float indexfingerRateMin,

float indexfingerRateMax,

float middlefingerRateMin,

float middlefingerRateMax,

float ringfingerRateMin,

float ringfingerRateMax,

float littlefingerRateMin,

float littlefingerRateMax

);

SbString testCommand();

};

La funzione cangeHandPosition() serve per modificare la posizione dellamano nel mondo virtuale, secondo lo status memorizzato in un’istanza dellaclasse HandStatus. La matrice di rotazione della mano viene scomposta nellesue componenti di traslazione, rotazione, scala e orientamento della scala,poi si assegna alla traslazione il nuovo valore. La matrice di rototraslazioneviene successivamente ricomposta e aggiornata.

void cangeHandPosition(){

SbMatrix *m_tmp = new SbMatrix(handMatrixTransform->matrix.getValue());

SbVec3f t;

SbRotation r;

SbVec3f s;

SbRotation so;

m_tmp->getTransform(t,r,s,so);

t[0]=myHandStatus->handPosition[0];

t[1]=myHandStatus->handPosition[1];

t[2]=myHandStatus->handPosition[2];

if((t[0]<-0.1f)||(t[1]<-0.1f)||(t[2]<-0.1f)){return;}

cout << "T:" << t[0] << " " << t[1] << " " << t[2] << endl;

m_tmp->setTransform(t,r,s);

21

Page 23: Recognizing Hand Gestures using WebCams

handMatrixTransform->matrix.setValue(*m_tmp);

}

Mediante la funzione cangeHandRotation() e possibile variare l’angolo dirotazione della mano nel mondo virtuale. Il funzionamento e molto simile acangeHandPosition(), con l’unica differenza che si modifaca la componentedi rotazione della matrice di rotatraslazione invece che la componente ditraslazione.

void cangeHandRotation(){

SbMatrix *m_tmp = new SbMatrix(handMatrixTransform->matrix.getValue());

SbVec3f t,s; SbRotation r,so;

m_tmp->getTransform(t,r,s,so);

SbRotation rr;

rr.setValue(SbVec3f(1, 0, 0),myHandStatus->deltaAngle);

myHandStatus->deltaAngle=0.0f;

SbMatrix sb1;

SbMatrix sb2;

rr.getValue(sb1);

r.getValue(sb2);

sb1.multRight(sb2);

SbVec3f t_tmp, s_tmp;

SbRotation so_tmp;

sb1.getTransform (t_tmp, r, s_tmp, so_tmp);

m_tmp->setTransform(t,r,s);

handMatrixTransform->matrix.setValue(*m_tmp);

}

6.5 Ricostruire i punti di una mano nel caso in cui non sia

possibile trovare la posizione delle dita o del palmo

Nel caso in cui dall’analisi dell’immagine ottenuta dalle telecamere non siapossibile identificare la posizione di alcune dita il software provvede a calco-larne una posizione approssimata. Il calcolo avviene utilizzando la posizionedel palmo e traslandosi nello spazio in una posizione opportuna. I puntipossono essere ricostruiti anche se nessun dito e visibile.

Nel caso in cui sia il palmo della mano a non venir riconosciuto dalsoftware, la posizione approssimata viene calcolata utilizzando la posizionedell’indice, in quanto da test effettuati questa e risultata la piu affidabile.

La chiamata al metodo getPoint della classe mHand da come risultato laposizione di un dito passato come parametro. Esso controlla che la posizionedel dito non sia irreperibile e in quel caso la ritorna, altrimenti la approssima.

My3dPoint* MyHand::getPoint(handEnum h){

switch( h ){

case HAND:

if(!hand->isLost()) return hand;

if(!indexFinger->isLost()){hand->setRect(indexFinger, -0.13f, 0.25f, 0.0f);}

return hand;

break;

case THUMB:

if(!thumb->isLost())return thumb;

if(!hand->isLost()){thumb->setRect(hand, 0.2f, -0.15f, 0.1f);}

return thumb;

22

Page 24: Recognizing Hand Gestures using WebCams

break;

case INDEXFINGER:

if(!indexFinger->isLost())return indexFinger;

if(!hand->isLost()){indexFinger->setRect(hand, 0.13f, -0.25f, 0.0f);}

return indexFinger;

break;

case MIDDLEFINGER:

if(!middleFinger->isLost())return middleFinger;

if(!hand->isLost()){middleFinger->setRect(hand, 0.05f, -0.25f, 0.0f);}

return middleFinger;

break;

case RINGFINGER:

if(!ringFinger->isLost())return ringFinger;

if(!hand->isLost()){ringFinger->setRect(hand, 0.0f, -0.25f, 0.0f);}

return ringFinger;

break;

case LITTLEFINGER:

if(!littleFinger->isLost())return littleFinger;

if(!hand->isLost()){littleFinger->setRect(hand, -0.02f, -0.25f, 0.0f);}

return littleFinger;

break;

default: ;

}

return NULL;

}

6.6 Calcolare quanto un dito e aperto

Per calcolare quanto un dito e aperto si confrontano le dimensioni dell’ellissedel palmo della mano con le dimensioni delle ellissi delle dita. Nel caso incui un dito non sia visibile, il software considera il dito come chiuso, l’ellissedel dito avra quindi dimensione minima.

La classe che si occupa di calcolare gli openrate delle dita e My3dPointe il metodo da chiamare e getOpenRate passando un dito come parametro.

float My3dPoint::getOpenRate(My3dPoint *point){

CvRect ellipse = frontalPoint->getEllipse();

CvRect handEllipse = point->getFrontalEllipse();

if(handEllipse.height != 0){

float tmp = ( ellipse.height ) / ( handEllipse.height/2 );

if(tmp > 1.0f) tmp = 1.0f;

return tmp;

}else

return -1.0f;

}

23