基于OpenCV与Python的口鼻距离计算及固定距离跟踪方案问询
嘿,很高兴能帮你解决这两个OpenCV面部特征点的问题,我在实际项目里经常碰到类似的需求,给你一步步拆解~
要计算鼻子和嘴巴的距离,核心是先定位到这两个部位的特征点,再计算它们的空间距离。这里我推荐用dlib的68点面部关键点检测器,因为它的精度足够,配合OpenCV使用很顺手:
第一步:安装依赖库
先确保你装了必要的包:pip install opencv-python dlib numpy另外需要下载dlib预训练的68点检测模型(
shape_predictor_68_face_landmarks.dat),你可以从dlib的官方模型仓库获取,这个是免费的。第二步:编写核心代码
下面是完整的示例,不管是处理静态图片还是摄像头流都适用:import cv2 import dlib import numpy as np from scipy.spatial import distance as dist # 加载检测器和关键点预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # 定义关键点索引(dlib的68点对应位置) NOSE_TIP = 30 # 鼻子尖的关键点索引 MOUTH_CENTER = 51 # 上唇中心点的关键点索引(也可以用57下唇中点,看你需求) # 处理摄像头流的例子(静态图片的话替换成cv2.imread即可) cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break # 转灰度图,提升检测效率 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 检测面部 faces = detector(gray) for face in faces: # 获取面部关键点 shape = predictor(gray, face) shape_np = np.zeros((68, 2), dtype=int) for i in range(68): shape_np[i] = (shape.part(i).x, shape.part(i).y) # 获取鼻子和嘴巴的坐标 nose_point = shape_np[NOSE_TIP] mouth_point = shape_np[MOUTH_CENTER] # 计算欧氏距离 nose_mouth_dist = dist.euclidean(nose_point, mouth_point) # 可视化(可选) cv2.circle(frame, (nose_point[0], nose_point[1]), 3, (0, 255, 0), -1) cv2.circle(frame, (mouth_point[0], mouth_point[1]), 3, (0, 0, 255), -1) cv2.putText(frame, f"Distance: {nose_mouth_dist:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) cv2.imshow("Face Landmarks", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()代码里的关键点索引是dlib标准68点的位置,你可以根据自己的需求调整,比如如果想算鼻子尖到嘴巴底部的距离,就把
MOUTH_CENTER换成57。
你现在遇到的问题是,像素距离会随着你和摄像头的远近线性变化——靠近时像素变大,远离时变小。要让距离“恒定”,本质是计算相对比例,也就是用目标距离除以一个面部的固定参考长度(这个长度也会随距离同步变化,所以比例保持不变)。
我常用的方法是选一个稳定的面部参考特征,比如瞳孔间距,因为它在不同距离下的比例几乎不变,步骤如下:
第一步:确定参考特征的关键点
还是用dlib的68点,左眼的瞳孔中心可以取左眼左右端点(36和39)的中点,右眼瞳孔中心取右眼左右端点(42和45)的中点,这两个点的距离就是瞳孔间距,作为参考长度。第二步:计算相对比例
把鼻子到嘴巴的像素距离除以瞳孔间距的像素距离,得到的比例就是恒定的,不管你离摄像头多远,这个比例都不会变。代码修改示例
在上面的代码基础上,添加参考距离的计算:# 新增瞳孔关键点索引 LEFT_EYE_LEFT = 36 LEFT_EYE_RIGHT = 39 RIGHT_EYE_LEFT = 42 RIGHT_EYE_RIGHT = 45 # 在循环里,获取面部关键点后: # 计算瞳孔中心 left_eye_center = ((shape_np[LEFT_EYE_LEFT][0] + shape_np[LEFT_EYE_RIGHT][0]) // 2, (shape_np[LEFT_EYE_LEFT][1] + shape_np[LEFT_EYE_RIGHT][1]) // 2) right_eye_center = ((shape_np[RIGHT_EYE_LEFT][0] + shape_np[RIGHT_EYE_RIGHT][0]) // 2, (shape_np[RIGHT_EYE_LEFT][1] + shape_np[RIGHT_EYE_RIGHT][1]) // 2) # 计算瞳孔间距(参考长度) eye_dist = dist.euclidean(left_eye_center, right_eye_center) # 计算相对比例(恒定值) normalized_dist = nose_mouth_dist / eye_dist # 可视化替换成这个比例 cv2.putText(frame, f"Normalized Distance: {normalized_dist:.2f}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)这样输出的
normalized_dist就是恒定的,比如不管你离摄像头1米还是2米,这个比例都会保持在一个固定区间(比如成人大概是0.8-1.2左右,具体看你选的关键点)。另外,你也可以用面部宽度(比如下颌线的两个端点1和17的距离)作为参考,原理是一样的,选哪个取决于你的场景哪个更稳定。
内容的提问来源于stack exchange,提问作者lololo wote




