Identificeren van mensen met de Snapdragon SDK van Qualcomm

Het was nog niet zo lang geleden dat het fotograferen behoorlijk duur was. Camera's vereisten film met beperkte capaciteit en het zien van de resultaten vereiste ook extra tijd en meer geld. Deze inherente beperkingen zorgden ervoor dat we selectief waren met de foto's die we maakten.

Snel vooruit naar vandaag en deze beperkingen zijn verminderd dankzij technologie, maar we staan ​​nu voor een nieuw probleem, het filteren, organiseren en onthullen van belangrijke foto's van de vele die we nemen.

Dit nieuwe probleem heeft deze tutorial geïnspireerd. Hierin zal ik laten zien hoe we nieuwe hulpmiddelen kunnen gebruiken om het leven van de gebruiker te vergemakkelijken door nieuwe manieren te introduceren voor het filteren en organiseren van onze inhoud..

1. Concept

Voor dit project gaan we op zoek naar een andere manier om door uw verzameling foto's te filteren. Onderweg leert u hoe u de Snapdragon SDK van Qualcomm kunt integreren en gebruiken voor gezichtsbehandeling en -herkenning.

We zullen de gebruiker in staat stellen om een ​​verzameling foto's te filteren op identiteit / identiteiten. De verzameling wordt gefilterd op identiteit van een foto waarop de gebruiker tikt, zoals hieronder wordt weergegeven.

2. Overzicht

De focus van deze post ligt op de introductie van gezichtsbehandeling en -herkenning met behulp van de Snapdragon SDK van Qualcomm, terwijl hopelijk-indirect nieuwe manieren van denken worden aangemoedigd en afgeleide metadata van inhoud worden gebruikt.

Om vastlopen in het sanitair te voorkomen, heb ik een sjabloon gemaakt die de basisdienst biedt voor het scannen door de verzameling foto's van de gebruiker en een raster voor het weergeven van de foto's. Ons doel is om dit te verbeteren met het hierboven voorgestelde concept.

In het volgende gedeelte zullen we deze componenten kort bespreken voordat we verder gaan met de introductie van de Snapdragon SDK van Qualcomm.

3. Skelet

Zoals hierboven vermeld, is ons doel om ons te concentreren op de Snapdragon SDK, dus ik heb een skelet gemaakt waarin al het loodgieterswerk is geïmplementeerd. Hieronder vindt u een schema en een beschrijving van het project, dat u kunt downloaden van GitHub.

Onze gegevens pakket bevat een implementatie van SQLiteOpenHelper (IdentityGalleryDatabase) verantwoordelijk voor het maken en beheren van onze database. De database zal bestaan ​​uit drie tabellen, een om op te treden als een verwijzing naar de media-record (foto), een andere voor gedetecteerde identiteiten (identiteit) en ten slotte de relatietabel die identiteiten verbindt met hun foto's (identity_photo).

We gebruiken de identiteitstabel om de kenmerken van de Snapdragon-SDK op te slaan, die in een later gedeelte van deze tutorial worden beschreven..

Ook opgenomen in het datapakket zijn a leverancier (IdentityGalleryProvider) en Contract (IdentityGalleryContract) klasse, die niets meer is dan een standaard leverancier fungerend als een wikkel van de SQLiteOpenHelper klasse.

Om u een idee te geven van hoe u moet omgaan met de leverancier klasse, de volgende code is overgenomen van de testProvider klasse. Zoals de naam al doet vermoeden, wordt het gebruikt voor het testen van de leverancier klasse. 

// ... Vraag voor alle foto's Cursorcursor = mContext.getContentResolver (). Query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null); // ... Zoekopdracht voor alle Foto's met een van de identiteiten binnen de verwezencursor Cursorcursor = mContext.getContentResolver (). Query (IdentityGalleryContract.PhotoEntity.buildUriWithReferencePhoto (photoId), null, null, null, null); // ... Identificatie van query-id's Cursorcursor = mContext.getContentResolver (). Query (IdentityGalleryContract.IdentityEntity.CONTENT_URI, null, null, null, null); // ... Query voor alle cursor Cursor = mContext.getContentResolver (). Query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null);

De service pakket is verantwoordelijk voor het itereren, catalogiseren en uiteindelijk verwerken van de afbeeldingen die beschikbaar zijn via de Mediastore. De service zelf breidt het IntentService als een eenvoudige manier om de verwerking op zijn eigen thread uit te voeren. Het eigenlijke werk is gedelegeerd aan de GalleryScanner, dat is de klasse die we gaan gebruiken voor gezichtsbehandeling en -herkenning.

Deze GalleryScannerIntentService wordt geïnstantieerd elke keer dat de Hoofdactiviteit is gemaakt met de volgende oproep:

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

Toen gestart, GalleryScannerIntentService haalt de laatste scandatum op en geeft deze door aan de constructor van de GalleryScanner. Vervolgens wordt het scannen methode om te beginnen met itereren door de inhoud van de MediaItem content provider-voor items na de laatste scandatum.

Als u de scannen methode van de GalleryScanner Klasse, je zult merken dat het vrij uitgebreid is - niets ingewikkelds gebeurt hier. De methode moet zoeken naar mediabestanden die intern zijn opgeslagen (MediaStore.Images.Media.INTERNAL_CONTENT_URI) en extern (MediaStore.Images.Media.EXTERNAL_CONTENT_URI). Elk item wordt vervolgens doorgegeven aan een haakmethode. Hier plaatsen we onze code voor gezichtsbehandeling en -herkenning.

private void processImage (ContentValues ​​contentValues, Uri contentUri) gooi new UnsupportedOperationException ("Hook-methode is momenteel niet geïmplementeerd"); 

Nog twee haakmethoden in de GalleryScanner class zijn beschikbaar voor ons (zoals de methode-namen suggereren) om het te initialiseren en de-initialiseren FacialProcessing aanleg.

private void initFacialProcessing () gooit UnsupportedOperationException throw new UnsupportedOperationException ("Hook-methode is momenteel niet geïmplementeerd");  private void deinitFacialProcessing () throw new UnsupportedOperationException ("Hook-methode is momenteel niet geïmplementeerd"); 

Het laatste pakket is het presentatiepakket. Zoals de naam al doet vermoeden, herbergt het de Activiteit klas die verantwoordelijk is voor het weergeven van onze galerij. De galerij is een Rasterweergave gekoppeld aan een CursorAdapter. Zoals hierboven uitgelegd, tikt u op een item om de database te vragen naar foto's die een van de identiteiten van de geselecteerde foto bevatten. Als u bijvoorbeeld op een foto van uw vriendin Lisa en haar vriend Justin tikt, filtert de query alle foto's die een of beide Lisa en Justin bevatten.

4. Qualcomm's Snapdragon SDK

Om ontwikkelaars te helpen hun hardware er goed uit te laten zien en recht te doen, heeft Qualcomm een ​​geweldige set SDK's uitgebracht, waaronder de Snapdragon-SDK. De Snapdragon SDK onthult een geoptimaliseerde set functies voor gezichtsbehandeling.

De SDK is grofweg opgesplitst in twee delen, gezichtsbehandeling en gezichtsherkenning. Aangezien niet alle apparaten beide of een van deze functies ondersteunen, wat waarschijnlijk de reden is dat deze functies gescheiden zijn, biedt de SDK een eenvoudige manier om te controleren welke functies het apparaat ondersteunt. We zullen dit later in meer detail bespreken.

De gezichtsbehandeling biedt een manier om functies uit een foto (van een gezicht) te extraheren, waaronder:  

  • Knipperdetectie: Meet hoe open elk oog is.
  • Gaze Tracking: Beoordeel waar het onderwerp naar op zoek is.
  • Smile-waarde: Schat de mate van de glimlach.
  • Gezichtsoriëntatie: Volg de gier, hoogte en rol van het hoofd.

Gezichtsherkenning biedt, zoals de naam al doet vermoeden, de mogelijkheid mensen in een foto te identificeren. Het is vermeldenswaard dat alle verwerking lokaal wordt uitgevoerd, in tegenstelling tot de cloud.

Deze functies kunnen in realtime (video / camera) of offline (galerij) worden gebruikt. In onze oefening gebruiken we deze functies offline, maar er zijn minimale verschillen tussen de twee benaderingen.

Raadpleeg de online documentatie voor ondersteunde apparaten voor meer informatie over gezichtsbehandeling en gezichtsherkenning.

5. Gezichtsverwerking en -herkenning toevoegen

In deze sectie zullen we die haakmethoden invullen - met verrassend weinig regels code - om onze applicatie de mogelijkheid te bieden om gezichtseigenschappen te extraheren en mensen te identificeren. Om mee te werken, download de bron van GitHub en open het project in Android Studio. Als alternatief kunt u het voltooide project downloaden.

Stap 1: De Snapdragon-SDK installeren

Het eerste dat we moeten doen is de SDK pakken van de website van Qualcomm. Let op: u moet zich registreren / inloggen en akkoord gaan met de algemene voorwaarden van Qualcomm.

Download na het downloaden de inhoud en navigeer naar /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Kopieer de sd-sdk-facial-processing.jar bestand in uw project / app / libs / map zoals hieronder getoond.

Na het kopiëren van de Snapdragon-SDK, klikt u met de rechtermuisknop op sd-sdk-facial-processing.jar en selecteer Voeg toe als bibliotheek ... uit de lijst met opties.

Hiermee wordt de bibliotheek als een afhankelijkheid in uw bibliotheek toegevoegd build.gradle bestand zoals hieronder getoond.

afhankelijkheden compile fileTree (dir: 'libs', include: ['* .jar']) compileer bestanden ('libs / sd-sdk-facial-processing.jar') compileer 'com.android.support:support-v13: 20.0.0 '

De laatste stap is om de native library toe te voegen. Hiertoe maakt u een map met de naam jniLibs in uw / App / src / main / map en kopieer de armeabi map (van de SDK-download) en de inhoud erin.

We zijn nu klaar om de logica te implementeren om mensen te identificeren die de functionaliteit van de API gebruiken. De volgende codefragmenten horen thuis in de GalleryScanner klasse.

Stap 2: Initialisatie

Laten we eerst de initialisatiehaakmethode aanpakken. 

private void initFacialProcessing () gooit UnsupportedOperationException if (! FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_PROCESSING) ||! FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_RECOGNITION)) throw new UnsupportedOperationException ("Gezichtsverwerking of -herkenning wordt niet ondersteund op deze apparaat");  mFacialProcessing = FacialProcessing.getInstance (); if (mFacialProcessing! = null) mFacialProcessing.setRecognitionConfidence (mConfidenceThreshold); mFacialProcessing.setProcessingMode (FacialProcessing.FP_MODES.FP_MODE_STILL); loadAlbum ();  else gooi nieuwe UnsupportedOperationException ("Een exemplaar is al in gebruik");

We moeten eerst controleren of het apparaat zowel gezichtsbehandeling als gezichtsherkenning ondersteunt. Als dat niet het geval is, gooien we een UnsupportedOperationException uitzondering.

Daarna wijzen we onze lokale referentie toe van de FacialProcessing klasse, mFacialProcessing, naar een nieuwe instantie met behulp van de fabrieksmethode getInstance. Dit komt terug nul als een exemplaar al in gebruik is, in welk geval de consument moet bellen vrijlating op die referentie.

Als we met succes een exemplaar van een FacialProcessing object, we configureren het door eerst het vertrouwen in te stellen. We doen dit met behulp van een lokale variabele, dat is 57 in dit geval van een bereik van 0 tot 100. Het vertrouwen is een drempelwaarde bij het oplossen van identiteiten. Alle overeenkomsten onder deze drempelwaarde worden als afzonderlijke identiteiten beschouwd.

In termen van het bepalen van de waarde, voor zover ik kan vertellen, is dit een proces van vallen en opstaan. Het is duidelijk dat hoe hoger de drempel, hoe nauwkeuriger de herkenning, met de wisselwerking van het verhogen van het aantal valse positieven.

We hebben vervolgens de FacialProcessing modus naar FP_MODE_STILL. Je opties hier zijn ook FP_MODE_STILL of FP_MODE_VIDEO. Zoals de namen suggereren, is de ene geoptimaliseerd voor stilstaande beelden en de andere voor doorlopende frames, beide met voor de hand liggende use-cases.

P_MODE_STILL, zoals je misschien vermoedt, geeft meer nauwkeurige resultaten. Maar zoals je later zult zien, FP_MODE_STILL wordt geïmpliceerd door de methode die we gebruiken om de afbeelding te verwerken, zodat deze regel kan worden weggelaten. Ik heb het alleen voor de volledigheid toegevoegd.

We bellen dan loadAlbum (methode van de GalleryScanner klas), waar we hierna naar zullen kijken.

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 = nieuwe byte [splitStringArray.length]; voor (int i = 0; i < splitStringArray.length; i++)  albumArray[i] = Byte.parseByte(splitStringArray[i]);  mFacialProcessing.deserializeRecognitionAlbum(albumArray);  

De enige interessante regel hier is:

mFacialProcessing.deserializeRecognitionAlbum (albumArray);

De tegenmethode is:

byte [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum ();

Een FacialProcessing bijvoorbeeld kan worden gezien als een sessie. Toegevoegde personen (hieronder toegelicht) worden binnen dat geval lokaal opgeslagen (het "herkenningsalbum"). Als u wilt dat uw album blijft bestaan ​​in meerdere sessies, moet u telkens wanneer u een nieuw exemplaar krijgt een manier om door te gaan en ze te laden.

De serializeRecogntionAlbum methode converteert het album naar een bytearray en omgekeerd deserializeRecognitionAlbum zal een eerder opgeslagen album laden en ontleden als een bytearray.

Stap 3: De-initialisatie

We weten nu hoe we het moeten initialiseren FacialProcessing klas voor gezichtsbehandeling en herkenning. Laten we nu onze aandacht richten op het de-initialiseren door het deinitFacialProcessing methode.

private void deinitFacialProcessing () if (mFacialProcessing! = null) saveAlbum (); mFacialProcessing.release (); mFacialProcessing = null; 

Zoals hierboven vermeld, kan er slechts één instantie van de FacialProcessing klasse tegelijk, dus we moeten ervoor zorgen dat we deze vrijgeven voordat we onze taak beëindigen. We doen dit via een vrijlating methode. Maar eerst maken we het herkenningsalbum aan zodat we de resultaten over meerdere sessies kunnen gebruiken. In dit geval willen we ervoor zorgen dat we de eerder herkende identiteiten voor dezelfde mensen gebruiken wanneer de gebruiker nieuwe foto's maakt of ontvangt.

private void 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 (); 

Stap 4: De afbeelding verwerken

We zijn eindelijk klaar om de laatste haakmethode uit te werken en de FacialProcessing klasse. De volgende codeblokken behoren tot de ProcessImage methode. Ik heb ze voor de duidelijkheid opgesplitst.

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 = nul; try bitmap = ImageUtils.getImage (mContext, uri);  catch (IOException e) terug;  if (bitmap! = null) // wordt hieronder voortgezet (1)

De methode neemt een verwijzing naar een instantie van de ContentValues klasse, die de metagegevens voor deze afbeelding bevat, samen met de URI die naar de afbeelding wijst. We gebruiken dit om de afbeelding in het geheugen te laden.

Het volgende codefragment is bedoeld om de bovenstaande opmerking te vervangen // vervolg hieronder (1).

if (! mFacialProcessing.setBitmap (bitmap)) ga terug;  int numFaces = mFacialProcessing.getNumFaces (); if (numFaces> 0) FaceData [] faceDataArray = mFacialProcessing.getFaceData (); if (faceDataArray == null) Log.w (TAG, contentUri.toString () + "is geretourneerd een NULL FaceDataArray"); terug te keren;  voor (int i = 0; i

Zoals hierboven vermeld, geven we eerst de statische afbeelding door aan de FacialProcessing bijvoorbeeld via de SetBitmap methode. Het gebruik van deze methode maakt impliciet gebruik van de FP_MODE_STILL modus. Deze methode retourneert waar als de afbeelding is verwerkt en vals als de verwerking is mislukt.

De alternatieve methode voor het verwerken van streamingafbeeldingen (meestal voor cameravoorbeeldkaders) is:

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

De meeste parameters liggen voor de hand. Je moet wel aangeven of het frame wordt omgedraaid (dit is meestal nodig voor de camera aan de voorzijde) en of er een rotatie is toegepast (meestal ingesteld via de setDisplayOrientation methode van a Camera aanleg).

We vragen vervolgens naar het aantal gedetecteerde gezichten en gaan alleen verder als er tenminste één is gevonden. De getFaceData methode retourneert de details voor elk gedetecteerd gezicht als een array van FaceData objecten, waar elk FaceData object bevat gelaatstrekken, waaronder:

  • gezicht grens (FACE_RECT)
  • gezichts-, mond- en ooglocaties (FACE_COORDINATES)
  • contour van het gezicht (FACE_CONTOUR)
  • mate van glimlach (FACE_SMILE)
  • richting van ogen (FACE_GAZE)
  • vlag die aangeeft of een van beide ogen (of beide ogen) knippert (FACE_BLINK)
  • yaw, pitch en rol van het gezicht (FACE_ORIENTATION)
  • gegenereerde of afgeleide identificatie (FACE_IDENTIFICATION)

Er is een overbelasting van deze methode die een set van enums (zoals hierboven beschreven) vereist voor het opnemen van feature points, het verwijderen / minimaliseren van overtollige berekeningen.

openbare FaceData [] getFaceData (java.util.EnumSet dataSet) genereert java.lang.IllegalArgumentException 

We gaan nu verder met het inspecteren van de FaceData object om de identiteit en functies uit te pakken. Laten we eerst eens kijken hoe gezichtsherkenning wordt gedaan.

Het volgende codefragment is bedoeld om de bovenstaande opmerking te vervangen // vervolg hieronder (2).

int personId = faceData.getPersonId (); if (personId == FacialProcessingConstants.FP_PERSON_NOT_REGISTERED) personId = mFacialProcessing.addPerson (i);  else if (mFacialProcessing.updatePerson (personId, i)! = FacialProcessingConstants.FP_SUCCESS) // TODO handle error long identityRowId = getOrInsertPerson (personId); // vervolg hieronder (3)

We vragen eerst om de toegewezen persoon-ID via de getPersonId methode. Dit komt terug -111 (FP_PERSON_NOT_REGISTERED) als er geen identiteit bestaat in het album dat op dat moment is geladen, anders wordt de id van een overeenkomende persoon uit het geladen album teruggezonden.

Als er geen identiteit bestaat, voegen we deze toe via de addPerson methode van de FacialProcessing object, het doorgeven van de index van de FaceData product dat we momenteel inspecteren. De methode retourneert de id van de toegewezen persoon als deze is geslaagd, anders wordt een fout geretourneerd. Dit gebeurt wanneer u probeert een bestaande identiteit toe te voegen.

Als de persoon is gekoppeld aan een identiteit die is opgeslagen in ons geladen album, kunnen we ook de FacialProcessing voorwerpen updatePerson methode, het doorgeven van de bestaande id en index van de FaceData item. Door een persoon meerdere keren toe te voegen, verbetert de herkenningsprestatie. U kunt maximaal tien gezichten toevoegen voor één persoon.

De laatste regel retourneert simpelweg de bijbehorende identiteits-id uit onze database en voegt deze toe als de persoon-ID nog niet bestaat.

Het wordt niet hierboven getoond, maar de FaceData instantie onthult de methode getRecognitionConfidence voor het teruggeven van het herkenningsvertrouwen (0 tot 100). Afhankelijk van uw behoeften, kunt u dit gebruiken om de flow te beïnvloeden.

Het laatste fragment laat zien hoe elk van de andere functies van de FaceData aanleg. In deze demo maken we er geen gebruik van, maar met een beetje fantasie weet ik zeker dat je manieren kunt bedenken om ze goed te gebruiken.

Het volgende codefragment is bedoeld om de bovenstaande opmerking te vervangen // vervolg hieronder (3).

int smileValue = faceData.getSmileValue (); int left EyeBlink = 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); 

Dat voltooit de verwerkingscode. Als je terugkeert naar de galerij en op een afbeelding tikt, zou je moeten zien dat het alle foto's filtert die geen personen bevatten die in de geselecteerde foto zijn geïdentificeerd.

Conclusie

We zijn deze tutorial begonnen met praten over hoe technologie kan worden gebruikt om de inhoud van de gebruiker te organiseren. In context aware computing, waarvan het doel is om context te gebruiken als een impliciete cue om de verarmde interactie van mens tot computer te verrijken, waardoor het gemakkelijker wordt om met computers te communiceren, wordt dit autotagging genoemd. Door content te markeren met zinvollere en nuttigere gegevens, zowel voor de computer als voor ons, zorgen we voor intelligentere filtering en verwerking.

We hebben dit veelvuldig gebruikt met tekstuele content. Het meest voor de hand liggende voorbeeld zijn spamfilters en, recenter, nieuwslezers, maar minder met rijke media-inhoud, zoals foto's, muziek en video. Hulpprogramma's zoals de Snapdragon-SDK bieden ons de mogelijkheid om zinvolle functies uit rijke media te extraheren en de eigenschappen ervan aan de gebruiker en de computer bloot te stellen.

Het is niet moeilijk je voor te stellen hoe je onze applicatie kunt uitbreiden om filteren op basis van sentiment mogelijk te maken door een glimlach te gebruiken als belangrijkste kenmerk of sociale activiteit door het aantal gezichten te tellen. Een dergelijke implementatie is te zien in deze functie Smart Gallery.