前言
人脸识别技术现在越来越火,那么我们今天教大家实现一个人脸识别组件。
资源
- element UI
- Vue.js
- tracking-min.js
- face-min.js
源码
由于我们的电脑有的有摄像头,有的没有摄像头,所以我们需要根据不同的场景来封装这个组件。先放个图吧,大家可以看得更加直观一些。
有摄像头的话,我们就显示(需要人像识别组件):没有摄像头的话,我们就显示(这个直接上传人像即可):判断有无摄像头,我们可以使用这个方法:
// 判断有无摄像头,推荐放在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>