最近做了一个小程序人脸识别的功能,主要就是前端做人脸检测, 后端调用人脸识别接口, 具体代码如下
// face-camera/index.wxml
{{tipsText}}
face-camera/index.wxss
/* pages/jobs/moving-clock/sign/comp/face-camera/index.wxss */
.camera{
/* width: 480rpx;
height: 480rpx; */
width: 29.56vh;
height: 29.56vh;
border-radius: 50%;
overflow: hidden;
position: relative;
z-index: 2;
}
.tips{
position: absolute;
bottom: 0;
height: 96rpx;
width: 100%;
background-color: rgba(0, 0, 0, 0.6);
}
.tips .text{
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #fff;
text-align: center;
height: 100%;
width: 100%;
font-weight: 400;
}
face-camera/index.js
// pages/jobs/moving-clock/sign/comp/face-camera/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
outTime: {
type: Number,
value: 15000
},
takePhotoIntervalTime: {
type: Number,
value: 1000
},
starting: {
type: Boolean,
value: false
}
},
/**
* 组件的初始数据
*/
data: {
ctx: null,
tipsText: '',
listener: null,
startTime: 0
},
setTimeId: null,
created() {
const ctx = wx.createCameraContext();
console.log('createCameraContext');
wx.initFaceDetect({
complete: (err) => {
console.log('initFaceDetect', err);
}
});
this.setData({
ctx
})
this.handleOnCameraFrame((frame) => {
if (this.data.starting) {
console.log('frame', frame);
this.handleWatchFaceData(frame);
}
});
},
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
wx.stopFaceDetect();
let listener = this.data.listener;
listener.stop();
},
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
wx.stopFaceDetect();
let listener = this.data.listener;
listener.stop();
},
/**
* 组件的方法列表
*/
methods: {
handleOnCameraFrame(callback) {
const that = this;
const context = that.data.ctx;
let listener = that.data.listener;
let startTime = new Date().getTime();
listener = context.onCameraFrame((frame) => {
// console.log('frame', frame);
const curTime = new Date().getTime()
if (curTime - startTime >= 1000) {
startTime = new Date().getTime();
callback && callback(frame);
}
})
listener.start({
success: (res) => {
console.log('listener.start', res);
},
fail: (err) => {
console.log('listener.fail', err);
}
})
this.setData({
listener
})
},
handleWatchFaceData(frame) {
const that = this;
let tipsText = '';
wx.faceDetect({
frameBuffer: frame.data,
width: frame.width,
height: frame.height,
enablePoint: true,
enableConf: true,
enableAngle: true,
success: (faceData) => {
console.log('faceDetect', faceData);
let face = faceData
if(faceData.x == -1 || faceData.y == -1) {
tipsText = '检测不到人脸'
}
if(faceData.faceInfo && faceData.faceInfo.length > 1) {
tipsText = '请保证只有一人做认证'
} else {
if(face.angleArray && (face.angleArray.pitch >= 0.3 || face.angleArray.roll >= 0.3 || face.angleArray.yaw >= 0.3)) {
tipsText = '请平视摄像头'
} else if(face.confArray && (face.confArray.global <= 0.8 || face.confArray.leftEye <= 0.8 || face.confArray.mouth <= 0.8 || face.confArray.nose <= 0.8 || face.confArray.rightEye <= 0.8)) {
tipsText = '请勿遮挡五官'
} else {
tipsText = '请不要移动!'
// 这里可以写自己的逻辑了
if (that.data.startTime === 0) {
that.takePhoto((res) => {
console.log('takePhoto', res);
that.setData({
startTime: new Date().getTime()
})
// 为了增强体验,不会造成闪拍,延迟三秒;
setTimeout(() => {
that.setData({
startTime: 0
})
that.triggerEvent('takePhoto', res);
}, 3000)
})
}
}
}
that.setData({
tipsText
})
},
fail:function (err) {
console.log(err)
if(err.x == -1 || err.y == -1) {
tipsText = '检测中,请面向摄像头'
} else {
tipsText = '网络错误,请退出页面重试'
}
// 这里可以写自己的逻辑了
if (that.data.startTime === 0) {
that.setData({
startTime: new Date().getTime()
})
// 为了增强体验,不会造成闪拍,延迟5秒;
setTimeout(() => {
that.takePhoto((res) => {
console.log('takePhoto', res);
that.setData({
startTime: 0
})
that.triggerEvent('takePhoto', res);
})
}, 4000)
}
that.setData({
tipsText
})
}
})
},
handleStartDetect() {
},
handleVideo(callback) {
// let that = this;
const CameraContext = this.data.ctx;
CameraContext.startRecord({
timeout: 15,
selfieMirror: true,
success: (res) => {
console.log('startRecord', res);
callback && callback(res);
},
timeoutCallback: (e) => {
const tempVideoPath = e.tempVideoPath;
console.log('timeoutCallback', e);
},
complete: (e) => {
console.log('complete', e);
}
})
},
// 拍照
takePhoto(callback) {
let that = this;
const ctx = this.data.ctx;
ctx.takePhoto({
quality: 'high',
success: (res) => {
callback && callback(res);
},
fail: function (res) {
wx.showToast({
title: '拍照错误',
icon: 'none',
duration: 2000
});
}
})
},
error(e) {
wx.$fn.showModal({
title: '提示',
context: '请允许小程序使用摄像头',
confirmText: '知道了',
mask: true,
confirm: function() {
wx.switchTab({
url: '/pages/jobs/jobs',
})
},
}, this);
},
getAuthInfo(callback) {
let systemScopeMsgList = [];
let obj = {
cameraAuthorized: true,
camera: true
}
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope
wx.getSetting({
success(settingRes) {
console.log('settingRes', settingRes);
if (settingRes.authSetting['scope.camera'] !== true) {
systemScopeMsgList.push('摄像头未授权')
obj.camera = false
}
wx.getSystemInfo({
success (res) {
console.log('getSystemInfo', res);
const cameraAuthorized = res.cameraAuthorized; // 允许微信使用摄像头的开关
if (!cameraAuthorized) {
obj.cameraAuthorized = false
systemScopeMsgList.push('微信使用摄像头的开关未开启');
}
// if (!res.model) {
// obj.model = false
// systemScopeMsgList.push('该机器为新机型,检测信息可能有误,如使用出现问题,请手动摄像头授权是否开启');
// }
obj.brand = res.brand;
callback && callback(systemScopeMsgList, obj)
}
})
}
})
}
},
})
index.json
{
"component": true,
"usingComponents": {}
}
下面是使用方式
data: {
isFaceStart: true,
isLoading: false,
serverUrl : 'http://xxx/xx/xx',
tabActiveId: 1
},
curTimeTimeId: null,
faceTimeId: null,
handleTakePhoto(e) {
const that = this;
const res = e.detail || {};
const { tempImagePath } = res;
if (this.data.isLoading) {
return false;
}
this.setData({
isFaceStart: false,
cameraFaceTime: 0,
isLoading: true
});
clearInterval(this.faceTimeId);
wx.showLoading()
this.uploadMedia(this.data.serverUrl + "/api/xcxoperator/face/validate", tempImagePath).then(res => {
console.log('sign-res', res);
let msg = res.msg || (res.code === '200' ? '验证成功' : '验证失败');
if (res.data && res.data.msg) {
msg = res.data.msg;
}
if (res && res.code === '200') {
wx.showModal({
title: '打卡成功',
context: msg,
confirmText: '知道了',
mask: true,
confirm: function() {
that.setData({
isLoading: false
})
wx.switchTab({
url: '/pages/home/index',
})
},
}, that);
} else {
wx.showModal({
title: '打卡失败',
context: msg,
confirmText: '知道了',
mask: true,
confirm: function() {
// 防重复提交
that.setData({
isLoading: false
});
},
}, that);
}
})
},
uploadMedia: function (serverUrl, url) {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: serverUrl,
name: 'file',
header: {
Cookie: wx.getStorageSync('Cookie')
},
formData: {
'faceType': +this.data.tabActiveId
},
filePath: url,
success: function (res) {
console.log(res);
const data = JSON.parse(res.data);
wx.hideLoading()
resolve(data);
},
fail: function () {
wx.hideLoading()
resolve({})
}
})
})
},