Identification des personnes avec le SDK Snapdragon de Qualcomm

Il n'y a pas si longtemps, prendre des photos coûtait assez cher. Les caméras nécessitaient des films avec une capacité limitée. Voir les résultats nécessitait également plus de temps et plus d’argent. Ces contraintes inhérentes nous ont permis d'être sélectifs avec les photos que nous avons prises.

Aujourd'hui encore, grâce à la technologie, ces contraintes ont été atténuées, mais nous sommes maintenant confrontés à un nouveau problème: filtrer, organiser et révéler d'importantes photos parmi les nombreuses photos que nous prenons..

Ce nouveau problème est ce qui a inspiré ce tutoriel. Je montrerai comment utiliser de nouveaux outils pour faciliter la vie de l'utilisateur en introduisant de nouvelles méthodes de filtrage et d'organisation de notre contenu..

1. Concept

Pour ce projet, nous allons examiner une manière différente de filtrer votre collection de photos. En cours de route, vous apprendrez à intégrer et à utiliser le SDK Snapdragon de Qualcomm pour le traitement et la reconnaissance faciaux..

Nous allons permettre à l'utilisateur de filtrer une collection de photos par identité / identités. La collection sera filtrée par identités à partir d'une photo sur laquelle l'utilisateur clique, comme illustré ci-dessous..

2. aperçu

L'objectif principal de ce billet est l'introduction du traitement du visage et de la reconnaissance à l'aide du SDK Snapdragon de Qualcomm, tout en encourageant indirectement de nouvelles façons de penser et l'utilisation de métadonnées dérivées du contenu..

Pour éviter d'être corrigé dans la plomberie, j'ai créé un modèle fournissant le service de base pour numériser la collection de photos de l'utilisateur et une grille pour l'affichage des photos. Notre objectif est d'améliorer ceci avec le concept proposé ci-dessus.

Dans la section suivante, nous passerons brièvement en revue ces composants avant de passer à l'introduction du SDK Snapdragon de Qualcomm..

3. squelette

Comme mentionné ci-dessus, notre objectif est de nous concentrer sur le SDK de Snapdragon. J'ai donc créé un squelette doté de toute la plomberie. Vous trouverez ci-dessous un diagramme et une description du projet, disponible au téléchargement sur GitHub..

Notre Les données package contient une implémentation de SQLiteOpenHelper (IdentityGalleryDatabase) responsable de la création et de la gestion de notre base de données. La base de données consistera en trois tables, l’une servant de pointeur sur l’enregistrement de média (photo), une autre pour les identités détectées (identité), et enfin la table des relations reliant les identités à leurs photos (identité_photo).

Nous allons utiliser la table d’identité pour stocker les attributs fournis par le SDK de Snapdragon, détaillés dans une section ultérieure de ce didacticiel..

Le paquet de données comprend également un Fournisseur (IdentityGalleryProvider) et Contrat (IdentityGalleryContract) classe, qui n'est rien de plus qu'une norme Fournisseur agissant comme une enveloppe de la SQLiteOpenHelper classe.

Pour vous donner une idée de la façon d’interagir avec le Fournisseur classe, le code suivant provient de la TestProvider classe. Comme son nom l'indique, il est utilisé pour tester le Fournisseur classe. 

//… Requête pour toutes les photos Cursor cursor = mContext.getContentResolver (). Query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null); //… Requête pour toutes les photos comprenant l'une des identités de la photo référencée Cursor cursor = mContext.getContentResolver (). Query (IdentityGalleryContract.Contrat.PhotoEntity.buildUriWithReferencePhoto (photoId), null, null, null, null); //… Identités d'appel de requête Curseur curseur = mContext.getContentResolver (). Query (IdentityGalleryContract.IdentityEntity.CONTENT_URI, null, null, null, null); //… Requête pour tous les curseurs curseur = mContext.getContentResolver (). Query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null);

le un service est responsable de l’itération, du catalogage et du traitement final des images disponibles via le MediaStore. Le service lui-même étend la IntentService comme un moyen facile d'effectuer le traitement sur son propre thread. Le travail réel est délégué au GalleryScanner, qui est la classe que nous allons étendre pour le traitement du visage et la reconnaissance.

Ce GalleryScannerIntentService est instancié chaque fois que le Activité principale est créé avec l'appel suivant:

@Override protected void onCreate (Bundle savedInstanceState) … GalleryScannerIntentService.startActionScan (this.getApplicationContext ());…

Quand commencé, GalleryScannerIntentService récupère la dernière date d'analyse et la transmet au constructeur du GalleryScanner. Il appelle ensuite le balayage méthode pour commencer à parcourir le contenu de la MediaItem fournisseur de contenu - pour les articles après la dernière date d'analyse.

Si vous inspectez le balayage méthode du GalleryScanner classe, vous remarquerez que c'est assez prolixe-rien compliqué ne se passe ici. La méthode doit rechercher les fichiers multimédias stockés en interne (MediaStore.Images.Media.INTERNAL_CONTENT_URI) et extérieurement (MediaStore.Images.Media.EXTERNAL_CONTENT_URI). Chaque élément est ensuite passé à une méthode de crochet, qui est où nous placerons notre code pour le traitement du visage et la reconnaissance.

private void processImage (ContentValues ​​contentValues, Uri contentUri) lance la nouvelle UnsupportedOperationException ("La méthode Hook n'est pas implémentée"); 

Deux autres méthodes de crochet dans le GalleryScanner sont disponibles (comme le suggèrent les noms de méthodes) pour initialiser et désinitialiser le Traitement du visage exemple.

private void initFacialProcessing () lève UnsupportedOperationException lève un nouveau UnsupportedOperationException ("La méthode Hook n'est pas implémentée");  private void deinitFacialProcessing () lance la nouvelle UnsupportedOperationException ("La méthode Hook n'est pas implémentée"); 

Le paquet final est le paquet de présentation. Comme son nom l'indique, il héberge le Activité classe responsable de rendre notre galerie. La galerie est un GridView attaché à un CursorAdapter. Comme expliqué ci-dessus, taper sur un élément interrogera la base de données pour toutes les photos contenant l'une des identités de la photo sélectionnée. Par exemple, si vous appuyez sur une photo de votre amie Lisa et de son copain Justin, la requête filtrera toutes les photos contenant Lisa ou Justin, ou les deux..

4. Le SDK Snapdragon de Qualcomm

Pour aider les développeurs à rendre leur matériel plus esthétique et à le rendre justice, Qualcomm a publié un ensemble étonnant de SDK, dont le SDK Snapdragon. Le SDK Snapdragon expose un ensemble optimisé de fonctions pour le traitement du visage..

Le SDK est divisé en deux parties: le traitement du visage et la reconnaissance faciale. Étant donné que tous les périphériques ne prennent pas en charge ces fonctionnalités, ni aucune de celles-ci, ce qui est probablement la raison pour laquelle ces fonctionnalités sont séparées, le SDK permet de vérifier facilement les fonctionnalités prises en charge par le périphérique. Nous verrons cela plus en détail plus tard.

Le traitement du visage permet d'extraire les caractéristiques d'une photo (d'un visage), notamment:  

  • Détection de clignotement: Mesurer l'ouverture de chaque œil.
  • Suivi du regard: Évaluer où le sujet regarde.
  • Valeur du sourire: Estimer le degré de sourire.
  • Orientation du visage: Suivre le lacet, le tangage et le roulis de la tête.

La reconnaissance faciale, comme son nom l'indique, permet d'identifier les personnes sur une photo. Il convient de noter que tous les traitements sont effectués localement, par opposition au cloud..

Ces fonctionnalités peuvent être utilisées en temps réel (vidéo / caméra) ou hors ligne (galerie). Dans notre exercice, nous utiliserons ces fonctionnalités hors ligne, mais les différences entre les deux approches sont minimes..

Consultez la documentation en ligne des périphériques pris en charge pour en savoir plus sur le traitement et la reconnaissance faciaux..

5. Ajout du traitement du visage et de la reconnaissance

Dans cette section, nous allons compléter ces méthodes de hook, avec étonnamment peu de lignes de code, pour donner à notre application la possibilité d'extraire les propriétés des faces et d'identifier les personnes. Pour continuer, téléchargez le code source de GitHub et ouvrez le projet dans Studio Android. Alternativement, vous pouvez télécharger le projet terminé.

Étape 1: Installation du SDK Snapdragon

La première chose à faire est de récupérer le SDK sur le site Web de Qualcomm. Notez que vous devez vous enregistrer / vous connecter et accepter les conditions générales de Qualcomm..

Une fois téléchargé, désarchivez le contenu et accédez à /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Copier le sd-sdk-facial-processing.jar déposer dans votre projet / app / libs / folder comme indiqué ci-dessous.

Après avoir copié le SDK Snapdragon, cliquez avec le bouton droit sur le sd-sdk-facial-processing.jar et sélectionnez Ajouter comme bibliothèque… de la liste des options.

Cela ajoutera la bibliothèque en tant que dépendance dans votre build.gradle fichier comme indiqué ci-dessous.

dependencies compile fileTree (dir: 'libs', include: ['* .jar'])) compiler des fichiers ('libs / sd-sdk-traitement-facial.jar') compiler 'com.android.support:support-v13: 20.0.0 '

La dernière étape consiste à ajouter la bibliothèque native. Pour ce faire, créez un dossier appelé jniLibs dans ton / app / src / main / dossier et copier le arméa dossier (à partir du téléchargement du SDK) et son contenu dans celui-ci.

Nous sommes maintenant prêts à mettre en œuvre la logique d'identification des personnes à l'aide des fonctionnalités de l'API. Les extraits de code suivants appartiennent à la GalleryScanner classe.

Étape 2: initialisation

Commençons par la méthode du crochet d'initialisation. 

Vous êtes à la recherche de formateurs de base (FacialProcessing.FEATURE_LIST.FEATURE_FROCESSING). dispositif");  mFacialProcessing = FacialProcessing.getInstance (); if (mFacialProcessing! = null) mFacialProcessing.setRecognitionConfidence (mConfidenceThreshold); mFacialProcessing.setProcessingMode (FacialProcessing.FP_MODES.FP_MODE_STILL); loadAlbum ();  else lancer la nouvelle UnsupportedOperationException ("Une instance est déjà utilisée");

Nous devons d’abord vérifier que le périphérique prend en charge le traitement et la reconnaissance faciaux. Si ce n'est pas le cas, nous lançons un UnsupportedOperationException exception.

Après cela, nous assignons notre référence locale du Traitement du visage classe, mFacialProcessing, à une nouvelle instance en utilisant la méthode factory getInstance. Cela va retourner nul si une instance est déjà utilisée, auquel cas le consommateur doit appeler Libération sur cette référence.

Si nous avons réussi à obtenir une instance de Traitement du visage objet, nous le configurons en définissant d’abord la confiance. Nous faisons cela en utilisant une variable locale, qui est 57 dans ce cas, la plage va de 0 à 100. La confiance est un seuil lorsque vous essayez de résoudre des identités. Toute correspondance en dessous de ce seuil sera considérée comme une identité distincte.

Pour ce qui est de déterminer la valeur, autant que je sache, il s'agit d'un processus d'essai et d'erreur. Évidemment, plus le seuil est élevé, plus la reconnaissance est précise, avec le compromis d'augmenter le nombre de faux positifs..

Nous avons ensuite mis le Traitement du visage mode à FP_MODE_STILL. Vos options ici sont soit FP_MODE_STILL ou FP_MODE_VIDEO. Comme le suggèrent leurs noms, l’un est optimisé pour les images fixes, l’autre pour les images continues, les deux ayant des cas d’utilisation évidents..

P_MODE_STILL, comme vous pouvez le soupçonner, fournit des résultats plus précis. Mais comme vous le verrez plus tard, FP_MODE_STILL est implicite par la méthode que nous utilisons pour traiter l'image afin que cette ligne puisse être omise. Je ne l'ai ajouté que par souci d'exhaustivité.

Nous appelons ensuite loadAlbum (méthode du GalleryScanner classe), qui est ce que nous allons regarder à la prochaine.

private void loadAlbum () SharedPreferences sharedPreferences = mContext.getSharedPreferences (TAG, 0); String arrayOfString = sharedPreferences.getString (KEY_IDENTITY_ALBUM, null); byte [] albumArray = null; if (arrayOfString! = null) String [] splitStringArray = arrayOfString.substring (1, arrayOfString.length () - 1) .split (","); albumArray = nouvel octet [splitStringArray.length]; pour (int i = 0; i < splitStringArray.length; i++)  albumArray[i] = Byte.parseByte(splitStringArray[i]);  mFacialProcessing.deserializeRecognitionAlbum(albumArray);  

La seule ligne intéressante ici est:

mFacialProcessing.deserializeRecognitionAlbum (albumArray);

Sa méthode de comptage est:

byte [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum ();

Un célibataire ou Individual Traitement du visage instance peut être considéré comme une session. Les personnes ajoutées (expliquées ci-dessous) sont stockées localement (appelé "album de reconnaissance") au sein de cette instance. Pour permettre à votre album de persister pendant plusieurs sessions, c’est-à-dire que chaque fois que vous obtenez une nouvelle instance, vous avez besoin d’un moyen de persister et de les charger..

le serializeRecogntionAlbum méthode convertit l'album en tableau d'octets et inversement désérialiserReconnaissanceAlbum chargera et analysera un album précédemment stocké sous forme de tableau d'octets.

Étape 3: désinitialisation

Nous savons maintenant comment initialiser le Traitement du visage classe de traitement du visage et de reconnaissance. Passons maintenant à la désinitialisation en implémentant la deinitFacialProcessing méthode.

Void privé deinitFacialProcessing () if (mFacialProcessing! = null) saveAlbum (); mFacialProcessing.release (); mFacialProcessing = null; 

Comme mentionné ci-dessus, il ne peut y avoir qu'une seule instance du Traitement du visage classe à la fois, nous devons donc nous assurer de la publier avant de terminer notre tâche. Nous faisons cela via un Libération méthode. Mais d'abord, nous faisons en sorte que l'album de reconnaissance persiste afin que nous puissions utiliser les résultats sur plusieurs sessions. Dans ce cas, lorsque l'utilisateur prend ou reçoit de nouvelles photos, nous voulons nous assurer que nous utilisons les identités précédemment reconnues pour les mêmes personnes..

saveAlbum () byte [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum (); SharedPreferences sharedPreferences = mContext.getSharedPreferences (TAG, 0); SharedPreferences.Editor editor = sharedPreferences.edit (); editor.putString (KEY_IDENTITY_ALBUM, Arrays.toString (albumBuffer)); editor.commit (); 

Étape 4: Traitement de l'image

Nous sommes enfin prêts à préciser la méthode du crochet final et à utiliser le Traitement du visage classe. Les blocs de code suivants appartiennent à la processImage méthode. Je les ai séparés pour plus de clarté.

private void processImage (ContentValues ​​contentValues, Uri contentUri) long photoRowId = ContentUris.parseId (contentUri); String uriAsString = contentValues.getAsString (GalleryContract.PhotoEntity.COLUMN_URI); Uri Uri = Uri.parse (uriAsString); Bitmap bitmap = null; try bitmap = ImageUtils.getImage (mContext, uri);  catch (IOException e) return;  if (bitmap! = null) // continué en dessous de (1)

La méthode prend une référence à une instance du ContentValues classe, qui contient les métadonnées de cette image, ainsi que l'URI pointant vers l'image. Nous l'utilisons pour charger l'image en mémoire.

L'extrait de code suivant doit remplacer le commentaire ci-dessus. // continué ci-dessous (1).

if (! mFacialProcessing.setBitmap (bitmap)) return;  int numFaces = mFacialProcessing.getNumFaces (); if (numFaces> 0) FaceData [] faceDataArray = mFacialProcessing.getFaceData (); if (faceDataArray == null) Log.w (TAG, contentUri.toString () + "a été renvoyé par un NULL FaceDataArray"); revenir;  pour (int i = 0; i

Comme mentionné ci-dessus, nous passons d'abord l'image statique à la Traitement du visage par exemple via le setBitmap méthode. Cette méthode utilise implicitement le FP_MODE_STILL mode. Cette méthode retourne Vrai si l'image a été traitée avec succès et Faux si le traitement a échoué.

La méthode alternative pour traiter les images en continu (généralement pour les images d'aperçu de la caméra) est la suivante:

public boolean setFrame (byte [] yuvData, int frameWidth, int frameHeight, boolean isMirrored, FacialProcessing.PREVIEW_ROTATION_ANGLE rotationAngle) 

La plupart des paramètres sont évidents. Vous devez indiquer si le cadre est retourné (cela est généralement nécessaire pour la caméra orientée vers l'avant) et si une rotation a été appliquée (généralement définie via le bouton setDisplayOrientation méthode d'un Caméra exemple).

Nous demandons ensuite le nombre de visages détectés et ne continuons que si au moins un est trouvé. le getFaceData La méthode retourne les détails de chaque visage détecté sous forme de tableau de FaceData objets, où chacun FaceData objet encapsule les traits du visage, y compris:

  • limite du visage (FACE_RECT)
  • le visage, la bouche et les yeux (FACE_COORDINATES)
  • contour du visage (FACE_CONTOUR)
  • degré de sourire (FACE_SMILE)
  • direction des yeux (FACE_GAZE)
  • drapeau indiquant si l'un des yeux (ou les deux yeux) clignote (FACE_BLINK)
  • lacet, tangage et roulis du visage (FACE_ORIENTATION)
  • identification générée ou dérivée (FACE_IDENTIFICATION)

Il y a une surcharge dans cette méthode qui prend un ensemble d'énums (comme décrit ci-dessus) pour que les points caractéristiques soient inclus, éliminant / réduisant les calculs redondants.

public FaceData [] getFaceData (java.util.EnumSet dataSet) lève java.lang.IllegalArgumentException 

Nous passons maintenant à inspecter le FaceData objet pour extraire l'identité et les fonctionnalités. Voyons d'abord comment se fait la reconnaissance faciale.

L'extrait de code suivant doit remplacer le commentaire ci-dessus. // continué ci-dessous (2).

int personId = faceData.getPersonId (); if (personId == FacialProcessingConstants.FP_PERSON_NOT_REGTED) personId = mFacialProcessing.addPerson (i);  else if (mFacialProcessing.updatePerson (personId, i)! = FacialProcessingConstants.FP_SUCCESS) // erreur de traitement TODO long identityRowId = getOrInsertPerson (personId); // continué ci-dessous (3)

Nous demandons d’abord l’identité de la personne assignée via getPersonId méthode. Cela va retourner -111 (FP_PERSON_NOT_REGTED) s'il n'y a pas d'identité dans l'album actuellement chargé, sinon renvoyer l'identifiant d'une personne correspondante de l'album chargé.

Si aucune identité n'existe, nous l'ajoutons via le addPerson méthode du Traitement du visage objet, en lui passant l'index du FaceData article que nous inspectons actuellement. Si cette méthode réussit, la méthode retourne l’identifiant attribué, sinon renvoie une erreur. Cela se produit lorsque vous essayez d'ajouter une identité qui existe déjà.

Alternativement, lorsque la personne a été associée à une identité stockée dans notre album chargé, nous appelons le Traitement du visage objets updatePerson méthode, en lui passant l'identifiant et l'index existants du FaceData article. Ajouter une personne plusieurs fois augmente les performances de reconnaissance. Vous pouvez ajouter jusqu'à dix visages pour une seule personne..

La dernière ligne renvoie simplement l'identifiant d'identité associé de notre base de données, en l'insérant si l'identifiant personnel n'existe pas déjà..

Ce n'est pas montré ci-dessus, mais le FaceData l'instance expose la méthode getRecognitionConfidence pour rendre la confiance de reconnaissance (0 à 100). En fonction de vos besoins, vous pouvez utiliser cela pour influencer le flux.

Le dernier extrait montre comment interroger chacune des autres fonctionnalités de la FaceData exemple. Dans cette démo, nous ne les utilisons pas, mais avec un peu d'imagination, je suis sûr que vous pouvez trouver des moyens de les utiliser à bon escient..

L'extrait de code suivant doit remplacer le commentaire ci-dessus. // continué ci-dessous (3).

int smileValue = faceData.getSmileValue (); int leftEyeBlink = faceData.getLeftEyeBlink (); int rightEyeBlink = faceData.getRightEyeBlink (); int roll = faceData.getRoll (); PointF gazePointValue = faceData.getEyeGazePoint (); int pitch = faceData.getPitch (); int yaw = faceData.getYaw (); int horizontalGaze = faceData.getEyeHorizontalGazeAngle (); int verticalGaze = faceData.getEyeVerticalGazeAngle (); Rect faceRect = faceData.rect; insertNewPhotoIdentityRecord (photoRowId, identityRowId, gazePointValue, horizontalGaze, verticalGaze, leftEyeBlink, rightEyeBlink, pitch, yaw, roll, smileValue, faceRect); 

Cela complète le code de traitement. Si vous revenez à la galerie et appuyez sur une image, vous devriez la voir filtrer toutes les photos ne contenant aucune personne identifiée dans la photo sélectionnée..

Conclusion

Nous avons commencé ce tutoriel sur l'utilisation de la technologie pour organiser le contenu de l'utilisateur. Dans le domaine de l'informatique contextuelle, dont le but est d'utiliser le contexte comme repère implicite pour enrichir l'interaction appauvrie des humains vers les ordinateurs, facilitant ainsi l'interaction avec les ordinateurs, on parle de marquage automatique. En marquant le contenu avec des données plus significatives et utiles, à la fois pour l'ordinateur et pour nous, nous permettons un filtrage et un traitement plus intelligents..

Nous l'avons vu fréquemment avec du contenu textuel, l'exemple le plus évident étant les filtres anti-spam et, plus récemment, les lecteurs de nouvelles, mais moins le contenu rich media, tel que des photos, de la musique et des vidéos. Des outils tels que le SDK Snapdragon nous permettent d'extraire des fonctionnalités significatives d'un contenu multimédia enrichi, en exposant ses propriétés à l'utilisateur et à l'ordinateur..

Il n’est pas difficile d’imaginer comment vous pouvez étendre notre application pour permettre un filtrage basé sur les sentiments en utilisant un sourire comme caractéristique principale ou une activité sociale en comptant le nombre de visages. Une telle implémentation est visible dans cette fonctionnalité Smart Gallery..