基于鼻-上唇距离的dlib面部标记咀嚼动作检测失效问题
问题:dlib面部标记检测咀嚼状态失效
使用dlib面部标记识别咀嚼状态,通过计算鼻尖到上唇的帧间距离变化判断,但实际检测时,即使人员咀嚼,输出仍显示“lip is not moving around too much!”,且帧间距离值始终固定(如0.75)。
问题根源分析
- 变量引用错误:检测循环中始终使用最后一帧的
shape值,而非当前帧的面部标记数据,导致距离计算完全不变。 - 距离维度错误:代码中计算的是鼻尖与上唇的水平距离(x轴差值),但咀嚼动作主要是上唇的垂直(y轴)移动,水平距离变化极小。
- 缺失必要初始化代码:未加载dlib的人脸检测器和面部标记预测器,也未定义
shape_to_list等依赖函数,可能导致标记检测异常。 - 阈值未归一化:固定阈值
0.015未考虑视频分辨率差异,不同尺寸的帧会导致阈值失效。
修复方案与代码调整
1. 补全基础初始化代码
添加dlib检测器、预测器加载,以及shape_to_list函数实现:
# 初始化dlib人脸检测器和面部标记预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # 需要下载该模型文件 def shape_to_list(shape): # 将dlib的面部标记转换为numpy数组列表 coords = np.zeros((68, 2), dtype=int) for i in range(0, 68): coords[i] = (shape.part(i).x, shape.part(i).y) return coords
2. 修正距离计算逻辑
改为计算鼻尖与上唇的垂直距离(y轴差值),这才是咀嚼时的主要变化维度:
def lip_nose_distance(shape): # 取上唇的关键标记点(50-53对应上唇上部) top_lip = shape[50:54] # 取鼻尖的标记点(33是鼻尖中心) nose = shape[33] # 计算垂直方向(y轴)的距离 distance = abs(nose[1] - np.mean(top_lip, axis=0)[1]) return distance
3. 修复帧循环中的变量引用
在标记缓冲循环中,使用当前帧的landmark作为计算依据,而非固定的shape:
old_lip_mar1 = 0.0 # 遍历每帧的面部标记数据 for (i, landmark) in enumerate(landmark_buffer): if landmark is None: print("未检测到人脸") continue # 使用当前帧的landmark计算距离 lip_dist = lip_nose_distance(landmark) if old_lip_mar1 > 0.0: # 计算帧间距离变化率(归一化处理,避免分辨率影响) change_ratio = abs(lip_dist - old_lip_mar1) / old_lip_mar1 # 调整阈值为相对变化率,比如超过5%则判断为咀嚼 if change_ratio > 0.05: print("检测到咀嚼动作!") else: print("未检测到咀嚼动作") old_lip_mar1 = lip_dist
4. 优化标记缓冲获取逻辑
移除未定义的get_rotated_mouth_loc_with_height函数,改用标准的dlib人脸检测流程:
landmark_buffer = [] for image in frame_buffer: # 检测人脸 rects = detector(image, 0) if len(rects) == 0: # 未检测到人脸时跳过或记录 landmark_buffer.append(None) continue # 获取面部标记 shape = predictor(image, rects[0]) shape = shape_to_list(shape) landmark_buffer.append(shape)
额外优化建议
- 添加人脸跟踪:如果视频中存在多个人脸,需要跟踪特定目标,避免切换人脸导致误判。
- 滑动窗口判断:不要仅依赖单帧变化,连续3-5帧的变化趋势更能准确判断咀嚼动作。
- 归一化处理:用面部整体高度(如下颌到头顶的距离)缩放鼻唇距离,消除不同拍摄距离的影响。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

