业务背景
vue项目,h5页面,用于微信公众号及嵌套在微信小程序,现要求在微信小程序中实现电子健康卡的人脸识别、用于验证用户是否本人。前端需要拍照或者上传用户的人脸照片到后端,由后端调用公安系统api来验证是否本人。从下面4个方面来调研如何实现人脸识别:
一、h5原生实现
- 利用浏览器的navigator.mediaDevices.getUserMedia API获取到摄像头拍摄的实时视频流数据,但是浏览器兼容性不好,并且必须是https才能使用。实现的人脸识别插件主要有2个,但是在微信浏览器中直接调不起摄像头,应该是微信浏览器屏蔽了getUserMedia方法,所以利用此方法在微信公众号和微信小程序里面行不通。不过还是大致说下期间我对这2个插件的探索:
- face-api.js 可以参考这篇文章,用此插件需要将人脸模型数据放在项目本地,包太大,导致vue项目启动直接卡死,果断放弃。
- tracking.js 可以参考这篇文章blog。这篇文章测试代码如下:
- 利用微信提供的网页(h5)人脸认证sdk,这种h5的sdk在小程序环境下不能使用。并且需要邮件申请人脸核身接口文档,会查验医院是否符合要求 注意:凡是想使用微信的人脸识别的东西都要满足一定要求才能使用,查看详情
二、微信小程序原生实现
如果是用微信原生实现的小程序,要求必须使用微信的人脸识别,不能使用第三方的。另外同样也要满足上面详情文档里面的要求才能使用微信的人脸核身。可能你会想到使用小程序的组件camera来获取人脸照片,然后将照片传给后端即可,但其实是不行的,发布小程序时会导致小程序审核不通过。微信小程序实现人脸识别直接看文档就行,这里不多说。
三、第三方人脸识别sdk
比如腾讯云、百度,需要付费。这个一般由公司产品去研究要不要使用吧。但是如果是微信小程序原生的话,不支持使用第三方人脸识别的
四、最后一招,修改交互
一开始想要的效果是,进入页面摄像头已经打开,然后直接点击拍照按钮就调接口识别信息。鉴于以上几种方法均未实现这个效果,只能把交互简单的改为实现拍照和选择相册照片,然后把照片传给后端即可。点击拍照就调起系统自带的前置相机拍照;点击上传就调起系统的相册让用户选择,然后将照片传给后端。 这里用input标签实现拍照和选择照片:
<van-image width="300" height="300" round fit="cover" :src="imgSrc" />
<van-button @click="toCam('camera')">拍照并认证</van-button>
<span @click="toCam('album')">上传照片认证</span>
<input
type="file"
id="input"
accept="image/*"
@change="fileChange"
ref="avatarCamera"
style="display: none"
/>
toCam(type) {
let cameraInput = this.$refs['avatarCamera'];
if (type === 'camera') {
// 调起前置摄像头
cameraInput.setAttribute('capture', 'user');
} else {
cameraInput.removeAttribute('capture');
}
cameraInput.click();
},
async fileChange(e) {
let files = e.target.files;
if (!files || files.length === 0) return;
let file = files[0];
const res = await this.$wxUtils.compressImg(file);
this.imgSrc = res.afterSrc;
// 调用后端接口验证身份
this.fetchfaceDetect(file);
}
除此以外,分享下使用微信公众wx.chooseImage实现选择拍照和选择照片,微信调起相机的话无法默认成前置摄像机,还有注意,如果是微信小程序嵌套h5,不要使用该方法,因为使用微信公众号sdk是需要校验公众号appId、签名这些东西,但是小程序不是公众号,appId是不一样的,刚开始我就是用了微信网页的sdk实现拍照,后面发现不行才换成input标签来实现。
<van-image width="300" height="300" round fit="cover" :src="imgSrc" />
<van-button
class="take-photo configMainBg configMainBorderColor"
@click="takePhoto('camera')"
>拍照并认证</van-button
>
<span
class="upload-photo"
:style="$themeColor"
@click="takePhoto('album')"
>上传照片认证</span
>
created() {
this.checkWxConfig();
},
async checkWxConfig() {
// 调接口返回公众号appId等信息
const res = await getWxJsApiconfig();
const { appId, timestamp, nonceStr, signature } = res || {};
window.wx.config({
debug: false,
appId,
timestamp,
nonceStr,
signature,
jsApiList: ['checkJsApi', 'chooseImage', 'getLocalImgData'],
});
window.wx.ready(() => {
window.wx.checkJsApi({
jsApiList: ['checkJsApi', 'chooseImage', 'getLocalImgData'],
success: data => {
console.log('配置检查结果成功', data);
},
fail: data => {
console.log('配置检查结果失败', data);
},
});
});
window.wx.error(err => {
console.log('微信配置错误~~~~~~', err);
});
},
takePhoto(sourceType) {
if (!this.$wxUtils.isWeiXin()) {
this.$toast('请在微信公众号或微信小程序环境下使用该服务');
return;
}
window.wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: [sourceType], // 'album', 'camera'可以指定来源是相册还是相机,默认二者都有
success: res => {
let localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
window.wx.getLocalImgData({
localId: localIds[0],
success: res => {
let localData = res.localData;
//兼容处理,安卓获取的图片base64码没有前缀,而苹果有,但base64前缀并不固定
if (localData.indexOf('data:image') === -1) {
localData = 'data:image/jpeg;base64,' + localData;
}
localData = localData
.replace(/\r|\n/g, '')
.replace('data:image/jgp', 'data:image/jpeg');
this.imgSrc = localData;
this.fetchfaceDetect();
},
fail: error => {
console.log('转换图片为base64失败', error);
},
});
},
fail: error => {
console.log('调起相机失败', error);
},
});
},
fetchfaceDetect(file) {
const toast = this.$toast.loading({
mask: true,
loadingType: 'spinner',
duration: 0,
message: '识别中...',
});
const { name, idCard } = this.$route.query;
const url = '/ehCard/healthCard/faceDetect';
let params = new FormData();
params.append('file', file);
params.append('name', name);
params.append('idCard', idCard);
this.$postRequest(url, params, false, {
'Content-Type': 'multipart/form-data',
showError: false,
})
.then(({ data }) => {
toast.clear();
if (data.success) {
// 认证成功
}
})
.catch(() => {
});
},
想要更多实战干货欢迎到微信搜索公众号【前端干货站】关注、查阅~