你好,我是Vam的金豆之路,可以叫我豆哥。2019年年度博客之星、技术领域博客专家。主要领域:前端开发。我的微信是 maomin9761,有什么疑问可以加我哦,自己创建了一个微信技术交流群,可以加我邀请你一起交流学习。最后自己也创建了一个微信公众号:前端历劫之路,欢迎关注。
欢迎阅读本博文,本博文主要讲述【Vue回炉重造之封装一个实用的人脸识别组件】,文字通俗易懂,如有不妥,还请多多指正。
人脸识别技术现在越来越火,那么我们今天教大家实现一个人脸识别组件。
由于我们的电脑有的有摄像头,有的没有摄像头,所以我们需要根据不同的场景来封装这个组件。先放个图吧,大家可以看得更加直观一些。
有摄像头的话,我们就显示(需要人像识别组件):
没有摄像头的话,我们就显示(这个直接上传人像即可):
判断有无摄像头,我们可以使用这个方法:
// 判断有无摄像头,推荐放在created里
var deviceList = [];
navigator.mediaDevices
.enumerateDevices()
.then(devices => {
devices.forEach(device => {
deviceList.push(device.kind);
});
if (deviceList.indexOf("videoinput") == "-1") {
console.info("没有摄像头");
return false;
} else {
console.info("有摄像头");
this.videoinput = true; // 这是我自定义的一个状态,初始值为false
}
})
.catch(function(err) {
alert(err.name + ": " + err.message);
});
index.vue
<template>
<el-dialog
:visible.sync="openFaceView"
width="581px"
:show-close="false"
v-loading="faceloading"
element-loading-text="人脸识别中"
>
<div class="ovf" style="padding:20px;">
<el-upload
v-if="!videoinput"
class="upload-demo"
action
multiple
:limit="1"
:file-list="fileList"
:on-change="handleChange"
:on-exceed="handleExceed"
:before-remove="beforeRemove"
:auto-upload="false"
>
<el-button size="small" type="primary">点击上传人像图片el-button>
el-upload>
<div v-if="videoinput">
<el-button size="small" type="primary" @click="checkFace">点击进行人脸识别el-button>
<div slot="tip" class="el-upload__tip">此功能需到非IE浏览器进行div>
div>
<div class="dialog-footer">
<el-button @click="openFaceView = false">取 消el-button>
<el-button type="primary" @click="postFace()">确 定el-button>
div>
div>
el-dialog>
<el-dialog :visible.sync="checkFaceView" width="581px" :show-close="false">
<Face :faceView="checkFaceView" @canvasToImage="getImgFile">Face>
el-dialog>
template>
<script>
import { verifyFace } from "../../request/api"; //引入人脸识别接口
import Face from "./Face"; // 引入人脸识别组件
export default {
name: "MyClassRoom",
data() {
return {
openFaceView:true,
faceloading: false,
videoinput: false,
fileList: [],
face: "",
}
},
components: {
Face
},
methods: {
// 弹出人脸识别框
checkFace() {
this.checkFaceView = true;
},
// 限制上传照片
handleExceed() {
this.$message.warning({
message: "不要重复上传!",
offset: 380,
duration: 1000
});
},
// 移除人像图片
beforeRemove(file) {
return this.$confirm(`确定移除 ${file.name}?`);
},
// 上传的文件
handleChange(file) {
this.face = file.raw;
},
// 获取截取图片
getImgFile(d) {
this.face = d;
this.checkFaceView = false;
},
// 人脸识别完毕
postFace() {
this.faceloading = true;
this.checkFaceView=false;
let formData = new FormData();
formData.append("face", this.face);
/*人脸识别接口,把获取到的照片传到后台,我这里使用了封装axios。需要注意使用 config.headers = {'Content-Type':'multipart/form-data'} 传照片
*/
verifyFace(formData, { isUpload: true })
.then(res => {
console.log(res);
if (res.code == 0) {
this.faceloading = false;
this.$message.success({
message: "人脸识别成功!",
offset: 380,
duration: 1000
});
} else {
this.$message.error({
message: "人脸识别失败!",
offset: 380,
duration: 1000
});
this.faceloading = false;
}
})
.catch(err => {
console.log(err);
});
}
},
created() {
// 判断有无摄像头
var deviceList = [];
navigator.mediaDevices
.enumerateDevices()
.then(devices => {
devices.forEach(device => {
deviceList.push(device.kind);
});
if (deviceList.indexOf("videoinput") == "-1") {
console.info("没有摄像头");
return false;
} else {
console.info("有摄像头");
this.videoinput = true;
}
})
.catch(function(err) {
alert(err.name + ": " + err.message);
});
},
}
script>
Face.vue
<template>
<div class="face">
<div class="container">
<video id="video" preload autoplay loop muted>video>
<canvas id="canvas" width="581" height="436">canvas>
<canvas id="canvas1" width="581" height="436">canvas>
div>
<div class="btns">
<el-button type="primary" @click="start">打开摄像头el-button>
<el-button type="primary" @click="screenshot">手动截图el-button>
<el-button type="primary" @click="keepImg">保存图片el-button>
<p class="tips">1、首先打开摄像头;2、将人像放在框中自动截取,也可点击手动截取。截取的图片将会出现在下方未保存图片栏;3、最后点击保存,下方可预览保存后的图片。p>
div>
<div class="imgs" v-show="imgView">
<p>未保存图片p>
<canvas id="shortCut" width="140" height="140">canvas>
<p>已保存图片p>
<div id="img">div>
div>
div>
template>
<script>
import "../../assets/js/tracking-min.js"; // 需要引入(下载链接在文末)
import "../../assets/js/face-min.js"; // // 需要引入(下载链接在文末)
export default {
name: "testTracking",
props: ["faceView"],
data() {
return {
saveArray: {},
imgView: false
};
},
methods: {
// 打开摄像头
start() {
var saveArray = {};
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
// eslint-disable-next-line no-undef
var tracker = new window.tracking.ObjectTracker("face");
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
// eslint-disable-next-line no-undef
this.trackerTask = window.tracking.track("#video", tracker, {
camera: true
});
tracker.on("track", function(event) {
context.clearRect(0, 0, canvas.width, canvas.height);
event.data.forEach(function(rect) {
context.strokeStyle = "#fff";
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
context.fillStyle = "#fff";
saveArray.x = rect.x;
saveArray.y = rect.y;
saveArray.width = rect.width;
saveArray.height = rect.height;
});
});
var canvas1 = document.getElementById("canvas1");
var context1 = canvas1.getContext("2d");
context1.strokeStyle = "#69fff1";
context1.moveTo(190, 118);
context1.lineTo(390, 118);
context1.lineTo(390, 318);
context1.lineTo(190, 318);
context1.lineTo(190, 118);
context1.stroke();
setInterval(() => {
if (
saveArray.x > 200 &&
saveArray.x + saveArray.width < 400 &&
saveArray.y > 120 &&
saveArray.y + saveArray.height < 320 &&
saveArray.width < 180 &&
saveArray.height < 180
) {
console.log(saveArray);
this.getPhoto();
for (var key in saveArray) {
delete saveArray[key];
}
}
}, 2000);
},
// 获取人像照片
getPhoto() {
var video = document.getElementById("video");
var can = document.getElementById("shortCut");
var context2 = can.getContext("2d");
context2.drawImage(video, 210, 130, 210, 210, 0, 0, 140, 140);
this.imgView = true;
},
// 截屏
screenshot() {
this.getPhoto();
},
// 将canvas转化为图片
convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
},
//将base64转换为文件,dataurl为base64字符串,filename为文件名(必须带后缀名,如.jpg,.png)
dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
},
// 保存图片
keepImg() {
var can = document.getElementById("shortCut");
var img = document.getElementById("img");
var photoImg = document.createElement("img");
photoImg.src = this.convertCanvasToImage(can).src;
img.appendChild(photoImg);
//获取到转化为base64的图片地址
this.$emit(
"canvasToImage",
this.dataURLtoFile(this.convertCanvasToImage(can).src, "person.jpg")
);
console.log(
this.dataURLtoFile(this.convertCanvasToImage(can).src, "person.jpg")
);
},
clearCanvas() {
var c = document.getElementById("canvas");
var c1 = document.getElementById("canvas1");
var cxt = c.getContext("2d");
var cxt1 = c1.getContext("2d");
cxt.clearRect(0, 0, 581, 436);
cxt1.clearRect(0, 0, 581, 436);
},
closeFace() {
console.log("关闭人脸识别窗口");
this.imgView = false;
this.clearCanvas();
// 停止侦测
this.trackerTask.stop();
console.log(this.trackerTask);
// 关闭摄像头
var video = document.getElementById("video");
video.srcObject.getTracks()[0].stop();
}
},
watch: {
faceView(v) {
if (v == false) {
this.closeFace();
}
},
imgView(v) {
if (v == true) {
this.$message.success({
message: "截取成功!点击保存图片",
offset: 380,
duration: 1000
});
}
}
},
destroyed() {}
};
script>
<style scoped lang="scss">
.face {
.container {
background: #000;
position: relative;
width: 581px;
height: 436px;
#canvas1 {
position: absolute;
}
video,
#canvas,
#canvas1 {
position: absolute;
width: 581px;
height: 436px;
}
}
.btns {
padding: 10px;
.tips {
font-size: 14px;
color: #666;
margin: 10px 0;
line-height: 24px;
}
}
.imgs {
padding: 10px;
p {
font-size: 16px;
}
}
}
style>
这样,一个简单又实用的人像识别就这样完成了。
face-min.js
链接: https://pan.baidu.com/s/1gB0Yd178a_8a_Bp3zKunHg
提取码: 9q7q
tracking-min.js
链接: https://pan.baidu.com/s/1LP7pZIbAgfYdAqp-NchQLw
提取码: qx75
谢谢阅读,如果觉得有感触,麻烦帮忙点个赞,关个注吧!