如何将face-api.js图片人脸识别改造为webcam实时识别且不使用Node.js
纯前端实时人脸识别实现方案
1. 替换HTML代码
移除原有的文件上传input组件,替换为视频播放标签,完整HTML代码如下:
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script defer src="face-api.min.js"></script> <script defer src="script.js"></script> <title>Real-time Face Recognition</title> <style> body { margin: 0; padding: 0; width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; flex-direction: column } canvas { position: absolute; top: 0; left: 0; } </style> </head> <body> <video id="video" autoplay muted playsinline></video> </body> </html>
2. 替换script.js代码
核心逻辑替换为摄像头调用、逐帧人脸检测,完整代码如下:
const video = document.getElementById('video') Promise.all([ faceapi.nets.faceRecognitionNet.loadFromUri('/models'), faceapi.nets.faceLandmark68Net.loadFromUri('/models'), faceapi.nets.ssdMobilenetv1.loadFromUri('/models') ]).then(start) async function start() { const container = document.createElement('div') container.style.position = 'relative' document.body.append(container) document.body.append('Models Loaded, Please Allow Camera Permission') // 加载已标记的人脸特征 const labeledFaceDescriptors = await loadLabeledImages() const faceMatcher = new faceapi.FaceMatcher(labeledFaceDescriptors, 0.6) // 加载摄像头流 const stream = await navigator.mediaDevices.getUserMedia({ video: {} }) video.srcObject = stream // 初始化画布 const canvas = faceapi.createCanvasFromMedia(video) container.append(video) container.append(canvas) video.addEventListener('loadedmetadata', () => { const displaySize = { width: video.videoWidth, height: video.videoHeight } faceapi.matchDimensions(canvas, displaySize) // 每100ms执行一次人脸检测 setInterval(async () => { const detections = await faceapi.detectAllFaces(video) .withFaceLandmarks() .withFaceDescriptors() const resizedDetections = faceapi.resizeResults(detections, displaySize) // 清空上一帧的绘制内容 canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height) const results = resizedDetections.map(d => faceMatcher.findBestMatch(d.descriptor)) results.forEach((result, i) => { const box = resizedDetections[i].detection.box const drawBox = new faceapi.draw.DrawBox(box, { label: result.toString() }) drawBox.draw(canvas) }) }, 100) }) } function loadLabeledImages() { const labels = ['Black Widow', 'Captain America', 'Captain Marvel', 'Hawkeye', 'Jim Rhodes', 'Thor', 'Tony Stark'] return Promise.all( labels.map(async label => { const descriptions = [] for (let i = 1; i <= 2; i++) { const img = await faceapi.fetchImage(`https://raw.githubusercontent.com/WebDevSimplified/Face-Recognition-JavaScript/master/labeled_images/${label}/${i}.jpg`) const detections = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor() descriptions.push(detections.descriptor) } return new faceapi.LabeledFaceDescriptors(label, descriptions) }) ) }
注意事项
- 项目根目录需要保留原有的
models文件夹,存放face-api所需的模型文件 - 必须在localhost或HTTPS环境下运行,VSCode的LiveServer环境符合要求,否则浏览器会禁止调用摄像头权限
- 如果出现检测卡顿,可以将代码中setInterval的间隔时间从100调大,降低检测频率
内容的提问来源于stack exchange,提问作者Ali Firoozi
相关产品推荐
相关产品推荐

