需求:前端獲取到攝像頭信息,通過模型來進行判斷人像是否在鏡頭中,鏡頭是否有被遮擋。
實現(xiàn)步驟:
1、通過video標簽來展示攝像頭中的內(nèi)容
2、通過canvas來繪制視頻中信息進行展示
3、在拍照時候?qū)anvas的當前幀轉(zhuǎn)成圖片
第一步:下載引入必要包
下載依賴
face-api.js是核心依賴必須要下
npm install face-api.js
element-ui為了按鈕好看一點(可以不下) ,axios用于請求發(fā)送
npm istall element-ui axios -S
?element-ui根據(jù)官方文檔進行引入使用
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
下載model
下載地址:?模板地址?如果訪問出現(xiàn)異常請科學上網(wǎng)
將項目中的model放入VUE中的public文件加下
第二步:先把HTML寫上去?
<template>
<div>
<el-button type="primary" @click="useCamera">打開攝像頭</el-button>
<el-button type="plain" @click="photoShoot">拍照</el-button>
<el-alert
:title="httpsAlert"
type="info"
:closable="false"
v-show="httpsAlert !== ''">
</el-alert>
<div class="videoImage" ref="faceBox">
<video ref="video" style="display: none;"></video>
<canvas ref="canvas" width="400" height="400" v-show="videoShow"></canvas>
<img ref="image" :src="picture" alt="" v-show="pictureShow">
</div>
</div>
</template>
第三步?可以開始代碼了
首先引入下載好的face-api.js包
import * as faceApi from 'face-api.js'
?以下是需要用到的屬性
1、視頻和圖片不同時出現(xiàn)
videoShow: false,
pictureShow: false,
2、生成圖片后用于保存圖片路徑
picture: '',
3、因為在操作時會用到DOM所以將要用到虛擬DOM保存在data中
canvas: null,
video: null,
image: null,
?4、模型識別時直接傳入此屬性,在初始化時賦值(可省略,直接卸載邏輯代碼中)
options: ''
?5、在人臉識別時對結(jié)果進行反饋(識別出人像數(shù)量大于1或小于1時給出提示)
noOne: '',
moreThanOne: '',
6、如果用戶不是在https下進行使用攝像頭調(diào)用給出提示
httpsAlert: ''
?
屬性準備好之后就可以開始初始化了
1、初始化模型
2、獲取需要用到的虛擬DOM
async init() {
await faceApi.nets.ssdMobilenetv1.loadFromUri("/models");
await faceApi.loadFaceLandmarkModel("/models");
this.options = new faceApi.SsdMobilenetv1Options({
minConfidence: 0.5, // 0.1 ~ 0.9
});
// 視頻中識別使用的節(jié)點
this.video = this.$refs.video
this.canvas = this.$refs.canvas
this.image = this.$refs.image
}
?調(diào)用攝像頭
通過navigator.mediaDevices.getUserMedia()
useCamera(){
this.videoShow = true
this.pictureShow = false
this.cameraOptions()
},
cameraOptions(){
let constraints = {
video: {
width: 400,
height: 400
}
}
// 如果不是通過loacalhost或者通過https訪問會將報錯捕獲并提示
try{
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then((MediaStream) => {
// 返回參數(shù)
this.video.srcObject = MediaStream;
this.video.play();
this.recognizeFace()
}).catch((error) => {
console.log(error);
});
}catch(err){
this.httpsAlert = `您現(xiàn)在在使用非Https訪問,
請先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
添將當前鏈接${window.location.href}添加到列表,
并且將Insecure origins treated as secure修改為enabled,
修改完成后請重啟瀏覽器后再次訪問!`
}
}
識別視頻中的人像
這里通過遞歸的方式將視頻中的內(nèi)容用canvas顯示
將canvas的節(jié)點傳入到faceApi的方法中進行識別
通過faceApi返回的數(shù)組可以得到當前人臉的識別狀況(數(shù)組長度0沒有識別到人臉,長度1識別到一個人臉...以此類推)
async recognizeFace(){
if (this.video.paused) return clearTimeout(this.timeout);
this.canvas.getContext('2d', { willReadFrequently: true }).drawImage(this.video, 0, 0, 400, 400);
const results = await faceApi.detectAllFaces(this.canvas, this.options).withFaceLandmarks();
if(results.length === 0){
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
if(this.noOne === ''){
this.noOne = this.$message({
message: '未識別到人臉',
type: 'warning',
duration: 0
});
}
}else if(results.length > 1){
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne === ''){
this.moreThanOne = this.$message({
message: '檢測到鏡頭中有多個人',
type: 'warning',
duration: 0
});
}
}else{
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
}
this.timeout = setTimeout(() => {
return this.recognizeFace()
});
},
拍照上傳?
async photoShoot(){
// 拿到圖片的base64
let canvas = this.canvas.toDataURL("image/png");
// 拍照以后將video隱藏
this.videoShow = false
this.pictureShow = true
// 停止攝像頭成像
this.video.srcObject.getTracks()[0].stop()
this.video.pause()
if(canvas) {
// 拍照將base64轉(zhuǎn)為file流文件
let blob = this.dataURLtoBlob(canvas);
let file = this.blobToFile(blob, "imgName");
// 將blob圖片轉(zhuǎn)化路徑圖片
let image = window.URL.createObjectURL(file)
this.picture = image
return
let formData = new FormData()
formData.append('file', this.picture)
axios({
method: 'post',
url: '/user/12345',
data: formData
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
} else {
console.log('canvas生成失敗')
}
},
需要用到的圖片格式轉(zhuǎn)換方法?
方法1:先將base64轉(zhuǎn)為文件文章來源:http://www.zghlxwxcb.cn/news/detail-428670.html
方法2:設(shè)置新的文件中的參數(shù)信息文章來源地址http://www.zghlxwxcb.cn/news/detail-428670.html
dataURLtoBlob(dataurl) {
let 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 Blob([u8arr], {
type: mime
});
},
blobToFile(theBlob, fileName) {
theBlob.lastModifiedDate = new Date().toLocaleDateString();
theBlob.name = fileName;
return theBlob;
},
完整代碼
import bingImage from '@/assets/bbt1.jpg';
import BingWallpaper from '@/assets/BingWallpaper.jpg';
import * as faceApi from 'face-api.js'
export default {
name: 'Recognize',
data(){
return{
videoShow: false,
pictureShow: false,
// 圖片地址
picture: '',
// 用于視頻識別的節(jié)點
canvas: null,
video: null,
image: null,
timeout: 0,
// 模型識別的條件
options: '',
// 提示控制
noOne: '',
moreThanOne: '',
// 不是通過Https訪問提示
httpsAlert: '',
}
},
mounted() {
// 初始化
this.init()
},
beforeDestroy() {
clearTimeout(this.timeout);
},
methods: {
async init() {
await faceApi.nets.ssdMobilenetv1.loadFromUri("/models");
await faceApi.loadFaceLandmarkModel("/models");
this.options = new faceApi.SsdMobilenetv1Options({
minConfidence: 0.5, // 0.1 ~ 0.9
});
// 視頻中識別使用的節(jié)點
this.video = this.$refs.video
this.canvas = this.$refs.canvas
this.image = this.$refs.image
},
/**
* 使用視頻來成像攝像頭
*/
useCamera(){
this.videoShow = true
this.pictureShow = false
this.cameraOptions()
},
/**
* 使用攝像頭
*/
cameraOptions(){
let constraints = {
video: {
width: 400,
height: 400
}
}
// 如果不是通過loacalhost或者通過https訪問會將報錯捕獲并提示
try{
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then((MediaStream) => {
// 返回參數(shù)
this.video.srcObject = MediaStream;
this.video.play();
this.recognizeFace()
}).catch((error) => {
console.log(error);
});
}catch(err){
this.httpsAlert = `您現(xiàn)在在使用非Https訪問,
請先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
添將當前鏈接${window.location.href}添加到列表,
并且將Insecure origins treated as secure修改為enabled,
修改完成后請重啟瀏覽器后再次訪問!`
}
},
/**
* 人臉識別方法
* 通過canvas節(jié)點識別
* 節(jié)點對象執(zhí)行遞歸識別繪制
*/
async recognizeFace(){
if (this.video.paused) return clearTimeout(this.timeout);
this.canvas.getContext('2d', { willReadFrequently: true }).drawImage(this.video, 0, 0, 400, 400);
const results = await faceApi.detectAllFaces(this.canvas, this.options).withFaceLandmarks();
if(results.length === 0){
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
if(this.noOne === ''){
this.noOne = this.$message({
message: '未識別到人臉',
type: 'warning',
duration: 0
});
}
}else if(results.length > 1){
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne === ''){
this.moreThanOne = this.$message({
message: '檢測到鏡頭中有多個人',
type: 'warning',
duration: 0
});
}
}else{
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
}
// 通過canvas顯示video信息
this.timeout = setTimeout(() => {
return this.recognizeFace()
});
},
/**
* 拍照上傳
*/
async photoShoot(){
// 拿到圖片的base64
let canvas = this.canvas.toDataURL("image/png");
// 拍照以后將video隱藏
this.videoShow = false
this.pictureShow = true
// 停止攝像頭成像
this.video.srcObject.getTracks()[0].stop()
this.video.pause()
if(canvas) {
// 拍照將base64轉(zhuǎn)為file流文件
let blob = this.dataURLtoBlob(canvas);
console.log(blob)
let file = this.blobToFile(blob, "imgName");
console.info(file);
// 將blob圖片轉(zhuǎn)化路徑圖片
let image = window.URL.createObjectURL(file)
this.picture = image
// 將拍照后的圖片發(fā)送給后端
let formData = new FormData()
formData.append('file', this.picture)
axios({
method: 'post',
url: '/user/12345',
data: formData
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
} else {
console.log('canvas生成失敗')
}
},
/**
* 將圖片轉(zhuǎn)為blob格式
* dataurl 拿到的base64的數(shù)據(jù)
*/
dataURLtoBlob(dataurl) {
let 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 Blob([u8arr], {
type: mime
});
},
/**
* 生成文件信息
* theBlob 文件
* fileName 文件名字
*/
blobToFile(theBlob, fileName) {
theBlob.lastModifiedDate = new Date().toLocaleDateString();
theBlob.name = fileName;
return theBlob;
},
}
}
到了這里,關(guān)于VUE+faceApi.js實現(xiàn)攝像頭拍攝人臉識別的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!