Det var ikke så lenge siden at fotografering var ganske dyrt. Kameraer krevde film med begrenset kapasitet og å se resultatene krevde også ekstra tid og mer penger. Disse inneboende begrensninger sørget for at vi var selektive med bildene vi tok.
Rask frem til i dag, og disse begrensningene har blitt redusert takket være teknologien, men vi står nå overfor et nytt problem, filtrerer, organiserer og avdekker viktige bilder fra de mange vi tar.
Dette nye problemet er det som inspirerte denne opplæringen. I det vil jeg demonstrere hvordan vi kan bruke nye verktøy for å gjøre brukerens liv lettere ved å introdusere nye måter å filtrere og organisere innholdet på.
For dette prosjektet skal vi se på en annen måte å filtrere gjennom samlingen av bilder. Underveis lærer du hvordan du integrerer og bruker Qualcomms Snapdragon SDK for ansiktsbehandling og anerkjennelse..
Vi vil gjøre det mulig for brukeren å filtrere en samling bilder etter identitet / identiteter. Samlingen vil bli filtrert av identiteter fra et bilde som brukeren tapper på, som vist nedenfor.
Hovedfokus for dette innlegget er introduksjonen av ansiktsbehandling og anerkjennelse ved hjelp av Qualcomms Snapdragon SDK, mens forhåpentligvis indirekte oppfordrer nye måter å tenke på og bruke avledede metadata fra innhold.
For å unngå å bli fikset i VVS, har jeg opprettet en mal som gir grunnleggende tjenesten for å skanne gjennom brukerens samling av bilder og et rutenett for visning av bildene. Vårt mål er å forbedre dette med det ovenfor foreslåtte konseptet.
I det følgende avsnittet vil vi kort gjennomgå disse komponentene før du går videre til introduksjon av Qualcomms Snapdragon SDK.
Som nevnt ovenfor er målet vårt å fokusere på Snapdragon SDK, så jeg har laget et skjelett som har implementert alle rørleggerarbeid. Nedenfor er et diagram og en beskrivelse av prosjektet, som er tilgjengelig for nedlasting fra GitHub.
Våre data pakken inneholder en implementering av SQLiteOpenHelper
(IdentityGalleryDatabase
) ansvarlig for å opprette og administrere vår database. Databasen vil bestå av tre tabeller, en til å fungere som en peker til medieposten (bilde
), en annen for detekterte identiteter (identitet
), og til slutt forholdet tabellen forbinder identiteter med sine bilder (identity_photo
).
Vi vil bruke identitetstabellen til å lagre attributter gitt av Snapdragon SDK, detaljert i en senere del av denne opplæringen.
Også inkludert i datapakken er a Forsørger
(IdentityGalleryProvider
) og Kontrakt
(IdentityGalleryContract
) klasse, noe som ikke er noe mer enn en standard Forsørger
fungerer som en wrapper av SQLiteOpenHelper
klasse.
Å gi deg en følelse av hvordan du skal samhandle med Forsørger
klasse, er følgende kode hentet fra TestProvider
klasse. Som navnet antyder, brukes det til å teste Forsørger
klasse.
// ... Forespørsel for alle bilder Markørmarkør = mContext.getContentResolver () .query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null); // ... Forespørsel for alle bilder som inneholder noen av identitetene i det refererte bildet Cursor cursor = mContext.getContentResolver () .query (IdentityGalleryContract.PhotoEntity.buildUriWithReferencePhoto (photoId), null, null, null, null); // ... Query call identiteter Markørmarkør = mContext.getContentResolver () .query (IdentityGalleryContract.IdentityEntity.CONTENT_URI, null, null, null, null); // ... Forespørsel for alle markørmarkør = mContext.getContentResolver () .query (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null);
De service pakken er ansvarlig for iterating gjennom, katalogisering, og til slutt behandling av bildene tilgjengelig via Media
. Tjenesten i seg selv utvider IntentService
som en enkel måte å utføre behandlingen på sin egen tråd. Det faktiske arbeidet er delegert til GalleryScanner
, som er klassen vil vi strekke oss til ansiktsbehandling og anerkjennelse.
Dette GalleryScannerIntentService
er instantiated hver gang Hoved aktivitet
er opprettet med følgende anrop:
@Override protected void onCreate (Bundle savedInstanceState) ... GalleryScannerIntentService.startActionScan (this.getApplicationContext ()); ...
Når startet, GalleryScannerIntentService
henter siste skanningsdato og overfører dette til konstruktøren til GalleryScanner
. Det kaller da skanning
metode for å starte iterating gjennom innholdet i MediaItem
innholdsleverandør-for elementer etter siste skanningsdato.
Hvis du inspiserer skanning
metode av GalleryScanner
klassen, vil du legge merke til at det er ganske ordentlig-ingenting komplisert skjer her. Metoden må spørre om mediefiler som er lagret internt (MediaStore.Images.Media.INTERNAL_CONTENT_URI
) og eksternt (MediaStore.Images.Media.EXTERNAL_CONTENT_URI
). Hvert element blir så sendt til en krokmetode, som er der vi skal plassere vår kode for ansiktsbehandling og anerkjennelse.
Private void processImage (ContentValues contentValues, Uri contentUri) kaste nye UnsupportedOperationException ("Hook-metoden er for tiden ikke implementert");
En annen to krokmetoder i GalleryScanner
klassen er tilgjengelig for oss (som metodens navn antyder) for å initialisere og de-initialisere FacialProcessing
forekomst.
privat tomt initFacialProcessing () kaster UnsupportedOperationException kaster ny UnsupportedOperationException ("Hook-metoden er for tiden ikke implementert"); private void deinitFacialProcessing () kaste nye UnsupportedOperationException ("Hook-metoden er for tiden ikke implementert");
Den endelige pakken er presentasjonspakken. Som navnet antyder, er det vert for Aktivitet
Klassen er ansvarlig for gjengivelsen av vårt galleri. Galleriet er en Rutenett visning
festet til a CursorAdapter
. Som beskrevet ovenfor, vil du trykke på et element for å spørre databasen for bilder som inneholder en av identitetene til det valgte bildet. Hvis du for eksempel klikker på et bilde av vennen din Lisa og kjæresten Justin, vil søket filtrere alle bildene som inneholder enten eller Lisa og Justin.
For å hjelpe utviklere til å gjøre maskinvaren ser bra ut og gjøre det riktig, har Qualcomm gitt ut et fantastisk sett med SDKer, en som er Snapdragon SDK. Snapdragon SDK avslører et optimert sett med funksjoner for ansiktsbehandling.
SDK er splittet i to deler, ansiktsbehandling og ansiktsgjenkjenning. Gitt at ikke alle enheter støtter begge eller noen av disse funksjonene, noe som sannsynligvis er årsaken til at disse funksjonene er skilt, gir SDK en enkel måte å kontrollere hvilke funksjoner enheten støtter. Vi vil dekke dette mer detaljert senere.
Ansiktsbehandling gir en måte å trekke ut funksjoner fra et bilde (av et ansikt), inkludert:
Ansiktsgjenkjenning, som navnet antyder, gir muligheten til å identifisere personer i et bilde. Det er verdt å merke seg at all behandling foregår lokalt - i motsetning til skyen.
Disse funksjonene kan brukes i sanntid (video / kamera) eller frakoblet (galleri). I vår øvelse bruker vi disse funksjonene frakoblet, men det er små forskjeller mellom de to tilnærmingene.
Se online dokumentasjonen for støttede enheter for å lære mer om ansiktsbehandling og ansiktsgjenkjenning.
I denne delen fyller vi inn disse krokemetodene - med overraskende få linjer med kode - for å gi vår applikasjon muligheten til å trekke ut ansiktsegenskaper og identifisere personer. For å jobbe sammen, last ned kilden fra GitHub og åpne prosjektet i Android Studio. Alternativt kan du laste ned det gjennomførte prosjektet.
Det første vi må gjøre er å ta tak i SDK fra Qualcomms nettsted. Merk at du må registrere deg / logge inn og godta Qualcomms vilkår og betingelser.
Når du er lastet ned, arkiverer du innholdet og navigerer til /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Kopier sd-SDK-facial-processing.jar filen inn i prosjektets / app / libs / mappe som vist under.
Etter å ha kopiert Snapdragon SDK, høyreklikker du på sd-SDK-facial-processing.jar og velg Legg til som bibliotek ... fra listen over alternativer.
Dette vil legge til biblioteket som en avhengighet i din build.gradle filen som vist nedenfor.
avhengigheter compile fileTree (dir: 'libs', inkluderer: ['* .jar']) kompilere filer ('libs / sd-sdk-facial-processing.jar') kompilere 'com.android.support:support-v13: 20,0,0 '
Det siste trinnet er å legge til det opprinnelige biblioteket. For å gjøre dette, opprett en mappe som heter jniLibs i din / App / src / main / mappe og kopiere armeabi mappe (fra SDK-nedlastingen) og innholdet i den.
Vi er nå klare til å implementere logikken for å identifisere folk som bruker funksjonaliteten til API. Følgende kodestykker tilhører GalleryScanner
klasse.
La oss først takle initialiserings krokmetoden.
privat tomt initFacialProcessing () kaster UnsupportedOperationException hvis (FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_PROCESSING) ||! FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_RECOGNITION)) kaste ny UnsupportedOperationException ("Ansiktsbehandling eller anerkjennelse støttes ikke på dette enhet"); mFacialProcessing = FacialProcessing.getInstance (); hvis (mFacialProcessing! = null) mFacialProcessing.setRecognitionConfidence (mConfidenceThreshold); mFacialProcessing.setProcessingMode (FacialProcessing.FP_MODES.FP_MODE_STILL); loadAlbum (); ellers kast nytt UnsupportedOperationException ("En forekomst er allerede i bruk");
Vi må først kontrollere at enheten støtter både ansiktsbehandling og ansiktsgjenkjenning. Hvis det ikke gjør det, kaster vi en UnsupportedOperationException
unntak.
Deretter tilordner vi vår lokale referanse til FacialProcessing
klasse, mFacialProcessing
, til en ny forekomst ved hjelp av fabrikkmetoden getInstance
. Dette kommer tilbake null
hvis en forekomst allerede er i bruk, da forbrukeren må ringe utgivelse
på den referansen.
Hvis vi har fått en forekomst av a FacialProcessing
objekt, vi konfigurerer det ved først å sette inn tilliten. Vi gjør dette ved hjelp av en lokal variabel, som er 57
i dette tilfellet fra et område på 0 til 100. Tilliten er en terskel når du prøver å løse identiteter. Eventuelle kamper under denne terskelen vil bli vurdert som separate identiteter.
Når det gjelder å bestemme verdien, så vidt jeg kan fortelle, er dette en prøve- og feilprosess. Tydeligvis jo høyere terskelen, desto mer nøyaktig gjenkjenning, med avstanden om å øke antall falske positive.
Vi setter da FacialProcessing
modus til FP_MODE_STILL
. Alternativene dine her er enten FP_MODE_STILL
eller FP_MODE_VIDEO
. Som navnene antyder, er en optimalisert for stillbilder mens den andre for kontinuerlige rammer, begge har åpenbare brukstilfeller.
P_MODE_STILL
, som du kanskje mistenker, gir mer nøyaktige resultater. Men som du vil se senere, FP_MODE_STILL
er underforstått av metoden vi bruker til å behandle bildet slik at denne linjen kan utelates. Jeg har bare lagt den til fullstendighet.
Vi ringer da loadAlbum
(metode av GalleryScanner
klasse), som er hva vi skal se på neste.
Privat ugyldig lastAlbum () SharedPreferences sharedPreferences = mContext.getSharedPreferences (TAG, 0); String arrayOfString = sharedPreferences.getString (KEY_IDENTITY_ALBUM, null); byte [] albumArray = null; hvis (arrayOfString! = null) String [] splitStringArray = arrayOfString.substring (1, arrayOfString.length () - 1) .split (","); albumArray = ny byte [splitStringArray.length]; for (int i = 0; i < splitStringArray.length; i++) albumArray[i] = Byte.parseByte(splitStringArray[i]); mFacialProcessing.deserializeRecognitionAlbum(albumArray);
Den eneste interessante linjen her er:
mFacialProcessing.deserializeRecognitionAlbum (albumArray);
Tellerens metode er:
byte [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum ();
En eneste FacialProcessing
forekomst kan betraktes som en økt. Lagt til personer (forklart nedenfor) lagres lokalt (referert til som "anerkjennelsesalbum") innenfor den forekomsten. For å la albumet ditt fortsette over flere økter, det vil si hver gang du får en ny forekomst, trenger du en måte å fortsette og laste dem på.
De serializeRecogntionAlbum
Metode konverterer albumet til et byte array og omvendt deserializeRecognitionAlbum
vil laste og analysere et tidligere lagret album som et byte-array.
Vi vet nå hvordan du initialiserer FacialProcessing
klasse for ansiktsbehandling og anerkjennelse. La oss nå sette fokus på å de-initialisere det ved å implementere deinitFacialProcessing
metode.
private void deinitFacialProcessing () if (mFacialProcessing! = null) saveAlbum (); mFacialProcessing.release (); mFacialProcessing = null;
Som nevnt ovenfor, kan det bare være en forekomst av FacialProcessing
klassen om gangen, så vi må sørge for at vi slipper det før vi fullfører oppgaven vår. Vi gjør dette via a utgivelse
metode. Men først gjør vi at anerkjennelsesalbumet vedvarer slik at vi kan bruke resultatene over flere økter. I dette tilfellet, når brukeren tar eller mottar nye bilder, ønsker vi å sikre at vi bruker tidligere anerkjente identiteter for de samme personene.
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 er endelig klar til å kutte ut den endelige krokmetoden og bruke FacialProcessing
klasse. Følgende kodeblokker tilhører processImage
metode. Jeg har delt dem for klarhet.
Private void processImage (ContentValues contentValues, Uri contentUri) langt bildeRowId = ContentUris.parseId (contentUri); String uriAsString = contentValues.getAsString (GalleryContract.PhotoEntity.COLUMN_URI); Uri uri = Uri.parse (uriAsString); Bitmap bitmap = null; prøv bitmap = ImageUtils.getImage (mContext, uri); fangst (IOException e) return; hvis (bitmap! = null) // fortsettes under (1)
Metoden henviser til en forekomst av ContentValues
klassen, som inneholder metadataene for dette bildet, sammen med URI som peker på bildet. Vi bruker dette til å laste bildet i minnet.
Følgende kodestykke er å erstatte kommentaren ovenfor // Fortsatt nedenfor (1)
.
hvis (! mFacialProcessing.setBitmap (bitmap)) return; int numFaces = mFacialProcessing.getNumFaces (); hvis (numFaces> 0) FaceData [] faceDataArray = mFacialProcessing.getFaceData (); hvis (faceDataArray == null) Log.w (TAG, contentUri.toString () + "har blitt returnert en NULL FaceDataArray"); komme tilbake; for (int i = 0; iSom nevnt ovenfor overfører vi først det statiske bildet til
FacialProcessing
eksempel viaSetBitmap
metode. Bruk av denne metoden implisitt brukerFP_MODE_STILL
modus. Denne metoden returnererekte
hvis bildet ble behandlet ogFalsk
hvis prosessen mislyktes.Den alternative metoden for å behandle streaming bilder (vanligvis for kamera forhåndsvisning rammer) er:
offentlig boolsk setFrame (byte [] yuvData, int ramme Bredde, int rammeHøyde, boolsk erMirrored, FacialProcessing.PREVIEW_ROTATION_ANGLE rotationAngle)De fleste parametrene er åpenbare. Du må passere om rammen er vendt (dette er vanligvis nødvendig for kameraet som vender framover), og hvis noen rotasjon har blitt brukt (vanligvis satt via
setDisplayOrientation
metode for aKamera
forekomst).Vi spør deretter om antall ansikter som er oppdaget og bare fortsett hvis minst en er funnet. De
getFaceData
Metoden returnerer detaljene for hvert oppdaget ansikt som en rekke avFaceData
objekter, hvor hverFaceData
Objektet innkapsler ansiktsfunksjoner, inkludert:
- ansiktsgrense (
FACE_RECT
)- ansikt, munn og øye steder (
FACE_COORDINATES
)- kontur av ansiktet (
FACE_CONTOUR
)- grad av smil (
FACE_SMILE
)- retning av øyne (
FACE_GAZE
)- flagg som indikerer om øyet (eller begge øynene) blinker (
FACE_BLINK
)- yaw, tonehøyde og rulle i ansiktet (
FACE_ORIENTATION
)- generert eller avledet identifikasjon (
FACE_IDENTIFICATION
)Det er en overbelastning for denne metoden som tar et sett med enums (som beskrevet ovenfor) for funksjonspunkter som skal inkluderes, fjerne / minimere redundante beregninger.
offentlig FaceData [] getFaceData (java.util.EnumSetdataSet) kaster java.lang.IllegalArgumentException Vi fortsetter nå for å inspisere
FaceData
protestere mot å trekke ut identiteten og funksjonene. La oss først se hvordan ansiktsgjenkjenning er gjort.Følgende kodestykke er å erstatte kommentaren ovenfor
// Fortsatt nedenfor (2)
.int personId = faceData.getPersonId (); hvis (personId == FacialProcessingConstants.FP_PERSON_NOT_REGISTERED) personId = mFacialProcessing.addPerson (i); ellers hvis (mFacialProcessing.updatePerson (personId, i)! = FacialProcessingConstants.FP_SUCCESS) // TODO handle error lang identityRowId = getOrInsertPerson (personId); // Fortsatt nedenfor (3)Vi ber først den tildelte personens ID via
getPersonId
metode. Dette kommer tilbake-111
(FP_PERSON_NOT_REGISTERED
) hvis det ikke finnes noen identitet i det for øyeblikket lastede albumet, ellers returnerer iden til en matchende person fra det lastede albumet.Hvis det ikke finnes noen identitet, legger vi det til via
addPerson
metode avFacialProcessing
objekt, passerer den indeksen avFaceData
element vi inspiserer for øyeblikket. Metoden returnerer den tildelte person-IDen hvis det lykkes, ellers returnerer en feil. Dette skjer når du prøver å legge til en identitet som allerede eksisterer.Alternativt, når personen ble matchet med en identitet lagret i vårt lastede album, kaller vi
FacialProcessing
objektetsupdatePerson
metode, bestått den eksisterende ID og indeks avFaceData
punkt. Å legge til en person flere ganger øker anerkjennelsesprestasjonen. Du kan legge opp til ti ansikter for en enkelt person.Den endelige linjen returnerer bare det tilhørende identitets-IDet fra databasen, og setter det inn hvis person-ID ikke allerede eksisterer.
Det er ikke vist ovenfor, men
FaceData
eksempel avslører metodengetRecognitionConfidence
for å returnere anerkjennelsestilliten (0 til 100). Avhengig av dine behov, kan du bruke dette til å påvirke flyt.Den endelige versjonen viser hvordan du spør etter hver av de andre funksjonene fra
FaceData
forekomst. I denne demoen bruker vi ikke dem, men med litt fantasi er jeg sikker på at du kan tenke på måter å sette dem på god bruk.Følgende kodestykke er å erstatte kommentaren ovenfor
// Fortsatt nedenfor (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);Det fullfører behandlingskoden. Hvis du går tilbake til galleriet og klikker på et bilde, bør du se det filtrere ut eventuelle bilder som ikke inneholder noen identifiserte personer i det valgte bildet.
Konklusjon
Vi startet denne opplæringen og snakker om hvordan teknologi kan brukes til å organisere brukerens innhold. I kontekstbevisst databehandling, hvis mål er å bruke kontekst som en implisitt kø for å berikke den fattige samspillet fra mennesker til datamaskiner, noe som gjør det lettere å kommunisere med datamaskiner, kalles dette automatisk merking. Ved å markere innhold med mer meningsfylt og nyttig data - både for datamaskinen og oss - tillater vi mer intelligent filtrering og behandling.
Vi har sett dette ofte brukt med tekstinnhold, det mest åpenbare eksempelet er spamfiltre og nyere nyhetslesere, men mindre med innhold i rikt media, for eksempel bilder, musikk og video. Verktøy som Snapdragon SDK gir oss mulighet til å trekke ut meningsfulle funksjoner fra rikt media, utsette egenskapene til brukeren og datamaskinen.
Det er ikke vanskelig å forestille seg hvordan du kan utvide vår søknad for å tillate filtrering basert på følelser ved å bruke et smil som hovedfunksjon eller sosial aktivitet ved å telle antall ansikter. En slik implementering kan ses i denne Smart Gallery-funksjonen.