如何使用VUE+faceApi.js實現(xiàn)攝像頭拍攝人臉識別
需求:
前端獲取到攝像頭信息,通過模型來進(jìn)行判斷人像是否在鏡頭中,鏡頭是否有被遮擋。
實現(xiàn)步驟:
1、通過video標(biāo)簽來展示攝像頭中的內(nèi)容
2、通過canvas來繪制視頻中信息進(jìn)行展示
3、在拍照時候?qū)anvas的當(dāng)前幀轉(zhuǎn)成圖片
第一步:下載引入必要包
下載依賴
face-api.js是核心依賴必須要下
npm install face-api.js
element-ui為了按鈕好看一點(diǎn)(可以不下) ,axios用于請求發(fā)送
npm istall element-ui axios -S
element-ui根據(jù)官方文檔進(jìn)行引入使用
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
下載地址: 模板地址
將項目中的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é)果進(jìn)行反饋(識別出人像數(shù)量大于1或小于1時給出提示)
noOne: '', moreThanOne: '',
6、如果用戶不是在https下進(jìn)行使用攝像頭調(diào)用給出提示
httpsAlert: ''
屬性準(zhǔn)備好之后就可以開始初始化了
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é)點(diǎn) 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中修改配置, 添將當(dāng)前鏈接${window.location.href}添加到列表, 并且將Insecure origins treated as secure修改為enabled, 修改完成后請重啟瀏覽器后再次訪問!` } }
識別視頻中的人像
這里通過遞歸的方式將視頻中的內(nèi)容用canvas顯示
將canvas的節(jié)點(diǎn)傳入到faceApi的方法中進(jìn)行識別
通過faceApi返回的數(shù)組可以得到當(dāng)前人臉的識別狀況(數(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)為文件
方法2:設(shè)置新的文件中的參數(shù)信息
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é)點(diǎn) 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é)點(diǎn) 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中修改配置, 添將當(dāng)前鏈接${window.location.href}添加到列表, 并且將Insecure origins treated as secure修改為enabled, 修改完成后請重啟瀏覽器后再次訪問!` } }, /** * 人臉識別方法 * 通過canvas節(jié)點(diǎn)識別 * 節(jié)點(diǎn)對象執(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; }, } }
總結(jié)
到此這篇關(guān)于如何使用VUE+faceApi.js實現(xiàn)攝像頭拍攝人臉識別的文章就介紹到這了,更多相關(guān)VUE faceApi.js攝像頭拍攝人臉識別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue頁面切換項目實現(xiàn)轉(zhuǎn)場動畫的方法
這篇文章主要介紹了vue頁面切換項目實現(xiàn)轉(zhuǎn)場動畫的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11VueAwesomeSwiper在VUE中的使用以及遇到的一些問題
這篇文章主要介紹了VueAwesomeSwiper在VUE中的使用以及遇到的一些問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01unplugin-vue-components解決命名沖突問題
這篇文章主要介紹了unplugin-vue-components解決命名沖突問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03