Det var inte så länge sedan att ta bilder var ganska dyrt. Kameror krävde film med begränsad kapacitet och att se resultaten krävde också ytterligare tid och mer pengar. Dessa inneboende begränsningar gjorde att vi var selektiva med de bilder vi tog.
Snabbare fram till idag och dessa begränsningar har minskat tack vare tekniken, men vi står nu inför ett nytt problem, filtrerar, organiserar och avslöjar viktiga foton från de många vi tar.
Det här nya problemet är det som inspirerade denna handledning. I det kommer jag att visa hur vi kan använda nya verktyg för att underlätta användarens liv genom att införa nya sätt att filtrera och organisera innehållet.
För det här projektet ska vi titta på ett annat sätt att filtrera genom din samling bilder. Under tiden lär du dig att integrera och använda Qualcomms Snapdragon SDK för ansiktsbehandling och -känsla.
Vi kommer att göra det möjligt för användaren att filtrera en samling bilder med identitet / identiteter. Samlingen filtreras av identiteter från ett foto som användaren tappar på, vilket visas nedan.
Huvudfokusen för detta inlägg är introduktionen av ansiktsbehandling och erkännande med Qualcomms Snapdragon SDK, samtidigt som man förhoppningsvis indirekt uppmuntrar till nya sätt att tänka och använda härledda metadata från innehåll.
För att undvika att bli fixerad i VVS, har jag skapat en mall som tillhandahåller den grundläggande tjänsten för skanning genom användarens samling bilder och ett galler för att visa bilderna. Vårt mål är att förbättra detta med ovanstående koncept.
I det följande avsnittet kommer vi att granska dessa komponenter kort innan de flyttas till introduktion av Qualcomms Snapdragon SDK.
Som nämnts ovan är vårt mål att fokusera på Snapdragon SDK så jag har skapat ett skelett som har all rörinstallation implementerad. Nedan följer ett diagram och en beskrivning av projektet, som kan laddas ner från GitHub.
Vår data paketet innehåller en implementering av SQLiteOpenHelper
(IdentityGalleryDatabase
) ansvarig för att skapa och hantera vår databas. Databasen kommer att bestå av tre tabeller, en för att fungera som en pekare till medieposten (Foto
), en annan för detekterade identiteter (identitet
), och slutligen förhållandet bordet förbinder identiteter med sina foton (identity_photo
).
Vi använder identitetstabellen för att lagra attributen som ges av Snapdragon SDK, detaljerad i ett senare avsnitt av denna handledning.
Också i datapaketet ingår a provider
(IdentityGalleryProvider
) och Kontrakt
(IdentityGalleryContract
) klass, vilket är inget mer än en standard provider
agerar som ett omslag av SQLiteOpenHelper
klass.
Att ge dig en känsla av hur man interagerar med provider
klass, följande kod tas från testprovider
klass. Som namnet antyder används den för att testa provider
klass.
// ... Fråga för alla bilder Markörmarkör = mContext.getContentResolver () .fråga (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null); // ... Fråga för alla bilder som innehåller någon av identiteterna inom det refererade fotot Cursor cursor = mContext.getContentResolver () .fråga (IdentityGalleryContract.PhotoEntity.buildUriWithReferencePhoto (photoId), null, null, null, null); // ... Query call identities Markörmarkör = mContext.getContentResolver () .fråga (IdentityGalleryContract.IdentityEntity.CONTENT_URI, null, null, null, null); // ... Fråga för alla markörmarkör = mContext.getContentResolver () .fråga (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null);
De service Paketet ansvarar för iterering genom, katalogisering och så småningom bearbetning av bilderna tillgängliga via MediaStore
. Tjänsten i sig sträcker sig IntentService
som ett enkelt sätt att utföra behandlingen på egen tråd. Det faktiska arbetet delegeras till GalleryScanner
, vilken klass kommer vi att sträcka till ansiktsbehandling och erkännande.
Detta GalleryScannerIntentService
är instansierad varje gång Huvudaktivitet
skapas med följande samtal:
@Override protected void onCreate (Bundle savedInstanceState) ... GalleryScannerIntentService.startActionScan (this.getApplicationContext ()); ...
När startade, GalleryScannerIntentService
hämtar det sista skanningsdatumet och skickar detta till konstruktören av GalleryScanner
. Det kallar då skanna
metod för att starta iterering genom innehållet i MediaItem
innehållsleverantör-för objekt efter det senaste skanningsdatumet.
Om du inspekterar skanna
metod för GalleryScanner
klass, märker du att det är ganska ordentligt-inget komplicerat händer här. Metoden behöver söka efter mediefiler som sparas internt (MediaStore.Images.Media.INTERNAL_CONTENT_URI
) och externt (MediaStore.Images.Media.EXTERNAL_CONTENT_URI
). Varje objekt skickas sedan till en krokmetod, vilket är var vi ska placera vår kod för ansiktsbehandling och erkännande.
Private void processImage (ContentValues contentValues, Uri contentUri) släng nya UnsupportedOperationException ("Hook-metoden är för närvarande inte implementerad");
Ytterligare två krokmetoder i GalleryScanner
klassen är tillgänglig för oss (som metodnamnen antyder) för att initiera och de-initialisera FacialProcessing
exempel.
privat tomt initFacialProcessing () kasta UnsupportedOperationException kasta nytt UnsupportedOperationException ("Hook-metoden implementeras inte för närvarande"); privat void deinitFacialProcessing () släng ny UnsupportedOperationException ("Hook-metoden implementeras inte för närvarande");
Det sista paketet är presentationspaketet. Som namnet antyder, är det värd för Aktivitet
klass som ansvarar för att göra vårt galleri. Galleriet är a Gridview
fäst vid a CursorAdapter
. Såsom förklarats ovan kommer att peka på en artikel för att fråga databasen för några foton som innehåller en av identiteterna för det valda fotot. Om du till exempel trycker på ett foto på din vän Lisa och hennes pojkvän Justin, kommer sökningen att filtrera alla foton som innehåller antingen eller både Lisa och Justin.
För att hjälpa utvecklare att göra hårdvaran ser bra ut och göra det rättvisa, har Qualcomm släppt en fantastisk uppsättning SDK: en är Snapdragon SDK. Snapdragon SDK exponerar en optimerad uppsättning funktioner för ansiktsbehandling.
SDK är uppdelad i två delar, ansiktsbehandling och ansiktsigenkänning. Med tanke på att inte alla enheter stöder båda eller någon av dessa funktioner, vilket förmodligen är orsaken till att dessa funktioner är separerade, ger SDK ett enkelt sätt att kontrollera vilka funktioner enheten stöder. Vi kommer att täcka detta mer i detalj senare.
Ansiktsbehandling ger ett sätt att extrahera funktioner från ett foto (i ansiktet) inklusive:
Ansiktsigenkänning, som namnet antyder, ger möjlighet att identifiera personer i ett foto. Det är värt att notera att all bearbetning görs lokalt, i motsats till molnet.
Dessa funktioner kan användas realtid (video / kamera) eller offline (galleri). I vår övning använder vi dessa funktioner offline, men det finns minimala skillnader mellan de båda metoderna.
Läs online-dokumentationen för enheter som stöds för att lära dig mer om ansiktsbehandling och ansiktsigenkänning.
I det här avsnittet kommer vi att fylla i dessa krokmetoder - med överraskande få rader av kod - för att ge vår applikation möjlighet att extrahera ansiktsegenskaper och identifiera personer. För att arbeta tillsammans, ladda ner källan från GitHub och öppna projektet i Android Studio. Alternativt kan du ladda ner det färdiga projektet.
Det första vi behöver göra är att ta tag i SDK från Qualcomms hemsida. Observera att du måste registrera / logga in och godkänna Qualcomms användarvillkor.
När du har laddat ner, arkiverar du innehållet och navigerar till /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Kopiera sd-sdk-ansikts-processing.jar filen till ditt projekt / app / libs / mapp enligt nedanstående.
Efter att ha kopierat Snapdragon SDK högerklickar du på sd-sdk-ansikts-processing.jar och välj Lägg till som bibliotek ... från listan över alternativ.
Detta lägger till biblioteket som ett beroende i din build.gradle filen som visas nedan.
beroenden compile fileTree (dir: 'libs', inkludera: ['* .jar']) kompilera filer ('libs / sd-sdk-facial-processing.jar') kompilera 'com.android.support:support-v13: 20,0,0 '
Det sista steget är att lägga till det ursprungliga biblioteket. För att göra detta, skapa en mapp som heter jniLibs i din / App / src / main / mapp och kopiera armeabi mapp (från SDK-hämtningen) och dess innehåll i den.
Vi är nu redo att genomföra logiken för att identifiera personer som använder API: s funktionalitet. Följande kodfragment hör till i GalleryScanner
klass.
Låt oss först ta itu med initialiseringskrokmetoden.
privat tomt initFacialProcessing () kastar UnsupportedOperationException if (! Facial Processing. FetourSupported (Facial Processing.FeatURE_LIST.FEATURE_FACIAL_PROCESSING) ||! FacialProcessing.isFeatureSupported (Facial Processing.FEATURE_LIST.FEATURE_FACIAL_RECOGNITION)) släng ny UnsupportedOperationException ("Ansiktsbehandling eller erkännande stöds inte på detta anordning"); mFacialProcessing = FacialProcessing.getInstance (); om (mFacialProcessing! = null) mFacialProcessing.setRecognitionConfidence (mConfidenceThreshold); mFacialProcessing.setProcessingMode (FacialProcessing.FP_MODES.FP_MODE_STILL); loadAlbum (); annars släng nytt UnsupportedOperationException ("En instans är redan i bruk");
Vi måste först kontrollera att enheten stöder både ansiktsbehandling och ansiktsigenkänning. Om det inte gör vi kastar en UnsupportedOperationException
undantag.
Därefter tilldelar vi vår lokala referens till FacialProcessing
klass, mFacialProcessing
, till en ny instans med fabriksmetoden getInstance
. Detta kommer att återvända null
om en instans redan används, i vilket fall konsumenten är skyldig att ringa släpp
på den hänvisningen.
Om vi framgångsrikt fått en instans av a FacialProcessing
objekt, vi konfigurerar det genom att först ställa in förtroendet. Vi gör detta med en lokal variabel, vilket är 57
i detta fall från ett intervall från 0 till 100. Förtroendet är ett tröskelvärde när man försöker lösa identiteter. Alla matchningar under denna tröskel anses vara separata identiteter.
När det gäller att bestämma värdet, så långt det jag kan säga är det här en provprocess. Självklart är ju högre tröskeln, desto mer exakt är erkännandet, med avvägningen att öka antalet falska positiva.
Vi ställer sedan in FacialProcessing
läge till FP_MODE_STILL
. Dina alternativ här är antingen FP_MODE_STILL
eller FP_MODE_VIDEO
. Som namnen antyder är en optimerad för stillbilder medan den andra för kontinuerliga ramar, båda med uppenbara användningsfall.
P_MODE_STILL
, som du kan misstänka, ger mer exakta resultat. Men som du kommer se senare, FP_MODE_STILL
underförstås av den metod som vi använder för att bearbeta bilden så att den här linjen kan utelämnas. Jag lade bara till det för fullständighet.
Vi ringer sedan loadAlbum
(metod av GalleryScanner
klass), vilket är vad vi ska titta på nästa.
privat tomt belastningAlbum () SharedPreferences sharedPreferences = mContext.getSharedPreferences (TAG, 0); String arrayOfString = sharedPreferences.getString (KEY_IDENTITY_ALBUM, null); byte [] albumArray = null; om (arrayOfString! = null) String [] splitStringArray = arrayOfString.substring (1, arrayOfString.length () - 1) .split (","); albumArray = ny byte [splitStringArray.length]; för (int i = 0; i < splitStringArray.length; i++) albumArray[i] = Byte.parseByte(splitStringArray[i]); mFacialProcessing.deserializeRecognitionAlbum(albumArray);
Den enda intressanta linjen här är:
mFacialProcessing.deserializeRecognitionAlbum (albumArray);
Dess räknemetod är:
byte [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum ();
En enda FacialProcessing
Exempel kan ses som en session. Tillagda personer (förklaras nedan) lagras lokalt (hänvisat till som "recognition album") inom det exemplet. För att låta ditt album fortsätta över flera sessioner, det vill säga varje gång du får en ny instans behöver du ett sätt att fortsätta och ladda dem.
De serializeRecogntionAlbum
Metoden omvandlar albumet till en byte array och omvänt deserializeRecognitionAlbum
kommer att ladda och analysera ett tidigare lagrat album som en byte array.
Vi vet nu hur man initialiserar FacialProcessing
klass för ansiktsbehandling och erkännande. Låt oss nu vända vårt fokus för att de-initialisera det genom att implementera deinitFacialProcessing
metod.
privat void deinitFacialProcessing () if (mFacialProcessing! = null) saveAlbum (); mFacialProcessing.release (); mFacialProcessing = null;
Som nämnts ovan kan det bara vara en förekomst av FacialProcessing
klass i taget så vi måste se till att vi släpper det innan vi avslutar vår uppgift. Vi gör det via a släpp
metod. Men först gör vi det erkännande albumet kvarstår så att vi kan använda resultaten över flera sessioner. I det här fallet vill vi, när användaren tar eller tar emot nya foton, se till att vi använder de tidigare kända identiteterna för samma personer.
privat 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 ();
Vi är äntligen redo att krossa den slutliga krokmetoden och använda FacialProcessing
klass. Följande kodblock tillhör processImage
metod. Jag har delat upp dem för tydlighet.
Private void processImage (ContentValues contentValues, Uri contentUri) långt fotoRowId = ContentUris.parseId (contentUri); String uriAsString = contentValues.getAsString (GalleryContract.PhotoEntity.COLUMN_URI); Uri uri = Uri.parse (uriAsString); Bitmapp bitmapp = null; prova bitmapp = ImageUtils.getImage (mContext, uri); fånga (IOException e) return; om (bitmapp! = null) // fortsatte nedan (1)
Metoden hänvisar till en instans av ContentValues
klass, som innehåller metadata för den här bilden tillsammans med URI som pekar på bilden. Vi använder detta för att ladda bilden i minnet.
Följande kodbit ska ersätta ovanstående kommentar // fortsatte nedan (1)
.
om (! mFacialProcessing.setBitmap (bitmapp)) return; int numFaces = mFacialProcessing.getNumFaces (); om (numFaces> 0) FaceData [] faceDataArray = mFacialProcessing.getFaceData (); om (faceDataArray == null) Log.w (TAG, contentUri.toString () + "har returnerats en NULL FaceDataArray"); lämna tillbaka; för (int i = 0; iSom nämnts ovan skickar vi först den statiska bilden till
FacialProcessing
exempel viaSetBitmap
metod. Användning av denna metod använder implicit användningen avFP_MODE_STILL
läge. Denna metod returnerarSann
om bilden lyckades bearbetas ochFalsk
om behandlingen misslyckades.Alternativmetoden för bearbetning av strömmande bilder (vanligtvis för kamerabildramar) är:
public boolean setFrame (byte [] yuvData, int frameWidth, int frameHeight, boolean isMirrored, FacialProcessing.PREVIEW_ROTATION_ANGLE rotationAngle)De flesta parametrarna är uppenbara. Du måste passera om ramen är vänd (det är vanligtvis nödvändigt för den främre kameran) och om någon rotation har tillämpats (vanligtvis inställd via
setDisplayOrientation
Metod av aKamera
exempel).Vi frågar sedan för antalet ansikten som detekterats och fortsätter endast om minst en finns. De
getFaceData
Metoden returnerar detaljerna för varje detekterat ansikte som en uppsättning avFaceData
objekt, var varderaFaceData
Objektet inkapslar ansiktsfunktioner, inklusive:
- ansiktsgräns (
FACE_RECT
)- ansikte, mun och ögonplatser (
FACE_COORDINATES
)- kontur av ansiktet (
FACE_CONTOUR
)- grad av leende (
FACE_SMILE
)- ögonriktning (
FACE_GAZE
)- flagga som indikerar om ett öga (eller båda ögonen) blinkar (
FACE_BLINK
)- yaw, pitch och ansiktsrull (
FACE_ORIENTATION
)- genererad eller härledd identifiering (
FACE_IDENTIFICATION
)Det finns en överbelastning för denna metod som tar en uppsättning enummar (som beskrivits ovan) för att funktionspunkter ska inkluderas, avlägsnande / minimering av redundanta beräkningar.
offentlig FaceData [] getFaceData (java.util.EnumSetdataSet) kastar java.lang.IllegalArgumentException Vi fortsätter nu med att inspektera
FaceData
motsätta sig identiteten och funktionerna. Låt oss först se hur ansiktsigenkänning är klar.Följande kodbit ska ersätta ovanstående kommentar
// fortsatte nedan (2)
.int personId = faceData.getPersonId (); om (personId == FacialProcessingConstants.FP_PERSON_NOT_REGISTERED) personId = mFacialProcessing.addPerson (i); annat om (mFacialProcessing.updatePerson (personId, i)! = FacialProcessingConstants.FP_SUCCESS) // TODO hantera fel lång identityRowId = getOrInsertPerson (personId); // fortsatte nedan (3)Vi begär först den tilldelade personens ID via
getPersonId
metod. Detta kommer att återvända-111
(FP_PERSON_NOT_REGISTERED
) om det inte finns någon identitet i det aktuella inlägget, annars returnerar du en matchande persons ID från det laddade albumet.Om det inte finns någon identitet, lägger vi till det via
addPerson
metod förFacialProcessing
objekt, passerar det indexet förFaceData
objekt vi inspekterar för närvarande. Metoden returnerar det tilldelade person-idet om det lyckades, annars returnerar ett fel. Detta inträffar när du försöker lägga till en identitet som redan finns.Alternativt, när personen matchades med en identitet lagrad i vårt laddade album, kallar vi
FacialProcessing
objektupdatePerson
metod, passerar den befintliga id och index förFaceData
Artikel. Att lägga till en person flera gånger ökar erkännandeprestandan. Du kan lägga till upp till tio ansikten för en enda person.Den slutliga raden returnerar helt enkelt det associerade ID-numret från vår databas och infogar det om personidentifikationen inte existerar.
Det visas inte ovan, men
FaceData
Exempel exponerar metodengetRecognitionConfidence
för att återvända igenkänningsförtroendet (0 till 100). Beroende på dina behov kan du använda detta för att påverka flödet.Det sista stycket visar hur man frågar var och en av de andra funktionerna från
FaceData
exempel. I denna demo använder vi inte dem, men med lite fantasi är jag säker på att du kan tänka på sätt att använda dem bra.Följande kodbit ska ersätta ovanstående kommentar
// fortsatte nedan (3)
.int smileValue = faceData.getSmileValue (); int vänsterEyeBlink = 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);Det kompletterar bearbetningskoden. Om du återvänder till galleriet och trycker på en bild ska du se att det filtrerar bort några foton som inte innehåller några personer som identifierats i det valda fotot.
Slutsats
Vi startade den här handledningen om hur tekniken kan användas för att organisera användarens innehåll. I kontextmedveten databehandling, vars mål är att använda kontext som en implicit cue för att berika den fattiga interaktionen från människor till datorer, vilket gör det lättare att interagera med datorer, kallas detta auto-tagging. Genom att markera innehåll med mer meningsfull och användbar data - både för datorn och oss - tillåter vi mer intelligent filtrering och bearbetning.
Vi har sett det här bruket ofta med textinnehåll, det mest uppenbara exemplet är spamfilter och, mer nyligen, nyhetsläsare, men mindre med rich media-innehåll, till exempel foton, musik och video. Verktyg som Snapdragon SDK ger oss möjlighet att extrahera meningsfulla funktioner från rich media, exponera sina egenskaper för användaren och datorn.
Det är inte svårt att föreställa sig hur du kan förlänga vår ansökan för att tillåta filtrering baserat på känslor genom att använda ett leende som huvudfunktion eller social aktivitet genom att räkna antalet ansikten. Ett sådant genomförande kan ses i den här Smart Gallery-funktionen.