Vue回爐重造之封裝一個實用的人臉識別組件
前言
人臉識別技術(shù)現(xiàn)在越來越火,那么我們今天教大家實現(xiàn)一個人臉識別組件。
資源
element UI
Vue.js
tracking-min.js
face-min.js
源碼
由于我們的電腦有的有攝像頭,有的沒有攝像頭,所以我們需要根據(jù)不同的場景來封裝這個組件。先放個圖吧,大家可以看得更加直觀一些。
有攝像頭的話,我們就顯示(需要人像識別組件):
沒有攝像頭的話,我們就顯示(這個直接上傳人像即可):
// 判斷有無攝像頭,推薦放在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; // 這是我自定義的一個狀態(tài),初始值為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: "不要重復(fù)上傳!",
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、將人像放在框中自動截取,也可點擊手動截取。截取的圖片將會出現(xiàn)在下方未保存圖片欄;3、最后點擊保存,下方可預(yù)覽保存后的圖片。</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轉(zhuǎn)化為圖片
convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
},
//將base64轉(zhuǎn)換為文件,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);
//獲取到轉(zhuǎn)化為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("關(guān)閉人臉識別窗口");
this.imgView = false;
this.clearCanvas();
// 停止偵測
this.trackerTask.stop();
console.log(this.trackerTask);
// 關(guān)閉攝像頭
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>
結(jié)語
這樣,一個簡單又實用的人像識別就這樣完成了。
作者:Vam的金豆之路
主要領(lǐng)域:前端開發(fā)
我的微信:maomin9761
微信公眾號:前端歷劫之路