如何编写OpenCV函数判断用户视线方向:左、右或正对摄像头
基于OpenCV的视线方向判断实现方案
现有代码问题梳理
当前代码已完成人脸椭圆绘制、眼睛矩形标记及最优人脸/眼睛筛选,但存在两个核心问题:
- 坐标逻辑混乱:筛选左右眼时错误将局部坐标转为全局坐标判断,导致筛选条件失真
- 缺失瞳孔定位逻辑:Haar级联仅能检测眼眶范围,而瞳孔位置才是判断视线方向的核心依据
实现步骤与修改后代码
1. 修复左右眼筛选逻辑
直接用人脸局部坐标判断:左眼处于人脸左半区域,右眼处于右半区域,同时限定在人脸的上半部分(排除脸颊误检),避免全局坐标干扰。
2. 添加瞳孔检测
对眼睛区域做灰度阈值处理,提取最暗区域作为瞳孔(瞳孔是眼睛中亮度最低的部分),通过轮廓识别定位瞳孔中心。
3. 视线判断逻辑
基于瞳孔在眼眶中的相对位置判断方向:
- 左眼:瞳孔偏左(相对比例<0.3)→视线左;居中(0.3-0.7)→正对;偏右(>0.7)→视线右
- 右眼:瞳孔偏左(相对比例<0.3)→视线右;居中(0.3-0.7)→正对;偏右(>0.7)→视线左
- 综合双眼结果输出最终判断
修改后的完整代码
import cv2 import glob def draw(color_face, list_of_coordinates): if not list_of_coordinates: return # 绘制眼睛框 eye_x, eye_y, eye_width, eye_height = list_of_coordinates[0] topleft = (eye_x, eye_y) bottomright = (eye_x + eye_width, eye_y + eye_height) cv2.rectangle(color_face, topleft, bottomright, (0, 255, 255), 2) def detect_pupil(eye_gray): # 阈值处理提取瞳孔(最暗区域) _, thresh = cv2.threshold(eye_gray, 30, 255, cv2.THRESH_BINARY_INV) # 寻找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return None # 取面积最大的轮廓作为瞳孔 largest_contour = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(largest_contour) # 返回瞳孔在眼睛区域的相对中心坐标 pupil_center_x = x + w//2 return pupil_center_x def judge_gaze(left_eye, right_eye, face_gray): gaze_result = "正对" # 处理左眼 if left_eye: le_x, le_y, le_w, le_h = left_eye eye_gray = face_gray[le_y:le_y+le_h, le_x:le_x+le_w] pup_x = detect_pupil(eye_gray) if pup_x is not None: ratio = pup_x / le_w if ratio < 0.3: gaze_result = "向左" elif ratio > 0.7: gaze_result = "向右" # 处理右眼,修正判断逻辑 if right_eye: re_x, re_y, re_w, re_h = right_eye eye_gray = face_gray[re_y:re_y+re_h, re_x:re_x+re_w] pup_x = detect_pupil(eye_gray) if pup_x is not None: ratio = pup_x / re_w if ratio < 0.3: gaze_result = "向右" elif ratio > 0.7: gaze_result = "向左" return gaze_result # 加载级联分类器 eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml') face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') images = glob.glob('faces1/*.png') for image in images[1:]: img = cv2.imread(image) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # OpenCV默认读取为BGR格式,修正颜色转换 # 检测人脸并筛选最大的人脸 faces = face_cascade.detectMultiScale(gray, 1.085, 2) if not faces: continue good_x, good_y, greatest_width, greatest_height = max(faces, key=lambda f: f[2]*f[3]) # 提取人脸区域 face_gray = gray[good_y:good_y+greatest_height, good_x:good_x+greatest_width] color_face = img[good_y:good_y+greatest_height, good_x:good_x+greatest_width] # 检测眼睛 eyes = eye_cascade.detectMultiScale(face_gray, 1.085, 2) good_left_eyes = [] good_right_eyes = [] # 筛选左右眼(基于人脸局部坐标) face_mid_x = greatest_width // 2 face_upper_y = greatest_height // 2 for eye in eyes: e_x, e_y, e_w, e_h = eye # 限定眼睛在人脸的上半部分 if e_y + e_h < face_upper_y: if e_x + e_w < face_mid_x: good_left_eyes.append(eye) elif e_x > face_mid_x: good_right_eyes.append(eye) # 绘制眼睛 draw(color_face, good_left_eyes) draw(color_face, good_right_eyes) # 判断视线方向并打印 left_eye = good_left_eyes[0] if good_left_eyes else None right_eye = good_right_eyes[0] if good_right_eyes else None gaze_dir = judge_gaze(left_eye, right_eye, face_gray) print(f"视线方向: {gaze_dir}") # 绘制人脸椭圆 center = (good_x + greatest_width//2, good_y + greatest_height//2) axes = (greatest_height//3, greatest_height//2) cv2.ellipse(img, center, axes, 0, 0, 359, (0, 0, 100), 3) # 添加视线方向文本 cv2.putText(img, gaze_dir, (good_x, good_y-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow('Gaze Detection', img) cv2.waitKey(10000) break cv2.destroyAllWindows()
关键优化说明
- 修正了
COLOR_RGB2GRAY的错误,OpenCV读取图片默认是BGR格式,需用COLOR_BGR2GRAY转换 - 瞳孔检测使用简单阈值,光线复杂场景可替换为
cv2.adaptiveThreshold提升鲁棒性 - 视线判断的比例阈值(0.3、0.7)可根据测试样本调整,优化判断准确率
内容的提问来源于stack exchange,提问作者zed
相关产品推荐
相关产品推荐

