Android平板百度人臉識別開發(fā)

前言

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

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

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

一、注冊百度開發(fā)者賬號

這個注冊稍微要點時間,大概需要幾個工作日,而且需要公司的資質(zhì)信息,我們當(dāng)時已經(jīng)有了所以我就直接拿來用

二、新建項目獲取授權(quán)文件

這一步算是前期測試的重要步驟,先要到控制臺(默認(rèn)第一步已經(jīng)完成了)鏈接

這個控制臺基本就是人臉識別的所有開發(fā)SDK,技術(shù)資料的地方了,前端的SDK在SDK管理里面進(jìn)行下載
首先你需要在采集SDK管理里面下載授權(quán)文件,他會讓你跟你據(jù)包名跟key的MD5來進(jìn)行生成,具體步驟按著他們的操作就行了


image.png

就是上圖的樣子,這個里面的License ID和包名都很重要,包名要根據(jù)你自己項目的包名來寫。
然后下載License等下會用到

SDK下載跟示例工程下載

在采集SDK管理這里下拉會看到下載SDK跟示例工程這兩欄
但是我建議你可以直接根據(jù)你們公司的業(yè)務(wù)需求下載對應(yīng)的示例工程,比如我下載的就是人臉登陸/考勤這個,這里面已經(jīng)包含了全套的離線SDK功能(活體檢測,人臉追蹤,質(zhì)量檢測等等)
下載完示例工程后把項目導(dǎo)入AS,下面就是改動下包名跟License

  • 修改包名
    在app build里面改成自己上圖里面的包名:com.test.facere
  • 修改License
    把剛下載的License文件導(dǎo)入到對應(yīng)的 assets包下,修改工程里面Config.java的licenseID(上圖的)以及l(fā)icenseFileName(對應(yīng)License的文件名)

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

代碼講解

以我用的人臉登陸/考勤這個示例工程修改優(yōu)化后的項目做參考來大致講解下(其實每個示例工程都有集成文檔,可以自己看)
我們項目目前的業(yè)務(wù)邏輯就是用人臉對比(1:N)進(jìn)行人臉識別開門,所以要運(yùn)用到的就是人臉活體檢測,人臉追蹤以及人臉對比這幾個技術(shù),其中人臉活體檢測跟人臉追蹤是在離線SDK里面實現(xiàn)的,人臉對比是跟百度云服務(wù)器進(jìn)行匹配的,當(dāng)然我們會在手機(jī)端先把人臉進(jìn)行注冊,不過手機(jī)APP端的人臉注冊重心在我們自己的服務(wù)器調(diào)用百度的人臉注冊,所以手機(jī)端就是上傳人臉就OK了。
人臉登陸/考勤這個示例工程這個項目里面有幾個Activity,主要是注冊人臉,人臉登陸,快速檢測人臉登陸,我用的是快速檢測人臉登陸(DetectLoginActivity.java)
1.FaceDetectManager
這個類封裝了人臉檢測的整體邏輯包括開啟人臉檢測start,關(guān)閉人臉檢測stop,設(shè)置人臉檢測監(jiān)聽器setOnFaceDetectListener,設(shè)置人檢跟蹤回調(diào)setOnTrackListener

2faceDetectManager.setOnFaceDetectListener設(shè)置人臉檢測監(jiān)聽器
這個監(jiān)聽器是人臉識別主要方法之一

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

這是它的回調(diào)方法,里面包含了人臉檢測狀態(tài) status(用于處理人臉距離角度方向等等檢測),人臉信息infos,這里面是一組人臉人信息不過只用到Infos[0]這個就好,還有封裝了一幀圖片的imageFrame,包含了該幀圖片的大小等信息

運(yùn)用這個方法回調(diào)可以實現(xiàn)在攝像預(yù)覽上遮蓋一個圓形或者其他圖形的控件,提示用戶把人臉放入其中
image.png

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

這個類里面已經(jīng)有完善的提示功能其他代碼就暫時不貼了可以對照項目看。
說下遇到的問題

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

設(shè)置為USB模式以及previewView預(yù)覽反轉(zhuǎn)
這里設(shè)置USB模式有個問題,就是這個工程其實是一個手機(jī)端的項目,但是我用到了平板端,在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設(shè)置人臉檢測監(jiān)聽器
回調(diào)方法是

public void onTrack(FaceFilter.TrackedModel trackedModel)

乍一看這個回調(diào)跟

faceDetectManager.setOnFaceDetectListener

差不多,其實如果仔細(xì)看FaceDetectManager這個類的話會發(fā)現(xiàn)在

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值也帶過去,用于判斷人臉位置
        }

當(dāng)value為0(表示是一張合格人臉)的時候會在FaceFilter中調(diào)用filter方法,并且在該方法中把一個單個face設(shè)置到onTrace回調(diào)中,如果listener不為空的話直接放到onDetectFace這個回調(diào)中,所以從這里也可以看出來其實

faceDetectManager.setOnFaceDetectListener

就是為了讓你獲取一張合格的人臉(可以在這個里面處理你具體的合格人臉操作)
回歸正題,既然onTrace回調(diào)是一個合格的人臉就好辦了,可以直接拿到TrackedModel里面的人臉圖片和服務(wù)器進(jìn)行比對,具體代碼邏輯示例代碼里面也已經(jīng)實現(xiàn)了,對比結(jié)束后會返回一定的分?jǐn)?shù)給你,如果大于80或者你覺得的分?jǐn)?shù)就認(rèn)定這個是你在手機(jī)端注冊過的人臉,然后進(jìn)行邏輯處理(比如開門)

遇到的問題

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

這行代碼放在平板端會報錯,所以需要進(jìn)行修改,經(jīng)過調(diào)試發(fā)現(xiàn)出錯的原因在FaceCropper這個類里面

 /**
     * 裁剪argb中的一塊兒,裁剪框如果超出圖片范圍會被調(diào)整,所以記得檢查。
     * @param argb 圖片argb數(shù)據(jù)
     * @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 變成正數(shù),之前是一個負(fù)數(shù)造成越界報錯

  1. 判斷臉部的中心點位置用于處理臉是否在對應(yīng)的布局里面
faceDetectManager.setOnFaceDetectListener(new FaceDetectManager.OnFaceDetectListener() { //設(shè)置人臉檢測監(jiān)聽器,檢測后的結(jié)果會回調(diào)。
            @Override
            public void onDetectFace(final int retCode, FaceInfo[] infos, ImageFrame frame) {

上面代碼里里面的infos[0] 里面有兩個參數(shù),一個是臉中心點X軸坐標(biāo),一個是Y坐標(biāo)

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 ="請把臉移入框內(nèi)";
                            }else {
                                headXY=true;
                            }
                        }

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容