背景
产品经理想要在H5项目上实现人脸识别功能,一开始用的是navigator.mediaDevices.getUserMedia方法,但效果实在一般,最终我找到了第三方库tracking.js。大致的流程为:获取摄像头权限->识别到人脸->截取图片并转码->调用接口发送后端。
一、下载并引入第三方库文件
1. 引入
放在合适的地方
在vue文件中引入
import '@/common/tracking/build/data/face-min.js';
二、使用步骤
1. 获取视频流,创建检测
init() {
// 初始化设置
this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
// 固定写法
let tracker = new window.tracking.ObjectTracker('face');
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
this.tra = window.tracking.track('#video', tracker, {
camera: true
});
tracker.on('track', function (event) {
if (!this.tipFlag) {
// 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height);
if (event.data.length === 0) {
if (!this.faceflag) {
// 未检测到人脸
}
} else {
event.data.forEach(function (rect) {
// 标记人脸的位置
context.strokeStyle = '#0764B7';
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
});
// 当检测到人脸时并且uploadLock锁为true时,自动触发
this.uploadLock && this.screenshotAndUpload();
}
}
});
}
2. 利用canvas画出人脸,并渲染在页面上
<template>
<div class="content">
<div class="video-box">
<video class="vd" id="video" width="480" height="360" preload autoplay loop muted playsinline></video>
<canvas id="canvas" width="480" height="360"></canvas>
</div>
<canvas id="screenshotCanvas" width="480" height="360"></canvas>
<van-overlay :show="show" @click="show = false" style="display: flex;justify-content: center;align-items: center;">
<van-loading size="24px" vertical>{{ jumpTitle }}</van-loading>
</van-overlay>
</div>
</template>
3. 当检测到人脸时触发方法
screenshotAndUpload() {
// 上锁避免重复发送请求
this.uploadLock = false;
this.show = true
// 绘制当前帧图片转换为base64格式
let canvas = this.screenshotCanvas;
let video = this.video;
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
let base64Img = canvas.toDataURL('image/jpeg');
// 请求接口
faceId({
picture: base64Img
}).then(res => {
// 请求接口成功以后打开锁
if (res.status === 'success') {
this.uploadLock = true
} else {
this.$toast.fail('认证失败')
setTimeout(() => {
this.uploadLock = true;
}, 1000);
}
})
}
4. 关闭摄像头及取消监听
// 关闭摄像头
facevideo.srcObject.getTracks().forEach(track => track.stop());
// 取消监听
tra.stop()
三、完整代码
<template>
<div class="content">
<div class="video-box">
<video class="vd" id="video" width="480" height="360" preload autoplay loop muted playsinline></video>
<canvas id="canvas" width="480" height="360"></canvas>
</div>
<canvas id="screenshotCanvas" width="480" height="360"></canvas>
<van-overlay :show="show" @click="show = false" style="display: flex;justify-content: center;align-items: center;">
<van-loading size="24px" vertical>{{ jumpTitle }}</van-loading>
</van-overlay>
</div>
</template>
<script>
import '@/common/tracking/build/data/face-min.js';
export default {
data() {
return {
video: null,
screenshotCanvas: null,
uploadLock: true, // 上传锁
tipFlag: false,
faceflag: false,
tra: null
}
},
mounted() {
this.init();
},
methods: {
init() {
// 初始化设置
this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
// 固定写法
let tracker = new window.tracking.ObjectTracker('face');
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
this.tra = window.tracking.track('#video', tracker, {
camera: true
});
tracker.on('track', function (event) {
if (!this.tipFlag) {
// 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height);
if (event.data.length === 0) {
if (!this.faceflag) {
// this.tipTitle = '未检测到人脸'
}
} else {
event.data.forEach(function (rect) {
// context.strokeStyle = '#0764B7';
// context.strokeRect(rect.x, rect.y, rect.width, rect.height);
});
// 上传图片
this.uploadLock && this.screenshotAndUpload();
}
}
});
},
// 上传图片
screenshotAndUpload() {
// 上锁避免重复发送请求
this.uploadLock = false;
this.show = true
// 绘制当前帧图片转换为base64格式
let canvas = this.screenshotCanvas;
let video = this.video;
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
let base64Img = canvas.toDataURL('image/jpeg');
// 请求接口
faceId({
picture: base64Img
}).then(res => {
// 请求接口成功以后打开锁
if (res.status === 'success') {
this.uploadLock = true
} else {
this.$toast.fail('认证失败')
setTimeout(() => {
this.uploadLock = true;
}, 1000);
}
})
}
}
}
</script>
<style lang="less" scoped>
/* 绘图canvas 不需显示隐藏即可 */
#screenshotCanvas {
display: none;
}
.video-box {
width: 9rem;
height: 9rem;
border-radius: 50%;
overflow: hidden;
}
.vd {
transform: rotateY(180deg);
width: 100%;
object-fit: fill
}
</style>