前言
在进行人脸追踪的学习过程中,网上大部分的分享的技术点都是face-api。在大致学习了face-api有关文档之后,感觉它的作用主要是人脸识别(并没有体会到有进行人脸的追踪过程),所以后续挖掘到了有人分享了使用tracking去实现人脸的追踪。
安装依赖: npm i tracking
项目导入:
require('tracking/build/tracking-min.js')
require('tracking/build/data/face-min.js')
之前有使用过 import的导入方式,没有获取到对应的依赖,没有发现原因是什么
或者直接下载依赖包,进行导入
简单说一下以下代码实现的业务需求
1. 开启设备摄像头(某些原因导致设备不支持,也提供了图片上传的功能)
2. 进行人脸追踪
3. 获取人脸照片,并转换成base64传给后端进行人脸校验
以下代码使用react进行分享
---------------- DOM结构 ----------------
reader(
<div>
{ showContainer ?
<div>
<video id='video' ref={video} style={{width: 200, height: 200, borderRadius: '50%'}} />
<canvas id="refCanvas" style={{width: 250, height: 250, position: 'absolute', left: 0}}></canvas>
</div> :
<img style={{width: 200, height: 200, borderRadius: isRadius}} src={imgUrl} />}
{
upImg ?
<div style={{ width: '200px' }}>
<input type='file' accept='image/*' onChange={upImage} name='file' id='file'/>
</div> : ''
}
<BodyContainer>
</div>
)
const [imgUrl, setImgUrl] = useState(IconFace)
const [tipFlag, setTipFlag] = useState(false) // 提示用户已经检测到
const [removePhotoID, setRemovePhotoID] = useState(null)
const [showContainer, setShowContainer] = useState(true)
const [upImg, setUpImg] = useState(false)
const [isRadius, setIsRadius] = useState('50%') // 图片是否圆角
let trackertask = null
let streamIns = null // 视频流
let isTakePhoto = false // 判断是否已经拍照
useEffect(() => {
if (video) {
playVideo()
}
},[video])
const playVideo = () =>{
getUserMedia({
//摄像头拍摄的区域
video: {
width: 200,
height: 200,
facingMode: "user",/* 前置优先 */,
}
},
success, error
)
}
const getUserMedia = (constrains, success, error) => {
if (navigator.mediaDevices.getUserMedia) {
//最新标准API
navigator.mediaDevices.getUserMedia(constrains)
.then(success)
.catch(error)
} else if (navigator.webkitGetUserMedia) {
//webkit内核浏览器
navigator.webkitGetUserMedia(constrains).then(success).catch(error)
} else if (navigator.mozGetUserMedia) {
//Firefox浏览器
navagator.mozGetUserMedia(constrains).then(success).catch(error)
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constrains).then(success).catch(error)
} else {
Toast.show('你的浏览器不支持访问用户媒体设备')
}
}
const success = (stream) => {
streamIns = stream
const video = document.getElementById("video")
try {
video.srcObject = stream
} catch {
Toast.show('你的浏览器不支持访问用户媒体设备')
}
// 苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
setTimeout(() => {
video.play()
initTracker()// 人脸捕捉
}, 100)
}
const error = (err) => {
Toast.show('访问用户媒体失败,请进行人像图像上传')
setIsRadius('0')
setUpImg(true) // 调用图片上传
setShowContainer(false)
}
const initTracker = () => {
const context = document.getElementById("refCanvas").getContext("2d")
const canvas = document.getElementById("refCanvas")
const video = document.getElementById("video")
const tracker1 = new window.tracking.ObjectTracker("face") // 使用监听人脸的包
tracker1.setInitialScale(4)
tracker1.setStepSize(2)
tracker1.setEdgesDensity(0.1)
try {
trackertask = tracking.track("#video", tracker1) // 开始追踪
} catch (e) {
Toast.show('访问用户媒体失败,请重试')
}
//开始捕捉方法 一直不停的检测人脸直到检测到人脸
tracker1.on("track", (e) => {
//画布描绘之前清空画布
context.clearRect(0, 0, canvas.width, canvas.height)
if (e.data.length === 0) {
Toast.show('未检测到人脸,请将人脸置于框中间或调整一下姿势')
} else {
e.data.forEach((rect) => {
//设置canvas 方框的颜色大小
context.strokeStyle = '#42e365'
context.lineWidth = 2
context.strokeRect(rect.x, rect.y, rect.width, rect.height)
})
if (!tipFlag) {
Toast.show('检测成功,正在拍照,请保持不动3秒')
}
// 2秒后拍照,仅拍一次 给用户一个准备时间
// flag 限制一直捕捉人脸,只要拍照之后就停止检测
if (!isTakePhoto) {
isTakePhoto = true
setRemovePhotoID(setTimeout(() => {
tackPhoto()
video.pause()
setTipFlag(true)
}, 2000))
}
}
})
}
const tackPhoto = () => {
// 在画布上面绘制拍到的照片
const context = document.getElementById("refCanvas").getContext("2d")
context.drawImage(document.getElementById("video"), 10, 0, 160, 160)
setImgUrl(saveAsPNG(document.getElementById("refCanvas")))
close()
}
// 保存为jpg,base64格式图片
const saveAsPNG = (c) => {
return c.toDataURL("image/jgp", 0.4)
}
const close = () => {
const video = document.getElementById("video")
isTakePhoto = false
setTipFlag(false)
setShowContainer(false)
// setContext(null)
clearTimeout(removePhotoID)
setRemovePhotoID(null)
if (streamIns) {
streamIns.enabled = false
streamIns.getTracks()[0].stop()
streamIns.getVideoTracks()[0].stop()
}
streamIns = null
if (trackertask != null) {
trackertask.stop()
}
video.pause()
}
// 图片上传
const upImage = () => {
var file = document.getElementById('file');
const reads = new FileReader()
var f = file.files[0]
reads.readAsDataURL(f)
reads.onload = (e) => {
setImgUrl(e.target.result)
}
}