Android人臉識別開發(fā)入門--基于虹軟免費(fèi)SDK實(shí)現(xiàn)

引言

人工智能時(shí)代快速來臨,其中人臉識別是當(dāng)前比較熱門的技術(shù)介返,在國內(nèi)也越來越多的運(yùn)用拴事,例如刷臉打卡,刷臉APP圣蝎,身份識別刃宵,人臉門禁等。當(dāng)前的人臉識別技術(shù)分為WEBAPI和SDK調(diào)用兩種方式徘公,WEBAPI需要實(shí)時(shí)聯(lián)網(wǎng)组去,SDK調(diào)用可以離線使用。

Android作為一個(gè)比較廣泛的平臺步淹,如何實(shí)現(xiàn)人臉識別功能呢从隆。

本文章將以一個(gè)示例的形式介紹一下我在這方面的經(jīng)驗(yàn)。

本次使用的虹軟提供的人臉識別的SDK缭裆,此SDK也可根據(jù)不同應(yīng)用場景設(shè)計(jì)键闺,針對性強(qiáng)惕艳。包括人臉檢測余素、人臉跟蹤、人臉識別迈窟,即使在離線環(huán)境下也可正常運(yùn)行缝其。
虹軟公司是一家具有硅谷背景的圖像處理公司挎塌,除了人臉技術(shù)以外,還有多項(xiàng)圖像及視頻處理技術(shù)内边。他們的雙攝像頭處理算法和人臉美化算法囊括了包括OPPO VIVO榴都,SUMAMNG一系列手機(jī)廠商。

項(xiàng)目的目標(biāo)

我們需要實(shí)現(xiàn)一個(gè)人臉識別功能漠其。簡單來說嘴高,就是機(jī)的后置攝像頭,識別攝像頭中實(shí)時(shí)拍到的人臉信息和屎,如果人庫注冊過拴驮,則顯示識別后的人臉信息,如登記的名字柴信;如果不在套啤,提示未注冊。
這個(gè)功能具有多個(gè)應(yīng)用場景随常,比如潜沦,火車站或者打卡和門禁系統(tǒng)中。

人臉識別的過程

人臉識別包括兩個(gè)必備的過程线罕,人臉注冊和實(shí)時(shí)識別止潮。
人臉注冊是指把人臉的特征信息注冊到人臉信息庫中。人臉注冊的來源可以有很多種钞楼,比如

國家身份證庫

企業(yè)自建人臉識別庫

互聯(lián)網(wǎng)大數(shù)據(jù)庫

人臉特征提取是一個(gè)不可逆的過程喇闸,你無法從人臉特征信息還原一個(gè)人的臉部照片。

在線庫在使用時(shí)询件,需要傳遞照片信息燃乍,或者提取圖像特征值,

離線的SDK相對安全宛琅,但是刻蟹,在線的SDK通常提供更多的接入和調(diào)用方式,這個(gè)要結(jié)合實(shí)際情況來選擇嘿辟。

人臉注冊和識別的過程可以用下面的圖來表示舆瘪。

image
image

準(zhǔn)備工作

在開發(fā)之前需要到虹軟的官網(wǎng)
http://www.arcsoft.com.cn/ai/arcface.html
下載用到的android庫片效,下載的壓縮包中有3個(gè)壓縮包,如下圖:

2.png

其中英古,第一個(gè)Face Detection淀衣,人臉檢測。

人臉檢測是人臉技術(shù)的基礎(chǔ)召调,使用虹軟人臉引擎膨桥,能夠幫助您檢測并且定位到影像(圖片或者視頻)中的人臉。

第二個(gè)Face Recognition唠叛,

人臉識別只嚣。引擎可獨(dú)立運(yùn)行在終端設(shè)備或者獨(dú)立服務(wù)器中,應(yīng)用端可獨(dú)立完成算法運(yùn)行艺沼,能保證用戶數(shù)據(jù)的私密性册舞,自主運(yùn)營與保護(hù)用戶敏感信息。
第三個(gè)Face Tracking澳厢,

人臉跟蹤环础。精確定位并追蹤面部區(qū)域位置,隨著人物臉部位置的變化能夠快速定位人臉位置剩拢,并且適用于不同表情线得、性別、年齡徐伐、姿態(tài)贯钩、光照等條件。

這三包的結(jié)構(gòu)基本相同办素,我們需要把它們解壓角雷。

  • doc 此目錄中存放GUIDE文檔,是說明文檔性穿,里面介紹了公開發(fā)布的一些API勺三,并提供了示例代碼。
  • libs 開發(fā)中需要用到的庫需曾,需要把它們目錄結(jié)構(gòu)不變的全部引用到你的項(xiàng)目項(xiàng)目中吗坚。
  • sampleCode 示例代碼

注意:開發(fā)中還需要APP_Id和SDK_Key的激活碼,這些激活碼可以在賬號管理--》您的申請記錄呆万,對應(yīng)的下載應(yīng)用中找到相應(yīng)的激活碼商源。

版本與環(huán)境要求

根據(jù)SDK的說明,我們使用的版本為android arm32谋减,版本為5.0.我們使用的IDE為android studio,你也可以使用eclipse牡彻,不過依然建議你使用android studio,因?yàn)樗F(xiàn)在已經(jīng)是事實(shí)上的標(biāo)準(zhǔn)出爹。

一步一步實(shí)現(xiàn)人臉識別功能

本文將以這三個(gè)庫為基礎(chǔ)庄吼,從人臉注冊開始缎除,到人臉識別結(jié)束。全程演示人臉識別的流程总寻。如果你不想從頭開始伴找,你可以到https://github.com/asdfqwrasdf/ArcFaceDemo 下載完整的示例程序

新建項(xiàng)目

打開android studio,建立項(xiàng)目,API兼容性選擇4.0废菱。

導(dǎo)入依賴包

虹軟人臉SDK的包是so包,你可以在下載的壓縮包中把這些文件找到并導(dǎo)入抖誉。
導(dǎo)入后的工程文件夾如下所示殊轴。

image.png

定義并實(shí)現(xiàn)人臉庫的相關(guān)功能

如前面所述,我們希望定義自己 的人臉庫袒炉,人臉庫在程序中使用List存儲旁理,在系統(tǒng)中保存為txt文件。

通過查詢引擎我磁,可以知道人臉信息是保存在AFR_FSDKFace類中的孽文。這的主要結(jié)構(gòu)為

  public static final int FEATURE_SIZE = 22020;
  byte[] mFeatureData;

如果要進(jìn)行人臉注冊,我們需要定義另外一個(gè)類來把人臉信息和姓名關(guān)聯(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需要包含引擎定義郁副,初始化减牺,把人臉信息保存在版本庫和從版本庫中讀出人臉信息這些功能

初始化引擎

為了程序結(jié)構(gòu)性考慮,我們將人臉識別相關(guān)的代碼獨(dú)立出來一個(gè)類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();
        }

    }

實(shí)現(xiàn)人臉增加和讀取功能

通常人臉庫會(huì)存放在數(shù)據(jù)庫中存谎,本次我們使用List來進(jìn)行簡單的模擬拔疚,并將其保存在文本文件中,需要時(shí)從文本中讀取既荚,保存時(shí)寫入到文件中稚失。

我們使用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;
    }

實(shí)現(xiàn)業(yè)務(wù)邏輯

實(shí)現(xiàn)人臉注冊功能

人臉識別的前提條件就是人臉信息要先注冊到人臉庫中,注冊人臉庫

第一步當(dāng)然是獲取待注冊的照片恰聘,我們可以可以使用攝像頭句各,也可以使用照片。我們使用AlertDialog彈出選擇框

new AlertDialog.Builder(this)
                        .setTitle("請選擇注冊方式")
                        .setIcon(android.R.drawable.ic_dialog_info)
                        .setItems(new String[]{"打開圖片", "拍攝照片"}, this)
                        .show();

在對應(yīng)的事件處理函數(shù)中進(jìn)行處理

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ù)我們就需要實(shí)現(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中實(shí)現(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中對應(yīng)的是FD惧所,提供了一個(gè)方法名稱骤坐,叫AFD_FSDK_StillImageFaceDetection 。
我們來看一下參數(shù)列表

類型 名稱 說明
byte[] data 輸入的圖像數(shù)據(jù)
int width 圖像寬度
int height 圖像高度
int format 圖像格式
List<AFD_FSDKFace> list 檢測到的人臉會(huì)放到到該列表里下愈。

注意AFD_FSDKFace對象引擎內(nèi)部重復(fù)使用,如需保存,請clone一份AFD_FSDKFace對象或另外保存

AFD_FSDKFace是人臉識別的結(jié)果纽绍,定義如下

public class AFD_FSDKFace {
    Rect mRect;
    int mDegree;
    }

mRect定義一個(gè)了一個(gè)矩形框Rect

在此之前我們需要注意虹軟人臉SDK使用的圖像格式是NV21的格式,所以我們需要將獲取到的圖像轉(zhuǎn)化為對應(yīng)的格式势似。在Android_extend.jar中提供了對應(yīng)的轉(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方法了

err  = engine.AFD_FSDK_StillImageFaceDetection(data, mBitmap.getWidth(), mBitmap.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, result);

繪出人臉框

在List<AFD_FSDKFace>中保存了檢測到的人臉的位置信息和深度信息拌夏。
我們可以將檢測到的人臉位置信息在圖片上用一個(gè)矩形框繪制出來表示檢測到的人臉信息。

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;
   }
}

將人臉注冊到人臉庫

檢測到了人臉履因,我們可以輸入相應(yīng)的描述信息障簿,加入到人臉庫中。

為了提高識別的準(zhǔn)確性栅迄,我們可以對一個(gè)人多次注冊人臉信息站故。

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()); 

實(shí)現(xiàn)人臉識別

上面的代碼準(zhǔn)備完畢后毅舆,就可以開始我們的人臉識別的功能了西篓。我們使用一個(gè)第三方的擴(kuò)展庫,ExtGLSurfaceView的擴(kuò)展 庫CameraGLSurfaceView憋活,用ImageView和TextView顯示檢測到的人臉和相應(yīng)的描述信息岂津。

首先是定義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>

因?yàn)橐嫘枰膱D像格式是NV21的余掖,所以需要將攝像頭中的圖像格式預(yù)設(shè)置為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庫在人臉跟蹤算法上對人臉檢測部分進(jìn)行了優(yōu)化盐欺,是專門為視頻處理而優(yōu)化的庫赁豆。

初始化人臉檢測引擎(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());

在攝像頭的預(yù)覽事件處理函數(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)中的其它信息進(jìn)行對比。

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;
      }
   }
}

當(dāng)score的特征信息大于0.6時(shí)属韧,我們就可以認(rèn)為匹配到了人臉安拟。顯示人臉匹配信息。

上面的循環(huán)中宵喂,可以看到糠赦,是遍歷了真?zhèn)€庫進(jìn)行尋找。我們的目的是為了演示,實(shí)際情況下拙泽,我們可以在找到一個(gè)匹配值比較高的人臉后淌山,就跳出循環(huán)。

運(yùn)行結(jié)果

我們來看一下運(yùn)行的結(jié)果顾瞻。
效果還不錯(cuò)吧泼疑。鐘漢良帥哥一枚。

zhl.png

本文檔中所有的代碼都可以在https://github.com/asdfqwrasdf/ArcFaceDemo 下載荷荤。如果你需要尋找更多的人臉識別的demo退渗,也可以到虹軟的論壇中去尋找。
http://www.arcsoft.com.cn/bbs/forum.php?mod=forumdisplay&fid=45&page=1

附錄:會(huì)遇到的問題及解決方案

如果你使用的是github中的示例蕴纳,你可能會(huì)遇到下面的問題氓辣。

Plugin with id 'com.android.application' not found

直接從github上下載的源代碼會(huì)有這個(gè)問題。

解決方案:打開 [項(xiàng)目文件夾]\app\build.gradle 文件

在文件末尾添加

buildscript {

    repositories {

        mavenCentral()

    }

    dependencies {

        classpath 'com.android.tools.build:gradle:1.0.0'

    }

}

Failed to find build tools revision 25.0.0.2

這個(gè)主要是build 的版本和gradle中指定的版本不一致袱蚓,按照提示下載或者修改版本指定就可以了。

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.2"
    }

install_failed_no_maching_abis

下載的代碼在gradle編譯完成后几蜻,直接默認(rèn)運(yùn)行會(huì)出現(xiàn)這個(gè)錯(cuò)誤喇潘。原因是由于使用了native libraries 。該native libraries 不支持當(dāng)前的cpu的體系結(jié)構(gòu)梭稚。
首先請檢查是否導(dǎo)入了必要的so文件颖低。一共需要導(dǎo)入四個(gè).so文件。
另外弧烤,請確認(rèn)使用是的真機(jī)調(diào)試忱屑。因?yàn)檎{(diào)用了攝像頭,請使用真機(jī)調(diào)試暇昂。

后記

人臉識別是當(dāng)前的熱點(diǎn)技術(shù)莺戒,使用范圍廣,用戶體驗(yàn)良好急波,對硬件的依賴低从铲,不需要昂貴的傳感器芯片。一個(gè)高清的攝像頭就可以完成澄暮。以前的成本是人臉識別的SDK比較貴名段,但現(xiàn)在虹軟的SDK免費(fèi)之后,集成的成本就大大降低了泣懊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伸辟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子馍刮,更是在濱河造成了極大的恐慌信夫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異忙迁,居然都是意外死亡脐彩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門姊扔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惠奸,“玉大人,你說我怎么就攤上這事恰梢》鹉希” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵嵌言,是天一觀的道長嗅回。 經(jīng)常有香客問我,道長摧茴,這世上最難降的妖魔是什么绵载? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮苛白,結(jié)果婚禮上娃豹,老公的妹妹穿的比我還像新娘。我一直安慰自己购裙,他們只是感情好懂版,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著躏率,像睡著了一般躯畴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薇芝,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天蓬抄,我揣著相機(jī)與錄音,去河邊找鬼夯到。 笑死倡鲸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的黄娘。 我是一名探鬼主播峭状,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逼争!你這毒婦竟也來了优床?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤誓焦,失蹤者是張志新(化名)和其女友劉穎胆敞,沒想到半個(gè)月后着帽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡移层,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年仍翰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片观话。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡予借,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出频蛔,到底是詐尸還是另有隱情灵迫,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布晦溪,位于F島的核電站瀑粥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏三圆。R本人自食惡果不足惜狞换,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舟肉。 院中可真熱鬧哀澈,春花似錦、人聲如沸度气。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磷籍。三九已至,卻和暖如春现柠,著一層夾襖步出監(jiān)牢的瞬間院领,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工够吩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留比然,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓周循,卻偏偏與公主長得像强法,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子湾笛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容