Android平板百度人臉識別開發

weixin_34007291發表於2018-09-25

前言

最近新公司剛來就一個新專案啟動,讓實現一個人臉識別的平板APP用於門店開門(人臉識別只是其中一個模組),剛好現在AI大潮來襲早晚都要接觸這些,這也是個契機,二話不說直接開幹。
目前市面上做人臉方面的公司非常多,列舉幾個:

  • 百度人臉識別
  • Face++
  • 商湯科技
  • 騰訊
  • 虹軟
    當然還有一些其他的,目前比較出名的大概就這些,差別其實更多是在技術支援上(至少我是這麼認為的)以及費用,我接觸的幾個:百度、Face++、虹軟,大概對比下:
  • 百度人臉識
    優點:價格便宜
    缺點:技術支援真的懶得說,常年不線上,找技術基本都是讓看看這個看看那個沒點用
  • Face++
    優點:技術更好,文件很清晰,因為沒有接入暫且不知道技術支援怎麼樣不過應該不差
    缺點:有點小貴
  • 虹軟
    優點:全套離線,後臺前端都可以離線實現,文件也挺詳細的
    缺點:看了下他們技術論壇,貌似問題也不少,而且回覆也不是很及時的樣子,最主要是需要自己搭建一套,他們的人臉對比居然也是放在本地APP資料庫的(當然這不算是缺點了)

說了這麼多,想用啥自己選擇就行,我們公司目前用的百度人臉識別,進入正題不多BB。

一、註冊百度開發者賬號

這個註冊稍微要點時間,大概需要幾個工作日,而且需要公司的資質資訊,我們當時已經有了所以我就直接拿來用

二、新建專案獲取授權檔案

這一步算是前期測試的重要步驟,先要到控制檯(預設第一步已經完成了)連結

這個控制檯基本就是人臉識別的所有開發SDK,技術資料的地方了,前端的SDK在SDK管理裡面進行下載
首先你需要在採集SDK管理裡面下載授權檔案,他會讓你跟你據包名跟key的MD5來進行生成,具體步驟按著他們的操作就行了


1903959-6438dbb8ecc546d5.png
image.png

就是上圖的樣子,這個裡面的License ID和包名都很重要,包名要根據你自己專案的包名來寫。
然後下載License等下會用到

SDK下載跟示例工程下載

在採集SDK管理這裡下拉會看到下載SDK跟示例工程這兩欄
但是我建議你可以直接根據你們公司的業務需求下載對應的示例工程,比如我下載的就是人臉登陸/考勤這個,這裡面已經包含了全套的離線SDK功能(活體檢測,人臉追蹤,質量檢測等等)
下載完示例工程後把專案匯入AS,下面就是改動下包名跟License

  • 修改包名
    在app build裡面改成自己上圖裡面的包名:com.test.facere
  • 修改License
    把剛下載的License檔案匯入到對應的 assets包下,修改工程裡面Config.java的licenseID(上圖的)以及licenseFileName(對應License的檔名)

以上兩步做完沒啥問題示例工程就可以跑起來了

程式碼講解

以我用的人臉登陸/考勤這個示例工程修改優化後的專案做參考來大致講解下(其實每個示例工程都有整合文件,可以自己看)
我們專案目前的業務邏輯就是用人臉對比(1:N)進行人臉識別開門,所以要運用到的就是人臉活體檢測,人臉追蹤以及人臉對比這幾個技術,其中人臉活體檢測跟人臉追蹤是在離線SDK裡面實現的,人臉對比是跟百度雲伺服器進行匹配的,當然我們會在手機端先把人臉進行註冊,不過手機APP端的人臉註冊重心在我們自己的伺服器呼叫百度的人臉註冊,所以手機端就是上傳人臉就OK了。
人臉登陸/考勤這個示例工程這個專案裡面有幾個Activity,主要是註冊人臉,人臉登陸,快速檢測人臉登陸,我用的是快速檢測人臉登陸(DetectLoginActivity.java)
1.FaceDetectManager
這個類封裝了人臉檢測的整體邏輯包括開啟人臉檢測start,關閉人臉檢測stop,設定人臉檢測監聽器setOnFaceDetectListener,設定人檢跟蹤回撥setOnTrackListener

2faceDetectManager.setOnFaceDetectListener設定人臉檢測監聽器
這個監聽器是人臉識別主要方法之一

public interface OnFaceDetectListener {
        void onDetectFace(int status, FaceInfo[] infos, ImageFrame imageFrame);
    }

這是它的回撥方法,裡面包含了人臉檢測狀態 status(用於處理人臉距離角度方向等等檢測),人臉資訊infos,這裡面是一組人臉人資訊不過只用到Infos[0]這個就好,還有封裝了一幀圖片的imageFrame,包含了該幀圖片的大小等資訊

運用這個方法回撥可以實現在攝像預覽上遮蓋一個圓形或者其他圖形的控制元件,提示使用者把人臉放入其中
1903959-d2e2b8587896e526.png
image.png

我是在外圍做了一些動畫效果,修改了一下它本來的這個圓形遮罩

這個類裡面已經有完善的提示功能其他程式碼就暫時不貼了可以對照專案看。
說下遇到的問題

  • 因為我是平板做的攝像頭用的USB所以距離檢測(其實就是人臉的長寬高)就暫時遮蔽了(因為攝像頭跟手機攝像頭相比還是有點模糊)
  • 用的平板USB攝像頭所以會出現映象卡幀,解決辦法就是
ICameraControl control = cameraImageSource.getCameraControl();
control.setPreviewView(previewView);
control.setCameraFacing(ICameraControl.CAMERA_USB);
previewView.getTextureView().setScaleX(-1);

設定為USB模式以及previewView預覽反轉
這裡設定USB模式有個問題,就是這個工程其實是一個手機端的專案,但是我用到了平板端,在ICameraControl這個介面中是沒有CAMERA_USB這個欄位的,需要在裡面加入

int CAMERA_FACING_BACK = 0;
 int CAMERA_FACING_FRONT = 1;
int CAMERA_USB = 2;

    @IntDef({CAMERA_FACING_FRONT, CAMERA_FACING_BACK, CAMERA_USB})
    @interface CameraFacing {
 }

這樣子就可以了

3.faceDetectManager.setOnTrackListener設定人臉檢測監聽器
回撥方法是

public void onTrack(FaceFilter.TrackedModel trackedModel)

乍一看這個回撥跟

faceDetectManager.setOnFaceDetectListener

差不多,其實如果仔細看FaceDetectManager這個類的話會發現在

private void process(int[] argb, int width, int height, ArgbPool pool)

這個方法裡面有這樣一段程式碼

if (value == 0) {
            faceFilter.filter(faces, frame);//等於0的時候才帶過去
        }
        if (listener != null) {
            listener.onDetectFace(value, faces, frame); //檢測人臉把value值也帶過去,用於判斷人臉位置
        }

當value為0(表示是一張合格人臉)的時候會在FaceFilter中呼叫filter方法,並且在該方法中把一個單個face設定到onTrace回撥中,如果listener不為空的話直接放到onDetectFace這個回撥中,所以從這裡也可以看出來其實

faceDetectManager.setOnFaceDetectListener

就是為了讓你獲取一張合格的人臉(可以在這個裡面處理你具體的合格人臉操作)
迴歸正題,既然onTrace回撥是一個合格的人臉就好辦了,可以直接拿到TrackedModel裡面的人臉圖片和伺服器進行比對,具體程式碼邏輯示例程式碼裡面也已經實現了,對比結束後會返回一定的分數給你,如果大於80或者你覺得的分數就認定這個是你在手機端註冊過的人臉,然後進行邏輯處理(比如開門)

遇到的問題

  1. 由於我們的業務需求是要求平板一直執行,就算是識別失敗或者成功也要返回到人臉識別的頁面(不finish人臉識別頁面),造成了效能影響很大,主要現象就是每次返回過來了在進行識別會出現卡住動不了,解決辦法就是在onPause和onResume裡面加上一個是否在前臺的標誌,如果不在前臺就讓人臉識別停止識別,在前臺後繼續識別(不是單純的faceDetectManager.stop()這樣子會報錯)
  2. 由於這個示例工程是手機端的,我拿到平板端使用肯定需要修改,程式碼講解部分已經說了USB這個問題,其實雖然就是兩行程式碼的事,但是當時我試了很久才解決(問百度那邊一直在扯犢子,寫工單也是很久回覆說些沒用的東西,拉了個微信群也是帶理不理的,最後放棄了,自己去研究)
  3. 裁剪處理器出現問題
// 設定檢測裁剪處理器
 faceDetectManager.addPreProcessor(cropProcessor);

這行程式碼放在平板端會報錯,所以需要進行修改,經過除錯發現出錯的原因在FaceCropper這個類裡面

 /**
     * 裁剪argb中的一塊兒,裁剪框如果超出圖片範圍會被調整,所以記得檢查。
     * @param argb 圖片argb資料
     * @param width 圖片寬度
     * @param rect 裁剪框
     */
    public static int[] crop(int[] argb, int width, Rect rect) {
        adjustRect(argb, width, rect);
        int[] image = new int[rect.width() * rect.height()];

        for (int i = rect.top; i < rect.bottom; i++) {
            int rowIndex = width * i;//9
            try {
                    System.arraycopy(argb, Math.abs(rowIndex + rect.left), image, rect.width() * (i - rect.top), rect.width());
            } catch (Exception e) {
                e.printStackTrace();
                return argb;
            }
        }
        return image;
    }

以上是修改後的程式碼,主要是

System.arraycopy(argb, Math.abs(rowIndex + rect.left), image, rect.width() * (i - rect.top), rect.width());

這段,需要把rowIndex+rect.left 變成正數,之前是一個負數造成越界報錯

  1. 判斷臉部的中心點位置用於處理臉是否在對應的佈局裡面
faceDetectManager.setOnFaceDetectListener(new FaceDetectManager.OnFaceDetectListener() { //設定人臉檢測監聽器,檢測後的結果會回撥。
            @Override
            public void onDetectFace(final int retCode, FaceInfo[] infos, ImageFrame frame) {

上面程式碼裡裡面的infos[0] 裡面有兩個引數,一個是臉中心點X軸座標,一個是Y座標

public class FaceInfo {
    public int mWidth;
    public int mAngle;
    public int mCenter_y;
    public int mCenter_x;
    public float mConf;
    public int[] landmarks;
    public int face_id;
    public float[] headPose;
    public int[] is_live;

也就是mCenter_xmCenter_y,有了這倆神器你就可以輕鬆處理臉部位置,讓他在你想要的佈局中,我自己邏輯的完整程式碼:

if (infos[0]!= null) {
                            if(info.mCenter_x<218||info.mCenter_x>600){
                                headXY=false;
                                str ="請把臉移入框內";
                            }else {
                                headXY=true;
                            }
                        }

以上,我也是剛接觸這塊,很多地方也是慢慢琢磨,有問題的地方還請大家多多指教

相關文章