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.
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.
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.
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.
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:
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ă.
Î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.
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ă.
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.
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 ();
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; iAșa cum am menționat mai sus, trecem mai întâi imaginea statică la
FacialProcessing
instanță prin intermediulsetBitmap
metodă. Utilizarea acestei metode utilizează implicitFP_MODE_STILL
Mod. Această metodă revineAdevărat
dacă imaginea a fost procesată cu succes șiFals
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 aaparat 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 deFaceData
obiecte, în cazul în care fiecareFaceData
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.EnumSetdataSet) 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ă aFacialProcessing
obiect, trecându-l indiceleFaceData
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
obiecteupdatePerson
metodă, trecând codul existent și indexul existentFaceData
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 metodagetRecognitionConfidence
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.