Identificarea persoanelor cu SDK-ul Snapdragon al Qualcomm

Nu a fost cu mult timp in urma ca fotografiile au fost destul de scumpe. Camerele de film au nevoie de film cu o capacitate limitată și de a vedea rezultatele au necesitat, de asemenea, timp suplimentar și mai mulți bani. Aceste constrângeri inerente ne-au asigurat că suntem selectivi cu fotografiile pe care le-am luat.

Deplasați-vă rapid spre ziua de azi și aceste constrângeri au fost diminuate datorită tehnologiei, dar acum ne confruntăm cu o nouă problemă, filtrarea, organizarea și descoperirea unor fotografii importante de la cei pe care îi luăm.

Această nouă problemă este ceea ce a inspirat acest tutorial. În ea, voi demonstra modul în care putem folosi noi instrumente pentru a ușura viața utilizatorului introducând noi modalități de filtrare și organizare a conținutului.

1. Concept

Pentru acest proiect, vom examina un mod diferit de filtrare prin colecția dvs. de fotografii. Pe parcurs, veți învăța cum să integrați și să utilizați SDK-ul Qualcomm Snapdragon pentru procesarea și recunoașterea facială.

Vom permite utilizatorului să filtreze o colecție de fotografii după identitate / identitate. Colecția va fi filtrată prin identități dintr-o fotografie pe care utilizatorul o execută, după cum se arată mai jos.

2. Prezentare generală

Principalul obiectiv al acestui post este introducerea prelucrării și recunoașterii faciale folosind programul SDK al Qualcomm, în timp ce, sperăm, încurajarea indirectă a unor noi moduri de gândire și utilizarea metadatelor derivate din conținut.

Pentru a evita fixarea în instalații, am creat un șablon care oferă serviciul de bază pentru scanarea prin colecția de fotografii a utilizatorului și o rețea pentru afișarea fotografiilor. Scopul nostru este de a spori acest lucru cu conceptul propus mai sus.

În următoarea secțiune, vom examina pe scurt aceste componente înainte de a trece la introducerea produsului Qualcomm's Snapdragon SDK.

3. Schelet

Așa cum am menționat mai sus, scopul nostru este să ne concentrăm pe SDK-ul Snapdragon, așa că am creat un schelet care a implementat toate instalațiile sanitare. Mai jos este o diagramă și o descriere a proiectului, disponibilă pentru descărcare de la GitHub.

Al nostru date pachetul conține o implementare a SQLiteOpenHelper (IdentityGalleryDatabase) responsabilă de crearea și gestionarea bazei noastre de date. Baza de date va consta din trei tabele, una va acționa ca un indicator al înregistrării mass-media (fotografie), un altul pentru identitățile detectate (identitate) și, în final, tabela de relații care leagă identitățile cu fotografiile lor (identity_photo).

Vom folosi tabelul de identitate pentru a stoca atributele furnizate de SDK Snapdragon, detaliate într-o secțiune ulterioară a acestui tutorial.

De asemenea, incluse în pachetul de date sunt: ​​a Furnizor de (IdentityGalleryProvider) și Contracta (IdentityGalleryContract), care nu este altceva decât un standard Furnizor de acționând ca un înveliș al SQLiteOpenHelper clasă.

Pentru a vă oferi un sentiment de interacțiune cu Furnizor de clasa, următorul cod este luat din TestProvider clasă. După cum sugerează și numele, este folosit pentru testarea Furnizor de clasă. 

// ... Interogare pentru toate fotografiile Cursor cursor = mContext.getContentResolver () interogare (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null); // ... Interogare pentru toate fotografiile care conțin oricare dintre identitățile din fotografia menționată Cursor cursor = mContext.getContentResolver () interogare (IdentityGalleryContract.PhotoEntity.buildUriWithReferencePhoto (photoId), null, null, null, null); // ... Interogarea identității apelurilor Cursor cursor = mContext.getContentResolver () interogare (IdentityGalleryContract.IdentityEntity.CONTENT_URI, null, null, null, null); // ... Interogare pentru toate cursorul Cursor = mContext.getContentResolver () interogare (IdentityGalleryContract.PhotoEntity.CONTENT_URI, null, null, null, null);

serviciu pachetul este responsabil pentru iterarea prin, catalogarea și, eventual, procesarea imaginilor disponibile prin MediaStore. Serviciul în sine extinde IntentService ca o modalitate ușoară de a efectua prelucrarea pe firul propriu. Lucrarea reală este delegată GalleryScanner, care este clasa pe care o vom extinde pentru prelucrarea și recunoașterea feței.

Acest GalleryScannerIntentService este instanțiată de fiecare dată Activitate principala este creat cu următorul apel:

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

Când a început, GalleryScannerIntentService aduce ultima dată de scanare și trece acest lucru în constructorul GalleryScanner. Apoi îl sună scanda metoda de a începe să iterăm prin conținutul MediaItem furnizor de conținut - pentru articole după ultima dată de scanare.

Dacă inspectați scanda metodă a GalleryScanner clasa, veți observa că este destul de verbose - nu se întâmplă nimic complicat aici. Metoda necesită interogarea fișierelor media stocate intern (MediaStore.Images.Media.INTERNAL_CONTENT_URI) și extern (MediaStore.Images.Media.EXTERNAL_CONTENT_URI). Fiecare element este apoi trecut pe o metodă de cârlig, în care vom plasa codul nostru pentru prelucrarea și recunoașterea feței.

private void processImage (ContentValues ​​contentValues, Uri contentUri) arunca noua UnsupportedOperationException ("metoda Hook nu este implementată în prezent"); 

Alte două metode de cârlig în GalleryScanner sunt disponibile pentru noi (după cum sugerează și numele metodelor) inițializarea și de-inițializarea FacialProcessing instanță.

private void initFacialProcessing () aruncă UnsupportedOperationException throw new UnsupportedOperationException (metoda "Hook" nu este implementată în prezent);  private void deinitFacialProcessing () throw new UnsupportedOperationException (metoda "Hook nu este implementată în prezent"); 

Pachetul final este pachetul de prezentare. După cum sugerează și numele, acesta găzduiește Activitate clasa responsabilă de redarea galeriei noastre. Galeria este a GridView atașat la CursorAdapter. Așa cum am explicat mai sus, atingerea unui element va interoga baza de date pentru toate fotografiile care conțin una dintre identitățile fotografiei selectate. De exemplu, dacă apăsați pe o fotografie a prietenului tău Lisa și a iubitului ei Justin, interogarea va filtra toate fotografiile care conțin fie Lisa, fie Justin.

4. SDK pentru Snapdragon Qualcomm

Pentru a ajuta dezvoltatorii să facă hardware-ul lor să arate excelent și să-l facă dreptate, Qualcomm a lansat un set uimitor de SDK-uri, unul fiind Snapdragon SDK. SDK Snapdragon expune un set optimizat de funcții pentru procesarea feței.

SDK este în mare parte împărțit în două părți, procesare facială și recunoaștere facială. Dat fiind faptul că nu toate dispozitivele suportă atât aceste caracteristici, cât și oricare dintre aceste caracteristici, probabil motivul pentru care aceste caracteristici sunt separate, SDK oferă o modalitate ușoară de a verifica ce caracteristici suportă dispozitivul. Vom acoperi acest lucru în detaliu mai târziu.

Prelucrarea feței oferă o modalitate de a extrage caracteristici dintr-o fotografie (a unei fețe) incluzând:  

  • Detectarea intermitentă: Măsurați cât de deschis este fiecare ochi.
  • Monitorizarea urmăririi: Evaluați unde se află subiectul.
  • Zâmbet Valoare: Estimați gradul de zâmbet.
  • Orientare față: Urmăriți răsturnarea, pitchul și rola capului.

Recunoașterea facială, așa cum sugerează și numele, oferă posibilitatea de a identifica oamenii într-o fotografie. Merită menționat faptul că toate prelucrările se fac local - spre deosebire de nor.

Aceste caracteristici pot fi utilizate în timp real (video / cameră) sau offline (galerie). În exercițiul nostru, vom utiliza aceste funcții offline, dar există diferențe minime între cele două abordări.

Consultați documentația online pentru dispozitivele acceptate pentru a afla mai multe despre procesarea facială și recunoașterea facială.

5. Adăugarea procesării și recunoașterii facială

În această secțiune, vom completa aceste metode de cârlig - cu surprinzător de puține linii de cod - pentru a oferi aplicației noastre capacitatea de a extrage proprietățile feței și de a identifica oamenii. Pentru a lucra împreună, descărcați sursa de la GitHub și deschideți proiectul Android Studio. Alternativ, puteți descărca proiectul finalizat.

Pasul 1: Instalarea SDK-ului Snapdragon

Primul lucru pe care trebuie să-l facem este să luăm SDK de pe site-ul Qualcomm. Rețineți că va trebui să vă înregistrați / autentificați și sunteți de acord cu termenii și condițiile Qualcomm.

După descărcare, dezarhivați conținutul și navigați la /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Copiați SD-facial-sdk-processing.jar fișier în proiectul dvs. / app / libs / folder așa cum se arată mai jos.

După copierea fișierului Snapdragon SDK, faceți clic dreapta pe SD-facial-sdk-processing.jar și selectați Adăugați ca Bibliotecă ... din lista opțiunilor.

Aceasta va adăuga biblioteca ca o dependență în dvs. build.gradle fișier după cum se arată mai jos.

dependență compile fileTree (dir: 'libs', include: ['* .jar']) compilați fișierele ('libs / sd-sdk-facial-processing.jar') comp.android.support:support-v13: 20.0.0 '

Ultimul pas este să adăugați biblioteca nativă. Pentru a face acest lucru, creați un dosar numit jniLibs în tine / App / src / main / folder și copiați armeabi folder (din descărcarea SDK) și conținutul său în el.

Suntem gata să implementăm logica pentru a identifica utilizatorii utilizând funcționalitatea API-ului. Următoarele fragmente de cod aparțin GalleryScanner clasă.

Pasul 2: Inițializare

Să abordăm mai întâi metoda de cârlig de inițializare. 

privat void initFacialProcessing () aruncă UnsupportedOperationException if (! FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_PROCESSING) || FacialProcessing.isFeatureSupported (FacialProcessing.FEATURE_LIST.FEATURE_FACIAL_RECOGNITION)) aruncă o nouă UnsupportedOperationException (" dispozitiv");  mFacialProcessing = FacialProcessing.getInstance (); dacă (mFacialProcessing! = null) mFacialProcessing.setRecognitionConfidence (mConfidenceThreshold); mFacialProcessing.setProcessingMode (FacialProcessing.FP_MODES.FP_MODE_STILL); loadAlbum ();  altceva throw new UnsupportedOperationException ("O instanță este deja în uz");

Mai întâi trebuie să verificăm dacă dispozitivul acceptă atât procesarea facială, cât și recunoașterea facială. Dacă nu, aruncăm unul UnsupportedOperationException excepție.

Apoi, atribuim referința noastră locală la FacialProcessing clasă, mFacialProcessing, la o nouă instanță folosind metoda din fabrică getInstance. Aceasta se va întoarce nul dacă o instanță este deja utilizată, caz în care consumatorul trebuie să sune eliberare pe această referință.

Dacă am obținut cu succes o instanță a FacialProcessing Obiect, îl configurăm prin stabilirea în primul rând a încrederii. Facem asta folosind o variabilă locală, care este 57 în acest caz, de la 0 la 100. Încrederea este un prag atunci când încearcă să rezolve identitățile. Orice potriviri sub acest prag vor fi considerate identități separate.

În ceea ce privește determinarea valorii, în măsura în care pot spune, acesta este un proces de încercare și de eroare. Evident, cu cât este mai mare pragul, cu atât este mai exactă recunoașterea, cu compromisul de creștere a numărului de false-pozitive.

Apoi am setat FacialProcessing mod pentru a FP_MODE_STILL. Opțiunile dvs. sunt aici FP_MODE_STILL sau FP_MODE_VIDEO. După cum sugerează numele, unul este optimizat pentru imagini statice, în timp ce celălalt pentru cadre continue, ambele având cazuri de utilizare evidentă.

P_MODE_STILL, după cum ați putea suspecta, oferă rezultate mai precise. Dar după cum veți vedea mai târziu, FP_MODE_STILL este implicată de metoda pe care o folosim pentru a procesa imaginea, astfel încât această linie poate fi omisă. Am adăugat-o doar pentru completare.

Apoi sunăm loadAlbum (metoda GalleryScanner clasa), ceea ce ne vom uita la urmatorul.

void private voidAlbum () SharedPreferences sharedPreferences = mContext.getSharedPreferences (TAG, 0); String arrayOfString = sharedPreferences.getString (KEY_IDENTITY_ALBUM, null); octet [] albumArray = null; dacă (arrayOfString! = null) String [] splitStringArray = arrayOfString.substring (1, arrayOfString.length () - 1) .split (";"); albumArray = nou octet [splitStringArray.length]; pentru (int i = 0; i < splitStringArray.length; i++)  albumArray[i] = Byte.parseByte(splitStringArray[i]);  mFacialProcessing.deserializeRecognitionAlbum(albumArray);  

Singura linie interesantă aici este:

mFacialProcessing.deserializeRecognitionAlbum (albumArray);

Metoda contra este:

octet [] albumBuffer = mFacialProcessing.serializeRecogntionAlbum ();

Un singur FacialProcessing instanța poate fi considerată ca o sesiune. Persoanele adăugate (explicate mai jos) sunt stocate local (denumite în continuare "albumul de recunoaștere"). Pentru a permite albumului să persiste în mai multe sesiuni, adică de fiecare dată când obțineți o nouă instanță, aveți nevoie de o cale de a persista și de a le încărca.

serializeRecogntionAlbum metoda convertește albumul într-o matrice octet și invers deserializeRecognitionAlbum va încărca și analiza un album stocat anterior ca array de octeți.

Pasul 3: De-inițializare

Acum știm cum să inițializăm FacialProcessing clasa pentru prelucrarea și recunoașterea feței. Să ne îndreptăm acum atenția asupra dezinalizării prin implementarea deinitFacialProcessing metodă.

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

După cum sa menționat mai sus, nu poate fi decât o instanță a FacialProcessing clasa la un moment dat, astfel încât trebuie să ne asigurăm că îl eliberăm înainte de a ne termina sarcina. Noi facem asta printr-o eliberare metodă. Dar mai întâi facem ca albumul de recunoaștere să persiste, astfel încât să putem folosi rezultatele pe mai multe sesiuni. În acest caz, atunci când utilizatorul primește sau primește fotografii noi, vrem să ne asigurăm că folosim identitățile recunoscute anterior pentru aceleași persoane.

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

Pasul 4: Prelucrarea imaginii

Suntem în sfârșit gata să elaborăm metoda finală a cârligului și să folosim FacialProcessing clasă. Următoarele blocuri de coduri aparțin processImage metodă. Le-am despărțit pentru claritate.

private void processImage (conținutul ContentValues ​​content, Uri contentUri) lung photoRowId = ContentUris.parseId (contentUri); String uriAsString = contentValues.getAsString (GalerieContract.PhotoEntity.COLUMN_URI); Uri uri = Uri.parse (uriAsString); Bitmap bitmap = null; încercați bitmap = ImageUtils.getImage (mContext, uri);  captură (IOException e) retur;  dacă (bitmap! = null) / a continuat mai jos (1)

Metoda face trimitere la o instanță a ContentValues clasă, care deține metadatele pentru această imagine, împreună cu URI indicând imaginea. Utilizăm acest lucru pentru a încărca imaginea în memorie.

Următorul fragment de cod va înlocui comentariul de mai sus // a continuat mai jos (1).

dacă (! mFacialProcessing.setBitmap (bitmap)) retur;  int numFaces = mFacialProcessing.getNumFaces (); dacă (numFaces> 0) FaceData [] faceDataArray = mFacialProcessing.getFaceData (); dacă (faceDataArray == null) Log.w (TAG, contentUri.toString () + "a fost returnat un NULL FaceDataArray"); întoarcere;  pentru (int i = 0; i

Așa cum am menționat mai sus, trecem mai întâi imaginea statică la FacialProcessing instanță prin intermediul setBitmap metodă. Utilizarea acestei metode utilizează implicit FP_MODE_STILL Mod. Această metodă revine Adevărat dacă imaginea a fost procesată cu succes și Fals dacă procesarea a eșuat.

Metoda alternativă pentru procesarea imaginilor streaming (de obicei pentru cadrele de examinare a camerei) este:

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

Majoritatea parametrilor sunt evideni. Trebuie să treceți dacă rama este rotită (aceasta este de obicei necesară pentru camera orientată spre față) și dacă a fost aplicată o rotație (de obicei setată prin setDisplayOrientation metoda de a aparat foto instanță).

Apoi, interogăm numărul de fețe detectate și continuăm numai dacă se găsește cel puțin unul. getFaceData metoda returnează detaliile pentru fiecare față detectată ca o matrice de FaceData obiecte, în cazul în care fiecare FaceData obiect încapsulă caracteristici faciale, inclusiv:

  • frontieră frontală (FACE_RECT)
  • față, gură și ochi (FACE_COORDINATES)
  • conturul feței (FACE_CONTOUR)
  • gradul de zâmbet (FACE_SMILE)
  • direcția ochilor (FACE_GAZE)
  • care indică dacă fiecare ochi (sau ambii ochi) clipește (FACE_BLINK)
  • răsucirea, întinderea și rola fața (FACE_ORIENTATION)
  • generat sau derivat (FACE_IDENTIFICATION)

Există o supraîncărcare la această metodă, care ia un set de enumuri (așa cum este descris mai sus) pentru punctele de caracter care trebuie incluse, eliminarea / minimizarea calculelor redundante.

public FaceData [] getFaceData (java.util.EnumSet dataSet) aruncă java.lang.IllegalArgumentException 

Acum mergem la inspectarea FaceData obiect pentru a extrage identitatea și caracteristicile. Să vedem mai întâi cum se face recunoașterea facială.

Următorul fragment de cod va înlocui comentariul de mai sus // a continuat mai jos (2).

int personId = faceData.getPersonId (); dacă (personId == FacialProcessingConstants.FP_PERSON_NOT_REGISTERED) personId = mFacialProcessing.addPerson (i);  altceva if (mFacialProcessing.updatePerson (personId, i)! = FacialProcessingConstants.FP_SUCCESS) // TODO error handle identitate lungăRowId = getOrInsertPerson (personId); // a continuat mai jos (3)

Solicităm mai întâi identitatea persoanei desemnate prin getPersonId metodă. Aceasta se va întoarce -111 (FP_PERSON_NOT_REGISTERED) dacă nu există nici o identitate în albumul încărcat în prezent, returnând în alt mod id-ul unei persoane care se potrivește cu albumul încărcat.

Dacă nu există nici o identitate, atunci o adăugăm prin addPerson metodă a FacialProcessing obiect, trecându-l indicele FaceData element pe care îl inspecționăm în prezent. Metoda returnează identitatea persoanei atribuite dacă a reușit, altfel returnând o eroare. Acest lucru se întâmplă atunci când încercați să adăugați o identitate care există deja.

Alternativ, atunci când persoana a fost asociată cu o identitate stocată în albumul încărcat, sunăm FacialProcessing obiecte updatePerson metodă, trecând codul existent și indexul existent FaceData articol. Adăugarea unei persoane de mai multe ori crește performanța recunoașterii. Puteți adăuga până la zece fețe pentru o singură persoană.

Linia finală returnează pur și simplu idul de identitate asociat din baza noastră de date, introducând-o dacă id-ul persoanei nu există deja.

Nu este arătat mai sus, ci FaceData instanța expune metoda getRecognitionConfidence pentru a returna încrederea în recunoaștere (0 la 100). În funcție de nevoile dvs., puteți folosi acest lucru pentru a influența fluxul.

Fragmentul final demonstrează modul de interogare a fiecărei alte caracteristici din FaceData instanță. În acest demo, nu le folosim, dar cu puțină imaginație sunt sigur că vă puteți gândi la modalități de a le folosi.

Următorul fragment de cod va înlocui comentariul de mai sus // a continuat mai jos (3).

int smileValue = faceData.getSmileValue (); int leftEyeBlink = faceData.getLeftEyeBlink (); int dreaptaEyeBlink = faceData.getRightEyeBlink (); int roll = faceData.getRoll (); PointF gazePointValue = faceData.getEyeGazePoint (); int pas = faceData.getPitch (); int yaw = faceData.getYaw (); int orizontalGaze = faceData.getEyeHorizontalGazeAngle (); int verticalGaze = faceData.getEyeVerticalGazeAngle (); Rect faceRect = faceData.rect; insertNewPhotoIdentityRecord (fotoRowId, identitateRowId, gazePointValue, orizontalGaze, verticalGaze, leftEyeBlink, rightEyeBlink, pitch, roll, smileValue, faceRect); 

Aceasta completează codul de procesare. Dacă vă întoarceți la galerie și apăsați pe o imagine, ar trebui să vedeți că filtrarea tuturor fotografiilor care nu conțin persoane identificate în fotografia selectată.

Concluzie

Am început acest tutorial vorbind despre modul în care tehnologia poate fi utilizată pentru a ajuta la organizarea conținutului utilizatorului. În contextul computerizării, al cărui scop este de a folosi contextul ca un indiciu implicit pentru a îmbogăți interacțiunea săracă de la oameni la computere, făcând mai ușor să interacționați cu computerele, acest lucru este cunoscut sub numele de etichetare automată. Prin marcarea conținutului cu date mai importante și mai utile - atât pentru computer cât și pentru noi - permitem o filtrare și o prelucrare mai inteligentă.

Am văzut acest lucru folosit frecvent cu conținut textual, cel mai evident exemplu fiind filtrele de spam și, mai recent, cititorii de știri, dar mai puțin cu conținut media bogat, cum ar fi fotografii, muzică și video. Unelte precum SDK Snapdragon ne oferă posibilitatea de a extrage caracteristici semnificative din mass-media bogată, expunând proprietățile sale utilizatorului și calculatorului.

Nu este greu să vă imaginați cum puteți extinde aplicația noastră pentru a permite filtrarea bazată pe sentiment prin folosirea unui zâmbet ca caracteristică majoră sau activitate socială prin numărarea numărului de fețe. O astfel de implementare poate fi văzută în această funcție Smart Gallery.

Cod