最近公司須要作一我的臉檢測的新功能,在網上找了找,有不少開源的第三方庫均可以用,例如OpenCV,虹軟,Face++,百度,阿里等等。java
因爲在APP的需求,只能本地檢測,因此Face++,百度,阿里這些須要用HTTP進行網絡請求返回結果的,只能捨棄了。集中研究OpenCV以及虹軟。android
首先介紹一下虹軟,這家公司開源了so庫以及jar包,可直接下載集成到項目中,簡單配置以後就可檢測人臉,並且識別率仍是不錯的。可借鑑此博客點擊打開連接。詳細教你在Android Studio中使用虹軟檢測以及識別人臉。網絡
接下來說一下使用OpenCV開源庫檢測人臉。其實OpenCV很是強大,有興趣的同窗能夠去查閱一下。目前只講一下使用OpenCV經過Camera動態檢測人臉。OpenCV搭建流程可百度,內容不少,這裏僅作簡單說明。app
首先下載OpenCV4Android Demo,新建項目等操做省略,而後倒入OpenCV Samples中的face_detection項目,使用NDK編譯檢測的so庫,倒入OpenCV SDK中的java Module到項目中,在app/src/main目錄下新建jniLibs,複製sdk/native/libs/armeabi-v7a/libopencv_java3.so到jniLibs/armeabi-v7a中(可多選,arm,x86等,因爲我只須要v7a便可,只倒入這個),複製sdk/native/jni/include到jniLibs下。可直接下載代碼OpenCVJ。ide
到此搭建完工程,點擊運行,發現只有橫屏下才能正確檢測到人臉,可是項目需求是在豎屏下檢測人臉,怎麼辦,接着尋找答案,發現這篇博客OpenCV on Android 開發 (4)豎屏預覽圖像問題解決方法-續,在此多謝這位兄臺的先驅行動,使用Core.rotate函數,最後一個參數填入Core.ROTATE_90_CLOCKWISE,旋轉Gray Mat後可正確檢測到人臉,返回MatOfRect,再把MatOfRect放入到mRgba中,再次經過Core.rotate函數,可是最後一個參數需填入Core.ROTATE_90_COUNTERCLOCKWISE,再返回到CameraBridgeViewBase中的deliverAndDrawFrame中,通過此操做後能夠正確顯示出人臉檢測框。函數
使用上述方法就基本完成了。按照慣例,文章沒有寫完,確定會有可是的,沒錯,這裏也有。post
可是:實際使用時,發現幀率只有10幀左右,徹底沒法接受,整個頁面都是卡頓的,怎麼辦,接着尋找方法。想到一個idea,既然是檢測,我只須要OpenCV的檢測功能,不須要OpenCV來本身畫圖,直接使用camera的預覽效果,我只把人臉檢測框畫到預覽圖上面去就好,這樣能夠保證預覽不卡頓,只是檢測框可能要一點時間才能顯示,這也是沒法避免的了。優化
首先看xml文件: ` <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:opencv="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">idea
<org.opencv.android.JavaCameraView android:id="@+id/fd_activity_surface_view" android:layout_width="match_parent" android:layout_height="match_parent" opencv:camera_id="back" /> <Button android:id="@+id/fd_switch_camera_view" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="220dp" android:layout_marginRight="20dp" android:layout_marginTop="20dp"/> <ImageView android:id="@+id/fd_image_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
使用JavaCameraView開啓Camera,id爲fd_switch_camera_view爲切換前置後置攝像頭,id爲fd_image_view用來顯示人臉檢測框。 再看FdActivity.java文件,只展現主要修改地方: ` @Override public Mat onCameraFrame(CvCameraViewFrame inputFrame) { mGray = inputFrame.gray(); Utils.bitmapToMat(mAlphaBitmap, mRgba); //使前置的圖像也是正的 if (mOpenCvCameraView.getCameraIndex() == CameraBridgeViewBase.CAMERA_ID_FRONT) { Core.flip(mRgba, mRgba, 1); Core.flip(mGray, mGray, 1); } if (mAbsoluteFaceSize == 0) { int height = mGray.rows(); if (Math.round(height * mRelativeFaceSize) > 0) { mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize); } if (mNativeDetector != null) { mNativeDetector.setMinFaceSize(mAbsoluteFaceSize); } } MatOfRect faces = new MatOfRect(); Core.rotate(mGray, gMatlin, Core.ROTATE_90_CLOCKWISE); Core.rotate(mRgba, Matlin, Core.ROTATE_90_CLOCKWISE); if (mNativeDetector != null) { mNativeDetector.detect(gMatlin, faces); } Rect[] faceArray = faces.toArray(); for (Rect rect : faceArray) { Imgproc.rectangle(Matlin, rect.tl(), rect.br(), new Scalar(0, 255, 0, 255), 2); } Core.rotate(Matlin, mRgba, Core.ROTATE_90_COUNTERCLOCKWISE); deliverAndDrawFrame(mRgba); return mRgba; } protected void deliverAndDrawFrame(Mat modified) { boolean bmpValid = true; if (modified != null) { try { Utils.matToBitmap(modified, mCacheBitmap, true); } catch (Exception e) { Log.e(TAG, "Mat type: " + modified); Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight()); Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); bmpValid = false; } } if (bmpValid && mCacheBitmap != null) { mHandler.post(new Runnable() { @Override public void run() { Matrix matrix = new Matrix(); // I rotate it with minimal process matrix.preTranslate((mViewWidth - mCacheBitmap.getWidth()) / 2, (mViewHeight - mCacheBitmap.getHeight()) / 2); matrix.postRotate(90f, (mViewWidth) / 2, (mViewHeight) / 2); float scale = (float) mViewWidth / (float) mCacheBitmap.getHeight(); matrix.postScale(scale, scale, mViewWidth / 2, mViewHeight / 2); // final Matrix matrix = new Matrix(); // I rotate it with minimal process // matrix.preTranslate((mViewWidth - mCacheBitmap.getWidth()) / 2, (mViewHeight - mCacheBitmap.getHeight()) / 2); // matrix.postRotate(90, mViewWidth / 2, mViewHeight / 2); // float scale = (float) mViewWidth / (float) mViewHeight; // matrix.postScale(scale, scale, mViewWidth / 2, mCacheBitmap.getHeight() / 2); Bitmap bitmap = Bitmap.createBitmap(mCacheBitmap, 0, 0, mCacheBitmap.getWidth(), mCacheBitmap.getHeight(), matrix, false); mImageView.setImageBitmap(bitmap); } }); } }
上面兩部分代碼都爲FdActivity中,將MatOfRect貼到Mat中,再轉換成Bitmap顯示到ImageView中。code
JavaCameraView.java的修改:
initializeCamera函數增長
mCamera.setPreviewDisplay(getHolder());
mCamera.setDisplayOrientation(90);
CameraBridgeBaseView.java的修改 ` protected void deliverAndDrawFrame(CvCameraViewFrame frame) { Mat modified;
if (mListener != null) { modified = mListener.onCameraFrame(frame); } else { modified = frame.rgba(); }
}
只須要調用onCameraFrame的回調便可,不用再貼圖了。 到此基本完成。 後續還須要優化,等優化完再更新。。。。 ----------更新更新----------- 回調onCameraFrame函數時,將Mat先縮小,而後再開始檢測,能夠大幅提高檢測速度。 使用Imgproc.resize()函數便可,具體使用可網上查閱。