基于MediaPipe与Three.js:获取头部旋转不变的面部特征点距离
问题:头部左右倾斜时,MediaPipe 3D面部特征点距离计算结果异常
我在浏览器应用中使用@mediapipe/tasks-vision库提取3D面部特征点,尝试计算左右眼角(特征点143和372)之间的距离。
当前使用Three.js计算距离的代码如下:
const distance = new THREE.Vector3( faces.faceLandmarks[0][143].x - faces.faceLandmarks[0][372].x, faces.faceLandmarks[0][143].y - faces.faceLandmarks[0][372].y, faces.faceLandmarks[0][143].z - faces.faceLandmarks[0][372].z ).length();
当用户正视摄像头时计算结果正常,但头部左右倾斜(Roll动作)时,计算出的距离会异常增大,尽管这两个点的实际物理距离并未改变。
如何在浏览器环境中,利用MediaPipe的3D特征点获取一致的、旋转不变的面部特征点距离?
解决方案
方法1:使用面部局部坐标系变换
MediaPipe返回的面部数据中包含面部姿态变换矩阵,可以将原始3D坐标转换到以面部自身为基准的局部坐标系,该坐标系不受头部旋转影响。
// 从MediaPipe结果中获取面部姿态变换矩阵 const transformMatrix = new THREE.Matrix4().fromArray(faces.faceTransformationMatrix); // 将两个眼角点转换到面部局部坐标系 const point143 = new THREE.Vector3( faces.faceLandmarks[0][143].x, faces.faceLandmarks[0][143].y, faces.faceLandmarks[0][143].z ).applyMatrix4(transformMatrix); const point372 = new THREE.Vector3( faces.faceLandmarks[0][372].x, faces.faceLandmarks[0][372].y, faces.faceLandmarks[0][372].z ).applyMatrix4(transformMatrix); // 计算局部坐标系下的两点距离 const distance = point143.distanceTo(point372);
方法2:计算相对距离(归一化到面部基准)
如果无法获取姿态矩阵,可以用目标距离除以面部固定基准点的距离,抵消旋转和缩放的影响。比如选择左右耳屏点(特征点234和454)的距离作为基准:
// 计算基准距离(左右耳屏) const baseDistance = new THREE.Vector3( faces.faceLandmarks[0][234].x - faces.faceLandmarks[0][454].x, faces.faceLandmarks[0][234].y - faces.faceLandmarks[0][454].y, faces.faceLandmarks[0][234].z - faces.faceLandmarks[0][454].z ).length(); // 计算目标距离(左右眼角) const targetDistance = new THREE.Vector3( faces.faceLandmarks[0][143].x - faces.faceLandmarks[0][372].x, faces.faceLandmarks[0][143].y - faces.faceLandmarks[0][372].y, faces.faceLandmarks[0][143].z - faces.faceLandmarks[0][372].z ).length(); // 归一化后的相对距离(旋转不变) const normalizedDistance = targetDistance / baseDistance;
方法3:反向旋转抵消Roll角
提取MediaPipe返回的面部Roll旋转角,创建反向旋转矩阵将点坐标还原到正视状态,再计算距离:
// 获取头部Roll角(弧度) const rollAngle = faces.faceRotation.roll; // 创建反向旋转矩阵(抵消Roll倾斜) const rotationMatrix = new THREE.Matrix4().makeRotationZ(-rollAngle); // 转换点坐标 const point143 = new THREE.Vector3( faces.faceLandmarks[0][143].x, faces.faceLandmarks[0][143].y, faces.faceLandmarks[0][143].z ).applyMatrix4(rotationMatrix); const point372 = new THREE.Vector3( faces.faceLandmarks[0][372].x, faces.faceLandmarks[0][372].y, faces.faceLandmarks[0][372].z ).applyMatrix4(rotationMatrix); // 计算校正后的距离 const distance = point143.distanceTo(point372);
内容的提问来源于stack exchange,提问作者Muhammad Afshal
相关产品推荐
相关产品推荐

