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..
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.
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.
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.
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:
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.
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.
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.
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.
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 ();
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; iZoals hierboven vermeld, geven we eerst de statische afbeelding door aan de
FacialProcessing
bijvoorbeeld via deSetBitmap
methode. Het gebruik van deze methode maakt impliciet gebruik van deFP_MODE_STILL
modus. Deze methode retourneertwaar
als de afbeelding is verwerkt envals
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 aCamera
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 vanFaceData
objecten, waar elkFaceData
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.EnumSetdataSet) 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 deFacialProcessing
object, het doorgeven van de index van deFaceData
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
voorwerpenupdatePerson
methode, het doorgeven van de bestaande id en index van deFaceData
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 methodegetRecognitionConfidence
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.