1.前沿
專注于為中小企業(yè)提供成都網(wǎng)站制作、成都網(wǎng)站建設服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)互助免費做網(wǎng)站提供優(yōu)質(zhì)的服務。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了數(shù)千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。
人工智能時代快速來臨,其中人臉識別是當前比較熱門的技術,在國內(nèi)也越來越多的運用,例如刷臉打卡,刷臉APP,身份識別,人臉門禁等。當前的人臉識別技術分為WEBAPI和SDK調(diào)用兩種方式,WEBAPI需要實時聯(lián)網(wǎng),SDK調(diào)用可以離線使用。
本次使用的虹軟提供的人臉識別的SDK,此SDK也可根據(jù)不同應用場景設計,針對性強。包括人臉檢測、人臉跟蹤、人臉識別,即使在離線環(huán)境下也可正常運行。
虹軟公司是一家具有硅谷背景的圖像處理公司,除了人臉技術以外,還有多項圖像及視頻處理技術。他們的雙攝像頭處理算法和人臉美化算法囊括了包括OPPO VIVO,SUMAMNG一系列手機廠商。
2.項目的目標
我們需要實現(xiàn)一個人臉識別功能。簡單來說,就是機的后置攝像頭,識別攝像頭中實時拍到的人臉信息,如果人庫注冊過,則顯示識別后的人臉信息,如登記的名字;如果不在,提示未注冊。
這個功能具有多個應用場景,比如,火車站或者打卡和門禁系統(tǒng)中。
3.人臉識別的過程
人臉識別包括兩個必備的過程,人臉注冊和實時識別。
人臉注冊是指把人臉的特征信息注冊到人臉信息庫中。人臉注冊的來源可以有很多種,比如
人臉特征提取是一個不可逆的過程,你無法從人臉特征信息還原一個人的臉部照片。
在線庫在使用時,需要傳遞照片信息,或者提取圖像特征值,
離線的SDK相對安全,但是,在線的SDK通常提供更多的接入和調(diào)用方式,這個要結(jié)合實際情況來選擇。
4.定義并實現(xiàn)人臉庫的相關功能
如前面所述,我們希望定義自己 的人臉庫,人臉庫在程序中使用List存儲,在系統(tǒng)中保存為txt文件。
通過查詢引擎,可以知道人臉信息是保存在AFR_FSDKFace類中的。這的主要結(jié)構(gòu)為
public static final int FEATURE_SIZE = 22020; byte[] mFeatureData;
如果要進行人臉注冊,我們需要定義另外一個類來把人臉信息和姓名關聯(lián)起來。
class FaceRegist { String mName; List<AFR_FSDKFace> mFaceList; public FaceRegist(String name) { mName = name; mFaceList = new ArrayList<>(); } }
包含特征信息的長度和內(nèi)容的byte數(shù)組。
我們把這些功能定義在類FaceDB中。FaceDB需要包含引擎定義,初始化,把人臉信息保存在版本庫和從版本庫中讀出人臉信息這些功能
5.初始化引擎
為了程序結(jié)構(gòu)性考慮,我們將人臉識別相關的代碼獨立出來一個類FaceDB,并定義必要的變量
public static String appid = "bCx99etK9Ns4Saou1EbFdC18xHdY9817EKw****"; public static String ft_key = "CopwZarSihp1VBu5AyGxfuLQdRMPyoGV2C2opc****"; public static String fd_key = "CopwZarSihp1VBu5AyGxfuLXnpccQbWAjd86S8****"; public static String fr_key = "CopwZarSihp1VBu5AyGxfuLexDsi8yyELdgsj4****"; String mDBPath; List<FaceRegist> mRegister; AFR_FSDKEngine mFREngine; AFR_FSDKVersion mFRVersion;
定義有參數(shù)的構(gòu)造函數(shù)來初始化引擎
public FaceDB(String path) { mDBPath = path; mRegister = new ArrayList<>(); mFRVersion = new AFR_FSDKVersion(); mUpgrade = false; mFREngine = new AFR_FSDKEngine(); AFR_FSDKError error = mFREngine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key); if (error.getCode() != AFR_FSDKError.MOK) { Log.e(TAG, "AFR_FSDK_InitialEngine fail! error code :" + error.getCode()); } else { mFREngine.AFR_FSDK_GetVersion(mFRVersion); Log.d(TAG, "AFR_FSDK_GetVersion=" + mFRVersion.toString()); } }
定義析構(gòu)函數(shù)釋放引擎占用的系統(tǒng)資源
public void destroy() { if (mFREngine != null) { mFREngine.AFR_FSDK_UninitialEngine(); } }
6.實現(xiàn)人臉增加和讀取功能
通常人臉庫會存放在數(shù)據(jù)庫中,本次我們使用List來進行簡單的模擬,并將其保存在文本文件中,需要時從文本中讀取,保存時寫入到文件中。
我們使用addFace方法將待注冊的人臉信息添加到人臉庫中
public void addFace(String name, AFR_FSDKFace face) { try { //check if already registered. boolean add = true; for (FaceRegist frface : mRegister) { if (frface.mName.equals(name)) { frface.mFaceList.add(face); add = false; break; } } if (add) { // not registered. FaceRegist frface = new FaceRegist(name); frface.mFaceList.add(face); mRegister.add(frface); } if (!new File(mDBPath + "/face.txt").exists()) { if (!saveInfo()) { Log.e(TAG, "save fail!"); } } //save name FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true); ExtOutputStream bos = new ExtOutputStream(fs); bos.writeString(name); bos.close(); fs.close(); //save feature fs = new FileOutputStream(mDBPath + "/" + name + ".data", true); bos = new ExtOutputStream(fs); bos.writeBytes(face.getFeatureData()); bos.close(); fs.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
使用loadFaces從文件中讀取人臉
public boolean loadFaces(){ if (loadInfo()) { try { for (FaceRegist face : mRegister) { Log.d(TAG, "load name:" + face.mName + "'s face feature data."); FileInputStream fs = new FileInputStream(mDBPath + "/" + face.mName + ".data"); ExtInputStream bos = new ExtInputStream(fs); AFR_FSDKFace afr = null; do { if (afr != null) { if (mUpgrade) { //upgrade data. } face.mFaceList.add(afr); } afr = new AFR_FSDKFace(); } while (bos.readBytes(afr.getFeatureData())); bos.close(); fs.close(); } return true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { if (!saveInfo()) { Log.e(TAG, "save fail!"); } } return false; }
7.實現(xiàn)業(yè)務邏輯
7.1實現(xiàn)人臉注冊功能
人臉識別的前提條件就是人臉信息要先注冊到人臉庫中,注冊人臉庫
第一步當然是獲取待注冊的照片,我們可以可以使用攝像頭,也可以使用照片。我們使用AlertDialog彈出選擇框
new AlertDialog.Builder(this) .setTitle("請選擇注冊方式") .setIcon(android.R.drawable.ic_dialog_info) .setItems(new String[]{"打開圖片", "拍攝照片"}, this) .show();
在對應的事件處理函數(shù)中進行處理
switch (which){ case 1://攝像頭 Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE"); ContentValues values = new ContentValues(1); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); mPath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, mPath); startActivityForResult(getImageByCamera, REQUEST_CODE_IMAGE_CAMERA); break; case 0://圖片 Intent getImageByalbum = new Intent(Intent.ACTION_GET_CONTENT); getImageByalbum.addCategory(Intent.CATEGORY_OPENABLE); getImageByalbum.setType("image/jpeg"); startActivityForResult(getImageByalbum, REQUEST_CODE_IMAGE_OP); break; default:; }
獲取一張照片后,后續(xù)我們就需要實現(xiàn)人臉檢測功能。
if (requestCode == REQUEST_CODE_IMAGE_OP && resultCode == RESULT_OK) { mPath = data.getData(); String file = getPath(mPath); //TODO: add image coversion }
在上面的代碼中,我們獲取到了我們需要的圖像數(shù)據(jù)bmp,把圖片取出來
我們在Application類用函數(shù) decodeImage中實現(xiàn)這段代碼
public static Bitmap decodeImage(String path) { Bitmap res; try { ExifInterface exif = new ExifInterface(path); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); BitmapFactory.Options op = new BitmapFactory.Options(); op.inSampleSize = 1; op.inJustDecodeBounds = false; //op.inMutable = true; res = BitmapFactory.decodeFile(path, op); //rotate and scale. Matrix matrix = new Matrix(); if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { matrix.postRotate(90); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { matrix.postRotate(180); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { matrix.postRotate(270); } Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true); Log.d("com.arcsoft", "check target Image:" + temp.getWidth() + "X" + temp.getHeight()); if (!temp.equals(res)) { res.recycle(); } return temp; } catch (Exception e) { e.printStackTrace(); } return null; }
調(diào)用AFD_FSDK_StillImageFaceDetection返回檢測到的人臉信息
人臉注冊 ,首先要先檢測出來人臉,對于靜態(tài)圖片,虹軟人臉SDK中對應的是FD,提供了一個方法名稱,叫AFD_FSDK_StillImageFaceDetection 。
我們來看一下參數(shù)列表
注意AFD_FSDKFace對象引擎內(nèi)部重復使用,如需保存,請clone一份AFD_FSDKFace對象或另外保存
AFD_FSDKFace是人臉識別的結(jié)果,定義如下
public class AFD_FSDKFace { Rect mRect; int mDegree; }
mRect定義一個了一個矩形框Rect
在此之前我們需要注意虹軟人臉SDK使用的圖像格式是NV21的格式,所以我們需要將獲取到的圖像轉(zhuǎn)化為對應的格式。在Android_extend.jar中提供了對應的轉(zhuǎn)換函數(shù)
byte[] data = new byte[mBitmap.getWidth() * mBitmap.getHeight() * 3 / 2]; ImageConverter convert = new ImageConverter(); convert.initial(mBitmap.getWidth(), mBitmap.getHeight(), ImageConverter.CP_PAF_NV21); if (convert.convert(mBitmap, data)) { Log.d(TAG, "convert ok!"); } convert.destroy();
現(xiàn)在我們就可以調(diào)用AFD_FSDK_StillImageFaceDetection方法了
8.繪出人臉框
在List<AFD_FSDKFace>中保存了檢測到的人臉的位置信息和深度信息。
我們可以將檢測到的人臉位置信息在圖片上用一個矩形框繪制出來表示檢測到的人臉信息。
Canvas canvas = mSurfaceHolder.lockCanvas(); if (canvas != null) { Paint mPaint = new Paint(); boolean fit_horizontal = canvas.getWidth() / (float)src.width() < canvas.getHeight() / (float)src.height() ? true : false; float scale = 1.0f; if (fit_horizontal) { scale = canvas.getWidth() / (float)src.width(); dst.left = 0; dst.top = (canvas.getHeight() - (int)(src.height() * scale)) / 2; dst.right = dst.left + canvas.getWidth(); dst.bottom = dst.top + (int)(src.height() * scale); } else { scale = canvas.getHeight() / (float)src.height(); dst.left = (canvas.getWidth() - (int)(src.width() * scale)) / 2; dst.top = 0; dst.right = dst.left + (int)(src.width() * scale); dst.bottom = dst.top + canvas.getHeight(); } canvas.drawBitmap(mBitmap, src, dst, mPaint); canvas.save(); canvas.scale((float) dst.width() / (float) src.width(), (float) dst.height() / (float) src.height()); canvas.translate(dst.left / scale, dst.top / scale); for (AFD_FSDKFace face : result) { mPaint.setColor(Color.RED); mPaint.setStrokeWidth(10.0f); mPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(face.getRect(), mPaint); } canvas.restore(); mSurfaceHolder.unlockCanvasAndPost(canvas); break; } }
9.將人臉注冊到人臉庫
檢測到了人臉,我們可以輸入相應的描述信息,加入到人臉庫中。
為了提高識別的準確性,我們可以對一個人多次注冊人臉信息。
public void addFace(String name, AFR_FSDKFace face) { try { //check if already registered. boolean add = true; for (FaceRegist frface : mRegister) { if (frface.mName.equals(name)) { frface.mFaceList.add(face); add = false; break; } } if (add) { // not registered. FaceRegist frface = new FaceRegist(name); frface.mFaceList.add(face); mRegister.add(frface); } if (!new File(mDBPath + "/face.txt").exists()) { if (!saveInfo()) { Log.e(TAG, "save fail!"); } } //save name FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true); ExtOutputStream bos = new ExtOutputStream(fs); bos.writeString(name); bos.close(); fs.close(); //save feature fs = new FileOutputStream(mDBPath + "/" + name + ".data", true); bos = new ExtOutputStream(fs); bos.writeBytes(face.getFeatureData()); bos.close(); fs.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
最后,別忘記了銷毀人臉檢測引擎
err = engine.AFD_FSDK_UninitialFaceEngine(); Log.d("com.arcsoft", "AFD_FSDK_UninitialFaceEngine =" + err.getCode());
10.實現(xiàn)人臉識別
上面的代碼準備完畢后,就可以開始我們的人臉識別的功能了。我們使用一個第三方的擴展庫,ExtGLSurfaceView的擴展 庫CameraGLSurfaceView,用ImageView和TextView顯示檢測到的人臉和相應的描述信息。
首先是定義layout。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.guo.android_extend.widget.CameraSurfaceView android:id="@+id/surfaceView" android:layout_width="1dp" android:layout_height="1dp"/> <com.guo.android_extend.widget.CameraGLSurfaceView android:id="@+id/glsurfaceView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true"/> <ImageView android:id="@+id/imageView" android:layout_width="120dp" android:layout_height="120dp" android:layout_marginLeft="10dp" android:layout_marginTop="10dp"/> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/imageView" android:layout_alignRight="@+id/imageView" android:layout_below="@+id/imageView" android:layout_marginTop="10dp" android:text="@string/app_name" android:textAlignment="center"/> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/imageView" android:layout_alignRight="@+id/imageView" android:layout_below="@+id/textView" android:layout_marginTop="10dp" android:text="@string/app_name" android:textAlignment="center"/> </RelativeLayout>
因為引擎需要的圖像格式是NV21的,所以需要將攝像頭中的圖像格式預設置為NV21
public Camera setupCamera() { // TODO Auto-generated method stub mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); try { Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mWidth, mHeight); parameters.setPreviewFormat(ImageFormat.NV21); for( Camera.Size size : parameters.getSupportedPreviewSizes()) { Log.d(TAG, "SIZE:" + size.width + "x" + size.height); } for( Integer format : parameters.getSupportedPreviewFormats()) { Log.d(TAG, "FORMAT:" + format); } List<int[]> fps = parameters.getSupportedPreviewFpsRange(); for(int[] count : fps) { Log.d(TAG, "T:"); for (int data : count) { Log.d(TAG, "V=" + data); } } mCamera.setParameters(parameters); } catch (Exception e) { e.printStackTrace(); } if (mCamera != null) { mWidth = mCamera.getParameters().getPreviewSize().width; mHeight = mCamera.getParameters().getPreviewSize().height; } return mCamera; }
從攝像頭識別人臉,需要使用FT庫,F(xiàn)T庫在人臉跟蹤算法上對人臉檢測部分進行了優(yōu)化,是專門為視頻處理而優(yōu)化的庫。
11.初始化人臉檢測引擎(FT)
和FD一樣,我們需要初始化人臉識別FT引擎。
Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode()); err = engine.AFT_FSDK_GetVersion(version); Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());
在攝像頭的預覽事件處理函數(shù)中,先調(diào)用FT的人臉識函數(shù)函數(shù),然后再調(diào)用FR中的人臉信息特征提取數(shù)函數(shù)。
AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result); AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21,mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);
這里面的result中保存了人臉特征信息。我們可以將其保存下來或下來并與系統(tǒng)中的其它信息進行對比。
AFR_FSDKMatching score = new AFR_FSDKMatching(); float max = 0.0f; String name = null; for (FaceDB.FaceRegist fr : mResgist) { for (AFR_FSDKFace face : fr.mFaceList) { error = engine.AFR_FSDK_FacePairMatching(result, face, score); Log.d(TAG, "Score:" + score.getScore() + ", AFR_FSDK_FacePairMatching=" + error.getCode()); if (max < score.getScore()) { max = score.getScore(); name = fr.mName; } } }
當score的特征信息大于0.6時,我們就可以認為匹配到了人臉。顯示人臉匹配信息。
上面的循環(huán)中,可以看到,是遍歷了真?zhèn)€庫進行尋找。我們的目的是為了演示,實際情況下,我們可以在找到一個匹配值比較高的人臉后,就跳出循環(huán)。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
本文名稱:android實現(xiàn)人臉識別技術的示例代碼
轉(zhuǎn)載注明:http://www.rwnh.cn/article20/igidjo.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供服務器托管、域名注冊、網(wǎng)站改版、商城網(wǎng)站、外貿(mào)建站、移動網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)