人脸识别云服务

1. 云端人脸识别API接口服务

商米提供云端人脸识别API接口服务,用户可以用标准的HTTP请求来获取人脸检测框和人脸特征。其中,人脸特征可用于批量构建人脸底库,使用sqlite数据库进行人脸库的存储,其构建表结构如下:

CREATE TABLE SUNMI_FACE (
AUTOID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
                 "ID VARCHAR(64), "
                 "NAME VARCHAR(64), "
                 "IMG_ID VARCHAR(64), "
                 "FEATURE BLOB,"
                 "CreatedTime TimeStamp NOT NULL DEFAULT CURRENT_TIMESTAMP);

数据表中各个字段的赋值,需注意以下几点:

  • ID是用户的唯一标识,同一个用户ID相同,不用用户ID不同。
  • NAME是用户姓名,允许重复。
  • IMG_ID是对应人脸图片在数据库中的唯一标识,不可重复,需要采用UUID0算法生成唯一不重复的uuid。
  • FEATURE是通过人脸API服务获取的人脸特征。

1.1. 人脸检测-请求参数

url地址:请联系商米获取云服务的url 地址

参数名称是否必选类型默认值说明
imagestring/图片信息 (总数据大小应小于2M),图片上传方式根据image_type来判断
image_typestring/图片类型 BASE64:图片的base64值。base64编码后的图片数据,其图片大小不超过2M;URL:图片的 URL地址(可能由于网络等原因导致下载图片时间过长)
max_face_countint1最多处理人脸的数目,默认值为1,仅检测图片中人脸置信度最高的那个人脸;最大值10,检测图片中面积最大的几张人脸。
predict_modeint参考下面的预测模式定义。7即为默认人脸检测+人脸特征+人脸属性。(年龄和性别都包含在人脸属性中)

预测模式定义示例代码如下:

typedef enum {
    PredictMode_None = 0,
    PredictMode_Detect = 1 ‹‹ 0,                                // face detect
    PredictMode_Feature = (1 ‹‹ 1) + PredictMode_Detect,        // face detect and extract features
    PredictMode_Age = (1 ‹‹ 2) + PredictMode_Detect,            // face detect and age predict
    PredictMode_Gender = PredictMode_Age,                       // face detect and gender predict
} SunmiFaceMode;

1.2. 人脸识别API返回数据

字段必选类型说明
face_numint检测到的图片中的人脸数量
face_listarray人脸信息列表,具体参数参考下面的列表
+locationarray人脸在图片中的位置
++leftdouble人脸框左上角x坐标
++topdouble人脸框左上角y坐标
++widthdouble人脸框宽度
++heightdouble人脸框高度
+ face_probabilitydouble人脸置信度
+ anglearray人脸旋转角度
++ yawdouble左右旋转角[-90(左), 90(右)]
++ pitchdouble俯仰旋转角[-90(上), 90(下)]
++ rolldouble平面内旋转角[-90(逆时针), 90(顺时针)]
+ landmarkarray5个关键点位置,左眼中心、右眼中心、鼻尖、左嘴角,右嘴角。
+ liveness_scoredouble2D活体检测得分,值越高说明是真人的概率高
+ featurearray人脸256维特征值

返回结果如为json格式,如以下示例:

{
    "face_num": 1,
    "face_list": [
        {
            "location": {
                "left": 216.1408233642578,
                "top": 157.1695556640625,
                "width": 90.74900817871094,
                "height": 115.81448364257812
            },
            "face_probability": 0.9972383975982666,
            "angle": {
                "yaw": 5.858273506164551,
                "pitch": 19.634262084960938,
                "roll": -1.6777983903884888
            },
            "landmark": [
                {
                    "x": 239.48606872558594,
                    "y": 203.29200744628906
                },
                {
                    "x": 280.5645446777344,
                    "y": 203.55821228027344
                },
                {
                    "x": 258.20745849609375,
                    "y": 226.3636016845703
                },
                {
                    "x": 242.2882080078125,
                    "y": 243.4677734375
                },
                {
                    "x": 276.392333984375,
                    "y": 243.5858917236328
                }
            ],
            "liveness_score": 0.9542617797851562,
            "gender_info": {
                "gender": "female",
                "score": 1.0
            },
            "age_info": {
                "age": 25,
                "score": 0.8857287168502808
            },
            "feature": [
                0.0012834692606702447,
                0.027195502072572708,
                -0.1606169044971466,
                -0.1035875454545021,
                -0.055142492055892944,
                …
                0.043723493814468384,
                0.07170124351978302,
                -0.10330264270305634
            ]
        }
    ]
}

1.3. 人脸识别API返回状态码

因API不提供自定义配置参数设置,如SDK中的人脸姿态阈值、光照阈值等,在API中均采用默认值,而FACE_CODE_BAD_ILLUMINATION,FACE_CODE_POSE_ERROR等依赖配置参数,所以下表提供的返回值仅针对默认配置参数。

人脸姿态可直接根据返回的json中的“angle”字段进行判断,人脸大小可根据“location”字段判断。

商米人脸识别状态码如下表所示:

枚举成员状态码说明
FACE_CODE_OK0正确
FACE_CODE_UNINIT1SDK没有初始化
FACE_CODE_EMPTY_IMAGE2图片为空
FACE_CODE_NO_MODEL3无加载模型文件
FACE_CODE_CONFIG_ERROR4配置错误
FACE_CODE_BAD_ILLUMINATION5光线条件不好
FACE_CODE_POSE_ERROR6面部位置不符合要求
FACE_CODE_SMALL_FACE_SIZE7人脸尺寸太小
FACE_CODE_LOW_IMAGE_QUALITY8人脸图像模糊
FACE_CODE_NOT_LIVENESS9非活体人脸图像
FACE_CODE_LICENSE_ERROR10授权文件无效
FACE_CODE_LICENSE_EXPIRED11授权文件过期
FACE_CODE_IMAGE_ID_ERROR101删除时图像id不存在
FACE_CODE_OTHER_ERROR1000其他错误

2. Python示例代码

2.1. 图片采用BASE64格式

对于BASE64格式的图片,使用如下示例进行API接口的调用:

import numpy as np
import cv2
import requests
import base64
import json
​
if __name__ == '__main__':
    url = "http://face-recognition.sunmi.com/v1/face/recognition"
    with open("test.jpg", "rb") as f:
        base64_data = base64.b64encode(f.read())
    data = {"image": base64_data, "image_type": "BASE64", "max_face_count": 1}
    r = requests.post(url, data=data)
    s = json.loads(r.content)
    print(s['msg'])

2.2. 图片采用URL格式

对于URL格式的图片,使用如下示例进行API接口的调用:

import numpy as np
import cv2
import requests
import base64
import json
​
if __name__ == '__main__':
    url = "****"
    data = {"image": "https://wifi.cdn.sunmi.com/UAT/IMG/FACE/AM4RDZ2LP5HWTIJ8G70KN8NFNY0766WK1", "image_type": "URL", "max_face_count": 1}
    r = requests.post(url, data=data)
    s = json.loads(r.content)
    print(s['msg'])

商米AI识客SDK

  1. 简介
  2. 准备工作
  3. 添加SDK到工程
  4. Demo APP使用
  5. 开发示例
  6. SDK详细接口说明

1 简介

商米AI识客SDK是一个可以配套商米AI识客摄像机(以下简称FS)和安卓收银硬件设备(简称POS),为商户提供完整会员识别解决方案的开发包,包括会员注册、会员进店实时提醒、会员到柜重识别等功能模块。当前提供的是安卓版本SDK,以aar包形式发布,并提供Demo APP以供参考。

详细说明:

  • 1. C/C++ library实现人脸特征提取,人脸比对等算法功能以及人脸库管理(人脸库的添加、查询、删除等操作)等功能。
  • 2. JNI层封装C/C++实现的功能,为Java上层提供native接口。
  • 3. Java层 人脸识别API 使用native层提供的接口为上层 APP提供人脸特征提取、人脸比对以及人脸库管理等接口,AI识客API提供配置FS、向FS注册人脸和获取FS人脸识别消息的功能。
  • 4. Demo APP,这里是示例代码,仅供开发者参考,非完美的APP,使用Java API层提供的功能实现人脸识别、人脸比对、人脸特征库管理、AI识客设备配置等业务层功能。

2 准备工作

SDK开发包采用一个设备一个Licence认证机制。先向商米售前技术团队申请SDK,而后通过SDK或Demo APP获取硬件指纹,再申请Licence。

2.1 申请SDK

步骤一:联系商米售前技术团队,提供如下信息:

内容 说明
客户名称软件商公司名称
业务场景简单描述业务场景

步骤二: 商米售前技术团队根据客户的申请,为客户提供SDK开发包。SDK开发包的内容如下:

序号文件/文件名说明
1FaceSdkDemoDemo工程源码
2facelib_xxxx.aar人脸识别开发套件,xxxx为版本号。
3ipcsdk_xxxx.aarFS开发套件,xxxx为版本号。
4asset人脸识别开发套件所需资源文件(人脸识别的模型文件、配置参数等)
5readme.mdSDK开发指南

2.2 申请Licence

获取SDK开发包后,按如下操作,提供硬件指纹信息给商米售前技术团队:

  1. 在POS设备上安装FaceDemo.apk,运行FaceDemo APP,给APP授权存储、摄像头权限。APP会在/sdcard目录生成一个硬件指纹信息文件device_fingerprint.txt。
  2. 或者集成SDK开发包后,调用SDK API SunmiFaceSDK.getDeviceFingerprint获取硬件指纹信息

商米售前技术团队会提供以下内容:

内容 说明
licence_valid.txt用于激活人脸识别的SDK的Licence
appid、secret_key、激活码用于激活FS的通信使用

备注:

  1. licence_valid.txt文件的使用方法请阅读章节3.4,放到指定的目录下即可。
  2. appid、secret_key、激活码的使用请阅读章节4.1,替换源码中的内容编译出Demo APP。

2.3 开发调试环境

在软件正式发布上线之前,建议在UAT环境下与FS进行联调,请参考升级开发环境固件将FS固件升级到UAT环境,因此商米首先提供UAT环境下的appid、secret_key和激活码 ,开发完成后再提供Release环境的 appid、secret_key和激活码 ,并参照升级线上环境固件将FS固件升级到Release环境。

3 添加SDK到工程

SDK开发包开发环境:

开发工具:Android Studio

NDK版本:android-ndk-r14b (可以使用高版本)

Android版本:Android 7.0及以上

3.1 Android Studio导入SDK开发包

右键点击app,选择New->Module->Import .JAR/.AAR Package

点击Finish即导入aar模块。

按同样的操作可以导入人脸识别facelib_xxxx.aar模块。

3.2 权限申明

SDK开发包涉及网络操作以及wifi相关操作,需要在 AndroidManifest.xml 增加以下权限:

‹uses-permission android:name="android.permission.INTERNET" /›
‹uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /›
‹uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /›
‹uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /›

Demo APP还涉及到camera和存储器相关操作,还需要在AndroidManifest.xml增加以下权限:

‹uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/›
‹uses-permission 
‹uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /›
‹uses-permission android:name="android.permission.CAMERA" /›

3.3 导入asset资源

运行SDK开发包还需要导入asset资源文件,用于初始化人脸识别SDK。

说明:资源文件在APP启动的时需要复制到APP的运行目录。

3.4 导入licence文件

获取到licence_valid.txt文件,可以放到asset资源目录,也可以放到sdcard目录。

  1. 放到assets资源目录,则需要把licence_valid.txt文件从资源文件复制到app的工作目录。
  2. 如果放到sdcard目录,则需要app申请sdcard读取权限。

无论哪一种情况,最后使用的时候,需要传递licence_valid.txt文件的路径,以激活人脸识别SDK。人脸识别SDK详细初始化代码如下:

public boolean initFaceSdkInstance(Context context, String licencePath) {
         //初始化配置文件
        int ret = SunmiFaceSDK.init(confPath);
        if (licencePath == null) {
            Log.d(TAG, "Please input licence");
            return false;
        }
        File f = new File(licencePath);
        if (!f.exists()) {
            Log.d(TAG, "licence is not exist");
            return false;
        }
        String licence = readToString(licencePath);
        //验证Licence
        ret = SunmiFaceSDK.verifyLicence(context, licence);
        if (ret != 0) {
            Log.d(TAG, "Licence is not OK ErrorCode " + ret);
            return false;
        }
        //设置配置参数
        SunmiFaceConfigParam param = new SunmiFaceConfigParam();
        param.setDistance_threshold_(1.0f); //范围在1.0~1.2比较合适,越小,人脸识别越严格
        param.setYaw_threshold_(50.f);//人脸姿态阈值,设置较小的人脸姿态阈值,阈值越小,人脸姿态要求越严格,保证相对正面的人脸姿态做识别
        param.setPitch_threshold_(50.f);
        param.setRoll_threshold_(50.f);
        param.setMin_face_size(60);//最小人脸检测尺寸,较大较清晰的人脸识别较准确
        param.setImage_quality_threshold_(10);//阈值越高,人脸清晰度越好,人脸识别越准确
        param.setMin_luminance_(10);//最小光照
        param.setMax_luminance_(180);//最大光照
        param.setLiveness_on_(true);//开启人脸活体检测
        param.setThread_num_(1); //使用一个cpu核心执行人脸检测,人脸识别
        //设置配置参数
        ret = SunmiFaceSDK.setConfig(param);

        if (ret != 0) {
            Log.d(TAG, "param is not init");
            return false;
        }
        return true;
    }

public void init(Context context) {
      //设置licence_valid.txt为sd卡路径,也可以换成app运行工作目录。
      String licencePath = Environment.getExternalStorageDirectory() + File.separator + "licence_valid.txt";
      initFaceSdkInstance(context, licence_path);
     // 调用IPCameraManager静态类getInstance初始化IPCManger对象
     mIPCManager = IPCameraManager.getInstance(context);
}

4 Demo APP

4.1 编译Demo APP

(1) Android studio 导入Demo APP源代码,File->open,选中Demo APP工程目录。

(2)在文件local.properties文件增加ndk编译程序路径:ndk.dir=D\:\ndk\\android-ndk-r14b。路径地址为ndk存储路径。

(3)修改app module目录下的源代码MainActivity.java文件,激活FS设备。代码如下:

init函数的三个参数为app_id, secret_key, 激活码。这三个参数是由2.2 申请licence获得。

//init三个参数为:app_id, secret_key, 激活码
manager.init("test", "123456", "123456");

(4)Build->Make Project,编译工程,生成APP。

4.2 Demo APP工程目录

4.2.1 app Module

app Module是Demo的入口。其中包含了各个模块的入口函数调用。

4.2.2 commonlib

commonlib Module包含了整个Demo通用的接口。其中包含摄像头管理,工具类。如下图:

4.2.2.1 FaceCameraManager

POS设备自带的摄像头采用Android系统提供的操作方式。外置USB 摄像头使用第三方UVC库提供的操作方式。 FaceCameraManager类统一管理Android POS设备自带的摄像头和外置USB摄像头,封装Android系统操作方式和UVC操作方式,对上层APP应用提供统一的方法:startPreview和 stopPreview。

注:本文的摄像头AI识客摄像机是两个概念。本文中的摄像头指POS设备自带的摄像镜头和外置USB摄像头(兼容UVC协议的USB摄像镜头)。 AI识客摄像机是一款独立的设备,运行单独的操纵系统,能够独立的进行人脸识别。在此需要注意区分。

4.2.2.2 NormalCameraManager

NormalCameraManager使用Android系统提供的摄像头操作方式,实现startPreview和stopPreview。并提供获取摄像头每一帧数据的接口。

4.2.2.3 UVCCameraManager

UVCCameraManager使用第三方库UVC lib提供的方法用于操作外置USB摄像头,实现startPreview和stopPreview。 并提供获取摄像头每一帧数据的接口。

4.2.2.4 ByteUtils

ByteUtils提供Byte操作的一些通用方法。

4.2.2.5 DBHelper

DBHelper提供创建数据的方法。

4.2.2.6 ImageUtils

ImageUtils提供图片操作的一些方法。

4.3 libuvccamera

libuvccamera Module采用第三方库开源库libuvccamera。

4.4 facedemo

facedemo 使用人脸识别API和AI识客API实现会员注册、会员管理、会员查询等功能。

4.5 ipcdemo

ipcdemo 主要用于AI识客设备配置。

5 开发示例

5.1 配置FS

首次使用FS时,需要激活设备。激活FS时,FS必须连接互联网,进行在线激活。当FS激活后,可以离线使用。使用FS开发包按照以下步骤即使用FS:

5.1.1 FS开发包初始化

通过商米售前技术团队申请Licence后,会获取以下信息。使用以下信息进行SDK初始化:

app_id:激活与API调用校验使用的账号。

secret_key:API调用所需的签名密钥。

licence:激活API所需的激活码。

初始化代码如下:

IPCameraManager mIPCameraManager = IPCameraManager.getInstance(context);
mIPCameraManager.init(app_id, secret_key, licence);

5.1.2 FS设备配网

FS可以通过有线(以太网卡)和无线(wifi)接入互联网。无论哪种方式,FS必须和POS设备处于同一局域网下。两种接入方式配网方式如下:

有线接入:

有线接入只需通过网线把FS设备和POS设备接入到同一局域网即可,不需要其它设置。有线接入优先级高于无线接入。如果有线接入后,无线接入不可用。

无线接入:

无线接入方式相对要复杂点,需要进行无线配网,步骤如下:

Android POS设备连接AI识客设备AP热点

1. POS设备无线网卡扫描FS设备的AP热点,一般AP热点的名称为SUNMI_XXXX,其中XXXX为MAC地址最后2个字节的16进制数字,MAC地址可以通过设备机身后背的标贴或者包装盒的标贴查到,AP热点本身是无加密的。

2. POS设备的无线网卡连接到FS设备的AP热点。POS设备通过FS开发包提供的接口可以获取到FS设备信息,包含IP、MAC等,相关代码如下:

IPCameraManager.getInstance(context).registerListener(new IPCameraListener() {
    //IPCameraInfo获取的即是FS的设备信息,包含IP、MAC等。
    @Override
    public void onDeviceOnline(IPCameraInfo device) {
        showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]上线");
    }
    @Override
    public void onDeviceOffline(IPCameraInfo device) {
        showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]离线");
    }
});

3. 调用getApListWithoutAuth 接口可以在POS设备上获取到FS设备扫描到的wifi热点:

// 获取FS设备扫描到的AP热点
private void getWifiList() {
    BasicConfig.getInstance(context).getApListWithoutAuth(sunmiDevice.getDeviceid(),
            new RPCCallback‹RPCResponse‹IpcApBean››() {
                //扫描完成后调用onComplete,返回信息
                @Override
                public void onComplete(RPCResponse‹IpcApBean› result) {
                    if (result.code() == RPCErrorCode.SUCCESS) {
                        wifiListGetSuccess(result.data());
                    } else {
                        Log.i(TAG, "getApListWithoutAuth failed, errcode: " + result.code());
                    }
                }
        });

    ...
}

void wifiListGetSuccess(IpcApBean res) {
   ...
   wifiList.clear();
   //处理FS设备扫描到的AP热点信息
   wifiList.addAll(res.ap_list);
   initApList(wifiList);
}

4. 选中一个wifi热点后,调用setWifiConfWithoutAuth接口配置FS设备,使其能够连接到wifi热点,相关代码如下:

/*
 * 输入wifi热点名,密码
 *
 */
private void setIpcWifi(String ssid, String psw) {
BasicConfig.getInstance(context).setWifiConfWithoutAuth(sunmiDevice.getDeviceid(),ssid, psw, new RPCCallback‹RPCResponse›() {
                //调用成功后会调用onCompelte, 返回值保存再RPCResponse
                @Override
                public void onComplete(RPCResponse result) {
                    if (result.code() == RPCErrorCode.SUCCESS) {
                        hideLoadingDialog();
                        shortTip("配置成功,请等待设备联网,联网后指示灯会变成蓝色");
                        createWaitDialog();
                    } else {
                        hideLoadingDialog();
                        shortTip("配置失败");
                    }
                }
                @Override
                public void onError(Throwable t) {
                    hideLoadingDialog();
                    shortTip("配置失败");
                }
            });
}

5. 等待片刻后,AI识客设备通过无线连接上互联网,显示蓝灯。

5.1.3 FS设备激活

FS设备首次使用时,需要激活。如果FS设备没有激活,则会无法调用FS SDK的其他接口。激活设备需要连接互联网。如何连接互联网,可以参看 5.2 FS设备配网。POS设备调用active接口即可以激活FS设备,激活代码如下:

//激活FS设备,并设置回调接口
DeviceManage.getInstance(context).activate(ipcList.get(postion).getDeviceid(),
    new RPCCallback‹RPCResponse›() {
        //FS返回相关错误码。
        @Override
        public void onComplete(RPCResponse result) {
            if (result.code() == RPCErrorCode.SUCCESS || result.code() == RPCErrorCode.DEVICE_ACTIVATED) {
                Log.i(TAG, "activate ipc success");
            } else {
                Log.i(TAG, "activate ipc failed");
            }
        }
        //网络问题导致无法通信,会调用onError接口
        @Override
        public void onError(Throwable t) {
            Log.i(TAG, "activate ipc failed");
        }
    });

5.1.4 FS设备画面调整

FS设备进行人脸识别时,对人脸图像质量有一定要求,因此在使用FS提醒功能之前,需要调整FS设备画面。FS设备支持RTSP协议用于画面预览。可以使用Demo APP提供的RTSP播放器播放预览画面,也可以自定义一款RTSP播放器。预览AI识客设备的画面时,可以对FS的摄像镜头进行调焦、对焦,调整画面的清晰度,以满足FS设备的人脸识别要求。主要步骤如下:

5.1.4.1 预览画面播放

调用接口 getLiveStream 获取RTSP预览视频流播放地址,然后使用RTSP播放器进行播放。代码如下:

   private void openMediaPlayer() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //获取播放流                VideoStream.getInstance(context).getLiveStream(mDevice.getDeviceid(),
                        new RPCCallback‹RPCResponse‹RPCResponse.LiveAddressBean››() {
                            @Override
                            public void onComplete(RPCResponse‹RPCResponse.LiveAddressBean› result) {
                                if (result.code() == RPCErrorCode.SUCCESS) {
                                    Log.i(TAG, "live url: " + result.data().fhd_live_url);
                                    if (mPlayer == null) {
                                       //Sunmi rtsp播放器
                                        mPlayer = new SunmiPlayer(context);
                                        mPlayer.setListener(new SunmiPlayerListener() {
                                            @Override
                                            public void onPrepared(IMediaPlayer iMediaPlayer) {
                                                iMediaPlayer.start();
                                            }
                                        });
                                        mPlayer.setSurface(mVideoView.getHolder().getSurface());
                                        //获取到AI识客设备返回的播放流rtsp地址
                                        String liveUrl = result.data().fhd_live_url.replaceFirst("^rtsp://", "rtsp://admin:admin@");
                                        //Sunmi RTSP播放器开始播放rtsp视频流
                                        mPlayer.setUp(liveUrl);
                                    }
                                }
                            }
                });
            }
        }).start();
    }

5.1.4.2 FS设备调焦、对焦

画面预览时,如果画面不清晰,请调用接口 setZoom,autoFocus,manualFocus分别进行调整焦距、自动对焦、手动对焦。当画面清晰时,停止对焦。参考代码如下:

public void init() {
    ...
    mBasicConfig = BasicConfig.getInstance(context);
    ...
}

// 调焦
private void func1(){
    ...
    mBasicConfig.setZoom(mDevice.getDeviceid(), zfBean.zoom, new RPCCallback‹RPCResponse›() {
        @Override
        public void onComplete(RPCResponse result) {
            Log.i(TAG, "setZoom, code:" + result.code());
        }
    });
    ...
}

// 自动对焦
private void func2() {
    ...
    mBasicConfig.autoFocus(mDevice.getDeviceid(), xRelative, yRelative, new RPCCallback‹RPCResponse›() {
        @Override
        public void  onComplete(RPCResponse result) {
            Log.d(TAG, "autoFocus, code:" + result.code());
            if (result.code() == RPCErrorCode.SUCCESS) {
                mBasicConfig.getZoomFocusConf(mDevice.getDeviceid(), new RPCCallback‹RPCResponse‹RPCResponse.ZoomFocusBean››() {
                    @Override
                    public void onComplete(RPCResponse‹RPCResponse.ZoomFocusBean› result) {
                        if (result.code() == RPCErrorCode.SUCCESS) {
                            zfBean = result.data();
                            mSbZoom.setProgress(zfBean.zoom);
                        }
                    }
                });
            }
        }
    });
    ...
}

// 自动对焦如果不够清晰,可以手动对焦进行微调
private void func3() {
    ...
    mBasicConfig.manualFocus(mDevice.getDeviceid(), focus, new RPCCallback‹RPCResponse›() {
        @Override
        public void onComplete(RPCResponse result) {
            if (result.code() == RPCErrorCode.SUCCESS) {
                zfBean.focus = focus;
            }
        }
    });
    ....
}

5.1.5 FS设备客门线设置

首次FS设备时,需要设置门线位置。设置该门线位置是用于判定人脸进店。FS设备被换位置或者门线设置不正确时,也需要进行重新设置门线。调用接口setDoorLine可以设置门线。参考代码如下:

private void func() {
    ...
    PeopleFlowStats.getInstance(context).setDoorLine(mDevice.getDeviceid(), 0, lineStart[0],
            lineStart[1], lineEnd[0], lineEnd[1], new RPCCallback‹RPCResponse›() {
                @Override
                public void onComplete(RPCResponse result) {
                    if (result.code() == RPCErrorCode.SUCCESS) {
                        stopPlay();
                        finish();
                        startActivity(new Intent(context, MainActivity.class));
                    }
                }
            });
    ...
}

完成以上步骤后,就配置好AI识客设备,可以使用AI识客设备提供的识客进店功能。

5.2 会员注册

输入会员的ID、分组、备注信息,并采集会员人脸,提取人脸特征,完成会员注册。POS设备完成会员注册的同时,会把人脸照片以及会员ID同步传递给AI识客设备。

5.2.1 采集人脸照片

POS设备调用自带摄像头或者外置USB摄像头进行人脸拍照。获取人脸照片。相关代码如下:

{
//根据摄像头是USB还是系统摄像头,调用不同的方法,获取bitmap数据
if (FaceCameraManager.getInstance().getCurCamera() == FaceCameraManager.CAMERA_USB)
    bitmap = mUVCCameraView.captureStillImage();
else
    bitmap = mTextureView.getBitmap();
}

5.2.2 注册会员信息和人脸信息

POS设备使用SDK提供的人脸识别功能,对人脸照片提取特征值,并注册会员。相关参考代码如下:

/*
 * @bitmap 含有人脸bitmap
 * @maxFace 返回最大人脸数
 * @return 返回人脸特征值
 */   
 public ArrayList‹SunmiFaceFeature› getFeatures(Bitmap bitmap, int maxFace) {

        int ret = 0;
        //bitmap RGB图像转化为BRG
        byte[] srcData = ImageUtils.getPixelsBGR(bitmap);
        SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), maxFace);
        SunmiFaceImageFeatures features = new SunmiFaceImageFeatures();
        ret = SunmiFaceSDK.getImageFeatures(image, features);
        SunmiFaceFeature feature_ary = features.getFeatures();
        ArrayList‹SunmiFaceFeature› arrayList = new ArrayList‹›();
        if (features.getFeaturesCount() == 0) {
            SunmiFaceSDK.releaseImageFeatures(features);
            return arrayList;
        }
        for (int i = 0; i ‹ features.getFeaturesCount(); i++) {
            SunmiFaceFeature sunmiFaceFeature = SunmiFaceLib.SunmiFaceFeatureArrayGetItem(feature_ary, i);
            arrayList.add(sunmiFaceFeature);
        }
        SunmiFaceSDK.releaseImageFeatures(features);
        return arrayList;
        // return getFeature(srcData, bitmap.getWidth(),bitmap.getHeight(), 1);
    }
/*
 *向系统注册会员信息
 *@groupname, 会员组
 *@userNanem, 会员名
 *@picPath,   会员照片存放路劲
 *@userInfo,  会员详情
 *@faceFeature, 会员照片提取的特征值
 */
public boolean registerUserIntoDBmanager(String groupName, String userName, String picPath,String userInfo, SunmiFaceFeature faceFeature) {
        boolean isSuccess = false;
        Group group = new Group();
        group.setGroupId(groupName);
        User user = new User();
        user.setGroupId(groupName);
        //final String uid = UUID.randomUUID().toString();
        String uid = String.valueOf(System.currentTimeMillis());
        user.setUserId(uid);
        user.setUserName(userName);
        user.setFeature(faceFeature.getFeature());
        SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(faceFeature);
        record.setId(user.getUserId());
        record.setName(user.getUserName());
        //把人脸特征值添加到人脸特征库
        int ret = SunmiFaceSDK.addDBRecord(record);
        if (ret != 0) {
            Log.d(TAG, "addDBRecord failed " + SunmiFaceSDK.getErrorString(ret));
            return false;
        }
        user.setImageName(record.getImgId());
        if (userInfo != null) {
            user.setUserInfo(userInfo);
        }
        // 添加用户信息到数据库
        boolean importUserSuccess = FaceManager.getInstance().userAdd(user);
        if (importUserSuccess) {
            // 如果添加到数据库成功,则添加用户组信息到数据库
            // 如果当前图片组名和上一张图片组名相同,则不添加数据库到组表
            if (FaceManager.getInstance().groupAdd(group)) {
                isSuccess = true;
                if (mIPCManager != null) {
                    Log.i(TAG, "picPath: " + picPath);
                    Log.i(TAG, "uid: " + uid);
                    // 添加用户的同时,把人脸照片、id同步到AI识客设备
                    mIPCManager.addFaceRecord(picPath, uid, new RPCCallback‹RPCResponse›() {
                        @Override
                        public void onComplete(RPCResponse result) { 
                        }
                        @Override
                        public void onError(Throwable t) {
                            super.onError(t); 
                        }
                    });
                }
            } else {
                isSuccess = false;
            }

        } else {
            isSuccess = false;
        }
        return isSuccess;
    }

5.3 会员管理

包括会员列表、会员注册 、会员删除、会员查看。

会员注册:见 5.2.2 注册会员信息和人脸信息

会员删除:删除会员的时候,需要把删除信息同步到FS设备。相关参考代码如下:

    /**
     * 删除用户
     */
    public boolean userDelete(String userId, String groupId) {
        if (TextUtils.isEmpty(userId) || TextUtils.isEmpty(groupId)) {
            return false;
        }
        User user = DBManager.getInstance().queryUser(groupId, userId);
        if (user == null) {
            Log.d(TAG, "Database has no user " + userId);
            return false;
        }
        Log.d(TAG, "userDelete userId" + user.getUserId() + " userImage: " + user.getImageName());

        //删除会员信息
        boolean ret = DBManager.getInstance().deleteUser(userId, groupId);
        DBManager.getInstance().deleteUserEventByUserId(userId);
        //从人脸库中删除人脸特征值
        int result = SunmiFaceSDK.deleteDBRecord(user.getImageName());

        if (mIPCManager != null) {
            //删除会员时,同步删除AI识客设备上的人脸数据信息
            mIPCManager.deleteFaceRecord(userId,new RPCCallback‹RPCResponse‹RPCResponse.FaceDeleteSubResult››() {
                @Override
                public void onComplete(RPCResponse‹RPCResponse.FaceDeleteSubResult› result) {
                    Log.i(TAG, "deleteFaceRecord, code:" + result.code());
                    if (result.code() != 0){
                        ToastUtils.showToast(context, "删除人脸失败 code: " + result.code());
                    } else {
                        ToastUtils.showToast(context, "删除人脸成功");
                    }
                }

                @Override
                public void onError(Throwable t) {
                    super.onError(t);
                    Log.i(TAG, "addFaceRecord, Exception:" + t.getMessage());
                }

            });
        }
        return ret;
    }

会员查看:主要显示会员ID、组、会员描述信息以及会员注册头像。

5.4 会员实时查询

主要功能实现了实时的获取POS设备自带摄像头或者外置USB摄像头的预览数据,并且对获取的图像数据做实时人脸特征提取,并查询根据人脸特征信息查询会员信息。主要使用到的功能有:实时获取摄像头预览数据人脸特征提取人脸识别等功能

5.4.1 实时获取摄像头数据

POS设备实时获取摄像头数据相关代码如下:

//开启摄像头预览
FaceCameraManager.getInstance().startPreview(getApplicationContext(), this, FaceCameraManager.getInstance().getCurCamera(), cameraView, PREVIEW_WIDTH, PREVIEW_HEIGHT, cameraDataCallback);
//摄像头数据回调接口	
CameraDataCallback cameraDataCallback = new CameraDataCallback() {
    @Override
    public void onImageDataArrival(Bitmap bitmap, int width, int height) {
        if (mBackgroundHandler != null) {
            if (mClickAction == 1 && !faceDetectState.get()) {
                //在一个独立线程中对获取到的图像数据进行人脸特征提取、识别
                mBackgroundHandler.post(new MotionDetector(bitmap, width, height));
            } else {
                bitmap.recycle();
            }
        }

    }
};

5.4.2 特征提取功能

使用开发包提供的人脸特征提取功能,相关参考代码如下:

/*
 * @bitmap 含有人脸bitmap
 * @maxFace 返回最大人脸数
 * @return 返回人脸特征值
 */      
public ArrayList‹SunmiFaceFeature› getFeatures(Bitmap bitmap, int maxFace) {

        int ret = 0;
        //bitmap的RGB数据转化成BGR数据
        byte[] srcData = ImageUtils.getPixelsBGR(bitmap);
        //通过BGR数据构造SunmifaceImage对象
        SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), maxFace);
        SunmiFaceImageFeatures features = new SunmiFaceImageFeatures();
        //从SunmifaceImage数据中提取人脸特征
        ret = SunmiFaceSDK.getImageFeatures(image, features);
        //返回人脸特征数组
        SunmiFaceFeature feature_ary = features.getFeatures_();
        ArrayList‹SunmiFaceFeature› arrayList = new ArrayList‹›();
        if (features.getFeatures_count_() == 0) {
            SunmiFaceSDK.releaseImageFeatures(features);
            return arrayList;
        }
        for (int i = 0; i ‹ features.getFeatures_count_(); i++) {
            SunmiFaceFeature sunmiFaceFeature = SunmiFaceLib.SunmiFaceFeatureArray_getitem(feature_ary, i);
            arrayList.add(sunmiFaceFeature);
        }
        SunmiFaceSDK.releaseImageFeatures(features);
        return arrayList;
        // return getFeature(srcData, bitmap.getWidth(),bitmap.getHeight(), 1);
    }

5.4.3 人脸识别

提取人脸特征后,查询人脸特征数据库,返回用户信息,相关代码如下:

public User getUserByFeature(SunmiFaceFeature feature) {
    SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(feature);
    SunmiFaceDBIdInfo info = new SunmiFaceDBIdInfo();
    //在人脸特征数据查询对应的特征值
    int ret = SunmiFaceSDK.searchDB(record, info);
    if (!info.getIsMatched()) {
        return null;
    } else {
        //查询到匹配的特征值后,查询用户信息,并返回用户信息。
        return DBManager.getInstance().queryUser("default", info.getId());
    }
}

6 SDK详细接口说明

本SDK开发包包含人脸识别和FS设备SDK。请分别详细查阅以下链接了解API接口详细内容。

脸识别API FS设备API

人脸识别离线SDK包

请通过以下链接下载DemoAPP和离线SDK包,DemoAPP演示了人脸识别SDK的的基本功能调用,并可以直观尝试人脸识别功能的效果, 并可提供APP源代码方便参考开发。

注:功能需设备授权激活,请联系您的商米销售获取3个月免费授权,若无专属销售,请邮件发送详细需求至AI部门(meijuan@sunmi.com)获取 3个月免费授权 ,详见本文3.1介绍。

人脸识别DemoAPP_v1.3.1(49.6M):下载
人脸识别SDK包 _v1.3.1(35.6M):下载

【版本更新记录】
2021-01-05:
SDK_v1.3.1版本相比于SDK_v1.3.0版本修改了授权。
APP_v1.3.1 版本相比于APP_v1.3.0版本修改了授权 。
2020-12-17:
SDK_v1.3.0版本相比于SDK_v1.2.0版本增加了商米远程授权功能。
APP_v1.3.0 版本相比于APP_v1.2.0版本增加了商米远程授权功能。

注:
1) 商米人脸识别离线SDK支持 Android7及以上系统环境 ,适配K1、K2、D2、D2mini、T2等商米设备,新设备适配能力请咨询销售和相关技术人员。
2)活体检测功能:包含RGB活体和3D活体功能,其中3D活体功能需要配备3D双目摄像头。

1. 概述

SUNMI人脸识别SDK是一种面向安卓设备的人脸技术开发包。此SDK开发包包含人脸检测、人脸识别和人证比对等功能,以aar包+models的形式发布。本文档介绍了SDK的使用说明以及数据结构和接口的定义,便于开发者在调用SDK接口时参考。

1.1. 文档使用范围

该人脸识别接口文档主要适用于人脸识别开发的相关人员参考阅读。

1.2. 功能清单

SUNMI人脸识别SDK主要提供如下功能:

模块功能
人脸检测检测图片中出现人脸的位置,并标记出人脸的五个关键点位置
人脸识别将当前人脸和数据库中的人脸进行匹配,确定当前人脸身份信息
人证比对采集身份证上的照片,并且判断当前人脸是否和身份证上的人脸匹配
人脸属性预测根据图片上出现的脸,预测人的性别及年龄
活体检测判断图片上出现的脸是否来自于真人;当采用3D摄像头时预测结果更准确

1.3. 开发包SDK的说明

SUNMI人脸识别SDK包含sunmi_facelib.aar文件和models文件夹。其中models文件夹包含一个配置文件和六个模型文件:

文件/文件夹名说明
sunmi_facelib.aarSDK lib库相关代码的aar
models配置文件:config.json模型文件:face_graph.json、face_param.params、detect_graph.json、detect_param.params、liveness_graph.json和liveness_param.params、depth_detector.yml、attribute_graph.json和attribute_param.params

注: SUNMI人脸识别SDK 只有一个实例,是线程不可重入的。只能同时由一个线程调用,不能在多个线程里面同时调用。

2. 环境信息

系统环境平台编译环境
Android7及以上arm32, arm64NDK

3. Java SDK使用说明

3.1. 获取授权文件

SUNMI人脸识别SDK的授权需要绑定Android系统信息。在第一次使用前,开发者需要获取人脸识别SDK的license授权文件,主要有以下两种方式。

3.1.1 远程授权

开发者需获取设备的SN号提供给商米,商米将验证并远程提供人脸识别SDK的license授权文件。该文件默认发送至“/sdcard/ SunmiRemoteFiles” 目录,开发者可根据需求移动授权文件,可以存放在SD卡某个路径下,也可以存放在APP文件路径下 ,能够正常上传被替换即可。

注:DemoAPP_v.1.2.1及之后的版本增加远程授权功能,且Demo的授权路径默认为 “/sdcard/ SunmiRemoteFiles” 目录 。

3.1.2 获取设备硬件指纹

开发者可以通过如下代码获取硬件指纹信息提供给商米,商米将验证并提供人脸识别SDK的license授权文件。该文件一般与模型文件存放在同级目录下用于3.6中的验证授权,可以存放在SD卡某个路径下,也可以存放在APP文件路径下 ,能够正常上传被替换即可。

注:1)商米提供的人脸识别demo默认从sdcard根目录读取 license授权文件 。
2)需保证wifi开关为打开状态。

String fingerprint = SunmiFaceSDK.getDeviceFingerprint(context);

3.2. 导入SDK

在Android Studio项目中导入SDK aar包即可。

3.3. 存放模型文件

模型文件(.json和.params文件)可以存放在SD卡某个路径下,也可以存放在APP文件路径下。请务必保证模型所在路径可以被访问。

3.4. 配置config.json文件

使用模型包中config.json文件作为模板,或者自行创建一个config.json文件,拷贝如下代码。根据3.3中9个文件face_graph.json、face_param.params、detect_graph.json、detect_param.params、liveness_graph.json、liveness_param.params、depth_detector.yml、attribute_gragh.json和attribute_param.params存放的实际路径进行修改。SDK需通过读取此config.json配置文件进行初始化。

{  
   // 人脸识别库文件,此文件在SDK Jnilibs里,一般不需要修改
   "face_lib_path": "libface_lib.so", 
   // 人脸识别库网络描述文件,需要绝对路径 
   "face_graph_path": "/storage/emulated/0/faceTest/face_graph.json",  
   // 人脸识别库网络参数文件,需要绝对路径 
   "face_param_path": "/storage/emulated/0/faceTest/face_param.params",  
   // 人脸检测库文件,此文件在SDK Jnilibs里,一般不需要修改  
   "detect_lib_path": "libdetect_lib.so", 
   // 人脸检测库网络描述文件,需要绝对路径 
   "detect_graph_path": "/storage/emulated/0/faceTest/detect_graph.json",  
   // 人脸检测库网络参数文件,需要绝对路径 
   "detect_param_path": "/storage/emulated/0/faceTest/detect_param.params",  
   // 2D活体检测库文件,此文件在SDK Jnilibs里,一般不需要修改 
   "liveness_lib_path": "libliveness_lib.so",  
   // 2D活体检测库网络描述文件,需要绝对路径
   "liveness_graph_path":"/storage/emulated/0/faceTest/liveness_graph.json",
   // 2D活体检测库网络参数文件,需要绝对路径
  "liveness_param_path":"/storage/emulated/0/faceTest/liveness_param.params",
   // 人脸库存放路径,需要绝对路径  
   "face_db_file": "/storage/emulated/0/faceTest/sunmi_face.db", 
   // 3D活体检测模型,需要绝对路径
   "depth_detector": "/storage/emulated/0/faceTest/depth_detector.yml"
   // 人脸属性预测库文件,此文件在SDK Jnilibs里,一般不需要修改
   "attr_lib_path": "libattribute_lib.so",
   // 人脸属性预测网络描述文件,需要绝对路径
   "attr_graph_path": "/storage/emulated/0/faceTest/attribute_graph.json",
   //  人脸属性预测库网络参数文件,需要绝对路径
   "attr_param_path": "/storage/emulated/0/faceTest/attribute_param.params",

3.5. 始化SDK

以下为SDK调用init接口执行初始化的示例。init接口参数为3.4中的config.json文件的路径。

String confPath = Environment.getExternalStorageDirectory() + 
"/faceTest/config.json";
int code = SunmiFaceSDK.init(confPath);

3.6. 验证授权文件

使用类似如下代码读取授权文件内容到Java String中,再通过verifyLicense接口验证。若验证通过,可正常调用SDK其他接口。若验证失败,调用SDK其他接口将返回授权错误代码。错误代码为“10”,则表示授权文件无效;错误代码为“11”,则表示授权文件过期。如有授权问题,请联系商米解决。
注:需保证wifi开关为打开状态。

String license = readToString(Environment.getExternalStorageDirectory() + 
"/faceTest/license_valid.txt");
int code = SunmiFaceSDK.verifyLicense(context, license); // context为Android Context

3.7. 配置SDK

SUNMI人脸识别SDK参数都可动态配置,建议先通过getConfig得到当前参数,根据需要将参数修改完成后,再用setConfig接口进行设置。示例代码如下:

SunmiFaceConfigParam param = new SunmiFaceConfigParam();
SunmiFaceSDK.getConfig(param);
param.setDistanceThreshold(0.9f); //当SDK应用于支付场景,推荐值是0.9;当应用于人证比对场景时,推荐值为1.1;阈值越小,SDK对于相同人的判定越严格
param.setYawThreshold(50.0f);//人脸姿态阈值,设置较小的人脸姿态阈值,阈值越小,人脸姿态要求越严格,保证相对正面的人脸姿态做识别
param.setPitchThreshold(50.0f);
param.setRollThreshold(50.0f);
param.setMinFaceSize(60);//最小人脸检测尺寸,较大较清晰的人脸识别较准确
param.setImageQualityThreshold(10);//阈值越高,人脸清晰度越好,人脸识别越准确
param.setMinLuminance(10);//最小光照
param.setMaxLuminance(180);//最大光照
param.setLivenessOn(true);//开启人脸活体检测
param.setThreadNum(1); //使用一个cpu核心执行人脸检测,人脸识别
int code = SunmiFaceSDK.setConfig(param);

3.8. 提取人脸特征

提取步骤:

  1. 获取的Bitmap图片:用户根据需求,从摄像头获取bitmap传给SDK。
  2. 传入图片的尺寸:人脸SDK检测最佳输入图的宽高比为1:1。若宽高比过大,可能导致检测性能略微下降。
  3. 图像格式处理:将获取的Bitmap图片转为BGR byte buffer格式。(可参考如下示例代码)
  4. 接口调用:传入BGR byte buffer,通过SunmiFaceImage构造函数创建SunmiFaceImage。再通过getImageFeatures接口传入SunmiFaceImage图片数据,得到SunmiFaceImageFeatures人脸结果。
  5. 多人脸识别逻辑:如果有多张人脸,SunmiFaceFeature在SunmiFaceImageFeatures中按照置信度score从高到低排列。
  6. 人脸库记录:faceFeature2FaceDBRecord把得到的人脸特征转换为人脸库的记录,设置相关ID,name。
  7. 资源释放:SunmiFaceImageFeatures在SDK中动态分配内存空间,使用完毕后需要调用接口释放资源。

示例代码如下:

public static byte[] getPixelsBGR(Bitmap image) {
    // 计算图片有多少字节
    int bytes = image.getByteCount();

    ByteBuffer buffer = ByteBuffer.allocate(bytes); // 创建新的buffer
    image.copyPixelsToBuffer(buffer); // 移动字节数据到buffer

    byte[] temp = buffer.array(); // 获取包含数据的底层数组
    byte[] pixels = new byte[(temp.length / 4) * 3]; // 分配空间给BGR

    // 对像素重组
    for (int i = 0; i ‹ temp.length / 4; i++) {
        pixels[i * 3] = temp[i * 4 + 2];        //B
        pixels[i * 3 + 1] = temp[i * 4 + 1];    //G
        pixels[i * 3 + 2] = temp[i * 4];        //R
    }
    return pixels;
}
private void addFeature(String name) {
    if (bitmap == null) {
        Toast.makeText(context, "请先拍照", Toast.LENGTH_SHORT).show();
        return;
    }
    byte[] srcData = FileUtils.getPixelsBGR(bitmap);
    SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), 1);
    SunmiFaceImageFeatures features = new SunmiFaceImageFeatures();
    SunmiFaceSDK.getImageFeatures(image, features);
    SunmiFaceFeature feature_ary = features.getFeatures();
    Log.e("ma", "face info: " + features.getFeaturesCount());
    if (features.getFeaturesCount() == 0) {
        showId("没有检测到人脸,请重新拍照!");
        return;
    }
    SunmiFaceFeature feature = SunmiFaceLib.SunmiFaceFeatureArrayGetItem(feature_ary, 0);
    SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(feature);
    record.setId(name);
    record.setName(name);
    SunmiFaceSDK.addDBRecord(record);
    SunmiFaceSDK.releaseImageFeatures(features);
    Log.e("face", "采集成功 " + etName.getText().toString());
}

3.9. 人脸库添加人脸特征

SunmiFaceDBRecord包含img_id_、id和name三个参数,其中img_id_无需设置,id和name需要调用者进行设置:

  1. img_id_无需设置。调用addDBRecord将记录成功加入到人脸库后,SunmiFaceDBRecord中img_id_会自动被设置为人脸库中代表此图片的唯一id。img_id_可以理解为录入图片的唯一名字,SDK保证返回的img_id不重复。用户可以以img_id为文件名来保存拍摄的人脸照片。根据此img_id可以调用deleteDBRecord来删除这条记录。
  2. id为被录入人脸的人员的唯一id。SDK考虑开发者调用的灵活性,没有对其唯一性进行限制。在通常情况下,建议调用者务必保证id的唯一性,id可理解为一般不会重号的身份证号,食堂卡号,会员号等。
  3. name可以不唯一,允许出现两个人名相同的情况,同时允许每个人录入多张人脸。

示例:
数据库中记录:
用户A,id为0001,姓名张三,图片1,图片2;
用户B, id为0002,姓名也是张三,图片3,图片4,图片5。
用户A和B同名,并且都有多张图片,存储每张图片提取的人脸特征。

注:建议在人脸识别过程中将被识别的人脸图片进行存储, 推荐采用addDBRecord接口返回的SunmiFaceDBRecord里面的getImgId()来命名图片,以绑定图片和人脸库中人脸特征,方便后期对人脸库进行相关操作和比对

3.10.  获取人脸关键点

通过getLandmark_得到人脸关键点数组,在获取数组中的每个关键点信息。示例代码如下:

SunmiFaceFeature feature = SunmiFaceLib.SunmiFaceFeatureArrayGetItem(feature_ary, 0);
SunmiFaceLmk lmk_ary = feature.getLandmark();
for(int i = 0; i ‹ SunmiFaceLibConstants.SUNMI_FACE_LANDMARK_LEN; i++) {
    SunmiFaceLmk lmk = SunmiFaceLib.SunmiFaceLmkArrayGetItem(lmk_ary, i);
}

3.11. 搜索人脸库

调用searchDB执行人脸库搜索寻找最相似的人脸。如果SDK判断人脸距离小于阈值,matched设为true,否则为false。示例代码如下:

SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(feature);
SunmiFaceDBIdInfo info = new SunmiFaceDBIdInfo();
SunmiFaceSDK.searchDB(record, info);
if (info.getIsMatched()) {
	Log.e("ma", “Found”);
}
else {
Log.e("ma", “Not Found”);
}

3.12. 人脸1:1比对

分别对要比对的两张图片进行人脸特征的提取,得到类型为SunmiFaceFeature两个人脸特征feature1,feature2。在调用compare1v1接口得到比对结果。getIsMatched()得到比对结果,getDistance()得到两张人脸的距离度量。

SunmiFaceCompareResult result;
int code = SunmiFaceSDK.compare1v1(feature1, feature2, result)
boolean isMatched = result.getIsMatched()
float distance = result.getDistance();

3.13. 活体检测

2D活体检测使用UVC摄像头实现2D活体检测功能,适用于没有3D摄像头的情况。
3D活体检测使用奥比中光3D结构光摄像头实现3D活体检测功能,其预测效果相比2D活体检测有很大提升。深度图depth为16bit数据,表示物体到摄像头以毫米为单位的距离。需要注意以下几点:

  1. 使用活体检测,要求BGR图像分辨率为 480×640,3D活体检测深度图分辨率如下表所示。
  2. 3D活体检测需保证BGR图像和深度图像在时间上是对应的。
  3. 彩色图和深度图存在一定的位置偏差,以彩色图检测到的人脸位置为参考,深度图在x方向和y方向的偏差如下表所示。
  4. 活体检测存在一定的真人判断为非活体的概率。所以推荐判断非活体需要执行3次左右来确认。非活体判断为真人的概率低于1%。
设备型号3D摄像头型号深度图分辨率深度图x方向偏移量深度图y方向偏移量是否旋转
K1Pro A640*48028-20
K2D2plus400*64075-15
T2

活体检测的使用步骤如下:

  • 1. 开启活体检测,配置阈值。
SunmiFaceConfigParam param = new SunmiFaceConfigParam();
SunmiFaceSDK.getConfig(param);
param.setLivenessOn(true);
param.setDepthLivenessThreshold(0.5f); //2D和3D都推荐阈值在0.5左右
param.setDepthXOffset(75); //3D根据设备型号设置深度图x方向偏移量
param.setDepthYOffset(-15); //3D根据设备型号设置深度图y方向偏移量
ret = SunmiFaceSDK.setConfig(param);
  • 2. 构造SunmiFaceImage,同时传入BGR图像和深度图像。
SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), depth, depth_height, depth_width, 1); // 3D活体检测

SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), 1); // 2D活体检测

注:当摄像头需要旋转时,在传入BGR和深度图像时之前需要对两幅图进行90度旋转,BGR图旋转的代码如下:

public static Bitmap rotateBitmap(Bitmap origin, float alpha) {
        if (origin == null) {
            return null;
        }
        int width = origin.getWidth();
        int height = origin.getHeight();
        Matrix matrix = new Matrix();
        matrix.setRotate(alpha);
        // 围绕原地进行旋转
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        if (newBM.equals(origin)) {
            return newBM;
        }

        return newBM;
    }

深度图旋转代码如下:

public static byte [] rotateDepthImage(byte[] depthByte, int height, int width){
        if(depthByte == null){return null;}

        int depthLen = height * width * 2;
        byte [] newDepth = new byte [depthLen];
        for (int i = 0; i<=width-1; i++){
            for(int j = 0; j<=height-1; j++){
                newDepth[2*(i*height+j)] = depthByte[2*((height-1-j)*width+i)];
                newDepth[2*(i*height+j)+1] = depthByte[2*((height-1-j)*width+i)+1];
            }
        }
        return newDepth;
    }
  • 3. 查看活体判断结果,如果code为FACE_CODE_OK则判断为活体。如果code为FACE_CODE_NOT_LIVENESS则判断为非活体。
int code = SunmiFaceSDK.getImageFeatures(image, features);

3.14. 获取人脸属性

人脸属性首先需要获取人脸特征faceFeature,再根据人脸特征获取年龄和性别属性,主要示例代码如下:

SunmiFaceFeature faceFeature = SunmiFaceLib.SunmiFaceFeatureArrayGetItem(features.getFeatures(), 0);
SunmiFaceAge ageInfo = faceFeature.getAge();
int age = ageInfo.getClassification();
float ageScore = ageInfo.getScore();
SunmiFaceGender genderInfo = faceFeature.getGender();
int gender = genderInfo.getClassification();
float genderScore = genderInfo.getScore();

3.15 执行功能选择

功能选择是为了方便开发者在实际开发中只选择所需的SDK功能,减少计算资源和时间的浪费。该功能可通过SunmiFaceImage中定义的predict_mode_成员进行控制。

例如:在3.8和3.14中,分别介绍了商米人脸识别SDK提取人脸特征和人脸属性获取的功能。实际上,SDK在这两小节提供的代码中同时完成了人脸检测、人脸特征提取及人脸属性识别。若开发者只需使用其中一个,三个功能同时进行会极大地浪费计算资源和时间。因此,可以使用功能选择进行控制,主要示例代码如下:

image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), 1);   //根据bitmap创建SunmiFaceImage对象
SunmiFaceImageFeatures features = new SunmiFaceImageFeatures();   //创建空的SunmiFaceImageFeatures对象
image.setPredictMode(SunmiFaceMode.PredictMode_Age);    //只进行年龄预测
image.setPredictMode(SunmiFaceMode.PredictMode_Feature);    //只进行特征提取
image.setPredictMode(SunmiFaceMode.PredictMode_Detect);    //只进行人脸检测
image.setPredictMode(SunmiFaceMode.PredictMode_Age|SunmiFaceMode.PredictMode_Feature);    //进行人脸特征提取和年龄预测
image.setPredictMode(SunmiFaceMode.PredictMode_Age|SunmiFaceMode.PredictMode_Feature|PredictMode_Gender);    //进行人脸特征提取及年龄性别预测,即执行SDK的全部功能,此行代码可以不写,SDK默认模式为执行全部功能
ret = SunmiFaceSDK.getImageFeatures(image, features);    //根据选择的模式提取相应特征

4. 数据类型定义

4.1. 类型别名

别名类型说明
SunmiFaceStatusintTypedef,定义在face_type.h 文件中
SunmiFaceHandlevoid*Typedef,定义在face.h 文件中

4.2. 宏

接口使用的数据类型定义在face_type.h头文件中,主要数据如下:

成员说明
SUNMI_FACE_FEATURE_LEN256人脸特征向量长度
SUNMI_FACE_ID_LEN64id字符长度限制
SUNMI_FACE_NAME_LEN64人脸用户名长度
SUNMI_FACE_IMG_ID_LEN64人脸采集图片ID长度
SUNMI_FACE_LANDMARK_LEN5人脸关键点数量

4.3. 枚举类型

接口使用的数据结构定义在face_type.h头文件中。

4.3.1. SunmiFaceStatusCode 商米人脸识别状态码

枚举成员状态码说明
FACE_CODE_OK0正确
FACE_CODE_UNINIT1SDK没有初始化
FACE_CODE_EMPTY_IMAGE2图片为空
FACE_CODE_NO_MODEL3无加载模型文件
FACE_CODE_CONFIG_ERROR4配置错误
FACE_CODE_BAD_ILLUMINATION5光线条件不好
FACE_CODE_POSE_ERROR6面部位置不符合要求
FACE_CODE_SMALL_FACE_SIZE7人脸尺寸太小
FACE_CODE_LOW_IMAGE_QUALITY8人脸图像模糊
FACE_CODE_NOT_LIVENESS9非活体人脸图像
FACE_CODE_LICENSE_ERROR10授权文件无效
FACE_CODE_LICENSE_EXPIRED11授权文件过期
FACE_CODE_IMAGE_ID_ERROR101删除时图像id不存在
FACE_CODE_OTHER_ERROR1000其他错误

4.3.2. SunmiFaceGenderType 性别类型

枚举成员说明
FACE_ATTR_FEMALE0性别女
FACE_ATTR_MALE1性别男

4.3.3 SunmiFaceMode 执行功能选择

枚举成员说明
PredictMode_None0不执行任何功能
PredictMode_Detect1执行人脸检测
PredictMode_Feature3执行人脸特征提取
PredictMode_Age5执行年龄预测
PredictMode_Gender5执行性别预测

4.4. 结构体类型

4.4.1. SunmiFaceRect人脸框位置

成员类型说明
x1_float人脸框左上角横坐标
y1_float人脸框左上角纵坐标
x2_float人脸框右下角横坐标
y2_float人脸框右小角纵坐标
score_float置信度

4.4.2. SunmiFaceLmk人脸关键点坐标

成员类型说明
x_float关键点横坐标
y_float关键点纵坐标

4.4.3. SunmiFaceImage人脸识别输入图像

成员类型说明
buf_unsigned char *图片像素字节地址
buf_len_int字节缓冲区长度
img_w_int图像宽度
img_h_int图像高度
max_face_count_int单图人脸数最大返回值
depth_buf_unsigned char *深度图数据字节地址
depth_buf_len_int深度图数据长度
depth_img_w_int深度图宽度
depth_img_h_int深度图高度
predict_mode_int需要执行的功能选择

4.4.4. SunmiFacePose人脸姿态

成员类型说明
pitch_float俯仰角度
yaw_float偏航角度
roll_float翻滚角度

4.4.5. SunmiFaceAge 年龄

成员类型说明
classification_int年龄范围(0-74岁)
score_float置信度

4.4.6. SunmiFaceGender 性别

成员类型说明
classification_int性别分类(0: female ; 1: male)
score_float置信度

4.4.7. SunmiFaceFeature单个特征提取输出信息结构体

成员类型说明
face_rect_SunmiFaceRect人脸矩形框位置
liveness_score_float活体分数
feature_[SUNMI_FACE_FEATURE_LEN]float人脸特征数组
landmark_[SUNMI_FACE_LANDMARK_LEN]SunmiFaceLmk关键点
pose_SunmiFacePose人脸姿态
age_ SunmiFaceAge 年龄
gender_SunmiFaceGender 性别

4.4.8. SunmiFaceImageFeatures多个特征提取输出信息结构体

成员类型说明
features_SunmiFaceFeature *特征数组
features_count_int特征数组长度

4.4.9. SunmiFaceConfigParam人脸配置参数

成员类型说明
thread_num_intSDK人脸检测人脸识别计算使用的线程数量(cpu核数),0代表使用所有的cpu核
distance_threshold_float人脸距离阈值
yaw_threshold_float偏航角度阈值
pitch_threshold_float俯仰角度阈值
roll_threshold_float翻滚角度阈值
min_face_sizeint人脸最小尺寸
image_quality_threshold_float图像质量阈值
min_luminance_int最低亮度
max_luminance_int最高亮度
rgb_liveness_threshold_floatRGB活体检测阈值
depth_liveness_threshold_float3D活体检测阈值
liveness_on_bool活体检测开关

4.4.10. SunmiFaceCompareResult人脸匹配结果

成员类型说明
is_matched_bool如果距离小于人脸间距阈值,则为真,否则为假
distance_float人脸距离

4.4.11. SunmiFaceDBRecord人脸数据库记录

成员类型说明
id_[SUNMI_FACE_ID_LEN]char人脸唯一id
name_[SUNMI_FACE_NAME_LEN]char用户名
img_id_[SUNMI_FACE_IMG_ID_LEN]char人脸采集图像唯一id
feature_[SUNMI_FACE_FEATURE_LEN]float人脸特征

4.4.12. SunmiFaceDBIdInfo人脸数据库信息

成员类型说明
id_[SUNMI_FACE_ID_LEN]char人脸唯一id
name_[SUNMI_FACE_NAME_LEN]char用户名
is_matched_bool如果距离小于人脸间距阈值,则为真,否则为假
distance_float人脸距离

5. C++函数接口定义

对外接口的定义在face.h头文件中。

5.1. 函数接口定义

5.1.1. SunmiFaceGetVersion

函数定义:SUNMI_FACE_API const char* SunmiFaceGetVersion();

函数功能:获取Sunmi人脸识别SDK版本

返回值:SDK版本字符串

5.1.2. SunmiFaceCreate

函数定义:SunmiFaceStatus SunmiFaceCreate(SunmiFaceHandle* handle);

函数功能:创建句柄,用户需要保持这个句柄直到释放

变量类型说明
handleSunmiFaceHandle*输出参数,创建人脸SDK句柄

返回值:SunmiFaceStatus

5.1.3. SunmiFaceInit

函数定义:SunmiFaceStatus SunmiFaceInit(SunmiFaceHandle handle,  const std::string& config_file);

函数功能:使用配置文件初始化SDK

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
config_fileconst std::string&配置文件路径

返回值:SunmiFaceStatus

5.1.4. SunmiFaceGetErrorString

函数定义:const char* SunmiFaceGetErrorString(SunmiFaceStatus code);

函数功能:通过SunmiFaceStatus得到错误描述字符串

变量类型说明
codeSunmiFaceStatusSunmiFaceStatus code

返回值:错误描述字符串

5.1.5. SunmiFaceVerifyLicense

函数定义:SunmiFaceStatus SunmiFaceVerifyLicense(SunmiFaceHandle handle, const std::string& fingerprint, const std::string& license);

函数功能:验证设备指纹和授权文件是否合法

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
fingerprintconst std::string&设备指纹字符串
licenseconst std::string&授权文件字符串

返回值:SunmiFaceStatus

5.1.6. SunmiFaceSetConfig

函数定义:SunmiFaceStatus SunmiFaceSetConfig(SunmiFaceHandle handle, const SunmiFaceConfigParam& config);  

函数功能:设置配置信息

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
configconst SunmiFaceConfigParam&配置参数

返回值:SunmiFaceStatus

5.1.7. SunmiFaceGetConfig

函数定义:SunmiFaceStatus SunmiFaceGetConfig(SunmiFaceHandle handle, SunmiFaceConfigParam& config);

函数功能:获取当前SDK参数

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
configSunmiFaceConfigParam&配置参数

返回值:SunmiFaceStatus

5.1.8. SunmiFaceRelease

函数定义:SunmiFaceStatus SunmiFaceRelease(SunmiFaceHandle handle);

函数功能:释放句柄

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄

返回值:SunmiFaceStatus

5.1.9. SunmiFaceGetImageFeatures

函数定义:SunmiFaceStatus SunmiFaceGetImageFeatures(SunmiFaceHandle handle, SunmiFaceImage image, SunmiFaceImageFeatures *features);

函数功能:从图像中获取人脸特征

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
imageSunmiFaceImage输入图片结构体
featuresSunmiFaceImageFeatures *返回人脸特征

返回值:SunmiFaceStatus

5.1.10. SunmiFaceCompare1v1

函数定义:SunmiFaceStatus SunmiFaceCompare1v1(SunmiFaceHandle handle, SunmiFaceFeature *feature1, SunmiFaceFeature *feature2, SunmiFaceCompareResult *compare_result);

函数功能:1:1人脸比对

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
feature1SunmiFaceFeature *比较特征1
feature2SunmiFaceFeature *比较特征2
compare_resultSunmiFaceCompareResult *比对结果

返回值:SunmiFaceStatus

5.1.11. SunmiFaceReleaseImageFeatures

函数定义:SunmiFaceStatus SunmiFaceReleaseImageFeatures(SunmiFaceImageFeatures *features);

函数功能:释放人脸特征

变量类型说明
featuresSunmiFaceImageFeatures *人脸特征

返回值:SunmiFaceStatus

5.1.12. SunmiFaceInitDB

函数定义:SunmiFaceStatus SunmiFaceInitDB(SunmiFaceHandle handle, const char* db_name);

函数功能:打开人脸数据库文件,如果不存在,则创建空的人脸数据库。SDK初始化时会默认打开config.json里面指定的face_db_file

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
db_nameconst char*数据库文件名称

返回值:SunmiFaceStatus

5.1.13. SunmiFaceAddDBRecord

函数定义:SunmiFaceStatus SunmiFaceAddDBRecord(SunmiFaceHandle handle, SunmiFaceDBRecord *record);

函数功能:添加一条人脸记录到人脸数据库中

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
recordSunmiFaceDBRecord *record人脸记录

返回值:SunmiFaceStatus

5.1.14. SunmiFaceSearchDB

函数定义:SunmiFaceStatus SunmiFaceSearchDB(SunmiFaceHandle handle, SunmiFaceDBRecord *record, SunmiFaceDBIdInfo *idInfo);

函数功能:1:N 人脸搜索

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
recordSunmiFaceDBRecord *人脸库记录结构体
idInfoSunmiFaceDBIdInfo *搜索结果

返回值:SunmiFaceStatus

5.1.15. SunmiFaceDeleteDBRecord

函数定义:SunmiFaceStatus SunmiFaceDeleteDBRecord(SunmiFaceHandle handle, char *img_id);

函数功能:根据图像id删除人脸数据库中的一个人脸记录

变量类型说明
handleSunmiFaceHandle输出参数,创建人脸SDK句柄
img_idchar *人脸采集图像在人脸库里的唯一ID

返回值:SunmiFaceStatus

6. Java函数接口定义

对外接口的定义在SunmiFaceSDK.java文件中。

6.1. 接口类定义

类名说明
SunmiFaceSDK人脸识别接口类名定义

6.2. 函数接口定义

6.2.1. getVersion

函数定义:public static String getVersion()

函数功能:获取Sunmi人脸识别SDK版本号

返回值:SDK版本字符串

6.2.2. Init

函数定义:public static int init(String configPath)

函数功能:初始化SDK

变量类型说明
configPathString配置文件路径

返回值:SunmiFaceStatusCode

6.2.3. verifyLicense

函数定义:public static int verifyLicense(Context context, String license)

函数功能:验证许可证

变量类型说明
contextContextAndroid context
licenseString许可证字符串

返回值:SunmiFaceStatusCode

6.2.4. getDeviceFingerprint

函数定义:public static String getDeviceFingerprint(Context context)

函数功能:获取Android设备指纹

变量类型说明
contextContextandroid context

返回值:设备指纹字符串

6.2.5. getErrorString

函数定义:public static String getErrorString(int code)

函数功能:获得错误字符串

变量类型说明
codeint错误代码

返回值:错误描述字符串

6.2.6. setConfig

函数定义:public static int setConfig(SunmiFaceConfigParam config)

函数功能:设置配置

变量类型说明
configSunmiFaceConfigParamSDK配置参数

返回值:SunmiFaceStatusCode

6.2.7. getConfig

函数定义:public static int getConfig(SunmiFaceConfigParam config)

函数功能:获取当前SDK配置

变量类型说明
configSunmiFaceConfigParamSDK配置参数

返回值:SunmiFaceStatusCode

6.2.8. getImageFeatures

函数定义:public static int getImageFeatures(SunmiFaceImage image, SunmiFaceImageFeatures features)

函数功能:从图像中进行人脸检测和人脸特征提取

变量类型说明
imageSunmiFaceImage输入图像
featuresSunmiFaceImageFeatures返回人脸特征

返回值:SunmiFaceStatusCode

6.2.9. compare1v1

函数定义:public int compare1v1(SunmiFaceFeature feature1, SunmiFaceFeature feature2, SunmiFaceCompareResult compare_result)

函数功能:人脸1:1比对

变量类型说明
features1SunmiFaceFeature人脸特征1
features2SunmiFaceFeature人脸特征2
compare_resultSunmiFaceCompareResult比对结果

返回值:SunmiFaceStatusCode

6.2.10. releaseImageFeatures

函数定义:public static int releaseImageFeatures(SunmiFaceImageFeatures features)

函数功能:释放人脸特征

变量类型说明
featuresSunmiFaceImageFeatures人脸特征

返回值:SunmiFaceStatusCode

6.2.11. initDB

函数定义:public static int initDB(String db_name)

函数功能:初始化新人脸数据库

变量类型说明
db_nameString人脸数据库文件名

返回值:SunmiFaceStatusCode

6.2.12. faceFeature2FaceDBRecord

函数定义:public static SunmiFaceDBRecord faceFeature2FaceDBRecord(SunmiFaceFeature feature)

函数功能:将人脸特征结构体转换成人脸库记录结构体

变量类型说明
featureSunmiFaceFeature人脸特征

返回值:SunmiFaceDBRecord

6.2.13. addDBRecord

函数定义:public static int addDBRecord(SunmiFaceDBRecord record)

函数功能:添加一条记录到人脸数据库,添加成功后通过img_id_可以的到人脸库中图片的ID,后续可以用来删除操作

变量类型说明
recordSunmiFaceDBRecord人脸库记录结构体

返回值:SunmiFaceStatusCode

6.2.14. searchDB

函数定义:public static int searchDB(SunmiFaceDBRecord record, SunmiFaceDBIdInfo idInfo)

函数功能:数据库中1:N人脸比对

变量类型说明
recordSunmiFaceDBRecord要搜索的人脸记录
idInfoSunmiFaceDBIdInfo搜索结果

返回值:SunmiFaceStatusCode

6.2.15. deleteDBRecord

函数定义:public static int deleteDBRecord(String img_id)

函数功能:根据图像id删除人脸数据库中的一条记录

变量类型说明
img_idString人脸采集图像的ID

返回值:SunmiFaceStatusCode