Introduzione a Intel Perceptual Computing SDK: Face Detection

In questo articolo ci occuperemo di Face Detection ed in partcolare delle funzionalità messe a disposizione dal Perceptual Computing SDK.

Cosè il face detection

Face detection è una tecnologia informatica che determina le posizioni e le dimensioni di volti umani all'interno di immagini (o di video). La tecnologia rileva le caratteristiche facciali e ignora tutto il resto, come edifici, alberi, corpi.
Più in generale, Face Detection è un caso specifico della tecnologia che va sotto il nome di Object-Class Detection (dove la classe di oggetto è il volto umano).

Perceptual Computing SDK e Face Detection

Crechiamo di capire cosa ci mette a disposizione Perceptual SDK per poter determinare se nel raggio di azione della web cam (sia essa la Creative Cam o la Web Cam di serie del nostro pc) è presente o meno un volto umano e dove esattamente si trova.
Come abbiamo già visto nell'articolo relativo all'utilizzo della web cam tramite Perceptual SDK (http://software.intel.com/it-it/articles/introduzione-a-perceptual-sdk-hellocam), dobbiamo creare una nostra classe Pipeline derivando dalla UtilMPipeline dell'SDK:

Public Class FaceDetectionPipeline
    Inherits UtilMPipeline
    Public Sub New()
        MyBase.New()
        EnableImage(PXCMImage.ColorFormat.COLOR_FORMAT_RGB24)
        EnableFaceLocation()
    End Sub
End Class

In questo caso abilitiamo la Pipeline all'utilizzo della parte video della web cam (con immagini RGB a 24 bit 640x480) e abilitiamo il face location.
Il comando EnableFaceLocation() farà in modo che, nel momento in cui andremo a richiedere le funzionalità di face location, queste siano disponibili.

Ogni "faccia" rilevata dall'SDK (in gergo Face) è identificata da un indice e da un identificativo: l'indice è un intero che indica l'ordine di rilevazione da parte di Perceptual, mentre l'identificativo è un intero che la contraddistingue in maniera univoca. Quando una faccia viene rilevata le viene assegnato un id univoco, se questa non viene più rilevata, nel momento in cui rientra nella visuale della web cam, ottiene un nuovo id diverso dal precedente..
Una volta attivata la localizzazione facciale, l'algoritmo da seguire per verificare la presenza di una "faccia" all'interno dell'immagine ripresa dalla cam è la seguente:

  1. recuperare l'istanza della classe PXCMFaceAnalysis che si occupa dell'effettivo algoritmo di face detection;
  2. per ogni indice dell'eventuale Face da recuperare, eseguire una query per capire quale è il suo identificativo
  3. tramite l'identificativo si recuperano (se ci sono) i dati veri e propri della posizione del viso.

Possiamo recuperare l'istanza di PXCMFaceAnalisys tramite il metodo QueryFace() della class UitlMPipeline:

Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()

Una volta che si ha l'istanza di PXCMFaceAnalysis (questa è Nothing se il Face Location non è stato abilitato con il metodo EnableFaceLocation()), si recupera l'identificativo della i-esima Face rilevata grazie al metodo QueryFace della stessa. Ad esempio l'identificativo della prima Face rilevata è:

Dim queryfaceresult As pxcmStatus = faceAnalysis.QueryFace(0, faceId, faceTimestamp)

Il valore di ritorno della funzione (come nella stragrande maggioranza delle funzioni esposte dal Perceotual SDK), indica se il metodo ha avuto successo (pxcmStatus.PXCM_STATUS_NO_ERROR) oppure se non è stata rilevata una face con tale indice (pxcmStatus.PXC_STATUS_ITEM_UNAVAILABLE).

Nel momento in cui è stata rilevata una Face, abbiamo a disposizione l'identificativo della stessa con il quale possiamo recuperare i dati di posizione:

Dim faceLocation as PXCMFaceAnalysis.Detection = CType(faceAnalysis.DynamicCast(PXCMFaceAnalysis.Detection.CUID), PXCMFaceAnalysis.Detection)
Dim faceLocationdata As PXCMFaceAnalysis.Detection.Data
Dim locationResult = faceLocation.QueryData(faceId, faceLocationdata)

Come possiamo vedere nel precedente pezzo di codice, il recupero dei dati di posizione della Face avviene tramite l'istanza della classe PXCMFaceAnalysis.Detection.
Questa si ottiene, a partire dalla PXCMFaceAnalysis, utilizzando il metodo DynamicCast.

Dim faceLocation As PXCMFaceAnalysis.Detection = CType(faceAnalysis.DynamicCast(PXCMFaceAnalysis.Detection.CUID), PXCMFaceAnalysis.Detection)

DynamicCast è un metodo che troviamo (una sorta di factory) su parecchie classi dell'SDK e che, dato il CUID (Class Unique IDentifer) della classe da istanziare, restituisce l'istanza della stessa. La creazione efettiva della classe è a carico del modulo corrispondente (in questo caso quello di Face Analysis).

Una volta ottenuta l'istanza di PXCMFaceAnalysis.Detection, siamo in grado di recuperare, finalmente, i dati relativi alla face:

Dim faceLocationdata As PXCMFaceAnalysis.Detection.Data
Dim locationResult As pxcmStatus = faceLocation.QueryData(faceId, faceLocationdata)

La struttura PXCMFaceAnalysis.Detection.Data contiene i dati relativi alla faccia rilevata:

  1. rectangle: struttura PXCRectU32 che individua fisicamente la faccia rilevata. La struttura è composta dalle coordinate x e y del punto in alto a sinistra, dall'altezza e dalla larghezza;
  2. fid: l'identificativo della faccia rilevata;
  3. confidence: indica la precisione con cui la faccia è stata rilevata. E' un valore intero da 0 a 100;
  4. viewAngle: contiene l'angolo che la faccia rilevata forma con la telecamera. Si tratta di un enumerazione ViewAngle, a maschera di bit, i cui valori sono riportati nella seguente figura:

Il valore VIEW_ANGLE_FRONTAL indica, ad esempio, che la faccia si trova parallela all'obiettivo della telecamera, mentre VIEW_ANGLE_0 indica che la faccia e' rivolta lontano dalla telecamera con un angolo di 90 gradi verso sinistra.

Il risultato del metodo QueryData della classe PXCMFaceAnalysis.Detection indica se i dati di posizione della faccia sono disponibili o meno. In particolare se il risultato è uguale a pxcmStatus.PXCM_STATUS_NO_ERROR, allora la faccia è stata correttamente rilevata e possiamo riempire l'argomento dell'evento CafeCaptured e lanciare lo stesso:

 Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
 Dim faceId As Integer
 Dim faceTimestamp As ULong
 Dim queryFaceResult As pxcmStatus = faceAnalysis.QueryFace(0, faceId, faceTimestamp)
 Dim e As FaceDetectionEventArgs = New FaceDetectionEventArgs With {.FaceDetected = (queryFaceResult = pxcmStatus.PXCM_STATUS_NO_ERROR)}
 If queryFaceResult = pxcmStatus.PXCM_STATUS_NO_ERROR Then
  Dim faceLocation As PXCMFaceAnalysis.Detection = CType(faceAnalysis.DynamicCast(PXCMFaceAnalysis.Detection.CUID), PXCMFaceAnalysis.Detection)
  Dim faceLocationdata As PXCMFaceAnalysis.Detection.Data
  Dim locationResult As pxcmStatus = faceLocation.QueryData(faceId, faceLocationdata)
  With e
   .FaceDetected = (locationResult = pxcmStatus.PXCM_STATUS_NO_ERROR)
   .Confidence = faceLocationdata.confidence
   .FaceID = faceId
   .FaceIndex = 0
   .TimeStamp = faceTimestamp
   .ViewAngle = faceLocationdata.viewAngle
   .FaceRectangle = New System.Drawing.Rectangle(CInt(faceLocationdata.rectangle.x),
                CInt(faceLocationdata.rectangle.y),
                CInt(faceLocationdata.rectangle.w),
                CInt(faceLocationdata.rectangle.h))
  End With
 End If
 RaiseEvent FaceCaptured(Me, e)

 

L'interfaccia grafica

Realizzata la pipeline che ci permette di avere le immagini provenienti dalla web cam e gli eventuali dati relativi alla faccia rilevata, è il momento di pensare all'interfaccia grafica.
La seguente figura mostra la semplice interfaccia che realizzeremo:

Il code behind della form e' il seguente:

Public Class Form1
    Private pipeline As FaceDetectionPipeline
    Private LastFaceLocationData As FaceDetectionEventArgs
    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
        RemoveHandler pipeline.ImageCaptured, AddressOf ImageCapturedHandler
        RemoveHandler pipeline.FaceCaptured, AddressOf FaceCapturedHandler
        pipeline.Dispose()
    End Sub
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        pipeline = New FaceDetectionPipeline
        AddHandler pipeline.ImageCaptured, AddressOf ImageCapturedHandler
        AddHandler pipeline.FaceCaptured, AddressOf FaceCapturedHandler
        pipeline.LoopFrames()
    End Sub
    Private Sub ImageCapturedHandler(sender As Object, e As ImageCapturedEventArgs)
        UpdateInterface(e)
    End Sub
    Private Delegate Sub UpdateImageDelegate(e As ImageCapturedEventArgs)
    Private Sub UpdateInterface(e As ImageCapturedEventArgs)
        If Me.InvokeRequired Then
            Me.Invoke(New UpdateImageDelegate(AddressOf UpdateInterface), e)
        Else
            If LastFaceLocationData IsNot Nothing AndAlso LastFaceLocationData.FaceDetected Then
                Using drawer = Graphics.FromImage(e.Image)
                    drawer.FillRectangle(New SolidBrush(Color.FromArgb(128, 255, 255, 255)),
                                         New Rectangle(New Point(LastFaceLocationData.FaceRectangle.X, LastFaceLocationData.FaceRectangle.Y),
                                                       New Size(LastFaceLocationData.FaceRectangle.Width, LastFaceLocationData.FaceRectangle.Height)))
                    drawer.DrawString(String.Format("FaceID={0}", LastFaceLocationData.FaceID), New Font("Times Roman", 12), Brushes.Black,
                                      New Point(LastFaceLocationData.FaceRectangle.X + 5, LastFaceLocationData.FaceRectangle.Y + 5))
                End Using
                prgConfidence.Value = CInt(LastFaceLocationData.Confidence)
            Else
                prgConfidence.Value = 0
            End If
            pctImage.Image = e.Image
        End If
    End Sub
    Private Sub FaceCapturedHandler(sender As Object, e As FaceDetectionEventArgs)
        LastFaceLocationData = e
    End Sub
End Class

La form contiene un'istanza della pipeline la quale viene creata nell'evento di load della pagina. Contestualmente alla creazione dell'istanza di FaceDetectionPipeline, vengono agganciati i gestori di evento per il recupero dell'immagine e dei dati dell'eventuale faccia rilevata.
Il gestore dell'evento di recupero delle informazioni della faccia rilevata si occupa di salvare in una variabile i dati stessi in modo che il gestore di recupero dell'immagine si ain grado di disegnare il rettangolo corrispondente.
Il metodo di aggiornamento dell'interfaccia (in cui utilizziamo il pattern InvokeRequired/Invoke perchè l'evento di cattura dell'immagine potrebbe essere lanciato su un thread che non èquello principale) recupera il contesto grafico dell'immagine proveniente dalla web cam e disegna rettangolo e id della faccia tracciata:

 If LastFaceLocationData IsNot Nothing AndAlso LastFaceLocationData.FaceDetected Then
  Using drawer = Graphics.FromImage(e.Image)
   drawer.FillRectangle(New SolidBrush(Color.FromArgb(128, 255, 255, 255)),
         New Rectangle(New Point(LastFaceLocationData.FaceRectangle.X, LastFaceLocationData.FaceRectangle.Y),
              New Size(LastFaceLocationData.FaceRectangle.Width, LastFaceLocationData.FaceRectangle.Height)))
   drawer.DrawString(String.Format("FaceID={0}", LastFaceLocationData.FaceID), New Font("Times Roman", 12), Brushes.Black,
         New Point(LastFaceLocationData.FaceRectangle.X + 5, LastFaceLocationData.FaceRectangle.Y + 5))
  End Using
  prgConfidence.Value = CInt(LastFaceLocationData.Confidence)
 Else
  prgConfidence.Value = 0
 End If
 pctImage.Image = e.Image

Il risultato è mostrato nella figura seguente:

E se volessimo più facce?

La pipeline implementata è in grado di rilevare la presenza nel campo d'azione della web cam di una faccia. E se volessimo rilevarne di più?
La soluzione è abbastanza semplice: ci basta iterare il metodo QueryFace della PXCMFaceAnalysis per il numero di facce che vogliamo rilevare.

 Dim faceAnalysis = QueryFace()
 Dim faceId As Integer
 Dim faceTimestamp As ULong
 For index As UInteger = 0 To CUInt(NumberOfFaceToDetect - 1)
  faceAnalysis.QueryFace(index, faceId, faceTimestamp)
  Dim faceLocation = CType(faceAnalysis.DynamicCast(PXCMFaceAnalysis.Detection.CUID), PXCMFaceAnalysis.Detection)
  Dim faceLocationdata As PXCMFaceAnalysis.Detection.Data
  Dim locationResult = faceLocation.QueryData(faceId, faceLocationdata)
  Dim e = New FaceDetectionEventArgs() With {.FaceDetected = (locationResult = pxcmStatus.PXCM_STATUS_NO_ERROR),
               .Confidence = faceLocationdata.confidence,
               .FaceIndex = index,
               .FaceID = faceId,
               .TimeStamp = faceTimestamp,
               .ViewAngle = faceLocationdata.viewAngle,
               .FaceRectangle = New System.Drawing.Rectangle(CInt(faceLocationdata.rectangle.x),
                         CInt(faceLocationdata.rectangle.y),
                         CInt(faceLocationdata.rectangle.w),
                         CInt(faceLocationdata.rectangle.h))}
  RaiseEvent FaceCaptured(Me, e)
 Next

NumberOfFaceToDetect è un parametro che impostiamo nella pipeline al momento in cui la istanziamo e che indica il numero massimo di facce da rilevare.
La nuova pipeline, quindi, genererà un evento a seguito della cattura dell'immagine e n eventi di FaceCaptured per ogni faccia richiesta.
Dovremo, quindi adattare l'interfaccia grafica:

    Private Sub UpdateInterface(e As ImageCapturedEventArgs)
        If Me.InvokeRequired Then
            Me.Invoke(New UpdateImageDelegate(AddressOf UpdateInterface), e)
        Else
            For Each locationData In LastFaceLocationData
                If locationData IsNot Nothing AndAlso locationData.FaceDetected Then
                    Using drawer = Graphics.FromImage(e.Image)
                        drawer.FillRectangle(New SolidBrush(FillColors(CInt(locationData.FaceIndex))),
                                             New Rectangle(New Point(locationData.FaceRectangle.X, locationData.FaceRectangle.Y),
                                                           New Size(locationData.FaceRectangle.Width, locationData.FaceRectangle.Height)))
                        drawer.DrawString(String.Format("FaceID={0}", locationData.FaceID), New Font("Times Roman", 12), Brushes.Black,
                                      New Point(locationData.FaceRectangle.X + 5, locationData.FaceRectangle.Y + 5))
                    End Using
                End If
            Next
            pctImage.Image = e.Image
        End If
    End Sub
AllegatoDimensione
Scarica facedetection.zip428.75 KB
Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione

Commenti

Ritratto di Alder Lopez

I would have liked to learn in time, too late to sign up for what I read.

Alder Lopez Software Engineer