前端捕获人脸识别实践
前言
公司有个项目涉及人脸识别,需要前端能力支持,需要用户进入摄像头后识别人脸进行截图,然后上传后端进行数据交换。不过目前主流摄像头支持调用,开源社区有个 trackingjs
类库问题解决计算机视觉解决方案。
实践
trackingjs
很多工作量帮你做好,只需要引入他的库就可以用了:
<div class="app-container">
<video ref="videoRef" class="video-wrapper" preload autoplay loop muted />
<canvas ref="captureFaceRef" width="600" height="560" class="capture-face-wrapper" />
</div>
<div class="result-wrapper">
<h3>捕获到人脸数据:</h3>
<canvas ref="shortCutRef" class="short-cut-wrapper" />
</div>
想实现功能至少需要 videoRef
和 shortCutRef
节点,videoRef
主要是调用摄像头获取流媒体进行播放,而 shortCutRef
节点就是人脸截图,你可以拿到里面的图片进行提交,至于图片转 base64
数据流根据需求定吧。上面多一个 captureFaceRef
主要是捕获人脸的状态圈,告知用户捕获状态,而这个状态 trackingjs
事件回调里包含其中。
本项目是 vue3
项目,所以先初始化相关的业务:
const videoRef = ref(null)
const captureFaceRef = ref(null)
const shortCutRef = ref(null)
// 捕捉人脸坐标对象
const saveArray = {}
let captureFaceCanvas = null
let captureFaceContext = null
let shortCutRefCanvas = null
let shortCutRefContext = null
// tracking 实例化对象
let tracker = null
然后在 mounted
生命周期拿到 dom
节点信息和初始化 trackingjs
:
onMounted(() => {
captureFaceCanvas = captureFaceRef.value
shortCutRefCanvas = shortCutRef.value
captureFaceContext = captureFaceCanvas.getContext('2d')
shortCutRefContext = shortCutRefCanvas.getContext('2d')
initTracker()
captureFacePhoto(shortCutRefCanvas)
})
initTracker
方法要注意下声明 face
的模块,因为 trackingjs
机器学习模型很多,我这边只用到人脸,所以声明该模型:
const initTracker = () => {
// 实例化 face 模块
tracker = new window.tracking.ObjectTracker('face')
// 初始化抓取人脸边框配置
tracker.setInitialScale(4)
tracker.setStepSize(2)
tracker.setEdgesDensity(0.1)
// 摄像头开启
window.tracking.track(videoRef.value, tracker, {
camera: true
})
// 追踪事件回调
tracker.on('track', event => {
captureFaceContext.clearRect(0, 0, captureFaceCanvas.width, captureFaceCanvas.height)
event.data.forEach(function (rect) {
captureFaceContext.strokeStyle = 'red'
captureFaceContext.strokeRect(rect.x, rect.y, rect.width, rect.height)
captureFaceContext.fillStyle = 'red'
saveArray.x = rect.x
saveArray.y = rect.y
saveArray.width = rect.width
saveArray.height = rect.height
})
})
}
上面就是根据配置进行个性化配置,具体可以查阅官方文档。
然后制作截图功能:
const captureFacePhoto = canvas => {
// 将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照
shortCutRefContext.drawImage(videoRef.value, 140, 70, 340, 340, 0, 0, 140, 140)
const getImage = canvas.toDataURL('image/png')
console.log(getImage)
}
优化
至此为止基本功能已经实现了,不过在人脸识别过程中,却发现人脸识别经常失误,比如识别到侧边脸以及截图文件不完整。
const bestCaptureTimer = () => {
if (saveArray.x > 130 &&
saveArray.x + saveArray.width < 460 &&
saveArray.y > 58 &&
saveArray.y + saveArray.height < 388 &&
saveArray.width < 330 &&
saveArray.height < 330) {
captureFacePhoto(shortCutRefCanvas)
for (const key in saveArray) {
delete saveArray[key]
}
}
}
这个定时器让用户在画布中央进行截图,提高人脸识别率。
对于需要检验摄像头设备完全可以用浏览器的 api 实现:
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = constraints => {
// 首先,如果有getUserMedia的话,就获得它
const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia
// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则,为老的navigator.getUserMedia方法包裹一个Promise
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
navigator.mediaDevices.getUserMedia({ video: true }).then(() => {
// 人脸识别业务初始化
const tracker = new window.tracking.ObjectTracker('face')
// 一些业务...
}).catch(err => {
console.warn(err.name + ": " + err.message)
ElMessage.error({
message: '没有检验到摄像头,请插入摄像头设备!',
showClose: true,
duration: 0
})
})