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 modelloLa 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, è:
- Recuperare l'istanza della classe PXCMFaceAnalysis:
Dim faceAnalysis As PXCMFaceAnalysis = QueryFace()
- Ottenere, da quest'ultima, l'istanza della classe PXCMFaceAnalysis.Recognition:
Dim recognizer As PXCMFaceAnalysis.Recognition = faceAnalysis.DynamicCast(Of PXCMFaceAnalysis.Recognition)(PXCMFaceAnalysis.Recognition.CUID)
- Recuperare il profilo attivo della PXCMFaceAnalysis.Recognition:
Dim pInfo As PXCMFaceAnalysis.Recognition.ProfileInfo recognizer.QueryProfile(pInfo)
- Dimensionare opportunamente il buffer che conterrà la rappresentazione binaria del modello:
Dim modelBuffer As Byte() = New Byte(CInt(pInfo.modelSize)) {}
- 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 modelloLa 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:
- Recuperare l'istanza della classe PXCMFaceAnalysis;
- Ottenere, da quest'ultima, l'istanza della classe PXCMFaceAnalysis.Recognition;
- Recuperare il profilo attivo della PXCMFaceAnalysis.Recognition ed impostarlo nuovamente;
- 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
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.