Android Camera動態人臉識別+人臉檢測基於OpenCV(無需OpenCVManager)

       最近因爲需要實現了一下人臉識別,實現了這個功能之後,就想着把踩過的坑總結出來。參考過許多博客,發現主要有兩種形式,一種是基於 android SDK 實現人臉檢測,而另一種是利用openCvManager來實現。這兩種方式都實現了動態的人臉檢測,當然網上也有許多關於檢測靜態圖片中的人臉的文章。這裏我就不詳細介紹了。我這裏實現的功能主要是啓動Camera之後,當照相機中有人臉出現時,Camera界面動態的繪製出人臉矩形框,獲取到人臉框之後,Camera拍照,獲取到圖片,將圖片傳送到後臺來驗證人臉。這裏需要區分一下人臉檢測和人臉識別。人臉檢測是檢測camera中是否有人臉存在,如果存在的話會繪製出矩形框將人臉標誌出來,而人臉識別則是根據兩張人臉驗證是否是一個人的概念。
        先大概講一下第一種實現動態人臉檢測的方式.這是Camera基於Google自帶的FaceDetectionListener進行人臉檢測,當Camera拍攝到人臉後,會將人臉矩形繪製出來。這個具體大家可以參考這位大神的博客,http://blog.csdn.net/yanzi1225627/article/details/38098729/
他已經講的很細緻了。但感覺google自帶檢測算法在測試的時候檢測人臉比較慢,而且有些機型不支持這種人臉檢測接口,不過他的文章還是很具有參考價值的。那麼我就在這裏就具體介紹一下使用openCv實現動態人臉檢測的具體方法。

       先提供下opencv下載地址http://opencv.org/ 我下載的是最新的3.2版本,本來用的是2.4,但會發現比較卡頓。公司一直使用的eclipse,而且demo中的工程也是eclipse項目,所以直接使用eclipse來進行調試。當然網上也有許多在As中使用的教程,大家可以去多找找。
在我們下載sdk後解壓後,進入到samples目錄下會見到如下結構:

       我們直接運行example-face-detection.apk後,會發現提示要安裝opencv-manager,在sdk/apk目錄下,我們會發現不同種類的opencvmanager,我們根據不同的機型選擇對應的manager安裝,我選擇的是OpenCV_3.2.0_Manager_3.20_armeabi-v7a.apk這個包,安裝之後,我們打開應用後就可以發現攝像頭進行動態的人臉檢測了。
運行完官方的apk後,我們開始自己進行調試。我們導入OpenCV-android-sdk/samples的face-detection工程,然後新建個工程作爲Library,在我們的library中導入OpenCV-android-sdk/java中相關類和資源文件,

       然後對face-detection進行關聯,這時候我的工程仍然報錯,發現下面的錯誤:

       沒有找到ndk-build.cmd.原來是沒有配置ndk-build路徑,配置完NDKROOT即可。這裏就是右鍵properties-C/C++build,如下圖:

       這裏的ndk我下載的是android-ndk-r9d,在下載完畢之後我們直接解壓就行,這裏的NDKROOT就是我們解壓後的ndk的路徑。附帶上Ndk的下載路徑:
http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86.zip
       之後又碰見如下錯誤:

       這是因爲在jni/Android.mk中沒配置opencv的Opencv.mk的根路徑,同理和ndk配置方式一樣,我們配置完我們自己的${OPENCV_ANDROID_SDK}就行了

       至此,我們運行官方的demo,可直接就運行出來,但我們發現總需要安裝opencvmanager,很明顯,有時候這是不合適的。所以接下來我介紹下不需要安裝opencvmanager的方法,很簡單,分下面4步:
1.修改Android.mk前幾行 爲下列形式,

include $(CLEAR_VARS)
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED

2.打開FdActivity.java文件,在其中添加一個靜態初始化塊代碼,

 static {
        Log.i(TAG, "OpenCV library load!");
        if (!OpenCVLoader.initDebug()) {
            Log.i(TAG, "OpenCV load not successfully");
        } else {
           System.loadLibrary("detection_based_tracker");// load other libraries
        }
    }

它是用來加載OpenCV_java庫的,如果沒有加回報類似於native method not found的錯誤
3.註釋掉onResume中的initAsync那句,讓程序不去訪問OpenCV Manager。

@Override
public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
       //     OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

4.修改FdActivity.java的OnCreate()方法,從上面的private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this)代碼塊中拷貝try-catch塊放到OnCreate的setContentView()之後。

      try {
            // load cascade file from application resources
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
            FileOutputStream os = new FileOutputStream(mCascadeFile);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            is.close();
            os.close();

            mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
            if (mJavaDetector.empty()) {
                Log.e(TAG, "Failed to load cascade classifier");
                mJavaDetector = null;
            } else
                Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());

            mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);

            cascadeDir.delete();

        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
        }

之後運行,即可發現不用在依賴OpenCvManager,程序也可以直接運行了。
這裏上述進行的主要是camera動態監測出人臉,其實這基本上已經算完成了。現在我們集成到我們自己的工程中,通過利用百度的人臉識別,來判斷人臉是否一致,從而實現人臉識別。

首先,我們可以在百度AI開放平臺上 http://ai.baidu.com/ 創建我們自己的人臉識別應用
![這裏寫圖片描述](https://img-blog.csdn.net/20170712160653919? /2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjg5MzE2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

創建完後 我們的應用會有一個APPiD和ApI key我們利用這兩個屬性值可生成access_token具體的調用方式我們可參考
https://cloud.baidu.com/doc/FACE/Face-API.html生成access_token後,當發送post請求時,附帶上這個token即可,我們可以進行人臉識別了。我這裏調用的是人臉註冊和人臉認證接口,通過認證返回的分數值,我們可判斷是否是同一個人。具體請求參數和返回參數,大家可以參考百度人臉識別的API。
好了,現在開始創建我們的應用。在創建完工程後,我們把face-detection中的FdActivity和DetectionBasedTracker導入到我們自己的工程中,這之中的FdActivity相當於相機界面,這裏注意不要改動包名,否則可能會出現找不到本地方法的錯誤。同時,我們要將jni和Lib armead目錄複製到我們的目錄裏,之後我們要利用Eclipse自動編譯NDK/JNI。下面簡單介紹一下自動編譯的方法:

1.將Android項目轉換爲C/C++項目,如下圖,New -> Other -> C/C++ -> Convert to a C/C++ Project.

2. 配置NDK編譯路徑,Project->Properties,如下圖,其中Build-Command中ANDROID_NDK爲環境變量中配置的Android-NDK路徑;Build-Directory爲當前工程目錄

3.Project->Properties,CNU C和GNU C++中配置OpenCV的鏈接庫


配置完指後,我們需要稍微修改一下我們自己創建的opencvLibrary的內容。因爲camera中出現人臉框後需要拍照功能,我們需要簡單的修改OpenCvLibrary中的JavaCameraView,在這個類裏面我們可以調用camera的takepicture方法。同時通過PictureCallback將我們拍的照片保存到我們指定的路徑。

mCamera.takePicture(mShutterCallback, null, mJpegPictureCallback);
ShutterCallback mShutterCallback = new ShutterCallback() 
    {
        public void onShutter() {
        }
    };

    PictureCallback mJpegPictureCallback = new PictureCallback() 
    {
        public void onPictureTaken(byte[] data, Camera camera) {
            Log.d("hr", "拍照回調");
            Bitmap b = null;
            if (null != data) {
                b = BitmapFactory.decodeByteArray(data, 0, data.length);
                mCamera.stopPreview();
            }
            if (null != b) {
                Bitmap rotaBitmap = ImageUtil.getRotateBitmap(b, 00.0f);
                boolean bitmap = FileUtil.saveBitmap(rotaBitmap,idTag);
                if (bitmap) {
                    if (handler!=null) {
                        handler.sendEmptyMessage(2000);
                    }       
                }
            }
            mCamera.startPreview();
        }};

在FdActivity的 onCameraFramed中 facesArray 得長度 即是人臉顯示的數目

Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++)
            Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);

當相機捕捉到人臉後,我們可以通過handle自動拍照也可以手動拍照,這就看具體的需求了。保存圖片之後,我們調用百度的接口,根據返回值來判斷人體的相似度。

            url = new URL(urlPath);
            Bitmap bmp = FileUtil.getValidateBitmap();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            try {
                baos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            byte[] buffer = baos.toByteArray();
            // 將圖片的字節流數據加密成base64字符輸出
            String photo = Base64.encodeToString(buffer, 0, buffer.length,Base64.DEFAULT);
            HashMap<String, String> map=new HashMap<String, String>();
            map.put("uid", uid);
            map.put("group_id", groupid);
            map.put("image", photo);
            map.put("ext_fields", "faceliveness");
            String str=HttpClientUtil.doPost(urlPath, map);

獲取驗證和註冊的方式代碼基本上一樣,就是發送普通的網絡請求,我們根據返回值可以判斷出人臉是否一致。至此整個人臉驗證流程就算結束了。我這裏就光展示一下動態檢測人臉的效果
這裏寫圖片描述
當然還有認證的流程,因爲感覺難點主要在動態識別人臉這塊,所以調用百度的接口,實現人臉識別的過程就不再詳細介紹了。當然 ,還有一些小問題,當豎屏時,檢測不準確或者Camera界面沒有全屏, 或者是前置攝像頭的開啓。大家有興趣的話可以去研究一下。
最後附帶上我的demo地址,http://download.csdn.net/download/qq_28931623/10167057

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章