Android 实现人脸识别教程[运用虹软人脸识别SDK]

基于虹软人脸识别引擎,在Android平台上实现人脸识别功能,即使在离线的情况下依旧运行,不被人采集个人照片的感觉,还是爽爽的。经过整个测试过来,虹软的人脸识别还是很强大的,人脸检测可以控制在20ms之内,人脸识别大概在200ms左右。今天就来分享一下开发经验

项目的目标

我们需要实现一个人脸识别功能。简单来说,就是机的后置摄像头,识别摄像头中实时拍到的人脸信息,如果人库注册过,则显示识别后的人脸信息,如登记的名字;如果不在,提示未注册。 
这个功能具有多个应用场景,比如,火车站或者打卡和门禁系统中。

人脸识别的过程

人脸识别包括两个必备的过程,人脸注册和实时识别。 
人脸注册是指把人脸的特征信息注册到人脸信息库中。人脸注册的来源可以有很多种,比如

国家身份证库

企业自建人脸识别库

互联网大数据库

人脸特征提取是一个不可逆的过程,你无法从人脸特征信息还原一个人的脸部照片。
  • 1
  • 1
在线库在使用时,需要传递照片信息,或者提取图像特征值,
  • 1
  • 1
离线的SDK相对安全,但是,在线的SDK通常提供更多的接入和调用方式,这个要结合实际情况来选择。
  • 1
  • 1

人脸注册和识别的过程可以用下面的图来表示。 

准备工作

在开发之前需要到虹软的官网 
http://www.arcsoft.com.cn/ai/arcface.html 
下载用到的android库,下载的压缩包中有3个压缩包,如下图: 

其中,第一个Face Detection,人脸检测。 
人脸检测是人脸技术的基础,使用虹软人脸引擎,能够帮助您检测并且定位到影像(图片或者视频)中的人脸。

第二个Face Recognition,

人脸识别。引擎可独立运行在终端设备或者独立服务器中,应用端可独立完成算法运行,能保证用户数据的私密性,自主运营与保护用户敏感信息。 
第三个Face Tracking,

人脸跟踪。精确定位并追踪面部区域位置,随着人物脸部位置的变化能够快速定位人脸位置,并且适用于不同表情、性别、年龄、姿态、光照等条件。

这三包的结构基本相同,我们需要把它们解压。

  • doc 此目录中存放GUIDE文档,是说明文档,里面介绍了公开发布的一些API,并提供了示例代码。
  • libs 开发中需要用到的库,需要把它们目录结构不变的全部引用到你的项目项目中。
  • sampleCode 示例代码
注意:开发中还需要APP_Id和SDK_Key的激活码,这些激活码可以在账号管理--》您的申请记录,对应的下载应用中找到相应的激活码。
  • 1
  • 1

版本与环境要求

根据SDK的说明,我们使用的版本为android arm32,版本为5.0.我们使用的IDE为android studio,你也可以使用eclipse,不过依然建议你使用android studio,因为它现在已经是事实上的标准。

一步一步实现人脸识别功能

本文将以这三个库为基础,从人脸注册开始,到人脸识别结束。全程演示人脸识别的流程。如果你不想从头开始,你可以到https://github.com/asdfqwrasdf/ArcFaceDemo 下载完整的示例程序

新建项目

打开android studio,建立项目,API兼容性选择4.0。

导入依赖包

虹软人脸SDK的包是so包,你可以在下载的压缩包中把这些文件找到并导入。 
导入后的工程文件夹如下所示。

定义并实现人脸库的相关功能

如前面所述,我们希望定义自己 的人脸库,人脸库在程序中使用List存储,在系统中保存为txt文件。

通过查询引擎,可以知道人脸信息是保存在AFR_FSDKFace类中的。这的主要结构为

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

我们需要定义另外一个类来把人脸信息和姓名关联起来。

class FaceRegist {
        String mName;
        List<AFR_FSDKFace> mFaceList;

        public FaceRegist(String name) {
            mName = name;
            mFaceList = new ArrayList<>();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

包含特征信息的长度和内容的byte数组。 
我们把这些功能定义在类FaceDB中。FaceDB需要包含引擎定义,初始化,把人脸信息保存在版本库和从版本库中读出人脸信息这些功能

初始化引擎

为了程序结构性考虑,我们将人脸识别相关的代码独立出来一个类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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

定义有参数的构造函数来初始化引擎

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());
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

定义析构函数释放引擎占用的系统资源

public void destroy() {
        if (mFREngine != null) {
            mFREngine.AFR_FSDK_UninitialEngine();
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

实现人脸增加和读取功能

通常人脸库会存放在数据库中,本次我们使用List来进行简单的模拟,并将其保存在文本文件中,需要时从文本中读取,保存时写入到文件中。

我们使用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();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

使用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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

实现业务逻辑

实现人脸注册功能

人脸识别的前提条件就是人脸信息要先注册到人脸库中,注册人脸库

第一步当然是获取待注册的照片,我们可以可以使用摄像头,也可以使用照片。我们使用AlertDialog弹出选择框

new AlertDialog.Builder(this)
                        .setTitle("请选择注册方式")
                        .setIcon(android.R.drawable.ic_dialog_info)
                        .setItems(new String[]{"打开图片", "拍摄照片"}, this)
                        .show();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在对应的事件处理函数中进行处理

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:;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

获取一张照片后,后续我们就需要实现人脸检测功能。

if (requestCode == REQUEST_CODE_IMAGE_OP && resultCode == RESULT_OK) {
            mPath = data.getData();
            String file = getPath(mPath);
            //TODO: add image coversion
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在上面的代码中,我们获取到了我们需要的图像数据bmp,把图片取出来 
我们在Application类用函数 decodeImage中实现这段代码

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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

调用AFD_FSDK_StillImageFaceDetection返回检测到的人脸信息

人脸注册 ,首先要先检测出来人脸,对于静态图片,虹软人脸SDK中对应的是FD,提供了一个方法名称,叫AFD_FSDK_StillImageFaceDetection 。 
我们来看一下参数列表

类型 名称 说明 
byte[] data 输入的图像数据 
int width 图像宽度 
int height 图像高度 
int format 图像格式 
List list 检测到的人脸会放到到该列表里。

注意AFD_FSDKFace对象引擎内部重复使用,如需保存,请clone一份AFD_FSDKFace对象或另外保存

AFD_FSDKFace是人脸识别的结果,定义如下

public class AFD_FSDKFace {
    Rect mRect;
    int mDegree;
    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

mRect定义一个了一个矩形框Rect

在此之前我们需要注意虹软人脸SDK使用的图像格式是NV21的格式,所以我们需要将获取到的图像转化为对应的格式。在Android_extend.jar中提供了对应的转换函数

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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

现在我们就可以调用AFD_FSDK_StillImageFaceDetection方法了

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

绘出人脸框

在List中保存了检测到的人脸的位置信息和深度信息。 
我们可以将检测到的人脸位置信息在图片上用一个矩形框绘制出来表示检测到的人脸信息。

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;
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

将人脸注册到人脸库

检测到了人脸,我们可以输入相应的描述信息,加入到人脸库中。

为了提高识别的准确性,我们可以对一个人多次注册人脸信息。

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();
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

最后,别忘记了销毁人脸检测引擎哦

err = engine.AFD_FSDK_UninitialFaceEngine();
Log.d("com.arcsoft", "AFD_FSDK_UninitialFaceEngine =" + err.getCode());
  • 1
  • 2
  • 1
  • 2

实现人脸识别

上面的代码准备完毕后,就可以开始我们的人脸识别的功能了。我们使用一个第三方的扩展库,ExtGLSurfaceView的扩展 库CameraGLSurfaceView,用ImageView和TextView显示检测到的人脸和相应的描述信息。

首先是定义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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

因为引擎需要的图像格式是NV21的,所以需要将摄像头中的图像格式预设置为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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

从摄像头识别人脸,需要使用FT库,FT库在人脸跟踪算法上对人脸检测部分进行了优化,是专门为视频处理而优化的库。

初始化人脸检测引擎(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());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

在摄像头的预览事件处理函数中,先调用FT的人脸识函数函数,然后再调用FR中的人脸信息特征提取数函数。

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);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这里面的result中保存了人脸特征信息。我们可以将其保存下来或下来并与系统中的其它信息进行对比。

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;
      }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

当score的特征信息大于0.6时,我们就可以认为匹配到了人脸。显示人脸匹配信息。

上面的循环中,可以看到,是遍历了真个库进行寻找。我们的目的是为了演示,实际情况下,我们可以在找到一个匹配值比较高的人脸后,就跳出循环。

运行结果

我们来看一下运行的结果。 
效果还不错吧。钟汉良帅哥一枚。

本文档中所有的代码都可以在https://github.com/asdfqwrasdf/ArcFaceDemo 下载。如果你需要寻找更多的人脸识别的demo,也可以到虹软的论坛中去寻找。 
http://www.arcsoft.com.cn/bbs/forum.php?mod=forumdisplay&fid=45&page=1

附录:会遇到的问题及解决方案

如果你使用的是github中的示例,你可能会遇到下面的问题。

Plugin with id ‘com.android.application’ not found

直接从github上下载的源代码会有这个问题。

解决方案:打开 [项目文件夹]\app\build.gradle 文件

在文件末尾添加

buildscript {

    repositories {

        mavenCentral()

    }

    dependencies {

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

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Failed to find build tools revision 25.0.0.2

这个主要是build 的版本和gradle中指定的版本不一致,按照提示下载或者修改版本指定就可以了。

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.2"
    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

install_failed_no_maching_abis

下载的代码在gradle编译完成后,直接默认运行会出现这个错误。原因是由于使用了native libraries 。该native libraries 不支持当前的cpu的体系结构。 
首先请检查是否导入了必要的so文件。一共需要导入四个.so文件。 
另外,请确认使用是的真机调试。因为调用了摄像头,请使用真机调试。

后记

人脸识别是当前的热点技术,使用范围广,用户体验良好,对硬件的依赖低,不需要昂贵的传感器芯片。一个高清的摄像头就可以完成。以前的成本是人脸识别的SDK比较贵,但现在虹软的SDK免费之后,集成的成本就大大降低了。

时间: 08-29

Android 实现人脸识别教程[运用虹软人脸识别SDK]的相关文章

虹软人脸识别3.0 - 图像数据结构介绍(Android)

从虹软开放了2.0版本SDK以来,由于具有免费.离线使用的特点,我们公司在人脸识别门禁应用中使用了虹软SDK,识别效果还不错,因此比较关注虹软SDK的官方动态.近期上线了ArcFace 3.0 SDK版本,确实做了比较大的更新.首先本篇介绍一下关于Android平台算法的更新内容,下一篇将针对Windows平台的算法更新展开介绍. 特征比对支持比对模型选择,有生活照比对模型和人证比对模型 识别率.防***效果显著提升 特征值更新,升级后人脸库需重新注册 Android平台新增64位的SDK 图像

基于Android 虹软人脸、人证对比,活体检测

最近虹软新增了人证识别.活体检测的功能,好像之前的人脸识别也更新过版本,之前一篇文章用虹软Android SDK做人脸识 别,写过虹软人脸识别的用法,最近把人脸识别.人证识别,活体检测功能都简单的封装了一下,使用起来可以更简单一点: 但是由于appkey是和so库绑定的,所以不能直接依赖,需要下载项目换成自己的so库就能使用或者发布了,还是挺方便的 虹软人脸识别库的介绍``so库和appkey是绑定的 以人脸识别为例,它包括人脸检测.人脸追踪.人脸识别.年龄识别.性别识别这5种引擎,每个引擎都有

虹软人脸识别应用开发过程

趁空闲的一点点时间向大家分享一个好用的人脸识别的应用--来自虹软公司的人脸识别推荐这家的产品主要有以下几个理由~1.免费!免费!免费!它家比较良心.人脸识别.人证核验.活体检测等等一切的sdk都是免费下载使用的- -即使商用也可以~非常适合我这种小穷人2.根据不同操作平台提供不同的SDK目前可下载Windows x86,x64平台(c++/Java语言).Linux x64平台(c++/Java语言).iOS平台(Objective-C语言)与Android arm32平台(Java语言)3.官

虹软人脸识别3.0 - 图像数据结构介绍(C++)

从虹软开放了2.0版本SDK以来,由于具有免费.离线使用的特点,我们公司在人脸识别门禁应用中使用了虹软SDK,识别效果还不错,因此比较关注虹软SDK的官方动态.近期上线了ArcFace 3.0 SDK版本,确实做了比较大的更新.上一篇主要介绍了关于Android平台算法的改进,本篇将介绍一下关于Windows平台算法的更新. 特征比对支持比对模型选择,有生活照比对模型和人证比对模型 识别率.防***效果显著提升 特征值更新,升级后人脸库需重新注册 人脸检测同时支持全角度及单一角度 新增了一种图像

如何使用虹软人脸识别门禁应用套件

导语:虹软ArcFaceGo智慧门禁应用套件包括门禁软件APK.PC管理客户端和云端服务三部分,最快3分钟即可完成智慧门禁系统的部署,帮助中小型企业降低开发成本,加快实现产品智能化落地.本文来自开发者投稿. 应公司需求,最近开发了一款有考勤功能的人脸门禁应用. 相比传统密码门禁操作不便,指纹和刷卡门禁容易冒用和代打卡,刷脸门禁采用生物识别技术,必须本人刷脸才能起效,在原有门禁的基础上的改造也比较容易. 刷脸门禁虽好,然而最大问题是,我并没有从零开发一款人脸门禁的能力.多方研究之下,我选择了虹软A

C++开发人脸性别识别教程(5)——通过FaceRecognizer类实现性别识别

在之前的博客中已经解决了人脸检测的问题,我们计划在这篇博客中介绍人脸识别.性别识别方面的相关实现方法. 其实性别识别和人脸识别本质上是相似的,因为这里只是一个简单的MFC开发,主要工作并不在算法研究上,因此我们直接将性别识别视为一种特殊的人脸识别模式.人脸识别可能需要分为几十甚至上百个类(因为有几十甚至上百个人),而性别识别则是一种特殊的人脸识别——只有两个类. 一.基本工具 通过OpenCv进行性别识别的基本工具是FaceRecognizer.这是OpenCv2.x版本中的一个基本的人脸识别类

C++开发人脸性别识别教程(13)——针对单张图片的性别识别

在之前的博文中我们的性别识别程序已经初步成型,能够识别某个文件夹下的图片文件.不过这里有一个问题,假设这个文件夹下有着大量的图片,而我们希望识别这些图片中的某一张,此时需要我们不停的单击“下一张”按钮才会轮询到对应的图片,这是相当麻烦的,因此在这篇博客中我们向程序中添加一个功能——单张图片的性别识别. 一.基本思想 最基本的办法就是在主界面再添加一个按钮控件,命名为“图片文件”(之前的按钮为“图片文件夹”),不过这样会使得界面上的按钮控件过于繁多,给人一种“作者只会用button控件”的感觉.这

C++开发人脸性别识别教程(18)——辅助功能之文件名批量修改、方法验证

时光推移了30多天,这个人脸性别识别的小项目也接近尾声了,预计再通过三篇博文的篇幅来完成这个项目的收尾工作.在这篇博文中我们再为程序添加另外两个小的辅助功能:文件名批量修改.方法验证. 一 文件名批量修改 批量修改文件名是一件很基础也很常用的小操作,核心操作就是图像文件的批量读取.批量改名.批量保存.基本思想就是把文件读出来,然后在保存回去(注意不要和别的文件发生覆盖),从这个角度来讲文件名批量修改与上一篇博客C++开发人脸性别识别教程(17)——辅助功能之人脸批量分割中的人脸批量分割简直如出一

C++开发人脸性别识别教程(6)——通过SVM实现性别识别

上一篇教程中我们介绍了怎样使用OpenCv封装的FaceRecognizer类实现简单的人脸性别识别,这里我们为大家提供第二种主要的性别识别手段--支持向量机(SVM). 支持向量机在解决二分类问题方面有着强大的威力(当然也能够解决多分类问题).性别识别是典型的二分类模式识别问题,因此非常适合用SVM进行处理,同一时候OpenCv又对SVM进行了非常好的封装,调用非常方便,因此我们在这个性别识别程序中考虑增加SVM方法. 在这里我们採用了HOG+SVM的模式来进行,即先提取图像的HOG特征.然后