# 纯前端人脸实时识别 🚀
# 效果如下
# 代码分析
# 页面部分
- 1.第一部分主要是一个video来放视频流,然后加上一个自定义的遮罩层。
- 2.第二部分放canvas去截取视频中的一帧图像。
- 3.第三部分是img标签存放canvasToImage的结果。
- 4.最后一部分是一些调试信息,方便真机观看。
<div class="video-wrapper">
<!-- 视频遮罩层 -->
<div class="wrapper">
<div id="imgTip"><h2>正在检测人脸中...</h2></div>
<img class="img-wrapper" id="imgWrap" src="./face-wrapper.jpg"/>
<!--拍照按钮-->
<button id="capture" style="display: none;">拍照</button>
<button id="closeBtn" onclick="closeStream()">关闭摄像头</button>
<button id="openBtn" style="display: none;" onclick="openStream()">开启摄像头</button>
</div>
<!-- video用于显示媒体设备的视频流,自动播放 -->
<video id="video" autoplay ></video>
</div>
<!-- 描绘video截图 -->
<h1>Canvas Info</h1>
<div class="canvas-wrapper">
<canvas id="canvas" width="320" height="480"></canvas>
</div>
<h1>Img Info</h1>
<!-- canvas 转换成 img -->
<div id="imgView"></div>
<!-- 错误提示,用于移动端观察 -->
<h1>Error Info</h1>
<div id="errorTip"></div>
<!-- 设备信息 -->
<h1>Device Info</h1>
<div id="device" style="text-align: center;"></div>
# js部分
- 1.摄像部分
- 2.人脸识别部分
// 定义全局变量
const video = document.getElementById("video");
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const imgView = document.getElementById("imgView");
const errorTip = document.getElementById("errorTip");
const imgWrap = document.getElementById('imgWrap');
const device = document.getElementById('device');
const capture = document.getElementById('capture');
const imgTip = document.getElementById('imgTip');
const closeBtn = document.getElementById('closeBtn');
// 判断是否拍照
let flag = 0
let tipFlag = 0
// 视频流实例
let streamIns = null;
// 新建一个tracker实例
const tracker = new tracking.ObjectTracker(['face']); // 可选 face, eye, mouth, 需要引入相应的js
// 设置步长
tracker.setStepSize(1.7);
// 绑定监听方法
tracker.on('track', handleTracked);
// 开始追踪
tracking.track('#video', tracker);
const SECOND = 1000
// 10s后移除监听
setTimeout(removeTracker, 10 * SECOND);
// 处理追踪事件
function handleTracked(event) {
if (event.data.length === 0) {
// 没有检测到
console.log('没有检测到人脸');
} else {
// 提示用户已经检测到人脸
if(!tipFlag) {
imgTip.innerText = '检测人脸成功,正在拍照,请保持不动2秒!';
}
// 1秒后拍照,仅拍一次
if(!flag) {
flag = 1;
setTimeout(() => {
// 模拟btn点击事件
capture.click();
imgTip.innerText = '拍照完成!';
tipFlag = 1
}, 1000);
}
// 人脸数据点阵处理
event.data.forEach(window.plot)
}
}
// 移除监听事件
function removeTracker() {
errorTip.innerText = '移除追踪'
tracker.removeListener('track', handleTracked)
}
// 绘制追踪框
window.plot = function({x, y, width: w, height: h}) {
// 创建框对象
const rect = document.createElement('div');
document.querySelector('.video-wrapper').appendChild(rect);
rect.classList.add('rect');
rect.style.width = w + 'px';
rect.style.height = h + 'px';
rect.style.left = (video.offsetLeft + x) + 'px';
rect.style.top = (video.offsetTop + y) + 'px';
// 显示追踪点到页面上
device.innerText = "x:" + x + "y:" + y + "h:" +h + "w:" + w;
};
// 关闭摄像
function closeStream() {
errorTip.innerText = "我点击了关闭;视频流对象:" + streamIns
try {
streamIns.enabled = false;
streamIns.getTracks()[0].stop();
streamIns.getVideoTracks()[0].stop();
closeBtn.style.display = 'none';
openBtn.style.display = 'block';
} catch(e) {
throw new Error(e);
}
}
// 开启摄像
function openStream() {
window.location.reload()
}
// 访问用户媒体设备的兼容方法
function 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);
}
}
// 成功的回调函数
function success(stream){
streamIns = stream
//兼容webkit内核浏览器
const CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
try {
video.src = CompatibleURL.createObjectURL(stream);
} catch (e) {
throw new Error(e);
video.srcObject = stream;
}
// 播放视频
video.play();
}
// 异常的回调函数
function error(error){
errorTip.innerText = '访问用户媒体设备失败:' + error;
throw new Error("访问用户媒体设备失败:" + error.name + error.message);
}
// 保存成png格式的图片
function saveAsPNG(canvas) {
return canvas.toDataURL("image/png");
}
// canvas to Image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
// 注册拍照按钮的单击事件
document.getElementById("capture").addEventListener("click",function(){
//绘制画面
context.drawImage(video,0,0,320,480);
// 保持图片为base64格式
const imgUrl = saveAsPNG(canvas);
// 显示图片
imgView.appendChild(convertCanvasToImage(canvas));
});
// js运行入口
(function init() {
if (navigator.mediaDevices.getUserMedia
|| navigator.getUserMedia
|| navigator.webkitGetUserMedia
|| navigator.mozGetUserMedia
) {
//调用用户媒体设备,访问摄像头
getUserMedia({
video: {
width: 1920,
height: 1080,
facingMode: "user" // 前置优先
}
}, success, error);
} else {
errorTip.innerText = '你的浏览器不支持访问用户媒体设备';
alert("你的浏览器不支持访问用户媒体设备");
}
})()