前言:
最近应防疫要求,开发一套人脸识别+腕部测温+身份证+健康码通行的道闸项目,人脸识别采用的是虹软人脸识别算法。以人脸识别+测温、刷身份证+测温、刷健康码+测温为开门条件。(文章末尾附源码)
软硬件环境
平台为Android平台,采用kotlin+java混编
虹软SDK版本为最新的4.0可以戴口罩识别
终端摄像头采用双目摄像头模组IR活体识别
扫码头、测温头、身份证读卡器皆为本公司设备,就不一一介绍了
UI界面和机器展示
使用说明
人脸识别通过后自动测温,然后向后台上传温度和人员信息,后台判断温度是否异常,并且保存人员通行记录
项目总体流程
人脸注册:
人脸注册采用另一种终端和小程序注册两种方式,这里只说小程序。
用户使用小程序采集人脸照片上传至服务器–>人脸终端起服务定时向服务端请求终端没有注册过的人脸–>终端拿到人脸照片之后注册至本地。另外定时请求需要删除和更改的人脸信息,然后本地做删除更改操作。(直接同步人脸照片而不是特征值是因为虹软目前没有小程序的人脸识别sdk)
开门条件
以人脸识别+测温、刷身份证+测温、刷健康码+测温为开门条件。
本文主要讲解人脸+测温
项目主要类介绍
PullDataServerHelper
拉取人脸信息帮助类,实现了拿到信息之后注册人脸、删除人脸、更改信息的操作
DataSyncService
数据同步服务,此类为server,主要功能是定时调用PullDataServerHelper做网络请求
facedb包
此包中为数据库操作相关文件,本项目数据操作使用greendao,不了解的可以了解一下,非常好用。
项目的一些东西就先说这么多,文章最后会附上源码,接下来着重讲一些虹软SDK的使用
人脸识别部分(核心代码)
1.sdk的激活
SDK为一次激活永久使用,不可多次激活,本文使用在线激活的方式,后端录入终端绑定激活码,app带着终端唯一标识向后端请求激活码。
激活之前先判断是否已经激活,没有激活才继续激活操作,下面为代码:
fun Active() {
//获取激活文件
val activeFileInfo = ActiveFileInfo()
val code = FaceEngine.getActiveFileInfo(mContext, activeFileInfo)
if (code == ErrorInfo.MOK) {
//已经激活
isActive.value = true
return
} else {
//未激活 读取本地存储的激活码
var sdkKey = readString(
mContext,
Constants.APP_SDK_KEY
)
var appId = readString(
mContext,
Constants.APP_ID_KEY
)
var activeKey = readString(
mContext,
Constants.APP_ACTIVE_KEY
)
if (sdkKey.isNullOrEmpty()) {
//本地无激活码 从网络获取
getSdkInfo()
} else {
val code1 = FaceEngine.activeOnline(
mContext,
activeKey,
appId,
sdkKey
)
if (code1 == ErrorInfo.MOK) {
isActive.value = true
return
} else {
getSdkInfo()
}
}
}
}
private fun getSdkInfo() {
RetrofitManager.getInstance().createReq(ApiServer::class.java)
.getSdkInfo(AppUtils.getMac())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<SdkInfoResult>() {
override fun onSuccees(data: SdkInfoResult) {
if (data.code == 200 && null != data.data) {
write(mContext, Constants.APP_SDK_KEY, data.data.SdkKey)
write(mContext, Constants.APP_ID_KEY, data.data.AppId)
write(mContext, Constants.APP_ACTIVE_KEY, data.data.ActiveKey)
val code1 = FaceEngine.activeOnline(
mContext,
data.data.activeKey,
data.data.appId,
data.data.sdkKey
)
if (code1 == ErrorInfo.MOK) {
isActive.value = true
return
} else {
isActive.value = false
}
}
}
override fun onFailure(message: String?) {
isActive.value = false
}
})
}
2、sdk初始化
初始化的各个属性官方文档都有详细讲解,这里就不赘述了
public void init() {
Context context = CustomApplication.Companion.getMContext();
FaceServer.getInstance().init(context);
ftEngine = new FaceEngine();
int ftEngineMask = FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_MASK_DETECT;
int ftCode = ftEngine.init(context, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_90_ONLY, FaceConfig.RECOGNIZE_MAX_DETECT_FACENUM, ftEngineMask);
ftInitCode.postValue(ftCode);
frEngine = new FaceEngine();
int frEngineMask = FaceEngine.ASF_FACE_RECOGNITION;
if (FaceConfig.ENABLE_FACE_QUALITY_DETECT) {
frEngineMask |= FaceEngine.ASF_IMAGEQUALITY;
}
int frCode = frEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_90_ONLY,
10, frEngineMask);
frInitCode.postValue(frCode);
//启用活体检测时,才初始化活体引擎
int flCode = -1;
if (FaceConfig.ENABLE_LIVENESS) {
flEngine = new FaceEngine();
int flEngineMask = (livenessType == LivenessType.RGB ? FaceEngine.ASF_LIVENESS : (FaceEngine.ASF_IR_LIVENESS | FaceEngine.ASF_FACE_DETECT));
if (needUpdateFaceData) {
flEngineMask |= FaceEngine.ASF_UPDATE_FACEDATA;
}