You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改眨眼检测代码实现蜂鸣器按EAR阈值自动启停的求助

代码修改方案

核心修改点如下:

  • 修正原代码阻塞问题:删除蜂鸣器触发逻辑中的time.sleep()阻塞调用,避免主循环卡住无法实时检测EAR变化
  • 调整阈值匹配需求:新增两级EAR阈值,EAR_HIGH_THRESH=0.2(高于该值直接禁用蜂鸣器)、EAR_LOW_THRESH=0.1(低于该值才开始累计触发时长)
  • 新增蜂鸣器即时关闭逻辑:当EAR高于阈值、重置报警状态时,直接拉低蜂鸣器GPIO电平,实现睁眼立即停响
  • 修正原代码的导入遗漏:补全time.sleep的模块前缀,避免运行报错
  • 调整触发帧计数:按30FPS摄像头、持续3秒触发的需求,将连续帧阈值设为90,可根据自己的摄像头帧率自行调整(要N秒触发就设为 帧率*N)
修改后完整代码
# 导入必要的包
from imutils.video import VideoStream
from imutils import face_utils
import numpy as np
import argparse
import imutils
import time
import dlib
import cv2

def euclidean_dist(ptA, ptB):
    # 计算并返回两个点之间的欧氏距离
    return np.linalg.norm(ptA - ptB)

def eye_aspect_ratio(eye):
    # 计算两组垂直眼周关键点(x,y)坐标之间的欧氏距离
    A = euclidean_dist(eye[1], eye[5])
    B = euclidean_dist(eye[2], eye[4])
    # 计算水平眼周关键点(x,y)坐标之间的欧氏距离
    C = euclidean_dist(eye[0], eye[3])
    # 计算眼睛纵横比EAR
    ear = (A + B) / (2.0 * C)
    # 返回眼睛纵横比
    return ear

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-c", "--cascade", required=True,
    help = "人脸级联分类器文件路径")
ap.add_argument("-p", "--shape-predictor", required=True,
    help="人脸关键点预测器文件路径")
ap.add_argument("-a", "--alarm", type=int, default=0,
    help="是否使用TrafficHat蜂鸣器的布尔标识")
args = vars(ap.parse_args())

# 检查是否使用GPIO/TrafficHat作为报警设备
if args["alarm"] > 0:
    import RPi.GPIO as GPIO
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    buzzer=18
    GPIO.setup(buzzer,GPIO.OUT)
    print("[INFO] 使用蜂鸣器报警...")
    
# --------------------------修改部分:阈值调整--------------------------
# 两级EAR阈值
EAR_LOW_THRESH = 0.1 # 低于该值开始累计触发时长
EAR_HIGH_THRESH = 0.2 # 高于等于该值直接禁止报警
# 按30FPS、3秒触发计算,可根据需求调整
EYE_AR_CONSEC_FRAMES = 90
# 初始化帧计数器以及标识报警是否开启的布尔变量
COUNTER = 0
ALARM_ON = False
# --------------------------修改结束--------------------------

# 加载OpenCV的Haar级联分类器用于人脸检测(比dlib内置的HOG检测器更快,但准确率更低),然后创建人脸关键点预测器
print("[INFO] 加载人脸关键点预测器...")
detector = cv2.CascadeClassifier(args["cascade"])
predictor = dlib.shape_predictor(args["shape_predictor"])

# 分别获取左右眼对应的面部特征点索引
(lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]

# 启动视频流线程
print("[INFO] 启动视频流线程...")
vs = VideoStream(src=0).start()
# vs = VideoStream(usePiCamera=True).start()
time.sleep(1.0)

while True:
    # 从线程化视频文件流中抓取帧,调整大小并转换为灰度图
    frame = vs.read()
    frame = imutils.resize(frame, width=450)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 在灰度帧中检测人脸
    rects = detector.detectMultiScale(gray, scaleFactor=1.1, 
        minNeighbors=5, minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE)
    
    # 遍历检测到的人脸
    for (x, y, w, h) in rects:
        # 从Haar级联检测的边界框构造dlib矩形对象
        rect = dlib.rectangle(int(x), int(y), int(x + w),
            int(y + h))
        
        # 确定人脸区域的人脸关键点,然后将人脸关键点(x,y)坐标转换为NumPy数组
        shape = predictor(gray, rect)
        shape = face_utils.shape_to_np(shape)
        
        # 提取左右眼坐标,然后使用坐标计算双眼的眼睛纵横比
        leftEye = shape[lStart:lEnd]
        rightEye = shape[rStart:rEnd]
        leftEAR = eye_aspect_ratio(leftEye)
        rightEAR = eye_aspect_ratio(rightEye)
        # 计算双眼的平均眼睛纵横比
        ear = (leftEAR + rightEAR) / 2.0
        
        # 计算左右眼的凸包,然后可视化每只眼睛
        leftEyeHull = cv2.convexHull(leftEye)
        rightEyeHull = cv2.convexHull(rightEye)
        cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
        cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
        
        # --------------------------修改部分:逻辑调整--------------------------
        if ear < EAR_LOW_THRESH:
            # EAR低于0.1才开始计数
            COUNTER += 1
            # 计数达到阈值触发报警
            if COUNTER >= EYE_AR_CONSEC_FRAMES:
                if not ALARM_ON:
                    ALARM_ON = True
                    # 开启蜂鸣器,删除阻塞的sleep逻辑
                    if args["alarm"] > 0:
                        GPIO.output(buzzer, GPIO.HIGH)
                # 在帧上绘制报警提示
                cv2.putText(frame, "DROWSINESS ALERT!", (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        elif ear >= EAR_HIGH_THRESH:
            # EAR高于等于0.2,直接重置计数器、关闭报警
            COUNTER = 0
            if ALARM_ON:
                ALARM_ON = False
                # 立即关闭蜂鸣器
                if args["alarm"] > 0:
                    GPIO.output(buzzer, GPIO.LOW)
        # EAR在0.1~0.2之间时,保持当前计数器和报警状态不变
        # --------------------------修改结束--------------------------
            
        # 在帧上绘制计算得到的眼睛纵横比,用于调试和设置正确的眼睛纵横比阈值和帧计数器
        cv2.putText(frame, "EAR: {:.3f}".format(ear), (300, 30),
            cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
 
    # 显示帧
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
 
    # 如果按下`q`键,则跳出循环
    if key == ord("q"):
        break

# 清理资源
if args["alarm"]>0:
    GPIO.output(buzzer, GPIO.LOW)
    GPIO.cleanup()
cv2.destroyAllWindows()
vs.stop()

内容的提问来源于stack exchange,提问作者이수환

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 14:27:05