☰
基于YOLOv5+DeepSORT的驾驶员分心检测系统实现
2026/9/28 13:27:10 网站建设 项目流程

简介:YOLOv5+DeepSort方案面向驾驶员分心驾驶行为预警,整合目标检测与多目标跟踪,实现对疲劳状态及危险动作的实时识别,适合深度学习、计算机视觉方向的毕业设计、课程设计与工程实践。压缩包共60个文件,以Python源码为核心(约20个py文件),配套18个YAML模型配置、训练好的best.pt权重、人脸关键点检测dat模型、界面UI文件及演示视频,整体约110.69MB,目录结构清晰,便于按模块学习。源码已本地编译通过,可直接运行,附有演示视频与说明文档,能够帮助快速还原检测、跟踪、预警的完整流程。项目评审得分98分,内容经过助教审定,难度适中,已有120人学习使用,可作为高分毕设或多类实践项目的可靠参考。

1. 驾驶员分心检测为什么值得用 YOLOv5+DeepSORT+Python 来做

疲劳驾驶和分心驾驶是交通事故里最隐蔽的两类诱因,很多车队和车企的 DMS(驾驶员监测系统)都在做同一件事:实时盯住驾驶员的头、眼、手,判断他是闭眼打盹、低头玩手机,还是伸手拿东西。这个任务放到现在的技术栈里,最成熟的一条路就是 YOLOv5 做目标检测、DeepSORT 做轨迹跟踪、Python 把二者串起来写行为判定逻辑。这套组合不挑硬件、社区资料多、坑也基本被踩平了,非常适合作为毕设或者 DMS 方向的入门项目。我会按自己做过的流程,把环境配置、数据集制作、模型训练、跟踪接入和行为判定完整讲一遍,也会把那些让你想砸键盘的翻车点提前指出来。

2. 先把技术选型看透:YOLOv5 负责"看",DeepSORT 负责"记"

2.1 YOLOv5 在驾驶员监测里的角色:检测框是后续一切判定的地基

驾驶员分心检测本质上是一个"多目标检测 + 时序判定"的问题。摄像头对着驾驶员,画面里会出现头部、眼睛、嘴巴、手、手机、水杯等目标。你要先知道"眼睛在哪、嘴巴状态如何、手在干什么",才能谈得上判断疲劳和危险行为。YOLOv5 在这套系统里承担的就是"看"的职责:每一帧画面输入,输出若干检测框,每个框带类别和置信度。

为什么不直接端到端检测"疲劳"?因为疲劳不是稳定的视觉类别。一个人闭眼 0.3 秒是眨眼,闭眼 2 秒才是疲劳,单帧检测器天生分不清这两个状态。把视觉检测和时序判定拆开,各自做各自擅长的事,是这套方案能落地的根本原因。

选型上,YOLOv5 而非 YOLOv8 或 RT-DETR,我考虑两点。生态成熟度排第一:yolov5 的源码、预训练权重、改进思路在开源社区里沉淀了三年多,毕设阶段遇到报错,基本搜一下就有人踩过;部署灵活排第二:detect.py 和 export.py 把训练、推理、导出 ONNX/TensorRT 的链路都封好了,省掉大量造轮子时间。模型规模用 yolov5s 起步就够了,驾驶员监测画面相对固定,目标尺度变化不大,s 版本在 GTX 1660 上能跑到 60 FPS 以上,精度也够用。

这里要强调一个容易被低估的点:YOLOv5 的检测框质量直接决定后续 DeepSORT 和疲劳判定的上限。检测框在驾驶员低头时频繁丢失,或者把方向盘误检成手,后面逻辑再漂亮也白搭。所以类别不要一上来定太杂,先保证"睁眼、闭眼、张嘴、手部动作"这些核心类检得稳,再逐步加喝水、吸烟等细分类别。

2.2 DeepSORT 的轨迹关联:单帧检测为什么不够用

只做单帧检测,会立刻碰到一个现实问题:驾驶员打哈欠时嘴巴张到最大可能持续半秒,闭眼时眼睛在画面里完全消失一两帧。如果每一帧独立判断,就会出现"上一帧检测到闭眼、下一帧眼睛没了、再下一帧又出现"的抖动,疲劳统计根本没法做。DeepSORT 解决的是"记"的问题:通过卡尔曼滤波预测目标下一帧位置,再用匈牙利算法把当前帧检测框和历史轨迹做最优匹配,给每个目标分配稳定 ID,把零散的检测连成连续轨迹。

DeepSORT 的核心机制有三块:卡尔曼滤波做运动预测,解决目标短暂消失期间的位置外推;匈牙利算法做检测框和轨迹的二分匹配,解决"这个框到底属于谁";级联匹配按轨迹最近更新时间排序,优先处理刚更新过的轨迹,避免旧轨迹抢占新目标。还有一块常被忽略但很关键:外观特征提取。它用一个小型 ReID 网络为每个检测框生成特征向量,当目标被遮挡后重新出现时,靠运动预测的匹配会失败,靠外观特征能恢复原 ID。

这也是为什么社区里 deepsort 改进的工作大多集中在特征提取网络上——在驾驶员这种"目标基本不动、只有局部变化"的场景里,运动模型区分度有限,外观特征才是 ID 稳定的关键。接入时需要建立几个参数直觉:n_init 太小,轨迹容易被噪声检测框带偏;max_age 太短,驾驶员低头两秒再抬头会被当成新目标。这两个参数直接影响行为统计的连续性,后面避坑章节会具体讲。

2.3 疲劳与危险行为的标签体系:数据集怎么定义决定模型上限

模型只是工具,真正决定系统"像不像样"的是行为标签体系。我习惯把标签分两层:检测层和行为层。检测层只负责识别物理目标,不掺任何抽象概念;行为层在跟踪轨迹之上做时序判断。分层的好处是职责清晰、可调参。

层级内容示例
检测层物理目标识别eye_open(睁眼)、eye_closed(闭眼)、mouth_open(张嘴)、hand_steering(手握方向盘)、hand_phone(手持手机)、hand_other(其他手部动作)
行为层时序状态判定闭眼占比超 40% 持续 3 秒判疲劳;手持手机超 1 秒判分心

类别数量需要拿捏。驾驶员监测公认比较稳妥的做法是 6 到 10 类,再多的话,眼睛这种小目标和手这类相似目标之间会产生大量混淆,标注成本也直线上升。我的第一版只做 6 类:睁眼、闭眼、张嘴、正常手、手持手机、其他动作。跑通之后再考虑把"其他动作"拆成喝水、吸烟、低头看导航等,每拆一类都要补对应的标注数据,这是最容易低估的成本。

3. 从零搭出可运行的环境与数据:conda 配置与数据集组织

3.1 yolov5 环境配置:conda 建环境与依赖版本对照

这套系统最让人头疼的不是训练,而是环境。yolov5 的依赖横跨 PyTorch、OpenCV、numpy 多个库,版本冲突时报错像黑匣子一样难猜。我的习惯是先用 conda 单独建一个环境,和系统 Python 隔离,python 版本选 3.8 或 3.9 最稳,太新的版本有些 CUDA 相关库还没适配。

conda create -n dms python=3.8 -y conda activate dms conda install cudatoolkit=11.8 cudnn=8.6 -c conda-forge -y pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt python detect.py --weights yolov5s.pt --source data/images/bus.jpg

这段命令做了四件事:创建 python 3.8 的独立 conda 环境;安装配套的 CUDA 11.8 和 cuDNN;安装 PyTorch 2.0.1 的 CUDA 版本;克隆 yolov5 源码并安装依赖。最后一条 detect.py 用来验证环境是否跑通,能看到 bus.jpg 的推理结果图,说明 GPU 调用和依赖都没问题。

需要注意 conda 和 pip 混装的顺序:先装 cudatoolkit,再装 torch,如果反了,PyTorch 会去调用系统的 CUDA runtime,版本对不上就只能跑 CPU 模式。机器上没有 N 卡的话,把 torch 安装那行换成 CPU 版即可,训练慢几倍但逻辑一样能跑通。另外 requirements.txt 里的版本是 ultralytics 在某时间点锁定的,如果先前装过更高版本的 opencv 或 numpy,建议先卸载再装,否则会出现 "module 'numpy' has no attribute 'bool'" 这类典型兼容报错。

提示:每次改环境前先conda list记录当前版本,翻车时能快速回退,这是环境问题排查的后悔药。

3.2 自制驾驶员分心数据集:采集、标注、目录结构

开源数据集里,State Farm 的 Distracted Driver Dataset 类别偏粗,且多为静态图片,缺少连续帧,做不了 DeepSORT 跟踪验证。我更推荐自己采集一段短视频再抽帧标注。采集时摄像头放在仪表盘上方偏右 30 度左右,这个角度能看到驾驶员侧面轮廓和手部动作,比纯正面视角更容易区分手在方向盘上还是手机上。分辨率至少 1280×720,太低了眼睛这种小目标基本没法用。

标注工具用 LabelImg 或 LabelStudio 都行,关键在标注规范要统一。我的规范是:眼睛半闭按闭眼标,嘴巴微张按正常标,手被遮挡超过一半不标。规范不统一是数据集最大的坑,两个标注员对同一帧理解不同,训练出来的模型就会在边界样本上反复横跳。每人标完,我还会抽 10% 交叉复查一遍,宁可慢也不要脏数据。

标注完的目录结构按 yolov5 的习惯组织:

dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 与 images/train 一一对应的 txt 标签 │ ├── val/ │ └── test/ └── data.yaml # yolov5 数据集配置文件

每个 txt 文件名与对应图片名相同,内容是一行一个目标,格式为class_id x_center y_center width height,坐标全部归一化到 0~1 浮点数。LabelImg 默认输出 PASCAL VOC 的 XML 格式,不是 yolov5 要的 txt,所以需要转换脚本,这就引出了下一节。

3.3 把数据集转成 YOLO 格式:标签归一化与划分脚本

下面是我常用的 XML 转 YOLO 脚本,核心是读取 bndbox 坐标,换算成归一化中心点和宽高,同时按 8:1:1 划分数据集。

import os import xml.etree.ElementTree as ET import random from pathlib import Path CLASSES = ["eye_open", "eye_closed", "mouth_open", "mouth_normal", "hand_steering", "hand_phone", "hand_other"] def xml_to_yolo(xml_path, save_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): cls = obj.find("name").text if cls not in CLASSES: continue cls_id = CLASSES.index(cls) box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) x_c = ((x1 + x2) / 2) / img_w y_c = ((y1 + y2) / 2) / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h lines.append(f"{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}") with open(save_path, "w") as f: f.write("\n".join(lines)) xml_root = Path("annotations") img_root = Path("images") for img_file in img_root.glob("*.jpg"): xml_file = xml_root / (img_file.stem + ".xml") if xml_file.exists(): xml_to_yolo(xml_file, img_root.with_name("labels") / (img_file.stem + ".txt"), img_w=1920, img_h=1080)

脚本里两个关键参数:CLASSES 列表的顺序就是类别 ID,后续 data.yaml 必须同序;img_w 和 img_h 是原始图片实际尺寸,采集分辨率不是 1920×1080 就一定改成真实值,否则归一化坐标全错。运行后抽几个 txt 检查坐标是否都在 0~1 区间,出现大于 1 或小于 0 的值,说明标注框出了图像边界,需要回标注工具修正。

划分数据集用简单的 shuffle 加比例切分即可,但要注意一个纪律:同一段视频里的帧,train、val、test 不能混着出现。相邻帧几乎一样,混了之后 val 指标会虚高,答辩时一跑连续视频就露馅。最稳妥的做法是先用视频片段 ID 分组,再把整个片段划分到不同集合,而不是逐帧随机切。

4. 训练与推理落地:用 YOLOv5 训练自己的数据集并接 DeepSORT

4.1 yolov5 训练自己的数据集:训练命令与超参数

数据集就绪后,训练命令本身不复杂,但超参数的选择直接决定是顺利收敛还是白跑一天。我的经验是先小 batch、小尺寸跑 5 个 epoch 验证数据链路,确认 loss 在正常下降,再上正式参数。

# 快速验证数据链路 python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 8 --epochs 5 # 正式训练 python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 32 --epochs 100 --cache --workers 8

正式训练的参数逐个说:--img 640 是输入分辨率,驾驶员画面里眼睛是很小的目标,显卡显存够的话建议试 768 或 896,小目标召回率会有肉眼可见的提升。--batch 32 在 11GB 显存的 RTX 2080Ti 上刚好放得下 yolov5s,batch 太小会导致 BN 层统计不稳定,loss 震荡。--cache 把图片缓存进内存,省去每个 epoch 重复读磁盘的时间,代价是内存占用,32GB 内存的机器没问题。

yolov5 的超参数文件是 data/hyp.scratch-low.yaml,里面主要有 lr0 初始学习率、momentum 动量、weight_decay 权重衰减、fl_gamma focal loss 系数。我改得最多的是 fl_gamma,从默认 0.0 改成 0.5,因为驾驶员数据集里闭眼和打哈欠这类正样本占比小,focal loss 能缓解正负样本不平衡。数据增强参数 hsv_h、hsv_s、degrees、translate,驾驶舱画面角度相对固定,rotate 和 translate 不建议调大,眼睛小目标经不起大幅度几何扭曲。

训练结束后,runs/train/exp 下会有 weights/best.pt 和 weights/last.pt。best.pt 是验证集 mAP 最高的权重,测试和部署用这个;last.pt 是最后一轮权重,想继续训练或迁移学习时用这个。两个都要留好,别只留一个。

4.2 DeepSORT 接入:检测框到跟踪轨迹的数据流

yolov5 源码本身不带 DeepSORT,需要额外拉 deep_sort_pytorch 这类实现。接入的核心是把 YOLOv5 的检测结果(xyxy 坐标、置信度、类别)转成 DeepSORT 能吃的格式,然后每帧调用一次 update。下面是一个最小可跑的封装:

import torch import numpy as np from deep_sort_pytorch.deep_sort import DeepSort class DMSPipeline: def __init__(self, detector_weight="best.pt"): self.model = torch.hub.load("ultralytics/yolov5", "custom", path=detector_weight, force_reload=True) self.deepsort = DeepSort("deep_sort_pytorch/deepsort/deepsort_model.pt", max_dist=0.2, max_iou_distance=0.7, max_age=45, n_init=3) self.last_tracks = [] def process_frame(self, frame): results = self.model(frame, size=640) detections = results.xyxy[0].cpu().numpy() # 只保留置信度>0.5 的检测框,断掉低质量检测对轨迹的干扰 detections = detections[detections[:, 4] > 0.5][:, :5] bboxes = detections[:, :4] confs = detections[:, 4] clss = detections[:, 5].astype(int) tracks = self.deepsort.update(bboxes, confs, clss, frame) self.last_tracks = tracks return tracks

这段代码的执行逻辑:先用自定义权重做推理,得到原始检测框;按 0.5 置信度过滤低质量检测;再把 bbox、置信度、类别一起交给 deepsort.update。返回的 tracks 每行是 [x1, y1, x2, y2, track_id, class_id, conf],后续疲劳判定全部基于 track_id 聚合,而不是单帧检测。

参数按场景调:max_dist 是外观特征距离阈值,默认 0.2,驾驶员经常低头导致目标短期消失时放宽到 0.3,代价是可能把不同目标串成一条轨迹;max_age 是目标消失后轨迹保留帧数,45 意味着约 1.5 秒(30FPS)内重新出现还能维持原 ID,对"低头看导航再抬头"这个动作很关键。n_init 保持 3,太低会让轨迹在目标刚出现时就开始输出,容易带噪声。

4.3 疲劳行为判定逻辑:PERCLOS 与持续时间的阈值设计

检测和跟踪跑通后,最后一步是把轨迹变成告警。疲劳判定我采用 PERCLOS(眼睛闭合时间占比)加连续闭眼时长两个指标的组合。具体做法:对每个 track_id 维护一个滑动窗口,窗口内统计 eye_closed 帧数占比,超过 0.4 且持续满窗口触发疲劳告警。

from collections import defaultdict, deque class FatigueJudge: def __init__(self, window_size=90, closed_ratio=0.4, alert_frames=45): self.window = defaultdict(lambda: deque(maxlen=window_size)) self.closed_ratio = closed_ratio self.alert_frames = alert_frames self.state = defaultdict(bool) def update(self, tracks): alerts = [] for t in tracks: tid = int(t[4]) cls_id = int(t[5]) # 类别 1 对应 eye_closed,写入 1;其余写 0 self.window[tid].append(1 if cls_id == 1 else 0) buf = self.window[tid] if len(buf) < 30: continue ratio = sum(buf) / len(buf) if ratio > self.closed_ratio and len(buf) == buf.maxlen: if not self.state[tid]: alerts.append((tid, "fatigue", ratio)) self.state[tid] = True else: self.state[tid] = False return alerts

窗口大小 90 对应 30FPS 下 3 秒的统计周期,闭眼占比超 40% 且窗口填满才告警,能有效滤掉正常眨眼。这里有个容易忽略的细节:deque(maxlen=window_size) 在长度达到上限后,新元素会自动挤掉最老的,因此len(buf) == buf.maxlen确保窗口真正填满,避免系统启动前几秒因数据量不足造成误判。state 字典的用途是告警防抖,同一个 track_id 已经在告警状态时不重复触发。

危险行为判定逻辑类似但阈值不同。手持手机是持续性行为,连续 30 帧(1 秒)的 hand_phone 才告警,避免只是抬手碰一下就报警;打哈欠是脉冲式行为,连续 15 帧的 mouth_open 即可提示,为降误报可把 mouth_open 的置信度阈值从 0.5 提到 0.65。这些阈值没有通用最优值,都要拿一段真实连续视频回放调,我后面会说怎么验证。

5. 避坑指南:训练、部署与行为判定最常见的 5 个翻车点

5.1 现象:loss 降了但 mAP 上不去,眼睛检测框乱跳

现象:训练 100 个 epoch,loss 从 0.05 降到 0.02,看着很漂亮,但验证集 mAP 卡在 0.35 上不去。把 best.pt 拉出来跑视频,眼睛的检测框经常有半个脸大,置信度忽高忽低。

原因:两个。第一,标注框质量问题——眼睛在画面里只有二三十像素,标注时手一抖框就画大,IoU 计算直接崩掉,正样本 AP 永远起不来。第二,正负样本失衡——一段 10 分钟正常驾驶视频里,闭眼和打哈欠的帧可能只有几十秒,模型学到的大概率全是"正常"模式。

解决:先抽 50 张图做标注质量复查,把宽度或高度超过目标真实尺寸 1.5 倍的框全部重标。然后补数据,闭眼和打哈欠单独录 3 分钟,抽帧后并入训练集,让正样本占比至少到 15%。如果还不行,把 hyp 里的 fl_gamma 调到 0.5~1.0,用 focal loss 拉升正样本梯度。

5.2 现象:同一个驾驶员被分配多个 ID,ID 频繁跳变

现象:视频跑起来,驾驶员低头看导航两秒再抬头,track_id 从 1 变成 7,过一会又从 7 变成 3。后端统计"这个驾驶员闭眼多久"的计数器全乱,疲劳时长完全对不上。

原因:DeepSORT 的 max_age 默认约 30 帧,1 秒左右。目标从画面消失超过这个帧数,轨迹就被终结,重新出现只能新建轨迹。另一个原因是 ReID 网络对"同一个人的头部"区分能力不够,低头再抬头后视角变化,外观特征距离超过 max_dist,匹配失败。

解决:把 max_age 调到 60~90 帧(2~3 秒),n_init 保持 3。max_dist 从 0.2 放宽到 0.3,但放宽后两个相邻位置的人可能被串成一条轨迹,要拿真实视频实测权衡。还跳 ID 就换更强的 ReID 权重,把默认 ckpt.t7 换成 ResNet50 类在 Market1501 上预训练的特征模型,或者自己做 deepsort 改进,在 ReID 分支并列一个人脸关键点分支,角度变化时用关键点兜底。

5.3 现象:疲劳检测频繁误报,眨一下眼就告警

现象:系统跑起来,驾驶员正常开车,眨一下眼就触发疲劳告警,频率高到没法用。回看日志,闭眼轨迹的帧数统计确实超过了阈值,看起来逻辑没错。

原因:正常眨眼约 0.3 秒,按 30FPS 算只有 9 帧,根本够不到 90 帧窗口 40% 的线。问题出在半闭状态被重复计数——眨眼过程中眼睛半闭,YOLOv5 同时输出了 eye_open 和 eye_closed 两个框,且都过了置信度阈值,DeepSORT 把两个类别当成两个目标分别建轨迹,闭眼轨迹帧数虚高一倍。

解决:在推理逻辑里加同轨迹去重,对同一个 track_id 下 IoU 超过 0.5 的 eye_open 和 eye_closed 框做二次 NMS,保留置信度高的那个。更彻底的方案是增加 eye_half(半闭)类别重新标注,但成本高,建议先用二次 NMS,跑一周数据看误报率再决定要不要加类别。

5.4 现象:GPU 显存溢出,训练中途 OOM

现象:配置看着没问题,训练到第 200 个 step,直接报 CUDA out of memory,之前的 epoch 全白跑。调低 batch 又不甘心,32 明明是官方推荐的基线值。

原因:OOM 不一定是模型参数占的,更多是 DataLoader 和图像缩放的开销。--cache 把图片预加载到内存,每个 worker 还要维护一份迭代状态;--workers 8 时数据预取的开销成倍放大。还有个隐藏点:--img 896 的特征图显存约是 640 的两倍,小显存卡一上去就顶穿。

解决:先降 workers 到 4,而不是降 batch;其次去掉 --cache,改用 SSD 读图;最后才考虑 batch 从 32 降到 16。想用 896 分辨率,就在 train.py 里加 --rect,让同一 batch 的图片保持相近宽高比,少做 padding,能省约 20% 显存。

5.5 现象:摄像头画面卡顿,推理帧率跌到个位数

现象:跑通后接上 USB 摄像头实测,检测画框加告警的完整流程只有 8 FPS,画面一步一卡,实时预警基本是空话。

原因:性能瓶颈不在 YOLOv5 而在 DeepSORT。deepsort.update 对每个检测框都要过一遍 ReID 网络提特征,驾驶员画面里同时有脸、手、手机六七个目标,每帧多跑六七次前向,GPU 一弱就堵死。OpenCV 的 imshow 显示也会拖慢主循环。

解决:检测降频立竿见影,每 2 帧跑一次检测,中间 1 帧用卡尔曼预测结果顶替,帧率能翻倍;再用双线程把摄像头读取和检测分离,读取线程只管抓帧,检测线程消费最新帧。边缘设备上做 yolov5 部署,就把模型导出成 ONNX 或 TensorRT engine,推理耗时能再砍一半以上。

6. 让预警系统真正能用:帧率控制与行为判定验证

6.1 用双线程队列把算法耗时藏起来

把摄像头读取、检测推理、行为判定拆成三个线程,之间用队列传帧。读取线程只管抓帧放入队列,检测线程取最新帧处理,判定线程消费检测结果。队列上限设 10 帧,满了丢最旧的,这样即使某帧检测耗时 100ms,也不会阻塞摄像头采集,画面依旧流畅。Python 里用 queue.Queue 加守护线程就能实现,Queue 本身线程安全,不需要额外加锁。这个结构顺便解决了程序退出时的资源回收问题,主线程结束时把守护线程一停,干净利落。

6.2 行为判定的准确率怎样才算数

很多项目止步于"检测准了",但行为判定的误报率没人测过。答辩时老师问"准确率多少",答 mAP 只有检测层意义。我用一段没参与训练的连续视频,人工标注每一秒的疲劳/正常状态,跑完整链路算出行为判定的精确率和召回率。疲劳行为往往发生在低光照或驾驶员偏头时,单独跑单帧模型看不出问题,跑连续视频立刻暴露。

6.3 我坚持的一个验证习惯

无论改阈值还是换权重,我都先跑同一段 10 分钟基准视频,对比改动前后的误报数和漏报数,再做决定。同时把每帧的检测结果、track_id、判定状态写进 CSV 日志,回放日志定位误报帧,比反复看视频高效得多。这个习惯帮我避开很多"这边好了那边坏"的尴尬,每次改完参数心里都有底。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询