人证比对在如今的社会中随处可见,如高铁、飞机、酒店入住、甚至景区入口都可以看到各种各样的人证应用,人脸识别SDK的也如雨后春笋一般层出不穷,如百度、商汤、Face++、虹软等。在尝试使用各家的SDK之后,最让我青睐的要数虹软科技的SDK了,最直接的一个原因就是虹软承诺永久免费。我从2.0版本开始就在使用了,实测效果确实不错,就在上个月收到消息ArcFace3.0更新了,作为一个白嫖党自然不会错过这次的更新,在上手了3.0 之后,发现 ArcFace 3.0有以下新特性

  • 特征比对支持比对模型选择,有 生活照比对模型人证比对模型
  • Android平台新增64位的SDK
  • 新增了一种图像数据传入方式

本文将根据以下几点对ArcFace3.0的人证场景使用进行介绍

  • ArcFace 3.0 SDK接口变动的得与失
  • 如何将人证 2.0 Demo程序改为搭载ArcFace 3.0 SDK的程序
  • 如何直接把ArcFace 3.0的demo修改为人证程序

一、ArcFace 3.0 SDK接口变动的得与失

接口变动的优势:

1.业务自由度变高

以人证 2.0为例,我们只能传入数据、传出结果,而一些中间产物,例如人脸特数据征就获取不到了。现在采用ArcFace 3.0之后,取消了固定的流程,检测、比对、提取等流程都可以由自己控制。

2.可以在同一个工程内实现生活照比对与人证比对

人证 SDK与ArcFace SDK 存在冲突,无法同时使用,若我们既想用人证又想用生活照,就要写两个工程,并且两个工程的流程还有些不同。而现在只需要接口内选择模型就可以实现模型的切换,完全可以在一个工程内实现人证与生活照程序的集成。

3.代码复用性

ArcFace 3.0中人证与身份证区别只有compare接口中的模型选择,其他完全一致,因此大部分的代码都可以复用,大大提高了开发的效率。

接口变动的缺点:

1.接口变动

万事有得必有失,由于ArcFace 3.0没有关于人证部分的封装,致使在升级过程中所有的接口都需要变更,相信也是所有程序员都不愿意看到的问题。

2.实现变困难

同样由于ArcFace 3.0没有关于人证部分的封装,使得原本接口中自带的一些流程与回调需要自己来实现,这对于刚上手的人来说,不是十分友好。

小结:

虽然上面说了一些ArcFace 3.0的缺点,但是我本人还是很赞成这次的升级,毕竟每个产品的革新总会带来一些冲击,但是相对于这些冲击来说,我相信接口、识别流程的统一为程序的适用性与业务的自由性都提高了,相信对于人证2.0来说这次“壮士断腕”的举措长远来看是值得的。

二、人证 2.0 Demo集成ArcFace 3.0 SDK

在上面我们看到了由于接口的变动,致使人证2.0程序所有的接口都要修改,接下来我将以人证2.0 Demo为例,讲解一下我是如何使用ArcFace 3.0 SDK进行升级的。

1、人证 2.0 Demo工程配置

考虑到可能有些用户对人证 2.0 Demo不太熟悉,先简单介绍一下官方Demo如何配置使用。
利用人脸识别SDK实现人证比对全过程-LMLPHP
首先,先将人证引擎如图所示放入demo内,接下来修改Constants内的APP_ID与SDK_KEY,APP_ID与SDK_KEY以及人证引擎均由官网的 开放平台上进行获取。然后在设备的SDCard根目录下放置一张命名为“sample.jpg”的图片做为模拟人证输入的图片(图片路径可以在MainActivity下的SAMPLE_FACE变量内进行修改),下图为配置完毕后运行的截图。
利用人脸识别SDK实现人证比对全过程-LMLPHP

2.ArcFace 3.0 SDK替换

首先我们要先获取ArcFace3.0的SDK,同样可以在 开放平台上进行获取。用新的SDK库替换掉原本的SDK,替换后的项目目录如下图所示

利用人脸识别SDK实现人证比对全过程-LMLPHP

3.ArcFace3.0接口替换

上面提到了,由于3.0的全面变更,所有的接口全部都发生了改变,因此我们要把原本2.0的接口全部替换为3.0。

3.1 引擎激活:

激活方面接口参数没有任何变化

人证 2.0 :

  1. IdCardVerifyManager.getInstance().active(Context context, String appId, String sdkKey);

ArcFace 3.0 :

  1. FaceEngine.active(Context context, String appId, String sdkKey);
3.2 引擎初始化:

从初始化开始,人证 2.0与ArcFace3.0接口有了较大的区别,人证 2.0有对Id Card信息与Camera信息监听,而3.0取消了这个监听机制,接口内的参数就不一一介绍了, 官方文档介绍的非常详细,大家可以去参考一下官方文档。

人证 2.0 :

  1. IdCardVerifyManager.getInstance().init(Context context, IdCardVerifyListener listener)

ArcFace 3.0 :

  1. FaceEngine.init(Context context, DetectMode detectMode, DetectFaceOrientPriority detectFaceOrientPriority, int detectFaceScaleVal, int detectFaceMaxNum, int combinedMask)
3.3 激活&初始化demo:

下面是我对2.0进行替换后的前后代码,可以给大家做一个参考:

人证 2.0 :

点击(此处)折叠或打开

  1. private void initEngine() {
  2.         int result = IdCardVerifyManager.getInstance().init(this, idCardVerifyListener);
  3.         LogUtils.dTag(TAG, "initResult: " + result);
  4.         if (result == IdCardVerifyError.MERR_ASF_NOT_ACTIVATED) {
  5.             Executors.newSingleThreadExecutor().execute(new Runnable() {
  6.                 @Override
  7.                 public void run() {
  8.                     int activeResult = IdCardVerifyManager.getInstance().active(
  9.                             MainActivity.this, APP_ID, SDK_KEY);
  10.                     runOnUiThread(new Runnable() {
  11.                         @Override
  12.                         public void run() {
  13.                             LogUtils.dTag(TAG, "activeResult: " + activeResult);
  14.                             if (activeResult == IdCardVerifyError.OK) {
  15.                                 int initResult = IdCardVerifyManager.getInstance().init(
  16.                                         MainActivity.this, idCardVerifyListener);
  17.                                 LogUtils.dTag(TAG, "initResult: " + initResult);
  18.                                 if (initResult != IdCardVerifyError.OK) {
  19.                                     toast("人证引擎初始化失败,错误码: " + initResult);
  20.                                 }
  21.                             } else {
  22.                                 toast("人证引擎激活失败,错误码: " + activeResult);
  23.                             }
  24.                         }
  25.                     });
  26.                 }
  27.             });
  28.         } else if (result != IdCardVerifyError.OK) {
  29.             toast("人证引擎初始化失败,错误码: " + result);
  30.         }
  31.     }
ArcFace 3.0 :

点击(此处)折叠或打开

  1. private void initEngine() {
  2.         int result = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 1,
  3.                 FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_FACE_RECOGNITION);
  4.         LogUtils.dTag(TAG, "initResult: " + result);
  5.         if (result == ErrorInfo.MERR_ASF_NOT_ACTIVATED) {
  6.             Executors.newSingleThreadExecutor().execute(() -> {
  7.                 int activeResult = FaceEngine.active(
  8.                         MainActivity.this, Constants.APP_ID, Constants.SDK_KEY);
  9.                 runOnUiThread(() -> {
  10.                     LogUtils.dTag(TAG, "activeResult: " + activeResult);
  11.                     if (activeResult == ErrorInfo.MOK) {
  12.                         int initResult = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 1,
  13.                                 FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_FACE_RECOGNITION);
  14.                         LogUtils.dTag(TAG, "initResult: " + initResult);
  15.                         if (initResult != ErrorInfo.MOK) {
  16.                             toast("人证引擎初始化失败,错误码: ", initResult));
  17.                         }
  18.                     } else {
  19.                         toast("人证引擎激活失败,错误码: ", activeResult));
  20.                     }
  21.                 });
  22.             });
  23.         } else if (result != ErrorInfo.MOK) {
  24.             toast("人证引擎初始化失败,错误码: " , result));
  25.         }
  26.     }
3.4 证件照部分的识别及特征提取

证件照部分我们需要将原本2.0的引擎自带的图像处理方法换成3.0包内的ArcSoftImageUtil的方法,同时由于特征提取成功后的回调监听从引擎内删除掉了,所以这个回调需要自己来写,这里我偷了一下懒,抄了一下人证 2.0 demo与3.0 demo中均有的faceHelper中的FaceListener作为监听回调,当然大家也可以自己实现回调。

人证 2.0 :

点击(此处)折叠或打开

  1. private void inputIdCard() {
  2.         if (bmp == null) {
  3.             return;
  4.         }
  5.         int width = bmp.getWidth();
  6.         int height = bmp.getHeight();

  7.         //图像裁剪
  8.         boolean needAdjust = false;
  9.         while (width % 4 != 0) {
  10.             width--;
  11.             needAdjust = true;
  12.         }
  13.         if (height % 2 != 0) {
  14.             height--;
  15.             needAdjust = true;
  16.         }
  17.         if (needAdjust) {
  18.             bmp = ImageUtils.imageCrop(bmp, new Rect(0, 0, width, height));
  19.         }
  20.         //转换为NV21数据格式
  21.         byte[] nv21Data = ImageUtils.getNV21(width, height, bmp);
  22.         //身份证图像数据输入
  23.         DetectFaceResult result = IdCardVerifyManager.getInstance().inputIdCardData(
  24.                 nv21Data, width, height);
  25.         LogUtils.dTag(TAG, "inputIdCardData result: " + result.getErrCode());
  26.     }
ArcFace 3.0 :

点击(此处)折叠或打开

  1. private void inputIdCard() {
  2.         if (bmp == null) {
  3.             return;
  4.         }
  5.         //图像4字节对齐 裁剪
  6.         bmp = ArcSoftImageUtil.getAlignedBitmap(bmp, true);
  7.         int width = bmp.getWidth();
  8.         int height = bmp.getHeight();
  9.         //转换为bgr格式
  10.         byte[] bgrData = ArcSoftImageUtil.createImageData(bmp.getWidth(), bmp.getHeight(), ArcSoftImageFormat.BGR24);
  11.         int translateResult = ArcSoftImageUtil.bitmapToImageData(bmp, bgrData, ArcSoftImageFormat.BGR24);
  12.         //转换成功
  13.         if (translateResult == ArcSoftImageUtilError.CODE_SUCCESS) {
  14.             List<FaceInfo> faceInfoList = new ArrayList<>();
  15.             //video模式不适合静态图片检测,这里新建了一个idFaceEngine 除了检测模式修改为Image其他参数与faceEngine一样
  16.             int detectResult = idFaceEngine.detectFaces(bgrData, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList);
  17.             if (detectResult == ErrorInfo.MOK && faceInfoList.size() > 0) {
  18.                 //这里的-2为trackID 因为Camera与证件照提取共用faceHelper 用trackID区分是哪边来的数据
  19.                 faceHelper.requestFaceFeature(bgrData, faceInfoList.get(0), width, height, FaceEngine.CP_PAF_BGR24, -2);
  20.             }
  21.         } else {
  22.             LogUtils.dTag(TAG, "translate Error result: " + translateResult);
  23.         }
  24.     }
3.5 Camera部分的识别及特征提取

人证2.0的onPreviewData接口内部其实是存在一个特征提取保护,即 上一个特征提取未完成前,不能进行下一个特征提取,但是在3.0没有外部的封装了,所以我们要自己来进行特征提取的控制,基础的策略就是根据trackId,每一个trackId若未进行提取或提取失败才会进行特征提取。

人证 2.0 :

点击(此处)折叠或打开

  1. public void onPreview(byte[] nv21, Camera camera) {
  2.                 if (faceRectView != null) {
  3.                     faceRectView.clearFaceInfo();
  4.                 }
  5.                 if (nv21 == null) {
  6.                     return;
  7.                 }
  8.                 //预览数据传入
  9.                 DetectFaceResult result = IdCardVerifyManager.getInstance().onPreviewData(nv21,
  10.                         previewSize.width, previewSize.height, true);
  11.                 Rect rect = result.getFaceRect();

  12.                 if (faceRectView != null && drawHelper != null && rect != null) {
  13.                     //生成实时人脸框
  14.                     drawHelper.draw(faceRectView, new DrawInfo(drawHelper.adjustRect(rect), "", Color.YELLOW));
  15.                 }
  16.             }
ArcFace 3.0 :

点击(此处)折叠或打开

  1. public void onPreview(byte[] nv21, Camera camera) {
  2.                 if (faceRectView != null) {
  3.                     faceRectView.clearFaceInfo();
  4.                 }
  5.                 if (nv21 == null) {
  6.                     return;
  7.                 }
  8.                 List<FaceInfo> faceInfoList = new ArrayList<>();
  9.                 int ftResult = faceEngine.detectFaces(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList);
  10.                 //人证比对场景下只有最大人脸有效,因此直接取第一个人脸即可,若有其他场景可以自行调整
  11.                 if (ftResult == ErrorInfo.MOK && faceInfoList.size() > 0) {
  12.                     Rect rect = faceInfoList.get(0).getRect();
  13.                     if (faceRectView != null && drawHelper != null && rect != null) {
  14.                         drawHelper.draw(faceRectView, new DrawInfo(drawHelper.adjustRect(rect), "", Color.YELLOW));
  15.                     }
  16.                     //等待身份证数据准备完毕后,才开始对Camera的数据进行特征提取 并根据trackId防止重复提取
  17.                     int trackId = faceInfoList.get(0).getFaceId();
  18.                     if (isIdCardReady && requestFeatureStatusMap != null && requestFeatureStatusMap.containsKey(trackId)) {
  19.                         //若一个人脸提取失败则进行重试
  20.                         if (requestFeatureStatusMap.get(trackId) == null || requestFeatureStatusMap.get(trackId) == RequestFeatureStatus.FAILED) {
  21.                             requestFeatureStatusMap.put(trackId, RequestFeatureStatus.SEARCHING);
  22.                             faceHelper.requestFaceFeature(nv21, faceInfoList.get(0), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList.get(0).getFaceId());
  23.                         }
  24.                     }
  25.                 }
  26.             }
3.6 camera及idCard数据回调

上文我们已经提到过,人证 2.0的引擎内对camera数据idCard数据分别有两个接口作为区分,同时有两个回调函数分别用于两个数据的处理。而ArcFace3.0时不仅取消了回调,而且camera数据idCard数据共用一个detect、extractFaceFeature,所以我们可以采用trackId来作为区分,并且因为引擎的变化,引擎内不再存储特征值,导致我们需要记录两个数据源处获得的特征值。

人证 2.0 :

点击(此处)折叠或打开

  1. private IdCardVerifyListener idCardVerifyListener = new IdCardVerifyListener() {
  2.         @Override
  3.         public void onPreviewResult(DetectFaceResult detectFaceResult, byte[] bytes, int i, int i1) {
  4.             runOnUiThread(() -> {
  5.                 //预览人脸特征提取成功
  6.                 if (detectFaceResult.getErrCode() == IdCardVerifyError.OK) {
  7.                     isCurrentReady = true;
  8.                     compare();
  9.                 }
  10.             });
  11.         }

  12.         @Override
  13.         public void onIdCardResult(DetectFaceResult detectFaceResult, byte[] bytes, int i, int i1) {
  14.             LogUtils.dTag(TAG, "onIdCardResult: " + detectFaceResult.getErrCode());
  15.             runOnUiThread(() -> {
  16.                 //身份证人脸特征提取成功
  17.                 if (detectFaceResult.getErrCode() == IdCardVerifyError.OK) {
  18.                     isIdCardReady = true;
  19.                     restartHandler.removeCallbacks(restartRunnable);
  20.                     readHandler.postDelayed(readRunnable, READ_DELAY);
  21.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  22.                     bmp.compress(Bitmap.CompressFormat.PNG, 80, baos);
  23.                     byte[] bmpBytes = baos.toByteArray();
  24.                     Glide.with(MainActivity.this).load(bmpBytes).into(ivIdCard);
  25.                     compare();
  26.                 }
  27.             });
  28.         }
  29.     }
ArcFace 3.0 :

点击(此处)折叠或打开

  1. FaceListener faceListener = new FaceListener() {
  2.             @Override
  3.             public void onFail(Exception e) {
  4.             }

  5.             @Override
  6.             public void onFaceFeatureInfoGet(@Nullable FaceFeature faceFeature, Integer requestId, Integer errorCode, long frTime, byte[] nv21) {
  7.                 //特征提取失败 将比对状态置为失败
  8.                 if (ErrorInfo.MOK != errorCode) {
  9.                     requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
  10.                     return;
  11.                 }
  12.                 //requestId 为-2则为身份证数据
  13.                 if (requestId == -2) {
  14.                     isIdCardReady = true;
  15.                     //由于接口变更feature不能在引擎内存储 所以用全局变量进行存储
  16.                     idFaceFeature = faceFeature;
  17.                     restartHandler.removeCallbacks(restartRunnable);
  18.                     readHandler.postDelayed(readRunnable, 5000);
  19.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  20.                     bmp.compress(Bitmap.CompressFormat.PNG, 100, baos);
  21.                     runOnUiThread(() -> {
  22.                         Glide.with(MainActivity.this).load(bmp).into(ivIdCard);
  23.                         compare();
  24.                     });
  25.                 } else {
  26.                     //由于接口变更feature不能在引擎内存储 所以用全局变量进行存储
  27.                     MainActivity.this.faceFeature = faceFeature;
  28.                     isCurrentReady = true;
  29.                     runOnUiThread(() -> {
  30.                         compare();
  31.                     });
  32.                 }
  33.             }
  34.         }
3.7 compare接口

比对接口相对于之前的来说改动就小很多了,只需要注意一下将比对模式修改为ID_CARD模式即可。

人证 2.0 :

点击(此处)折叠或打开

  1. private void compare() {
  2.         //.......
  3.         //人证特征比对接口
  4.         CompareResult compareResult = IdCardVerifyManager.getInstance().compareFeature(THRESHOLD);
  5.         LogUtils.dTag(TAG, "compareResult: result " + compareResult.getResult() + ", isSuccess "
  6.                 + compareResult.isSuccess() + ", errCode " + compareResult.getErrCode());
  7.         if (compareResult.isSuccess()) {
  8.             playSound(R.raw.compare_success);
  9.             ivCompareResult.setBackgroundResource(R.mipmap.compare_success);
  10.             tvCompareTip.setText(name);
  11.         } else {
  12.             playSound(R.raw.compare_fail);
  13.             ivCompareResult.setBackgroundResource(R.mipmap.compare_fail);
  14.             tvCompareTip.setText(R.string.tip_retry);
  15.         }
  16.         //.......
  17.     }
ArcFace 3.0 :

点击(此处)折叠或打开

  1. private void compare() {
  2.         //.......
  3.         //人证特征比对接口
  4.         FaceSimilar compareResult = new FaceSimilar();
  5.         faceEngine.compareFaceFeature(idFaceFeature, faceFeature, CompareModel.ID_CARD, compareResult);
  6.         //人证比对阈值为0.82
  7.         if (compareResult.getScore() > 0.82) {
  8.             playSound(R.raw.compare_success);
  9.             ivCompareResult.setBackgroundResource(R.mipmap.compare_success);
  10.             tvCompareTip.setText(name);
  11.         } else {
  12.             playSound(R.raw.compare_fail);
  13.             ivCompareResult.setBackgroundResource(R.mipmap.compare_fail);
  14.             tvCompareTip.setText(R.string.tip_retry);
  15.         }
  16.         //.......
  17.     }
3.8 结果展示

至此只要将人证 2.0 demo无用的代码删除掉,我们就将2.0成功升级为3.0了,让我们看看部队成功后的运行截图。
利用人脸识别SDK实现人证比对全过程-LMLPHP

三、ArcFace 3.0的demo修改为人证程序

相比较于将人证 2.0升级为将ArcFace3.0来说,直接在将ArcFace3.0版本上进行修改可简单太多了,毕竟不用将所有的接口全部都更改一遍,我们需要做的就只是增加人证部分的输入,人证部分的回调以及比对的逻辑。因此在这里我强烈推荐直接上手ArcFace3.0,如果不是有特殊原因修改3.0可比人证2.0快太多了。

修改界面选择

首先我们要选择demo中的一个Activity做为我们修改的模板,我看了一下RegisterAndRecognizeActivity是我认为最为合适的了,因为它的Camera的比对流程已经全部完成了,我们需要做的就是两点:

  • 增加Id Card数据输入源
    Id Card数据输入源我们采用与人证demo相同的方式模拟证件信息传入,因此可以完全套用inputIdCard方法。

点击(此处)折叠或打开

  1. public void onClickIdCard(View view) {
  2.         //模拟身份证姓名,可修改
  3.         FileInputStream fis;
  4.         //身份证图像数据
  5.         bmp = null;
  6.         try {
  7.             //模拟身份证图像数据来源,可修改
  8.             fis = new FileInputStream(SAMPLE_FACE);
  9.             bmp = BitmapFactory.decodeStream(fis);
  10.             fis.close();
  11.         } catch (Exception e) {
  12.             e.printStackTrace();
  13.         }
  14.         inputIdCard();
  15.     }

  16.     private void inputIdCard() {
  17.         if (bmp == null) {
  18.             return;
  19.         }
  20.         //图像4字节对齐 裁剪
  21.         bmp = ArcSoftImageUtil.getAlignedBitmap(bmp, true);
  22.         int width = bmp.getWidth();
  23.         int height = bmp.getHeight();
  24.         //转换为bgr格式
  25.         byte[] bgrData = ArcSoftImageUtil.createImageData(bmp.getWidth(), bmp.getHeight(), ArcSoftImageFormat.BGR24);
  26.         int translateResult = ArcSoftImageUtil.bitmapToImageData(bmp, bgrData, ArcSoftImageFormat.BGR24);
  27.         //转换成功
  28.         if (translateResult == ArcSoftImageUtilError.CODE_SUCCESS) {
  29.             List<FaceInfo> faceInfoList = new ArrayList<>();
  30.             //video模式不适合静态图片检测,我们选择frEngine 作为检测证件照的引擎 初始化时要增加 FaceEngine.ASF_FACE_DETECT 哦
  31.             int detectResult = frEngine.detectFaces(bgrData, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList);
  32.             if (detectResult == ErrorInfo.MOK && faceInfoList.size() > 0) {
  33.                 //这里的-2为trackID 因为Camera与证件照提取共用faceHelper 用trackID区分是哪边来的数据
  34.                 faceHelper.requestFaceFeature(bgrData, faceInfoList.get(0), width, height, FaceEngine.CP_PAF_BGR24, -2);
  35.             }
  36.         } else {
  37.             LogUtils.dTag(TAG, "translate Error result: " + translateResult);
  38.         }
  39.     }
  • 修改比对的底库

由于绝大部分场景下,人证比对都是1:1进行对比的,因而要在onFaceFeatureInfoGet回调内进行调整。首先通过我们在上面inputIdCard铺垫的以-2为trackID,作为标识身份证数据的手段。其次我们要记录一下要对比的身份证feature与camera下的人脸feature信息,这里我们采用全局变量的方式进行记录。最后由于比对的feature获取会有前后顺序区分,我们用一个状态位进行记录(当然也可以判断两个feature是否有数据,对此数据进行维护来进行两边数据的同步),等待两边的数据都准备完毕后,就可以进行比对了。

点击(此处)折叠或打开

  1. public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) {
  2.                 //FR成功
  3.                 if (faceFeature != null) {
  4.                     //接收身份证数据
  5.                     if (requestId == -2) {
  6.                         isIdCardReady = true;
  7.                         //feature用全局变量进行存储
  8.                         idFaceFeature = faceFeature;
  9.                         compare();
  10.                         return;
  11.                     }
  12. // Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);
  13.                     Integer liveness = livenessMap.get(requestId);
  14.                     //不做活体检测的情况,直接搜索
  15.                     if (!livenessDetect) {
  16.                         isCurrentReady = true;
  17.                         //防止对同一个人脸进行多次特征提取
  18.                         requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);
  19.                         compare();
  20. // searchFace(faceFeature, requestId);
  21.                     }
  22.                     //活体检测通过,搜索特征
  23.                     else if (liveness != null && liveness == LivenessInfo.ALIVE) {
  24.                         isCurrentReady = true;
  25.                         //防止对同一个人脸进行多次特征提取
  26.                         RegisterAndRecognizeActivity.this.faceFeature = faceFeature;
  27.                         requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);
  28.                         compare();
  29. // searchFace(faceFeature, requestId);
  30.                     }
  31.                     //活体检测未出结果,或者非活体,延迟执行该函数
  32.                     else {
  33.                         //......
  34.                     }
  35.                 }
  36.                 //特征提取失败
  37.                 else {
  38.                    //.........
  39.                 }
  40.             }

  41.             @Override
  42.             public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) {
  43.                 //.....
  44.             }
  45.         }
  • compare 函数:

点击(此处)折叠或打开

  1. private void compare() {
  2.         if (isCurrentReady && isIdCardReady) {
  3.             FaceSimilar similar = new FaceSimilar();
  4.             int compareResult = frEngine.compareFaceFeature(idFaceFeature, faceFeature, CompareModel.ID_CARD, similar);
  5.             if (compareResult == ErrorInfo.MOK && similar.getScore() > 0.82) {
  6.                 Log.i(TAG, "compare: success");
  7.             } else {
  8.                 Log.i(TAG, "compare: fail");
  9.             }
  10.             //比对完成后重置比对状态
  11.             isIdCardReady = false;
  12.             isCurrentReady = false;
  13.             //给同一个人脸若比对后仍想尝试,允许其进行特征提取
  14.             requestFeatureStatusMap.clear();
  15.         }
  16.     }
小结

使用ArcFace3.0进行修改,可以明显的感觉到修改“丝滑”了很多,我们在原代码的基础上只需要注意 Id Card的数据输入,以及 比对前后的逻辑即可,比对的难度几乎可以忽略不计,只是简单的调用接口而已。我这里也写的比较简单,有些业务逻辑如:增加身份证数据有效时间;规定双方数据强制的先后顺序;界面部分的展示都没有做,只打印了一下比对的结果。本文只提供思路给大家参考,业务逻辑还是需要自己添加,最后给大家看一下修改完成后运行比对成功的日志。

利用人脸识别SDK实现人证比对全过程-LMLPHP

结语

总的来说,虹软的人证SDK还是非常不错的。从识别效果来讲,我的身份证大概是7-8年前拍摄的,与现在的“胖若两人”,但是识别准确率依然很高。从开发成本来说,永久免费真的很吸引人,虹软科技称的上是个良心企业,为它点个赞。从二次开发的接口角度来说,在我接触过的各个SDK中,我个人认为虹软的接口算是很容易上手的一批,而且虹软的接口文档十分详尽,不仅有对每个接口的入参详细的介绍,还有单独接口调用的demo,以及整体流程的demo都为新手上路铺垫好了道路。期间也碰到过一些问题,虹软的客服人员也都很及时很准确的回答了我的问题。期待虹软今后的发展,希望能给我们带来更好的产品!

附录

1.人证 2.0 Demo集成ArcFace 3.0 SDK demo地址:
https://github.com/1244975831/ArcSoft_IdCardVeriDemo_Android

2.ArcFace 3.0的demo修改为人证程序 demo地址:
https://github.com/1244975831/ArcfaceDemo_IdCard_Android

如果我的demo对您有所帮助,请记得为我的项目star一下。





















01-05 19:48