VUE-----人脸识别功能实现
<template>
<div id="faceIdentification">
<div class="face-body">
<div v-show="showContainer" class="face-capture" id="face-capture">
<p class="tip">请保持人像在取景框内</p>
<div @click='getCountDown' class="count_down" v-if="iscountDown">{{ count_down }}</div>
<video id="video" :width="vwidth" :height="vheight" playsinline webkit-playsinline ></video>
<canvas id="refCanvas" :width="cwidth" :height="cheight"></canvas>
<p class="contentp">{{ scanTip }}</p>
</div>
<div v-if="!showContainer" class="img-face">
<img class="imgurl" :src="imgUrl" />
</div>
</div>
</div>
</template>
<script>
import storage from "store";
import { USERINFO } from "@/utils/mutation-types";
import { Toast } from "vant";
import tracking from "@/pages/checkout/assets/data/tracking-min";
import "@/pages/checkout/assets/data/face-min.js";
import "@/pages/checkout/assets/data/eye-min.js";
import "@/pages/checkout/assets/data/mouth-min.js";
export default {
data() {
return {
screenSize: {
width: window.screen.width,
height: window.screen.height,
},
URL: null,
streamIns: null, // 视频流
showContainer: true, // 显示
tracker: null,
tipFlag: false, // 提示用户已经检测到
flag: false, // 判断是否已经拍照
context: null, // canvas上下文
profile: [], // 轮廓
removePhotoID: null, // 停止转换图片
scanTip: "人脸识别中...", // 提示文字
imgUrl: "",
canvas: null,
trackertask: null,
vwidth: "266",
vheight: "266",
cwidth: "266",
cheight: "266",
userInfo: {},
orderData: {},
count_down: 5,
iscountDown: false,
imgbase64:''
};
},
mounted() {
const scale = this.screenSize.width / 375;
this.vwidth = 266 * scale;
this.vheight = 266 * scale;
this.cwidth = 266 * scale;
this.cheight = 266 * scale;
this.playVideo();
},
created() {
},
methods: {
playVideo() {
this.getUserMedia({
//摄像头拍摄的区域
video: {
width: 500,
height: 500,
facingMode: "user",
} /* 前置优先 */,
},
this.success,
this.error
);
},
// 访问用户媒体设备
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 {
this.scanTip = "你的浏览器不支持访问用户媒体设备";
}
},
success(stream) {
this.streamIns = stream;
const video = document.getElementById("video");
// webkit内核浏览器
this.URL = window.URL || window.webkitURL;
if ("srcObject" in video) {
video.srcObject = stream;
} else {
video.src = this.URL.createObjectURL(stream);
}
// 苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
setTimeout(() => {
video.play();
this.initTracker();// 人脸捕捉
}, 100);
},
error(e) {
this.scanTip = "访问用户媒体失败";
},
initTracker() {
this.context = document.getElementById("refCanvas").getContext("2d"); // 画布
this.canvas = document.getElementById("refCanvas");
this.tracker = new window.tracking.ObjectTracker("face"); // tracker实例
this.tracker.setInitialScale(4);
this.tracker.setStepSize(2); // 设置步长
this.tracker.setEdgesDensity(0.1);
try {
this.trackertask = window.tracking.track("#video", this.tracker); // 开始追踪
} catch (e) {
this.scanTip = "访问用户媒体失败,请重试";
}
this.tracker.on("track", (e) => {
//画布描绘之前清空画布
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (e.data.length === 0) {
this.scanTip = "未检测到人脸";
} else {
e.data.forEach((rect) => {
//设置canvas 方框的颜色大小
this.context.strokeStyle = "#42e365";
this.context.lineWidth = 2;
this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
});
if (!this.tipFlag) {
this.scanTip = "检测成功,正在拍照,请保持不动5秒";
}
// 1.5秒后拍照,仅拍一次 给用户一个准备时间
// falg 限制一直捕捉人脸,只要拍照之后就停止检测
if (!this.flag) {
this.scanTip = "拍照中...";
this.flag = true;
this.getCountDown()
// this.removePhotoID = setTimeout(() => {
// this.tackPhoto();
// document.getElementById("video").pause();
// this.tipFlag = true;
// }, 1500);
console.log( this.removePhotoID)
}
}
});
},
//到计数
getCountDown(){
this.iscountDown= true;
if (this.count_down > 1) {
setTimeout(() => {
this.count_down--;
this.getCountDown()
}, 1000);
}else{
this.iscountDown= false;
this.removePhotoID = setTimeout(() => {
this.tackPhoto();
document.getElementById("video").pause();
this.tipFlag = true;
}, 1500);
}
},
tackPhoto() {
// 在画布上面绘制拍到的照片
this.context.drawImage(
document.getElementById("video"),
0,
0,
this.vwidth,
this.vwidth
);
this.imgUrl = this.saveAsPNG(document.getElementById("refCanvas"));
// this.compare(imgUrl)
//判断图片大小
this.imgSize();
//this.faceToTengXun(); // 人脸比对
this.close();
},
imgSize() {
if (this.imgUrl) {
// 获取base64图片byte大小
const equalIndex = this.imgUrl.indexOf("="); // 获取=号下标
let size;
if (equalIndex > 0) {
const str = this.imgUrl.substring(0, equalIndex); // 去除=号
const strLength = str.length;
const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
size = Math.floor(fileLength / 1024); // 向下取整
console.log("size", size + "KB");
} else {
const strLength = this.imgUrl.length;
const fileLength = strLength - (strLength / 8) * 2;
size = Math.floor(fileLength / 1024); // 向下取整
console.log("size", size + "KB");
}
if (size > 1024) {
// 图片超过1M 按比例压缩
this.imgUrl = document.getElementById("refCanvas").toDataURL("image/png", 1024 / size);
}
console.log(this.imgUrl)
this.imgbase64 = this.imgUrl.substring(22)
console.log(this.imgbase64)
this.$emit("getbase64",this.imgbase64)
//this.$emit('getDetail', this.imgbase64)
}
},
//保存照片
getBlobBydataURI(dataURI, type) {
var binary = window.atob(dataURI.split(",")[1]);
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: type,
});
},
// 保存为png,base64格式图片
saveAsPNG(c) {
return c.toDataURL("image/png", 0.4);
},
close() {
this.flag = false;
this.tipFlag = false;
this.showContainer = false;
this.context = null;
this.scanTip = "人脸识别中...";
clearTimeout(this.removePhotoID);
if (this.streamIns) {
this.streamIns.enabled = false;
this.streamIns.getTracks()[0].stop();
this.streamIns.getVideoTracks()[0].stop();
}
this.streamIns = null;
this.trackertask.stop();
this.tracker = null;
}
},
};
</script>
<style scoped lang="less">
#faceIdentification{
position: absolute;
height: 100%;
width: 100%;
background: #f0f0f0;
top: 0px;
.face-body{
position: relative;
height: 100%;
width: 100%;
.count_down{
position: absolute;
font-size: 35px;
top: 220px;
color: #fff;
display: inline-block;
z-index: 99999;
opacity: 0.5;
}
}
}
#video{
z-index: 9999;
}
.face-capture {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.tip {
position: fixed;
top: 48px;
z-index: 5;
font-size: 18px;
font-weight: 500;
color: #333333;
line-height: 25px;
}
.face-capture{
video{
position: fixed;
top: 117.5px;
object-fit: cover;
z-index: 2;
background-repeat: no-repeat;
background-size: 100% 100%;
}
}
.face-capture{
canvas {
position: fixed;
top: 117.5px;
object-fit: cover;
z-index: 2;
background-repeat: no-repeat;
background-size: 100% 100%;
}
}
.face-capture {
.img-cover {
position: fixed;
top: 63px;
width: 375px;
height: 375px;
object-fit: cover;
z-index: 3;
background-repeat: no-repeat;
background-size: 100% 100%;
}
}
.face-capture {
.contentp {
position: fixed;
top: 438px;
font-size: 18px;
font-weight: 500;
color: #333333;
}
}
.face-capture {
.rect {
border: 2px solid #0aeb08;
position: fixed;
z-index: 4;
}
}
.img-face {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.img-face{
.imgurl {
position: fixed;
top: 117.5px;
width: 266px;
height: 266px;
border-radius: 133px;
}
}
</style>
转载与简书:原文连接https://www.jianshu.com/p/e53592bbedea
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)