今天給大家分享的內(nèi)容是純 javascript 實(shí)現(xiàn)的人臉識(shí)別。希望體驗(yàn)一下今天 web 端技術(shù)的飛速發(fā)展筏养。
簡(jiǎn)單創(chuàng)建一個(gè)項(xiàng)目斧抱,引入兩個(gè)關(guān)鍵文件和文件夾
- models 是訓(xùn)練出來(lái)模型,這些模型是通過(guò)大量圖片訓(xùn)練出來(lái)的撼玄。
- face-api.min.js 引入依賴文件
創(chuàng)建一個(gè) video 標(biāo)簽來(lái)捕捉我這張老臉夺姑。
<video id="video" width="720" height="560" autoplay muted></video>
簡(jiǎn)單地給樣式
<style>
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<script defer src="./face-api.min.js"></script>
<script defer src="./app.js"></script>
這里注意一下引用順序墩邀,我們需要引入 face-api.min.js 這個(gè)依賴掌猛。
通過(guò)攝像頭捕捉視頻
function startVideo(){
navigator.getUserMedia(
{video:{}},
stream => video.srcObject = stream,
err => console.log(err)
)
}
startVideo();
這里我們使用 navigator 的 getUserMedia 獲取設(shè)備的多媒體設(shè)備,傳入 video 表示要獲取視頻流,然后將視頻流傳入到 video 標(biāo)簽來(lái)顯示荔茬。
導(dǎo)入訓(xùn)練好的模型
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
faceapi.nets.faceRecognitionNet.loadFromUri('/models'),
faceapi.nets.faceExpressionNet.loadFromUri('/models'),
]).then(startVideo());
通過(guò) Promise 的 all 將這些文件同時(shí)進(jìn)行異步加載废膘,
- tinyFaceDetector 這是輕量級(jí)可以快速識(shí)別人臉的模型
- faceLandmark68Net 用于對(duì)人臉不同部位識(shí)別的模型
- faceRecognitionNet 識(shí)別出人臉的位置,和覆蓋的范圍
- faceExpressionNet 用于識(shí)別人的情緒慕蔚,是高興呀還是高興呀 呵呵
所有模型都加載完畢后再啟動(dòng)我們的視頻
獲取識(shí)別數(shù)據(jù)
video.addEventListener('play',()=>{
setInterval(async()=>{
const detections = await faceapi.detectAllFaces(video,
new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
// .withFaceExpressions()
console.log(detections);
},100)
})
-
識(shí)別是個(gè)耗時(shí)的操作丐黄,所有用異步方法獲取識(shí)別數(shù)據(jù),調(diào)用 faceapi 的 detectAllFaces 方法孔飒,傳入要識(shí)別的資源灌闺,也就是視頻,然后我們要識(shí)別什么可以傳入一個(gè) options 告訴識(shí)別器我們要識(shí)別什么坏瞄。打印識(shí)別出來(lái)的結(jié)果數(shù)據(jù)桂对。
識(shí)別數(shù)據(jù)
繪制人臉識(shí)別框
這里我們創(chuàng)建一個(gè) canvas 用于將識(shí)別出來(lái)數(shù)據(jù)繪制到視頻上,canvas 可以用 js 動(dòng)態(tài)創(chuàng)建
canvas {
position: absolute;
top: 0;
left: 0;
}
這里需要給 canvas 一個(gè)絕對(duì)定位鸠匀,以便和我們視頻對(duì)其蕉斜。
const canvas = faceapi.createCanvasFromMedia(video);
document.body.appendChild(canvas);
const displaySize = { width: video.width, height: video.height};
- faceapi.createCanvasFromMedia(video) 創(chuàng)建 canvas
video.addEventListener('play',()=>{
const canvas = faceapi.createCanvasFromMedia(video);
document.body.appendChild(canvas);
const displaySize = { width: video.width, height: video.height};
faceapi.matchDimensions(canvas,displaySize);
setInterval(async()=>{
const detections = await faceapi.detectAllFaces(video,
new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
const resizedDetections = faceapi.resizeResults(detections,displaySize)
canvas.getContext('2d').clearRect(0,0,canvas.width,canvas.height);
faceapi.draw.drawDetections(canvas,resizedDetections);
// .withFaceExpressions()
// console.log(detections);
},100)
})
const displaySize = { width: video.width, height: video.height}
讓 canvas 與我們 vidoe 大小匹配
const resizedDetections = faceapi.resizeResults(detections,displaySize)
將我們測(cè)試的框結(jié)果與顯示大小相匹配。
canvas.getContext('2d').clearRect(0,0,canvas.width,canvas.height);
清除上一次繪制的結(jié)果缀棍,下面就是講結(jié)果繪制到視頻上
faceapi.draw.drawDetections(canvas,resizedDetections);