基于YOLOv11和mediapipe的疲劳驾驶警告系统,且移植到Nvidia Jetson TX2开发板(TensorRT加速)并成功运行

https://pan.baidu.com/s/1uBtEI_cWiu9Cble35pRJUQ?pwd=1234

引言及灵感

在我发表这篇文章之前,我看到了一则新闻,三名乘客驾驶小米SU7在高速上开启了智能驾驶,之后从小米发布的检测结果来看,驾驶员似乎是因为开启了智能驾驶后就进入到疲劳驾驶状态,在高速上分心从而酿成了此次事故。观看这个新闻之后我调查了中国近10年因疲劳驾驶引发的交通事故发生率及相关数据:

疲劳驾驶是高速公路交通事故的主要成因之一,占事故总数的约20%,在高速公路上的比例更高,占伤亡事故的40%以上。全国范围内,每年因疲劳驾驶直接引发的交通事故超过10万起,造成约9万人死亡或重伤,其中货车事故占比高达54%。公安部统计显示,连续驾驶超过4小时的疲劳驾驶行为,是重特大交通事故的主要诱因,占此类事故的40%。

在高速公路上,疲劳驾驶4秒即可导致致命事故。2019-2023年韩国全罗北道类似统计显示,疲劳驾驶事故死亡率是酒驾的两倍(每100起事故死亡2.9人 vs 1.5人)。职业司机(如货车、网约车司机)是高风险群体。例如,重庆渝北一名网约车司机连续工作19小时后因疲劳驾驶撞车,承担全责。货车司机因经济压力大、睡眠不足,疲劳驾驶事故率显著高于普通车辆。

看来有一个疲劳驾驶警告系统至关重要,于是我决定开发一个可以放在车上,用摄像头检测的警告系统

YOLOv11,mediapipe,多线程运行方法等介绍

YOLO(You Only Look Once):是一种流行的目标检测算法,利用卷积神经网络对目标的动作,状态等进行检测,而YOLOv11是目前YOLO算法中最新版本,相交于之前版本,YOLOv11在检测的精度和速度上都有显著提升。YOLOv11采用了更深的网络结构,更复杂的网络提取模块以及更高效的训练策略,使其在复杂场景下的目标检测任务中表现出色。

YOLOv11网络结构图:

YOLOv11和YOLOv8的对比:

mediapipe:

作为一款跨平台框架,MediaPipe 不仅可以被部署在服务器端,更可以在多个移动端 (安卓和苹果 iOS)和嵌入式平台(Google Coral ,树莓派,Nvidia Jetson设备)中作为设备端机器学习推理 (On-device Machine Learning Inference)框架。

一款多媒体机器学习应用的成败除了依赖于模型本身的好坏,还取决于设备资源的有效调配、多个输入流之间的高效同步、跨平台部署上的便捷程度、以及应用搭建的快速与否。

基于这些需求,谷歌开发并开源了 MediaPipe 项目。除了上述的特性,MediaPipe 还支持 TensorFlow 和 TF Lite 的推理引擎(Inference Engine),任何 TensorFlow 和 TF Lite 的模型都可以在 MediaPipe 上使用。同时,在移动端和嵌入式平台,MediaPipe 也支持设备本身的 GPU 加速。

Opencv:

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,提供了丰富的图像处理和计算机视觉算法,涵盖了广泛的领域,如特征检测、图像处理、物体识别、运动跟踪等。它由一系列的C++库和Python接口组成,支持多种操作系统,包括Windows、Linux、Mac OS等,使其成为了学术界和工业界广泛使用的计算机视觉工具之一。

多线程架构:

⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码,main()⼀般被称为主线程(Main Thread)。单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源。

代码介绍(本文所用所有代码以及模型都可在开头链接中获取)

(一)Main.py主要运行代码:

1.导入模块
import sys
import cv2
import queue
from PyQt5.QtWidgets import (...)
from PyQt5.QtCore import (...)
from PyQt5.QtGui import (...)
import simpleaudio as sa
import config
from target_detector import TargetDetector
from concurrent.futures import ThreadPoolExecutor
  • sys: 系统相关操作(如退出程序)。

  • cv2 (OpenCV): 处理视频流和图像。

  • queue: 线程安全的队列,用于生产者和消费者模型。

  • PyQt5 相关模块: 构建 GUI 界面。

  • simpleaudio: 播放警报声音。

  • config: 配置文件(阈值参数等)。

  • TargetDetector: 自定义的目标检测类(检测手机、抽烟等行为)。

  • ThreadPoolExecutor: 线程池,用于并行处理视频帧。

2.全局变量初始化
COUNTER = 0       # 眨眼帧计数器
TOTAL = 0         # 眨眼总数
mCOUNTER = 0      # 打哈欠帧计数器
mTOTAL = 0        # 打哈欠总数
Roll = 0          # 疲劳检测循环帧计数
Rolleye = 0       # 循环内闭眼帧数
Rollmouth = 0     # 循环内打哈欠帧数

targetDetector = TargetDetector(model_path="pt/best.engine")
  • 全局变量用于跨函数/线程共享数据。

  • targetDetector: 加载预训练模型(YOLO 引擎文件)进行目标检测。

3.视频捕获线程(生产者)
class VideoCaptureThread(QThread):
    def __init__(self, frame_queue):
        super().__init__()
        self.frame_queue = frame_queue  # 共享队列
        self._run_flag = True           # 控制线程运行

    def run(self):
        cap = cv2.VideoCapture(1)       # 打开摄像头(设备号1)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)   # 降低分辨率
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
        while self._run_flag:
            ret, frame = cap.read()     # 读取一帧
            if ret:
                if self.frame_queue.qsize() < 10:  # 限制队列大小
                    self.frame_queue.put(frame)    # 放入队列
        cap.release()  # 释放摄像头

    def stop(self):
        self._run_flag = False  # 停止线程
        self.wait()
  • 作用: 从摄像头捕获帧并存入队列。

  • 优化: 降低分辨率减少计算量,限制队列大小防止内存溢出。

4.视频处理线程(消费者)
class VideoProcessThread(QThread):
    change_pixmap_signal = pyqtSignal(object, object, object, object)  # 自定义信号

    def __init__(self, frame_queue):
        super().__init__()
        self.frame_queue = frame_queue
        self._run_flag = True
        self.executor = ThreadPoolExecutor(max_workers=2)  # 线程池

    def run(self):
        while self._run_flag:
            if not self.frame_queue.empty():
                frame = self.frame_queue.get()  # 从队列取帧
                future = self.executor.submit(targetDetector.detect_target, frame)  # 异步提交任务
                ret, processed_frame = future.result()  # 获取结果
                lab, eye, mouth = ret
                # 发送信号更新UI
                self.change_pixmap_signal.emit(processed_frame, lab, eye, mouth)

    def stop(self):
        self._run_flag = False
        self.wait()
  • 作用: 从队列取出帧,用线程池并行处理(检测目标、计算纵横比)。

  • 信号机制: 将处理后的帧和结果通过信号发送到主线程更新UI。

5.主窗口类(VideoPlayerWindow)
5.1初始化
class VideoPlayerWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("疲劳检测系统")
        self.setGeometry(100, 100, 800, 600)  # 窗口位置和大小
        self.initUI()  # 初始化界面
        self.alert_sound = sa.WaveObject.from_wave_file(config.ALERT_SOUND_FILE)  # 加载警报音
        self.red_frame_is_active = False  # 红色边框状态
        self.frame_counter = 0  # 帧计数器(控制更新频率)

        # 初始化线程和队列
        self.frame_queue = queue.Queue(maxsize=10)
        self.video_capture_thread = VideoCaptureThread(self.frame_queue)
        self.video_process_thread = VideoProcessThread(self.frame_queue)
        self.video_process_thread.change_pixmap_signal.connect(self.update_image)  # 连接信号
        self.video_capture_thread.start()
        self.video_process_thread.start()
5.2界面布局(initUI)
def initUI(self):
    main_widget = QWidget(self)
    self.setCentralWidget(main_widget)
    layout = QHBoxLayout(main_widget)  # 水平布局

    # 视频显示区域
    self.video_widget = QWidget()
    self.video_widget.setStyleSheet("border: 5px solid transparent;")
    video_layout = QVBoxLayout(self.video_widget)
    self.video_label = QLabel()
    video_layout.addWidget(self.video_label)
    layout.addWidget(self.video_widget)

    # 右侧状态和日志区域(垂直分割)
    splitter = QSplitter(Qt.Vertical)
    status_widget, output_widget = self.createStatusAndOutputWidgets()
    splitter.addWidget(status_widget)
    splitter.addWidget(output_widget)
    layout.addWidget(splitter)
5.3状态和日志部件(createStatusAndOutputWidgets)
def createStatusAndOutputWidgets(self):
    # 状态标签(网格布局)
    status_widget = QWidget()
    status_layout = QVBoxLayout(status_widget)
    self.status_labels = {}
    modules = ["手机", "抽烟", "喝水", "状态", "眨眼", "哈欠"]
    self.initial_statuses = ["未使用", "未抽烟", "未喝水", "清醒", 0, 0]
    grid_layout = QGridLayout()
    for i, module in enumerate(modules):
        label = QLabel(f"{module}: {self.initial_statuses[i]}")
        self.status_labels[module] = label
        grid_layout.addWidget(label, i // 2, i % 2)  # 2列布局
    status_layout.addLayout(grid_layout)

    # 日志输出文本框
    output_widget = QWidget()
    output_layout = QVBoxLayout(output_widget)
    self.output_textedit = QTextEdit()
    self.output_textedit.setReadOnly(True)
    output_layout.addWidget(self.output_textedit)

    return status_widget, output_widget
5.4更新视频帧(update_image)
def update_image(self, cv_img, labels, eye_ar, mouth_ar):
    # 降低更新频率(每5帧更新一次)
    self.frame_counter += 1
    if self.frame_counter % 5 == 0:
        # 转换OpenCV BGR图像为Qt RGB格式
        show_image = QImage(
            cv_img.data, cv_img.shape[1], cv_img.shape[0], 
            QImage.Format_RGB888
        ).rgbSwapped()
        self.video_label.setPixmap(QPixmap.fromImage(show_image))
        self.updateStatus(labels, eye_ar, mouth_ar)  # 更新状态
5.5红色边框警告(trigger_red_frame)
def trigger_red_frame(self, show):
    if show:
        self.video_widget.setStyleSheet("border: 5px solid red;")
        self.alert_sound.play()  # 播放警报声
        # 单次定时器实现闪烁效果
        QTimer.singleShot(500, self.hide_red_frame)
    else:
        self.video_widget.setStyleSheet("")

def hide_red_frame(self):
    self.video_widget.setStyleSheet("")
    QTimer.singleShot(500, self.show_red_frame)

def show_red_frame(self):
    if self.red_frame_is_active:
        self.video_widget.setStyleSheet("border: 5px solid red;")
        QTimer.singleShot(500, self.hide_red_frame)
5.6更新状态和检测逻辑(updateStatus)
def updateStatus(self, labels, eye_ar, mouth_ar):
    # 更新分心行为状态
    self.status_labels["手机"].setText(f"手机: {'使用中' if 'phone' in labels else '未使用'}")
    self.status_labels["抽烟"].setText(f"抽烟: {'抽烟中' if 'smoke' in labels else '未抽烟'}")
    self.status_labels["喝水"].setText(f"喝水: {'喝水中' if 'drink' in labels else '未喝水'}")

    # 检测到分心行为时触发警报
    distraction_detected = 'phone' in labels or 'smoke' in labels or 'drink' in labels
    if distraction_detected:
        self.red_frame_is_active = True
        self.trigger_red_frame(True)
    else:
        self.red_frame_is_active = False
        self.trigger_red_frame(False)

    # 更新眨眼和哈欠计数(全局变量)
    global COUNTER, TOTAL, mCOUNTER, mTOTAL
    if eye_ar < config.EYE_AR_THRESH:
        COUNTER += 1
    else:
        if COUNTER >= config.EYE_AR_CONSEC_FRAMES:
            TOTAL += 1
            COUNTER = 0
    if mouth_ar > config.MAR_THRESH:
        mCOUNTER += 1
    else:
        if mCOUNTER >= config.MOUTH_AR_CONSEC_FRAMES:
            mTOTAL += 1
            mCOUNTER = 0

    self.status_labels["眨眼"].setText(f"眨眼: {TOTAL}")
    self.status_labels["哈欠"].setText(f"哈欠: {mTOTAL}")

    # 计算疲劳状态(PERCLOS算法)
    global Roll, Rolleye, Rollmouth
    Roll += 1
    if eye_ar < config.EYE_AR_THRESH:
        Rolleye += 1
    if mouth_ar > config.MAR_THRESH:
        Rollmouth += 1

    if Roll >= config.FATIGUE_CALCULATION_FRAMES:
        perclos = (Rolleye / Roll) + (Rollmouth / Roll) * 0.2  # 加权计算
        status = '疲劳' if perclos > config.PERCLOS_THRESHOLD else '清醒'
        self.status_labels["状态"].setText(f"状态: {status}")
        self.output_textedit.append(f"过去 {Roll} 帧中,Perclos 得分为 {perclos:.3f}")

        # 触发疲劳警报
        if perclos > config.PERCLOS_THRESHOLD:
            self.red_frame_is_active = True
            self.trigger_red_frame(True)
        else:
            self.red_frame_is_active = False
            self.trigger_red_frame(False)

        # 重置计数器
        Roll = Rolleye = Rollmouth = 0
        TOTAL = mTOTAL = 0  # 可选:是否重置总数?

眨眼检测算法(EYE_AR_THRESH

  • 原理:基于眼睛纵横比(Eye Aspect Ratio, EAR)

    • EAR 公式
      EAR= 2⋅∣∣p 1​ −p 4​ ∣∣∣∣p 2​ −p 6​ ∣∣+∣∣p 3​ −p 5​ ∣∣​ 
      (p1…p6p1​…p6​ 为眼部关键点坐标)

    • 代码逻辑

      1. 当 eye_ar < EYE_AR_THRESH 时,开始计数连续闭眼帧(COUNTER)。

      2. 若连续闭眼帧数超过 EYE_AR_CONSEC_FRAMES,判定为一次有效眨眼,TOTAL 累加。

    • 作用:频繁眨眼可能反映疲劳。

哈欠检测算法(MAR_THRESH

  • 原理:基于嘴巴纵横比(Mouth Aspect Ratio, MAR)

    • MAR 公式
      MAR= 3⋅∣∣m 1​ −m 5​ ∣∣∣∣m 2​ −m 8​ ∣∣+∣∣m 3​ −m 7​ ∣∣+∣∣m 4​ −m 6​ ∣∣​ 
      (m1…m8m1​…m8​ 为嘴部关键点坐标)

    • 代码逻辑

      1. 当 mouth_ar > MAR_THRESH 时,开始计数连续哈欠帧(mCOUNTER)。

      2. 若连续哈欠帧数超过 MOUTH_AR_CONSEC_FRAMES,判定为一次有效哈欠,mTOTAL 累加。

    • 作用:频繁打哈欠是疲劳的典型表现。

PERCLOS疲劳指数算法

  • 原理PERCLOS(Percentage of Eyelid Closure Over Time)是国际公认的疲劳指标,代码中改进为加权公式:

    • 加权公式
      Perclos=(RolleyeRoll)+(RollmouthRoll)×0.2Perclos=(RollRolleye​)+(RollRollmouth​)×0.2

      • Rolleye:周期内闭眼总帧数

      • Rollmouth:周期内打哈欠总帧数

      • Roll:检测周期总帧数(FATIGUE_CALCULATION_FRAMES

    • 判定逻辑
      若 Perclos > PERCLOS_THRESHOLD,判定为疲劳状态。

5.7关闭事件处理
def closeEvent(self, event):
    # 停止线程并等待结束
    self.video_capture_thread.stop()
    self.video_process_thread.stop()
    event.accept()
6.主程序入口
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = VideoPlayerWindow()
    window.show()
    sys.exit(app.exec_())
7.关键逻辑总结
  1. 多线程架构:

    • 生产者 (VideoCaptureThread) 捕获帧到队列。

    • 消费者 (VideoProcessThread) 处理帧并发送信号到主线程。

    • 主线程负责UI更新,避免阻塞。

  2. 疲劳检测算法:

    • 眨眼检测: 基于眼睛纵横比 (EYE_AR_THRESH) 和连续帧数。

    • 哈欠检测: 基于嘴巴纵横比 (MAR_THRESH) 和连续帧数。

    • PERCLOS: 结合闭眼和哈欠频率计算疲劳程度。

  3. 分心行为检测:

    • 使用YOLO模型检测手机、抽烟、喝水行为。

  4. UI交互:

    • 实时视频显示。

    • 状态标签动态更新。

    • 红色边框和声音警报。

(二)config.py代码(配置文件,定义阀值参数及警报音效文件)

# config.py

# 阈值设置
EYE_AR_THRESH = 0.15  # 眼睛长宽比阈值,低于此值视为闭眼
EYE_AR_CONSEC_FRAMES = 2  # 连续多少帧闭眼才算一次眨眼
MAR_THRESH = 0.65  # 嘴巴长宽比阈值,高于此值视为打哈欠
MOUTH_AR_CONSEC_FRAMES = 3  # 连续多少帧打哈欠才算一次哈欠

# 计算疲劳的参数
FATIGUE_CALCULATION_FRAMES = 150  # 每150帧计算一次疲劳度
PERCLOS_THRESHOLD = 0.15  # 疲劳度阈值

# 模型路径
MODEL_PATH = "pt/633 and 0.98best.engine"

# RTMP URL
RTMP_URL = ""

# 视频帧处理间隔
VIDEO_FRAME_INTERVAL = 30  # 根据需要调整间隔

# 声音文件路径
ALERT_SOUND_FILE = "sound/alert.wav"

# 人脸识别模型路径
RECOGNIZER_MODEL_PATH = "face/trainer/trainer.yml"

# 人脸识别配置
FACE_RECOGNITION_ENABLED = True

# 哈尔级联分类器路径
CASCADE_CLASSIFIER_PATH = "face/haarcascade_frontalface_alt2.xml"

# 识别置信度阈值
RECOGNITION_CONFIDENCE_THRESHOLD = 80

# 图片路径
FACES_IMAGE_PATH = "face/jm"

# 人脸特征点预测器路径
SHAPE_PREDICTOR_PATH = "face/shape_predictor_68_face_landmarks.dat"

(三)target_detector.py代码(自定义的目标检测类 TargetDetector 的实现文件)

1.类初始化
class TargetDetector:
    def __init__(self, model_path="pt/best.engine"):
        # 定义物体名称(YOLO 模型的类别标签)
        self.name = {0: "face", 1: "smoke", 2: "phone", 3: "drink"}
        
        # 加载 YOLO 模型(使用 Ultralytics 库)
        self.model = YOLO(model_path, task="detect")
        print("YOLO 模型加载完成。")

        # 初始化 MediaPipe 面部关键点检测
        self.mp_face_mesh = mp.solutions.face_mesh
        self.face_mesh = self.mp_face_mesh.FaceMesh(
            static_image_mode=False,  # 动态视频模式
            max_num_faces=1,           # 最多检测 1 张人脸
            refine_landmarks=True      # 使用更精细的关键点
        )
        print("MediaPipe 面部关键点检测模块加载完成。")

        # 定义关键点索引(MediaPipe 的预定义索引)
        self.LEFT_EYE_INDICES = [362, 385, 387, 263, 373, 380]  # 左眼关键点
        self.RIGHT_EYE_INDICES = [33, 160, 158, 133, 153, 144]  # 右眼关键点
        self.MOUTH_INDICES = [61, 291, 39, 181, 0, 17, 269, 405] # 嘴巴关键点

        # 初始化帧队列(多线程缓冲)
        self.frame_queue = queue.Queue(maxsize=10)  # 限制队列大小

        # 表情识别开关(默认关闭)
        self.enable_expression_recognition = False

        # 线程停止事件标志
        self.stop_event = Event()
2.核心算法方法
2.1眼睛纵横比计算(EAR)
    def _eye_aspect_ratio(self, eye):
        # 计算眼睛关键点之间的欧氏距离
        A = dist.euclidean(eye[1], eye[5])  # 垂直方向距离 1
        B = dist.euclidean(eye[2], eye[4])  # 垂直方向距离 2
        C = dist.euclidean(eye[0], eye[3])  # 水平方向距离
        ear = (A + B) / (2.0 * C)          # EAR 公式
        return ear

公式:

2.2嘴巴纵横比计算(MAR)
    def _mouth_aspect_ratio(self, mouth):
        A = dist.euclidean(mouth[2], mouth[6])  # 上下嘴唇垂直距离
        B = dist.euclidean(mouth[0], mouth[4])  # 左右嘴角水平距离
        mar = A / B  # MAR 公式
        return mar

公式:

3.疲劳检测流程
    def _detect_fatigue(self, frame):
        # 将 BGR 转换为 RGB(MediaPipe 要求)
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = self.face_mesh.process(frame_rgb)  # 获取关键点

        eyear = 0.0  # 默认眼睛纵横比
        mouthar = 0.0  # 默认嘴巴纵横比

        if results.multi_face_landmarks:
            for face_landmarks in results.multi_face_landmarks:
                # 提取左眼、右眼、嘴巴关键点坐标
                left_eye = np.array([(landmark.x * frame.shape[1], landmark.y * frame.shape[0]) 
                              for i in self.LEFT_EYE_INDICES])
                right_eye = np.array([...])  # 同上
                mouth = np.array([...])       # 同上

                # 计算眼睛纵横比(取左右眼平均值)
                left_ear = self._eye_aspect_ratio(left_eye)
                right_ear = self._eye_aspect_ratio(right_eye)
                eyear = (left_ear + right_ear) / 2.0

                # 计算嘴巴纵横比
                mouthar = self._mouth_aspect_ratio(mouth)

                # 绘制关键点轮廓
                cv2.drawContours(frame, [left_eye.astype("int")], -1, (0,255,0), 1)
                cv2.drawContours(frame, [right_eye.astype("int")], -1, (0,255,0), 1)
                cv2.drawContours(frame, [mouth.astype("int")], -1, (0,255,0), 1)

        return frame, eyear, mouthar
4.目标检测主逻辑
    def detect_target(self, frame):
        ret = []       # 返回结果列表
        labellist = [] # 检测到的物体标签

        # 使用 YOLO 检测目标(置信度阈值 0.5,GPU 设备)
        results = self.model.predict(frame, conf=0.5, device=0)

        # 遍历检测结果
        for result in results:
            if len(result.boxes.xyxy) > 0:
                # 提取边界框坐标、置信度、类别
                boxes_xyxy = result.boxes.xyxy.tolist()
                boxes_cls = result.boxes.cls.tolist()

                for i, box_xyxy in enumerate(boxes_xyxy):
                    class_label_index = int(boxes_cls[i])
                    modelname = self.name.get(class_label_index, "unknown")
                    labellist.append(modelname)

                    # 绘制边界框和标签
                    left, top, right, bottom = map(int, box_xyxy)
                    cv2.rectangle(frame, (left, top), (right, bottom), (0,255,0), 1)
                    cv2.putText(frame, modelname, (left, top-5), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 1)

                    # 如果检测到人脸,执行疲劳检测
                    if modelname == "face":
                        face_roi = frame[top:bottom, left:right]
                        face_roi, eye, mouth = self._detect_fatigue(face_roi)
                        frame[top:bottom, left:right] = face_roi  # 回写处理后的 ROI

        # 计算并显示 FPS
        fps = 1 / (time.time() - tstart)
        cv2.putText(frame, f"{fps:.2f} fps", (10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 1)

        # 返回结果
        ret.append(labellist)
        ret.append(round(eye, 3))  # 保留 3 位小数
        ret.append(round(mouth, 3))
        return ret, frame
5.多线程管理
    def start_capture(self, video_source=0):
        # 启动视频捕获线程
        def capture_frames():
            cap = cv2.VideoCapture(video_source)
            while not self.stop_event.is_set():
                ret, frame = cap.read()
                if ret:
                    self.frame_queue.put(frame)  # 将帧存入队列
            cap.release()

        self.capture_thread = Thread(target=capture_frames)
        self.capture_thread.start()

    def process_frames(self):
        # 处理队列中的帧
        while not self.stop_event.is_set():
            if not self.frame_queue.empty():
                frame = self.frame_queue.get()
                result, processed_frame = self.detect_target(frame)
                cv2.imshow("Processed Frame", processed_frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):  # 按 'q' 退出
                    self.stop_event.set()

        cv2.destroyAllWindows()

    def stop(self):
        # 停止所有线程
        self.stop_event.set()
        self.capture_thread.join()

系统运行结果

进入系统界面:

检测到人员正在吸烟,屏幕出现红色框框,并且伴随有嘀嘀嘀的警告声:

检测到人员正在玩手机,屏幕出现红色框框,并且伴随有嘀嘀嘀的警告声:

检测到人员正在喝水,屏幕出现红色框框,并且伴随有嘀嘀嘀的警告声:

同时,屏幕右侧栏目中会显示成员疲劳状态以及得分情况,警告驾驶员切勿疲劳驾驶

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值