Introduzione a Intel Perceptual Computing SDK: Face Recognition - Parte 2

In questa seconda parte vedremo le funzioni messe a disposizione dall' Intel Perceptual Cumputing SDK per memorizzare in maniera permanente i modelli di face recognition.

Nella prima parte abbiamo visto come riuscire a creare un modello e ad utilizzarlo per verificare che tale modello appartenga o meno ad un insieme di modelli che consideriamo validi.

Il limite riscontrato nell'implementazione proposta nella parte precedente è che il repository dei modelli validi era in memoria e, questo, ovviamente comporta che alla chiusura dellàapplicazione, tale repository venga rimosso completamente.

Serializzare un modello

La classe PXCMFaceAnalysis.Recognition.Model mette a disposizione il metodo Serialize che ci consente di ottenere la rapresentazione come array di byte del modello stesso.

La dimensione del modello (in byte) è contenuta nel profilo attivo della classe PXCMFaceAnalysis.Recognition e, per questo motivo, dato un modello, quello che dobbiamo fare, per ottenere la sua rappresentazione in byte, è:

  1. Recuperare l'istanza della classe PXCMFaceAnalysis:

    Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
    

     
  2. Ottenere, da quest'ultima, l'istanza della classe PXCMFaceAnalysis.Recognition:

    Dim recognizer As PXCMFaceAnalysis.Recognition = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
    


  3. Recuperare il profilo attivo della PXCMFaceAnalysis.Recognition:

    Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo
    recognizer.QueryProfile(pInfo)
    


  4. Dimensionare opportunamente il buffer che conterrà la rappresentazione binaria del modello:

    Dim modelBuffer As Byte() = New Byte(CInt(pInfo.modelSize)) {}
    


  5. Infine recuperare la rappresentazione binara tramite il metodo serialize:

    Dim status = model.Serialize(modelBuffer)
    If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
        ' modelBuffer contiene la rappresentazione binaria del modello
    Else
        modelBuffer = Nothing
    End If
    

La funzione completa è:

Public Function SerializeModel(model As PXCMFaceAnalysis.Recognition.Model) As Byte()
    Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
    Dim recognizer = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
    Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo
    recognizer.QueryProfile(pInfo)
    Dim modelBuffer As Byte() = New Byte(CInt(pInfo.modelSize)) {}
    Dim status = model.Serialize(modelBuffer)
    If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
        ' modelBuffer contiene la rappresentazione binaria del modello
    Else
        modelBuffer = Nothing
    End If
    recognizer.Dispose()
    faceAnalysis.Dispose()
    Return modelBuffer
End Function

Nel momento in cui abbiamo a disposizione il nostro array di byte, possiamo memorizzarlo in maniera permanente (ad esempio su disco).

Deserializzare un modello

La deserializzazione di un modello, ovvero l'operazione che, a partire da un array di byte, restituisce un'istanza della classe PXCMFaceAnalysis.Recognition.Model, avviene utilizzando il metodo Deserialize messo a disposizione della classe PXCMFaceAnalysis.Recognition.

I passi da compiere sono molto simili a quelli visti per la serializzazione e, inparticolare, sono:

  1. Recuperare l'istanza della classe PXCMFaceAnalysis;
  2. Ottenere, da quest'ultima, l'istanza della classe PXCMFaceAnalysis.Recognition;
  3. Recuperare il profilo attivo della PXCMFaceAnalysis.Recognition ed impostarlo nuovamente;
  4. Utilizzare il metodo Deserialize per ottenere il modello:

    Dim model As PXCMFaceAnalysis.Recognition.Model = Nothing
    Dim status = recognizer.DeserializeModel(modelBuffer, model)
    If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
        ' istanza valida di model
    Else
     
    End If
    

La funzione completa che implementa la deserializzazione è:

Public Function DeserializeModel(modelBuffer As Byte()) As PXCMFaceAnalysis.Recognition.Model
    Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
    Dim recognizer = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
    Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo
    recognizer.QueryProfile(pInfo)
    recognizer.SetProfile(pInfo)
    Dim model As PXCMFaceAnalysis.Recognition.Model = Nothing
    Dim status = recognizer.DeserializeModel(modelBuffer, model)
    If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
        ' istanza valida di model
    Else
 
    End If
    recognizer.Dispose()
    faceAnalysis.Dispose()
    Return model
End Function

Un repository per modelli su file system

Rirendiamo ora la classe SimpleFaceRecognitionPipeline realizzata nella prima parte dell'articolo e modifichiamola affinchè sia in grado di gestire un repository permanente di modelli.

Per prima cosa definiamo l'interfaccia di ciò che intendiamo come repository di modelli:

Public Interface IModelRepository
    Inherits IDictionary(Of String, PXCMFaceAnalysis.Recognition.Model)
     Function FindModel(model As PXCMFaceAnalysis.Recognition.Model, ByRef modelName As String) As Boolean
    Function SaveModel(modelName As String, modelBuffer As Byte()) As Boolean
    Function LoadAllModelNames() As IEnumerable(Of String)
    Function LoadModelBuffer(modelname As String) As Byte()
 
End Interface

Si tratta di un dictionary in cui potremo memorizzare i modelli in base al loro nome con i seguenti metodi:

  • FindModel : dato un modello come argomento esegue la ricerca all'interno dei modelli in esso memorizzati restituendo il nome del modello "uguale" (se questo esiste);
  • SaveModel : permette di salvare la forma binaria di un modello in base al proprio nome in maniera permanente;
  • LoadAllModelNames : carica l'elenco di tutti i nomi di modello memorizzati nel repository;
  • LoadModelBuffer : carica la forma binaria di un modello in base al proprio nome;

Definita la nostra interfaccia, possiamo implementare un repository che si occupi di salvare i modelli all'interno di una cartella su disco come file .mdl:

Public Class FileSystemModelRepository
    Inherits Dictionary(Of String, PXCMFaceAnalysis.Recognition.Model)
    Implements IModelRepository
 
    Public Sub New(path As String)
        If String.IsNullOrWhiteSpace(path) Then Throw New ArgumentNullException
        Me.Path = path
    End Sub
 
    Protected Const ModelExtension As String = ".mdl"
 
    Protected Property Path As String
   
    Public Function FindModel(model As PXCMFaceAnalysis.Recognition.Model, ByRef modelName As String) As Boolean Implements
IModelRepository.FindModel
        Dim index As UInt32
        Dim status = model.Compare(Me.Values.ToArray(), index)
        If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
            modelName = Me.Keys(CInt(index))
            Return True
        Else
            modelName = Nothing
            Return False
        End If
    End Function
 
    Public Function LoadAllModelNames() As IEnumerable(Of String) Implements IModelRepository.LoadAllModelNames
        Dim dirInfo = New DirectoryInfo(Path)
        If dirInfo.Exists Then
            Dim files = dirInfo.EnumerateFiles(String.Concat("*", ModelExtension))
            Return files.Select(Function(a) a.Name.Replace(a.Extension, ""))
        Else
            Return Nothing
        End If
    End Function
 
    Public Function LoadModelBuffer(modelname As String) As Byte() Implements IModelRepository.LoadModelBuffer
        If String.IsNullOrWhiteSpace(modelname) Then Throw New ArgumentNullException
        Dim fullfilename = System.IO.Path.Combine(Path, String.Concat(modelname, ModelExtension))
        If File.Exists(fullfilename) Then
            Dim buffer As Byte() = Nothing
            Try
                buffer = System.IO.File.ReadAllBytes(fullfilename)
            Catch ex As Exception
                buffer = Nothing
            End Try
            Return buffer
        Else
            Return Nothing
        End If
 
    End Function
 
    Public Function SaveModel(modelName As String, modelBuffer() As Byte) As Boolean Implements IModelRepository.SaveModel
        If String.IsNullOrWhiteSpace(modelName) Then Throw New ArgumentNullException
        If modelBuffer Is Nothing Then Throw New ArgumentNullException
        Dim dirInfo = New DirectoryInfo(Path)
        If dirInfo.Exists Then
            Dim fullfilename = System.IO.Path.Combine(Path, String.Concat(modelName, ModelExtension))
            Try
                File.WriteAllBytes(fullfilename, modelBuffer)
                Return True
            Catch
                Return False
            End Try
        Else
            Return False
        End If
    End Function
End Class
 

Come possiamo vedere si tratta di una classe che estende un Dictionary(of String,PXCMFaceAnalysis.Recognition.Model) e che implementa i metodi dell'interfaccia IModelRepository.

Prendiamo la pipeline SimpleFaceRecognitionPipeline che abbiamo implementato nella prima parte dell'articolo e la integriamo con quello che ci serve per gestire la memorizzazione dei modelli.

Per prima cosa implementiamo un costruttore attraverso il quale creiamo la pipeline passando un'istanza valida di una classe che implementa IModelRepository.  

Public Sub New(modelrepository As IModelRepository)
    MyBase.New()
    If modelrepository Is Nothing Then Throw New ArgumentNullException
    Me.ModelRepository = modelrepository
End Sub
 
Protected Property ModelRepository As IModelRepository

A questo punto non ci serve altro che aggiungere dei metodi alla pipeline che sfruttano l'istanza del repository per eseguire le seguenti operazioni:

  • Aggiunta di un nuovo modello alla lista dei modelli validi:

    Public Sub AddModel(modelName As String, model As PXCMFaceAnalysis.Recognition.Model)
        If ModelRepository.ContainsKey(modelName) Then
            ModelRepository(modelName) = model
        Else
            ModelRepository.Add(modelName, model)
        End If
    End Sub
    


  • Verifica di un modello rispetto ai modelli memorizzati:

    Public Function Compare(model As PXCMFaceAnalysis.Recognition.Model, ByRef modelName As String) As Boolean
        Return ModelRepository.FindModel(model, modelName)
    End Function
    


  • Salvataggio di tutta la collezione dei modelli presenti in memoria:

    Public Function SaveModels() As Boolean
        If ModelRepository.Keys.Any Then
            Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
            Dim recognizer = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
            Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo
            recognizer.QueryProfile(pInfo)
            Dim bytes As Byte() = New Byte(CInt(pInfo.modelSize)) {}
            For Each key In ModelRepository.Keys
                Dim status = ModelRepository(key).Serialize(bytes)
                If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
                    If Not ModelRepository.SaveModel(key, bytes) Then
                        Return False
                    End If
                Else
                    Return False
                End If
            Next
            recognizer.Dispose()
            faceAnalysis.Dispose()
            Return True
        End If
        Return True
    End Function
    


  • Caricamento di tutti i modelli:

    Public Function LoadModels() As Boolean
        Dim modelNames = ModelRepository.LoadAllModelNames()
        If modelNames.Any Then
            Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
            Dim recognizer = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
            Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo
            recognizer.QueryProfile(pInfo)
            recognizer.SetProfile(pInfo)
            For Each modelName In modelNames
                Dim modelBuffer = ModelRepository.LoadModelBuffer(modelName)
                If modelBuffer IsNot Nothing Then
                    Dim model As PXCMFaceAnalysis.Recognition.Model = Nothing
                    Dim status = recognizer.DeserializeModel(modelBuffer, model)
                    If status = pxcmStatus.PXCM_STATUS_NO_ERROR Then
                        ModelRepository.Add(modelName, model)
                    End If
                End If
            Next
            recognizer.Dispose()
            faceAnalysis.Dispose()
        End If
        Return True
    End Function
    

Creata la nuova pipeline, l'interfaccia deve esclusivamente preoccuparsi di caricare i modelli nel momento in cui parte e salvarli immediatamente prima di chiudere:

Public Class Form2
 
    Private Const ModelPath As String = "d:Models"
 
    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
        RemoveHandler pipeline.ImageCaptured, AddressOf ImageCapturedHandler
        RemoveHandler pipeline.FaceModelCaptured, AddressOf FaceModelCapturedHandler
        pipeline.SaveModels()
    End Sub
 
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim modelrepository = New FileSystemModelRepository(ModelPath)
        pipeline = New FaceRecognitionPipeline(modelrepository)
        AddHandler pipeline.ImageCaptured, AddressOf ImageCapturedHandler
        AddHandler pipeline.FaceModelCaptured, AddressOf FaceModelCapturedHandler
        pipeline.LoopFrames()
        pipeline.LoadModels()
    End Sub
 
    Private Sub UpdateInterface(e As ImageCapturedEventArgs)
        If Me.InvokeRequired Then
            Me.Invoke(New UpdateImageDelegate(AddressOf UpdateInterface), e)
        Else
            Dim bitmap As Bitmap = New Bitmap(e.Image)
            Dim modelname As String = Nothing
            If LastFaceModelData IsNot Nothing Then
                If LastFaceModelData.FaceDetected Then
                    If LastFaceModelData.Model IsNot Nothing Then
                        btnCreateFaceModel.Enabled = True
                        pipeline.Compare(LastFaceModelData.Model, modelname)
                    Else
                        btnCreateFaceModel.Enabled = False
                    End If
 
                    Using drawer = Graphics.FromImage(bitmap)
                        drawer.FillRectangle(New SolidBrush(Color.FromArgb(128, 255, 255, 255)),
                                             New Rectangle(New Point(LastFaceModelData.FaceRectangle.X, LastFaceModelData.FaceRectangle.Y),
                                                           New Size(LastFaceModelData.FaceRectangle.Width, LastFaceModelData.FaceRectangle.Height)))
                        If modelname IsNot Nothing Then
                            drawer.DrawString(String.Format("{0}", modelname), New Font("Times Roman", 12), Brushes.Red,
                                                            New Point(LastFaceModelData.FaceRectangle.X + 5, LastFaceModelData.FaceRectangle.Y + 5))
                        Else
                            drawer.DrawString(String.Format("FaceID={0}", LastFaceModelData.FaceID), New Font("Times Roman", 12), Brushes.Black,
                                                            New Point(LastFaceModelData.FaceRectangle.X + 5, LastFaceModelData.FaceRectangle.Y + 5))
                        End If
 
                    End Using
                End If
            End If
            lblRecognizedModelName.Text = modelname
            pctImage.Image = bitmap
        End If
    End Sub
 
    Private Sub btnCreateFaceModel_Click(sender As Object, e As EventArgs) Handles btnCreateFaceModel.Click
        If LastFaceModelData IsNot Nothing AndAlso LastFaceModelData.Model IsNot Nothing Then
            Dim model = LastFaceModelData.Model
            Dim modelName = InputBox("Nome del modello")
            If Not String.IsNullOrWhiteSpace(modelName) Then
                pipeline.AddModel(modelName, model)
            End If
        End If
    End Sub
        .
        .
        .
End Class

In allegato all'articolo trovate il progetto completo.

Tutte le funzionalità viste in questo articolo possono essere testate e sono funzionanti con la web cam integrata del vostro notebook senza bisogno di avere la Creative Gesture Cam.

Einzelheiten zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.