SlideShare a Scribd company logo
`
UNIVERSITA DEGLI STUDI DI TRENTO
 Facolt` di Science Matematiche Fisiche e Naturali
       a
       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
1    Descrizione del problema
Il problema che ci ´ stato sottoposto consistema nella ideazione e realiz-
                     e
zazione di un nuovo sistema di puntamento che, utilizzando due webcam,
potesse seguire il movimento della mano riproducendolo nel mondo 3D. Il
sistema avrebbe dovuto anche riconoscere alcuni comandi dati con partico-
lari posizioni della mano. Ci si prefiggeva l’obbiettivo di giungere ad avere
due macchine distinte per la cattura del movimento e la visualizzazione del
mondo 3D.


2    Stato dell’Arte
Al momento in cui ci siamo applicati al problema esistevano gi` diversi
                                                                      a
metodi per il riconoscimento della gestualit` della mano.Infatti erano gi`
                                               a                             a
state realizzate varie tipologie di guanti, con sensori pi` o meno precisi per
                                                          u
il riconoscimento della posizione delle dita. Erano stati ideati, per esempio,
alcuni metodi per il riconoscimento della gestualit` della mano in un’im-
                                                      a
magine, con algoritmi pi` o meno sofisticati che si basano sul confronto di
                            u
immagini. Interessante ´ il progetto che, nel 1997, ha consentito di arrivare
                          e
al risultato di implementare un algoritmo per il riconoscimento della ges-
tualit` basato sulla forma della mano in 2 dimensioni. Il sistema ottenuto
       a
permetteva di riprodurre movimenti della mano naturali riducendo il ru-
more dovuto allo sfondo. Questi sistemi sono il frutto di ricerche che hanno
diversi campi di applicazione,ad esempio potrebbero sviluppare un sistema
di riconoscimento del linguaggio dei segni al servizio di persone con pro-
blemi di handicap oppure potrebbero facilitare la comunicazione tra uomo
e macchina. Queste sono le precedenti esperienze nel settore specifico di cui
abbiamo trovato notizie, ma sicuramente esistono progetti simili.


3    Installazione
Il software ´ un’estensione della libreria OpenTracker e le sue funzioni vi
            e
sono incluse. Per l’installazione c’´ bisogno di alcune librerie di supporto
                                    e
che sono le seguenti:

ACE The Adaptive Communication Environment, a wrapper library for
   system services.

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

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


                                      1
OpenCV Means Intel Open Source Computer Vision Library. It is a collec-
    tion of C functions and few C++ classes that implement some popular
    algorithms 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 la
libreria. Riporto un esempio di come settare le variabili:

ACEROOT=C:GraphicsACE - Per ACE

ARTOOLKITROOT=C:GraphicsArToolKit - Per ArToolKit

COIN3DDI=C:GraphicsCoin3D-2 - Per Coin3D-2

OPENCVROOT=C:GraphicsOpenCV - Per OpenCV

XERCESCROOT=C:GraphicsXerces - Per Xerces

    Devo anche aggiungere una variabile d’ambiente per OpenTracker che
si chiama OTROOT alla quale devo assegnare “il path dove copio il mio
software”opentraker
    Poi devo assegnare alla variabile d’ambiente PATH tutte le variabile
sopra citate seguite da “bin”.
    Ora il sistema ´ installato e devo procedere con la configurazione me-
                    e
diante i due file XML come ´ spiegato nel paragrafo 4.1.
                               e
    Il passo successivo all’installazione del software ´ il posizionamento delle
                                                       e
webcam. 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 la
disponiamo sull’asse delle ordinate, quindi in grado di riprendere la mano
lateralmente. Mentre le due camere risultano in uno spazio tridimensionale
fisse e poste sullo stesso piano verticale, la mano si muove liberamente.




                                       2
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 i
settaggi dei software, vedremo in seguito come questi file sono fatti. Una
volta compilato il file di configurazione le due parti del software possono
essere eseguite da linea di comando passando come parametro proprio il file
di configurazione.
Esempio:
> cvsample MyHandTracker.xml

> world3d MyWorld3d.xml



    Per il tracker c’´ una sessione di configurazione da fare in cui si devono
                     e
settare 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 si
deve premere un tasto tra 1 e 6 e successivamente, evidenziando la porzione
di immagine corrispondente alla parte di mano che si intende settare, come
conferma appare una elisse che circoscrive la sezione. Dopo aver configurato
tutti 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 tornare
alla fase di configurazione in un secondo momento premendo il tasto ’c’.


                                      3
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. Iniziamo
con 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 file
di configurazione per il sistema di tracking, esso ha una sola sezione nella
quale 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 gli
eventuali 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
rappresentano, invece, le dimensioni del piano reale di riferimento orizzon-
tale e verticale. Gli altri due attributi frontalMovie e latgeralMovie non
sono obbligatori e rappresentano il nome del file contenente rispettivamente
la ripresa dall’alto e quella laterale.




         Figura 3: Posizionamento delle camere rispetto all’origine

   Vediamo ora come ´ fatto il file di configurazione del Mondo 3D. Come
                      e
possiamo notare nella figura 4, che rappresenta un esempio di file di config-
urazione per il mondo 3D, esso ´ diviso in tre parti principali evidenziate da
                               e
un 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 configurazione
     della mano che lo rappresenta, settandone il range valido di apertura
     delle 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
<?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
5    Architettura
Il sistema ´ facilmente divisibile in due parti:
           e

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

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

Per far si che le due parti comunichino abbiamo utilizzato Opentracker, un
particolare middleware che permette di far colloquiare due applicazioni sep-
arate. Esso si basa su una architettura sviluppata per essere una flessibile
soluzione per l’implementazione dei processi legati al tracking dei disposi-
tivi di imput ed al flusso di dati utilizzati da ambientazioni virtuali. Questo
ha permesso all’applicazione di funzionare tramite un flusso di dati gener-
ici attraverso la rete e di essere in modo nativo trasparente alla stessa. E ´
anche possibile stabilire nuovi tipi di interazioni mediante la configurazione
o l’estensione di file XML. Le nuove classi di OpenTracker, implementate
per i nostri scopi, leggono i dati provenienti dalle WebCam, effettuano le
necessarie trasformazioni, e inviano i dati cos` modificati verso l’altra ap-
                                                 ı
plicazione che utilizzando OpenTracker leggera i dati e li memorizzer` in a
una classe che rappresenta lo stato della mano, questa classe verra poi us-
ata dall’applicazione stessa che rappresenta la mano del mondo 3D.Come
rappresentato 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 dati
proveniente dal dispositivo di tracking con un modulo ad-hoc per il nostro
dispositivo.




                                       7
5.1   Nuovo sistema di tracking
Per il nuovo sistema di tracking abbiamo esteso la libreria di OpenTracker
con un nuovo modulo. Questo modulo rappresenta un nodo nell’albero e
viene configurato tramite XML. Come mostra la figura 6 il modulo ´ di-     e
viso in diverse classi: MyHandModule, che implementa nella loro struttura
le funzioni necessarie alla costruzione della struttura dati, al filtraggio ed
alla 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
5.2   Architettura di World3d
L’albero di scena del mondo virtuale pu´ essere scomposto in tre parti:
                                       o

  1. gli assi

  2. gli oggetti di scena

  3. la mano

Organizzati come in figura 7. Gli assi sono costituiti da 3 cilindri di diverso
colore orientati opportunamente. Il soparator wSep contiene la parte del
mondo virtuale sulla quale si pu´ agire: la mano e gli oggetti.
                                o




                        Figura 7: L’albero di scena

    Il Separator worldSeparator isola gli oggetti sui quali l’utente pu´ com-
                                                                       o
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 ´ a sua
                                                                       e
volta 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 altri
     oggetti, la loro selezionatura.

                                      9
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 mano
hanno una struttura identica (Figura 10): viene applicato un nodo SoColor
ad un ShapeKit. Il ShapeKit contine un nodo Complexity mediante il quale
si pu´ variare la precisione nel renderizzare le strutture, la matrice dei punti
     o
di controllo e una superficie NURBS.




                  Figura 10: L’albero di scena per il palmo



                                      10
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 ed
una struttura dati apposita. Si calcolano due rette, ciascuna passante per
una camera e per il punto che si prende in esame. Successivamento viene
identificato il punto su ogni retta pi´ vicino all’altra retta, poi si calcola il
                                        u
punto medio tra i due punti.
    Da file di configurazione vengono lette la posizione delle due telecamere
nello spazio reale e le dimensioni del piano di riferimento. Le due telecamere
devono essere localizzate in una posizione azzimutale rispetto al piano di
riferimento.
    Chiamando la funzione calculateRealPos() si individuano i due punti
sulle rette pi´ vicini e se ne calcola il punto medio.
              u
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 spazio
utilizzando un parametro di riferimento:

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

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

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 punto
su una retta data ´ la seguente (nel caso di coordinata generatrice z):
                  e
   input : val p(x, y, z) c(x, y, z)
   l = X c − Xp
   m = Y c − Yp
   n = Z c − Zp


                      (val − Zp ) l        (val − Zp ) m
        output =                    + Xp ,               + Yp , val
                           n                     n

                                       11
Questa formula viene chiamata per le coordinate non fisse, all’interno
del 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 deve
chiamare la funzione buildLine3d della classe My3dPoint. Questa funzione
ha come parametri 2 punti dello spazio reale, uno dato dalla posizione della
telecamera, l’altro ´ dato dalla proiezione del punto reale sul piano indi-
                    e
viduato dalla camera. La struttura MyLine3d ´ stata ideata in modo che
                                                  e
sia possibile scorrere sulla retta determinata dai due punti, variando un
parametro e la funzione buildLine costruisce la struttura in modo da gestire
anche 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
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
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 due
rette e, nel caso in cui le rette non si incrocino, trova il punto medio tra i
punti pi´ vicini sulle rette.
         u

6.2    Costruire un dito e farlo piegare
Un dito della mano visualizzata nel mondo 3d ´ formato da una NURBS,
                                                    e
la quale viene determinata in funzione di una matrice di punti di controllo
e da due vettori di nodi, uno per le posizioni orizzontali della superficie e
l’altro per le posizioni verticali della superficie.
     Nel scene graph ogni dito ´ delimitato da un SoSeparator e il sottoalbero
                                e
contiene 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
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 necessari
soltanto 3 parametri: la posizione nel mondo 3d dell’inizio del dito, l’angolo
rispetto all’asse X e un valore compreso tra 0 e 1 che indica di quanto il dito
´ piegato.
e
    Vengono calcolati 3 livelli di framework per individuare i punti di con-
trollo:

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


                                        15
• Il framework di livello 2 viene determinato partendo dal framework di
     livello 1. I punti si muovono su una circonferenza che ha come centro il
     punto di livello 1 e come raggio 1. La circonferenza ´ orientata secondo
                                                          e
     l’orientamento del dito. Al variare del parametro di apertura i punti
     si muovono sulla circonferenza.

   • Il framework di livello 3 viene determinato utilizzando come base il
     framework di livello 2. I punti di terzo livello sono posizionati lat-
     eralmente rispetto ai punti di secondo livello. Essi si muovono su una
     circonferenza posta su un piano parallelo alla circonferenza su cui si
     spostano 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 nodo
     dei controlPoints della NURBS - parametro;

                                     16
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, il
valore di ogni nodo in relazione agli altri indica quanto vicino si desidera
che passi la curva rispetto al punto di controllo. I primi 4 nodi e gli ultimi
4 vengono settati allo stesso valore in quanto si desidera che la curva inizi e
termini 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
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
}
    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 dove
     inizia 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 ´ comple-
                                                               e
     tamente chiuso il parametro varr´ 0.0, mentre, se ´ completamente
                                     a                 e
     aperto, varr´ 1.0 - parametro;
                 a

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 di
controllo, e poi si controlla che il cast sia corretto.
    Ogni frame di livello 1 ´ spostato dal precedente dipendentemente dal-
                              e
l’angolo di rotazione del dito e dall’angolo di apertura del dito. I frame di
livello 2 e di livello 3 vengono immessi nella matrice dei punti di controlli
nella posizione opportuna.
    Per chiudere il dito nella parte terminale vengono settati tutti i punti di
controllo al framework di livello 1.
    Infine la funzione ritorna il separator del dito modificato.



                                         19
6.3   Riconoscere un comando
Modificando le impostazione del file di configurazione ´ possibile customiz-
                                                    e
zare i comandi che vengono impartiti al software. Basta inserire un tag
HandCommand impostando gli attributi

   • name

   • thumbRateMin

   • thumbRateMax

   • indexfingerRateMin

   • indexfingerRateMax

   • middlefingerRateMin

   • middlefingerRateMax

   • ringfingerRateMin

   • ringfingerRateMax

   • littlefingerRateMin

   • littlefingerRateMax.

   L’attributo name indica il nome del comando, i comandi supportati
sono CommandSelect, CommandMove, CommandRGBChange e Command-
ScaleChange, mentre, tramite gli altri attributi, ´ possibile modificare il
                                                  e
range 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() ´  e
possibile riconoscere se la conformazione della mano ´ compatibile con un
                                                     e
comando.

6.4   Spostamento e rotazione della mano
Per applicare delle rotazioni e delle traslazioni alla rappresentazione della
mano nel mondo 3d si ´ scelto di inserire nell’albero di scena, prima della
                         e
rappresentazione della mano, un nodo SoMatrixTransform. Ogni qualvolta
la posizione o la rotazione della mano cambia, mediante le funzioni cange-
HandPosition() e cangeHandRotation(), la matrice di rototraslazione viene
scomposta, modificata in modo opportuno e successivamente ricomposta.
    La classe handStatus ´ rappresentativa dello stato della mano. In essa
                           e
vengono memorizzate le aperture delle dita,la posizione della mano e la sua
rotazione. Vengono anche memorizzati i comandi che la mano pu´ compiere.
                                                                  o


                                     20
Tramite chiamata al metodo insertCommand() ´ possibile inserire un nuovo
                                                e
comando, mentre chiamando testCommand, si controlla se la conformazione
della mano ´ compatibile con un comando, se il test ´ positivo viene ritornato
           e                                        e
il 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 della
mano nel mondo virtuale, secondo lo status memorizzato in un’istanza della
classe HandStatus. La matrice di rotazione della mano viene scomposta nelle
sue componenti di traslazione, rotazione, scala e orientamento della scala,
poi si assegna alla traslazione il nuovo valore. La matrice di rototraslazione
viene 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
handMatrixTransform->matrix.setValue(*m_tmp);
}


    Mediante la funzione cangeHandRotation() ´ possibile variare l’angolo di
                                               e
rotazione della mano nel mondo virtuale. Il funzionamento ´ molto simile a
                                                           e
cangeHandPosition(), con l’unica differenza che si modifaca la componente
di rotazione della matrice di rotatraslazione invece che la componente di
traslazione.
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 sia
possibile identificare la posizione di alcune dita il software provvede a calco-
larne una posizione approssimata. Il calcolo avviene utilizzando la posizione
del palmo e traslandosi nello spazio in una posizione opportuna. I punti
possono essere ricostruiti anche se nessun dito ´ visibile.
                                                     e
    Nel caso in cui sia il palmo della mano a non venir riconosciuto dal
software, la posizione approssimata viene calcolata utilizzando la posizione
dell’indice, in quanto da test effettuati questa ´ risultata la pi´ affidabile.
                                                    e                u
    La chiamata al metodo getPoint della classe mHand d´ come risultato la
                                                               a
posizione di un dito passato come parametro. Esso controlla che la posizione
del 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
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 ´ aperto
                                e
Per calcolare quanto un dito ´ aperto si confrontano le dimensioni dell’ellisse
                                e
del palmo della mano con le dimensioni delle ellissi delle dita. Nel caso in
cui un dito non sia visibile, il software considera il dito come chiuso, l’ellisse
del dito avr´ quindi dimensione minima.
             a
     La classe che si occupa di calcolare gli openrate delle dita ´ My3dPoint
                                                                   e
e il metodo da chiamare ´ getOpenRate passando un dito come parametro.
                           e
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

More Related Content

Similar to Recognizing Hand Gestures using WebCams

Android ed utilizzo_dei_sensori
Android ed utilizzo_dei_sensoriAndroid ed utilizzo_dei_sensori
Android ed utilizzo_dei_sensori
Danilo Riso
 
SkyMedia: La tecnologia al servizio dell'intrattenimento
SkyMedia: La tecnologia al servizio dell'intrattenimentoSkyMedia: La tecnologia al servizio dell'intrattenimento
SkyMedia: La tecnologia al servizio dell'intrattenimento
Mavigex srl
 
Sogei Premio PA Sostenibile 2018
Sogei Premio PA Sostenibile 2018Sogei Premio PA Sostenibile 2018
Sogei Premio PA Sostenibile 2018
leorob
 
Android Debug Monitor
Android Debug MonitorAndroid Debug Monitor
Android Debug Monitor
Pietro Alberto Rossi
 
Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012L Dr
 
Kinect : Just for fun?
Kinect : Just for fun?Kinect : Just for fun?
Kinect : Just for fun?
Massimo Bonanni
 
Progettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computerProgettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computer
Alessandro Mascherin
 
Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...
Giovanni Casabianca
 
Presentazione understand
Presentazione understandPresentazione understand
Presentazione understand
Luigi La Torre
 
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostriAcadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
Francesco Sciuti
 
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
anwarNazik
 
Dal C a Java (2/3)
Dal C a Java (2/3)Dal C a Java (2/3)
Dal C a Java (2/3)
Marcello Missiroli
 
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding ShellcodesMITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
Gianluca Gabrielli
 
Scanner 3D e reverse Engineering
Scanner 3D e reverse EngineeringScanner 3D e reverse Engineering
Scanner 3D e reverse Engineering
Paolo Aliverti
 
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
anwarNazik
 
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
Mattia Milleri
 
Push Notification, Live Tile e Background Agent
Push Notification, Live Tile e Background AgentPush Notification, Live Tile e Background Agent
Push Notification, Live Tile e Background Agent
DomusDotNet
 
Simulazione di un sistema fisico ts inginf
Simulazione di un sistema fisico ts inginfSimulazione di un sistema fisico ts inginf
Simulazione di un sistema fisico ts inginf
IonutAlexandruPascar
 
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
RiccardoScilla
 
Instruction Manual, XChange 2 User Manual Italian Language web4901 0133-2
Instruction Manual, XChange 2 User Manual Italian Language  web4901 0133-2Instruction Manual, XChange 2 User Manual Italian Language  web4901 0133-2
Instruction Manual, XChange 2 User Manual Italian Language web4901 0133-2
Serious Detecting
 

Similar to Recognizing Hand Gestures using WebCams (20)

Android ed utilizzo_dei_sensori
Android ed utilizzo_dei_sensoriAndroid ed utilizzo_dei_sensori
Android ed utilizzo_dei_sensori
 
SkyMedia: La tecnologia al servizio dell'intrattenimento
SkyMedia: La tecnologia al servizio dell'intrattenimentoSkyMedia: La tecnologia al servizio dell'intrattenimento
SkyMedia: La tecnologia al servizio dell'intrattenimento
 
Sogei Premio PA Sostenibile 2018
Sogei Premio PA Sostenibile 2018Sogei Premio PA Sostenibile 2018
Sogei Premio PA Sostenibile 2018
 
Android Debug Monitor
Android Debug MonitorAndroid Debug Monitor
Android Debug Monitor
 
Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012
 
Kinect : Just for fun?
Kinect : Just for fun?Kinect : Just for fun?
Kinect : Just for fun?
 
Progettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computerProgettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computer
 
Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...
 
Presentazione understand
Presentazione understandPresentazione understand
Presentazione understand
 
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostriAcadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
Acadevmy - AngularDay 2018 - Change Detection, Zone.js ed altri mostri
 
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
 
Dal C a Java (2/3)
Dal C a Java (2/3)Dal C a Java (2/3)
Dal C a Java (2/3)
 
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding ShellcodesMITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
 
Scanner 3D e reverse Engineering
Scanner 3D e reverse EngineeringScanner 3D e reverse Engineering
Scanner 3D e reverse Engineering
 
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
Realizzazione di un controllore basato su piattaforma robotica Thymio 2.
 
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
Progetto e implementazione di una pipeline di sviluppo software con tecnologi...
 
Push Notification, Live Tile e Background Agent
Push Notification, Live Tile e Background AgentPush Notification, Live Tile e Background Agent
Push Notification, Live Tile e Background Agent
 
Simulazione di un sistema fisico ts inginf
Simulazione di un sistema fisico ts inginfSimulazione di un sistema fisico ts inginf
Simulazione di un sistema fisico ts inginf
 
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
Progettazione e sviluppo di un sistema di visione artificiale per il monitora...
 
Instruction Manual, XChange 2 User Manual Italian Language web4901 0133-2
Instruction Manual, XChange 2 User Manual Italian Language  web4901 0133-2Instruction Manual, XChange 2 User Manual Italian Language  web4901 0133-2
Instruction Manual, XChange 2 User Manual Italian Language web4901 0133-2
 

More from graphitech

A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
graphitech
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
graphitech
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
graphitech
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
graphitech
 
Rescue Mission
Rescue MissionRescue Mission
Rescue Mission
graphitech
 
Rescue Mission
Rescue MissionRescue Mission
Rescue Mission
graphitech
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
graphitech
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
graphitech
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
graphitech
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
graphitech
 
Graph Matching
Graph MatchingGraph Matching
Graph Matching
graphitech
 
Shape Analysis
Shape AnalysisShape Analysis
Shape Analysis
graphitech
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
graphitech
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
graphitech
 
WebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World WindWebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World Wind
graphitech
 
Street Builder
Street BuilderStreet Builder
Street Builder
graphitech
 
Street Builder
Street BuilderStreet Builder
Street Builder
graphitech
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
graphitech
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
graphitech
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modification
graphitech
 

More from graphitech (20)

A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
Rescue Mission
Rescue MissionRescue Mission
Rescue Mission
 
Rescue Mission
Rescue MissionRescue Mission
Rescue Mission
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
 
Mashup - Sustainability
Mashup - SustainabilityMashup - Sustainability
Mashup - Sustainability
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Graph Matching
Graph MatchingGraph Matching
Graph Matching
 
Shape Analysis
Shape AnalysisShape Analysis
Shape Analysis
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
 
WebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World WindWebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World Wind
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modification
 

Recognizing Hand Gestures using WebCams

  • 1. ` UNIVERSITA DEGLI STUDI DI TRENTO Facolt` di Science Matematiche Fisiche e Naturali a 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
  • 2. 1 Descrizione del problema Il problema che ci ´ stato sottoposto consistema nella ideazione e realiz- e zazione di un nuovo sistema di puntamento che, utilizzando due webcam, potesse seguire il movimento della mano riproducendolo nel mondo 3D. Il sistema avrebbe dovuto anche riconoscere alcuni comandi dati con partico- lari posizioni della mano. Ci si prefiggeva l’obbiettivo di giungere ad avere due macchine distinte per la cattura del movimento e la visualizzazione del mondo 3D. 2 Stato dell’Arte Al momento in cui ci siamo applicati al problema esistevano gi` diversi a metodi per il riconoscimento della gestualit` della mano.Infatti erano gi` a a state realizzate varie tipologie di guanti, con sensori pi` o meno precisi per u il riconoscimento della posizione delle dita. Erano stati ideati, per esempio, alcuni metodi per il riconoscimento della gestualit` della mano in un’im- a magine, con algoritmi pi` o meno sofisticati che si basano sul confronto di u immagini. Interessante ´ il progetto che, nel 1997, ha consentito di arrivare e al risultato di implementare un algoritmo per il riconoscimento della ges- tualit` basato sulla forma della mano in 2 dimensioni. Il sistema ottenuto a permetteva di riprodurre movimenti della mano naturali riducendo il ru- more dovuto allo sfondo. Questi sistemi sono il frutto di ricerche che hanno diversi campi di applicazione,ad esempio potrebbero sviluppare un sistema di riconoscimento del linguaggio dei segni al servizio di persone con pro- blemi di handicap oppure potrebbero facilitare la comunicazione tra uomo e macchina. Queste sono le precedenti esperienze nel settore specifico di cui abbiamo trovato notizie, ma sicuramente esistono progetti simili. 3 Installazione Il software ´ un’estensione della libreria OpenTracker e le sue funzioni vi e sono incluse. Per l’installazione c’´ bisogno di alcune librerie di supporto e che sono le seguenti: ACE The Adaptive Communication Environment, a wrapper library for system services. ArToolKit ARToolKit applications allow virtual imagery to be superim- posed over live video of the real world.Although this appears magical it is not. The secret is in the black squares used as tracking markers. Coin3D-2 Is an OpenGL based, retained mode 3D graphics rendering library. 1
  • 3. OpenCV Means Intel Open Source Computer Vision Library. It is a collec- tion of C functions and few C++ classes that implement some popular algorithms 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 la libreria. Riporto un esempio di come settare le variabili: ACEROOT=C:GraphicsACE - Per ACE ARTOOLKITROOT=C:GraphicsArToolKit - Per ArToolKit COIN3DDI=C:GraphicsCoin3D-2 - Per Coin3D-2 OPENCVROOT=C:GraphicsOpenCV - Per OpenCV XERCESCROOT=C:GraphicsXerces - Per Xerces Devo anche aggiungere una variabile d’ambiente per OpenTracker che si chiama OTROOT alla quale devo assegnare “il path dove copio il mio software”opentraker Poi devo assegnare alla variabile d’ambiente PATH tutte le variabile sopra citate seguite da “bin”. Ora il sistema ´ installato e devo procedere con la configurazione me- e diante i due file XML come ´ spiegato nel paragrafo 4.1. e Il passo successivo all’installazione del software ´ il posizionamento delle e webcam. 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 la disponiamo sull’asse delle ordinate, quindi in grado di riprendere la mano lateralmente. Mentre le due camere risultano in uno spazio tridimensionale fisse e poste sullo stesso piano verticale, la mano si muove liberamente. 2
  • 4. 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 i settaggi dei software, vedremo in seguito come questi file sono fatti. Una volta compilato il file di configurazione le due parti del software possono essere eseguite da linea di comando passando come parametro proprio il file di configurazione. Esempio: > cvsample MyHandTracker.xml > world3d MyWorld3d.xml Per il tracker c’´ una sessione di configurazione da fare in cui si devono e settare 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 si deve premere un tasto tra 1 e 6 e successivamente, evidenziando la porzione di immagine corrispondente alla parte di mano che si intende settare, come conferma appare una elisse che circoscrive la sezione. Dopo aver configurato tutti 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 tornare alla fase di configurazione in un secondo momento premendo il tasto ’c’. 3
  • 5. 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. Iniziamo con 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 file di configurazione per il sistema di tracking, esso ha una sola sezione nella quale 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 gli eventuali 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
  • 6. rappresentano, invece, le dimensioni del piano reale di riferimento orizzon- tale e verticale. Gli altri due attributi frontalMovie e latgeralMovie non sono obbligatori e rappresentano il nome del file contenente rispettivamente la ripresa dall’alto e quella laterale. Figura 3: Posizionamento delle camere rispetto all’origine Vediamo ora come ´ fatto il file di configurazione del Mondo 3D. Come e possiamo notare nella figura 4, che rappresenta un esempio di file di config- urazione per il mondo 3D, esso ´ diviso in tre parti principali evidenziate da e un 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 configurazione della mano che lo rappresenta, settandone il range valido di apertura delle 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
  • 7. <?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
  • 8. 5 Architettura Il sistema ´ facilmente divisibile in due parti: e • la prima che utilizzando OpenCV cattura da WebCam i filmati e segue la mano calcolandone la posizione e i movimenti; • la seconda che rappresenta la mano nel mondo 3D e la fa muovere basandosi sui dati che riceve. Per far si che le due parti comunichino abbiamo utilizzato Opentracker, un particolare middleware che permette di far colloquiare due applicazioni sep- arate. Esso si basa su una architettura sviluppata per essere una flessibile soluzione per l’implementazione dei processi legati al tracking dei disposi- tivi di imput ed al flusso di dati utilizzati da ambientazioni virtuali. Questo ha permesso all’applicazione di funzionare tramite un flusso di dati gener- ici attraverso la rete e di essere in modo nativo trasparente alla stessa. E ´ anche possibile stabilire nuovi tipi di interazioni mediante la configurazione o l’estensione di file XML. Le nuove classi di OpenTracker, implementate per i nostri scopi, leggono i dati provenienti dalle WebCam, effettuano le necessarie trasformazioni, e inviano i dati cos` modificati verso l’altra ap- ı plicazione che utilizzando OpenTracker leggera i dati e li memorizzer` in a una classe che rappresenta lo stato della mano, questa classe verra poi us- ata dall’applicazione stessa che rappresenta la mano del mondo 3D.Come rappresentato 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 dati proveniente dal dispositivo di tracking con un modulo ad-hoc per il nostro dispositivo. 7
  • 9. 5.1 Nuovo sistema di tracking Per il nuovo sistema di tracking abbiamo esteso la libreria di OpenTracker con un nuovo modulo. Questo modulo rappresenta un nodo nell’albero e viene configurato tramite XML. Come mostra la figura 6 il modulo ´ di- e viso in diverse classi: MyHandModule, che implementa nella loro struttura le funzioni necessarie alla costruzione della struttura dati, al filtraggio ed alla 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
  • 10. 5.2 Architettura di World3d L’albero di scena del mondo virtuale pu´ essere scomposto in tre parti: o 1. gli assi 2. gli oggetti di scena 3. la mano Organizzati come in figura 7. Gli assi sono costituiti da 3 cilindri di diverso colore orientati opportunamente. Il soparator wSep contiene la parte del mondo virtuale sulla quale si pu´ agire: la mano e gli oggetti. o Figura 7: L’albero di scena Il Separator worldSeparator isola gli oggetti sui quali l’utente pu´ com- o 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 ´ a sua e volta 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 altri oggetti, la loro selezionatura. 9
  • 11. 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 mano hanno una struttura identica (Figura 10): viene applicato un nodo SoColor ad un ShapeKit. Il ShapeKit contine un nodo Complexity mediante il quale si pu´ variare la precisione nel renderizzare le strutture, la matrice dei punti o di controllo e una superficie NURBS. Figura 10: L’albero di scena per il palmo 10
  • 12. 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 ed una struttura dati apposita. Si calcolano due rette, ciascuna passante per una camera e per il punto che si prende in esame. Successivamento viene identificato il punto su ogni retta pi´ vicino all’altra retta, poi si calcola il u punto medio tra i due punti. Da file di configurazione vengono lette la posizione delle due telecamere nello spazio reale e le dimensioni del piano di riferimento. Le due telecamere devono essere localizzate in una posizione azzimutale rispetto al piano di riferimento. Chiamando la funzione calculateRealPos() si individuano i due punti sulle rette pi´ vicini e se ne calcola il punto medio. u 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 spazio utilizzando un parametro di riferimento: int coordGen ´ la coordinata che genera le altre: 0=x 1=y 2=z; e bool IsFixed ´ un vettore che tiene traccia se ogni coordianta varia in e funzione della coordinata generatrice oppure ´ fissa; e 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 punto su una retta data ´ la seguente (nel caso di coordinata generatrice z): e input : val p(x, y, z) c(x, y, z) l = X c − Xp m = Y c − Yp n = Z c − Zp (val − Zp ) l (val − Zp ) m output = + Xp , + Yp , val n n 11
  • 13. Questa formula viene chiamata per le coordinate non fisse, all’interno del 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 deve chiamare la funzione buildLine3d della classe My3dPoint. Questa funzione ha come parametri 2 punti dello spazio reale, uno dato dalla posizione della telecamera, l’altro ´ dato dalla proiezione del punto reale sul piano indi- e viduato dalla camera. La struttura MyLine3d ´ stata ideata in modo che e sia possibile scorrere sulla retta determinata dai due punti, variando un parametro e la funzione buildLine costruisce la struttura in modo da gestire anche 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
  • 14. 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
  • 15. 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 due rette e, nel caso in cui le rette non si incrocino, trova il punto medio tra i punti pi´ vicini sulle rette. u 6.2 Costruire un dito e farlo piegare Un dito della mano visualizzata nel mondo 3d ´ formato da una NURBS, e la quale viene determinata in funzione di una matrice di punti di controllo e da due vettori di nodi, uno per le posizioni orizzontali della superficie e l’altro per le posizioni verticali della superficie. Nel scene graph ogni dito ´ delimitato da un SoSeparator e il sottoalbero e contiene 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
  • 16. 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 necessari soltanto 3 parametri: la posizione nel mondo 3d dell’inizio del dito, l’angolo rispetto all’asse X e un valore compreso tra 0 e 1 che indica di quanto il dito ´ piegato. e Vengono calcolati 3 livelli di framework per individuare i punti di con- trollo: • Il framework di livello 1 connette gli estremi di falange, falangina e falangetta. Al variare del parametro di apertura i punti del framework di livello 1 si spostano su circonferenze che hanno come centro il punto precedente. 15
  • 17. • Il framework di livello 2 viene determinato partendo dal framework di livello 1. I punti si muovono su una circonferenza che ha come centro il punto di livello 1 e come raggio 1. La circonferenza ´ orientata secondo e l’orientamento del dito. Al variare del parametro di apertura i punti si muovono sulla circonferenza. • Il framework di livello 3 viene determinato utilizzando come base il framework di livello 2. I punti di terzo livello sono posizionati lat- eralmente rispetto ai punti di secondo livello. Essi si muovono su una circonferenza posta su un piano parallelo alla circonferenza su cui si spostano 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 nodo dei controlPoints della NURBS - parametro; 16
  • 18. 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, il valore di ogni nodo in relazione agli altri indica quanto vicino si desidera che passi la curva rispetto al punto di controllo. I primi 4 nodi e gli ultimi 4 vengono settati allo stesso valore in quanto si desidera che la curva inizi e termini 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
  • 19. 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
  • 20. } 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 dove inizia 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 ´ comple- e tamente chiuso il parametro varr´ 0.0, mentre, se ´ completamente a e aperto, varr´ 1.0 - parametro; a 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 di controllo, e poi si controlla che il cast sia corretto. Ogni frame di livello 1 ´ spostato dal precedente dipendentemente dal- e l’angolo di rotazione del dito e dall’angolo di apertura del dito. I frame di livello 2 e di livello 3 vengono immessi nella matrice dei punti di controlli nella posizione opportuna. Per chiudere il dito nella parte terminale vengono settati tutti i punti di controllo al framework di livello 1. Infine la funzione ritorna il separator del dito modificato. 19
  • 21. 6.3 Riconoscere un comando Modificando le impostazione del file di configurazione ´ possibile customiz- e zare i comandi che vengono impartiti al software. Basta inserire un tag HandCommand impostando gli attributi • name • thumbRateMin • thumbRateMax • indexfingerRateMin • indexfingerRateMax • middlefingerRateMin • middlefingerRateMax • ringfingerRateMin • ringfingerRateMax • littlefingerRateMin • littlefingerRateMax. L’attributo name indica il nome del comando, i comandi supportati sono CommandSelect, CommandMove, CommandRGBChange e Command- ScaleChange, mentre, tramite gli altri attributi, ´ possibile modificare il e range 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() ´ e possibile riconoscere se la conformazione della mano ´ compatibile con un e comando. 6.4 Spostamento e rotazione della mano Per applicare delle rotazioni e delle traslazioni alla rappresentazione della mano nel mondo 3d si ´ scelto di inserire nell’albero di scena, prima della e rappresentazione della mano, un nodo SoMatrixTransform. Ogni qualvolta la posizione o la rotazione della mano cambia, mediante le funzioni cange- HandPosition() e cangeHandRotation(), la matrice di rototraslazione viene scomposta, modificata in modo opportuno e successivamente ricomposta. La classe handStatus ´ rappresentativa dello stato della mano. In essa e vengono memorizzate le aperture delle dita,la posizione della mano e la sua rotazione. Vengono anche memorizzati i comandi che la mano pu´ compiere. o 20
  • 22. Tramite chiamata al metodo insertCommand() ´ possibile inserire un nuovo e comando, mentre chiamando testCommand, si controlla se la conformazione della mano ´ compatibile con un comando, se il test ´ positivo viene ritornato e e il 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 della mano nel mondo virtuale, secondo lo status memorizzato in un’istanza della classe HandStatus. La matrice di rotazione della mano viene scomposta nelle sue componenti di traslazione, rotazione, scala e orientamento della scala, poi si assegna alla traslazione il nuovo valore. La matrice di rototraslazione viene 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
  • 23. handMatrixTransform->matrix.setValue(*m_tmp); } Mediante la funzione cangeHandRotation() ´ possibile variare l’angolo di e rotazione della mano nel mondo virtuale. Il funzionamento ´ molto simile a e cangeHandPosition(), con l’unica differenza che si modifaca la componente di rotazione della matrice di rotatraslazione invece che la componente di traslazione. 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 sia possibile identificare la posizione di alcune dita il software provvede a calco- larne una posizione approssimata. Il calcolo avviene utilizzando la posizione del palmo e traslandosi nello spazio in una posizione opportuna. I punti possono essere ricostruiti anche se nessun dito ´ visibile. e Nel caso in cui sia il palmo della mano a non venir riconosciuto dal software, la posizione approssimata viene calcolata utilizzando la posizione dell’indice, in quanto da test effettuati questa ´ risultata la pi´ affidabile. e u La chiamata al metodo getPoint della classe mHand d´ come risultato la a posizione di un dito passato come parametro. Esso controlla che la posizione del 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
  • 24. 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 ´ aperto e Per calcolare quanto un dito ´ aperto si confrontano le dimensioni dell’ellisse e del palmo della mano con le dimensioni delle ellissi delle dita. Nel caso in cui un dito non sia visibile, il software considera il dito come chiuso, l’ellisse del dito avr´ quindi dimensione minima. a La classe che si occupa di calcolare gli openrate delle dita ´ My3dPoint e e il metodo da chiamare ´ getOpenRate passando un dito come parametro. e 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