如何将face-api.js获取的人脸关键点坐标用于p5.js的curveVertex实时绘制
问题原因
- 没有全局初始化
landmarkPositions变量,p5.js的draw()函数执行优先级高于face-api的异步检测逻辑,会直接读取到undefined报错 curveVertex()需要传入两个独立的数值参数(x坐标、y坐标),你直接传入点位对象会导致参数不匹配无法绘制- 重复调用了两次人脸检测接口,性能浪费,同时两个canvas尺寸不统一会出现点位错位
修复步骤
- 全局初始化变量
在代码最顶部添加变量声明,避免未定义报错:
let landmarkPositions = []; // 固定视频尺寸方便后续坐标适配 const videoWidth = 720; const videoHeight = 560;
- 优化检测逻辑,避免重复请求
修改playing事件中的setInterval逻辑,直接从已有的检测结果中提取关键点,不需要单独调用detectFaceLandmarks:
setInterval(async () => { const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks() const displaySize = {width: videoWidth, height: videoHeight} const resizedDetections = faceapi.resizeResults(detections, displaySize) canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height) faceapi.draw.drawFaceLandmarks(canvas, resizedDetections) // 提取单人脸的关键点数组 if (resizedDetections.length > 0) { landmarkPositions = resizedDetections[0].landmarks.positions; } }, 100)
- 修正p5.js的绘制逻辑
适配坐标缩放,正确传入参数给curveVertex():
function draw() { // 半透明背景可以保留拖影效果,不需要可改为background(0) background(0, 15); stroke(255); noFill(); strokeWeight(4); // 没有检测到人脸时直接跳过绘制,避免报错 if (landmarkPositions.length < 68) return; // 计算缩放比例,适配p5画布和视频的尺寸差 const scaleX = width / videoWidth; const scaleY = height / videoHeight; beginShape(); // curveVertex要求首尾各重复一次点位才能正确绘制曲线 const first = landmarkPositions[0]; curveVertex(first.x * scaleX, first.y * scaleY); // 遍历所有68个关键点传入 for (let pos of landmarkPositions) { curveVertex(pos.x * scaleX, pos.y * scaleY); } // 补全闭合需要的重复点位 const last = landmarkPositions[landmarkPositions.length - 1]; curveVertex(last.x * scaleX, last.y * scaleY); curveVertex(first.x * scaleX, first.y * scaleY); endShape(); }
可选优化
如果不需要绘制完整的68点轮廓,可根据face-api的关键点索引区间单独绘制指定部位:
- 0~16:下巴外轮廓
- 17~21:左眉毛
- 22~26:右眉毛
- 27~35:鼻子
- 36~41:左眼
- 42~47:右眼
- 48~67:嘴唇
内容的提问来源于stack exchange,提问作者Coco Yuan
相关产品推荐
相关产品推荐

