智慧交通系列-车辆闯红灯检测告警抓拍系统
基于深度定制的智能交通行业应用。
在保留原项目目标检测 / 姿态估计 / 图像分割三大 AI 能力的基础上,新增了车辆闯红灯检测、告警、抓拍留证等业务功能。
GUI运行界面
未闯红灯截图

闯红灯截图

目录
- [1. 项目简介](#1. 项目简介)
- [2. 主要功能](#2. 主要功能)
- [3. 技术栈](#3. 技术栈)
- [4. 目录结构](#4. 目录结构)
- [5. 安装与运行](#5. 安装与运行)
- [6. 界面说明](#6. 界面说明)
- [7. 核心架构](#7. 核心架构)
- [8. 闯红灯检测业务详解](#8. 闯红灯检测业务详解)
- [9. 多线程模型](#9. 多线程模型)
- [10. 模型与权重](#10. 模型与权重)
- [11. 关键文件清单](#11. 关键文件清单)
- [12. 常见问题](#12. 常见问题)
1. 项目简介
本项目以智慧交通行业中的"车辆闯红灯自动检测"为业务主线,将 YOLO 系列目标检测与
多目标跟踪(ByteTrack / DeepSort)作为底层能力,配套 Qt 图形界面,可对接:
- 本地图片 / 视频文件
- USB 摄像头 / 内置摄像头(设备编号)
- RTSP 网络视频流
系统运行时一边对画面中的车辆与交通信号灯做实时检测 / 跟踪,一边在用户预先框定的
"闯红灯区域" 内做业务判定,命中即触发告警(屏幕角标 + 事件日志 + 抓拍图片留证)。
适用场景:智能交通路口卡口、校园 / 单位出入口违规检测演示、AI 教学示例项目。
2. 主要功能
2.1 AI 基础能力
| 任务 | 模型后端 | 说明 |
|---|---|---|
| 目标检测 | YOLOv8 / YOLO11 / YOLO26(ultralytics .pt) |
默认任务 |
| 姿态估计 | YOLO11-Pose .pt |
关键点 + 骨架可视化 |
| 图像分割 | YOLO11-Seg .pt |
像素级 mask 可视化 |
2.2 目标跟踪
- ByteTrack:纯几何、速度快,参数已按 30FPS 场景调好。
- DeepSort:带外观特征,遮挡恢复更稳,但推理更慢。
- 关闭跟踪:不做时序关联,每帧独立("0-person" 这种伪 ID 会被强制置为 -1)。
2.3 输入源
- 本地图片:
jpg / png / bmp / tif / gif / webp / heif等 - 本地视频:
mp4 / avi / mov / flv / mkv / webm等 - 摄像头:本地 USB 摄像头(输入设备编号)
- 网络流:RTSP / HTTP 视频流地址
2.4 业务能力(项目定制部分)
- 闯红灯区域绘制:在画面上交互式画出任意多边形(最少 3 个、最多 8 个顶点),作为闯红灯判定区域。
- 闯红灯自动判定 :
- 帧内出现
red_light类目标; - 帧内出现
car类目标,且其边框中心点位于用户绘制的多边形内。 - 满足上述两条件 → 触发告警。
- 帧内出现
- 告警留证 :
- 实时画面:闯红灯车辆边框变红 + 标签加
[违章]前缀 + 左上角"车辆闯红灯(累计 N 辆)"半透明告警条; - 事件面板:实时写入事件日志(时间、类型、区域、置信度、ID、抓拍缩略图),支持点击缩略图在系统看图工具中打开;
- 本地抓拍:原图 + 红色粗框 + 标签 + 时间戳,整帧保存到
snapshots/red_light/。
- 实时画面:闯红灯车辆边框变红 + 标签加
- 抓拍去重 :开启跟踪时按
track_id去重,关闭跟踪时(id=-1)按 IoU 与最近记录去重,避免重复抓拍同一辆车。
2.5 系统能力
- GPU / CPU 自动切换;GPU 模式下默认启用 FP16 与 cuDNN benchmark。
- 多线程流水线(采集 / 推理 / 可视化分离),互不阻塞。
- 暂停 / 继续推理;视频可调帧间隔。
- 事件 CSV 导出。
3. 技术栈
| 类别 | 组件 |
|---|---|
| GUI | PyQt5(5.15.x) |
| 深度学习 | PyTorch 2.0.1 + ultralytics 8.x |
| 视觉 | OpenCV(opencv-python-headless) |
| 跟踪 | ByteTrack、DeepSort(含 CNN 特征提取 ckpt.t7) |
| 数值 | NumPy、SciPy |
| 字体 | Pillow(用于中文渲染) |
| Python | 3.9 / 3.10 |
注意:项目默认走 ultralytics
.pt后端 ,相比 ONNX 部署更简单且功能一致;如需 ONNX,仓库内仍保留yolo_detector_onnx.py等文件可切换。
4. 目录结构
text
f:\project_code_all\ITS-Red-Light Detection\
├── main.py # 程序入口(MainWindow、信号连线、图标)
├── environment.yml # Conda 环境定义
├── requirements.txt # pip 依赖列表
├── 说明文档.md # 本文档
│
├── data/ # 演示用素材(图片 / 视频 / UI 截图)
├── resources/ # 自定义图标(app.ico/app.png/app.jpg)
├── snapshots/ # 抓拍图保存目录
│ └── red_light/ # 闯红灯抓拍图(按时间戳命名)
│
├── weights/ # 模型权重
│ ├── detection/ # 目标检测 .pt / .onnx
│ ├── pose/ # 姿态估计 .pt / .onnx
│ ├── segmentation/ # 分割 .pt / .onnx
│ ├── classes.txt # 当前使用的类别文件
│ └── classes-ori.txt # 原始类别(备份)
│
├── src/
│ ├── data_type/ # 跨线程数据缓冲(LatestFrame、FrameBuffer)
│ │
│ ├── models/ # 模型层
│ │ ├── base/ # 基类 yolo_base.py
│ │ ├── detection/ # 目标检测(pt / onnx)
│ │ ├── pose/ # 姿态估计
│ │ ├── segmentation/ # 图像分割
│ │ └── tracking/ # 跟踪器
│ │ ├── byte_track/ # ByteTrack(含 kalman / matching / gmc)
│ │ └── deep_sort/ # DeepSort(含 sort + deep CNN)
│ │
│ ├── qt/ # Qt 业务层
│ │ ├── stream/ # 摄像头/AI 流
│ │ │ ├── video_capture.py # 摄像头/视频流抓取线程
│ │ │ ├── ai_worker.py # AI 推理 + 跟踪 + 闯红灯业务
│ │ │ ├── visualize.py # 结果绘制线程
│ │ │ └── redlight_violation.py # ★ 闯红灯业务核心
│ │ └── video/ # 文件处理
│ │ └── video_worker.py # 本地文件处理线程
│ │
│ ├── ui/ # UI 层
│ │ ├── main_window.py # 主窗口(来自 .ui)
│ │ ├── main_window.ui # Qt Designer 工程
│ │ ├── event_panel.py # ★ 智能分析侧边栏(事件/统计/目标)
│ │ ├── polygon_overlay.py # ★ 可交互多边形覆盖层
│ │ ├── apprcc.qrc # Qt 资源文件
│ │ ├── apprcc_rc.py # 资源 Python 模块
│ │ └── icons/ # 图标
│ │
│ └── utils/ # 工具
│ ├── general.py # 类别加载、图像 ID 标记等
│ ├── boxes.py # box 工具
│ └── visualize.py # ★ 绘制(框、关键点、告警 banner)
│
└── tests/ # 单元测试占位
5. 安装与运行
5.1 创建环境(任选其一)
方式 A:Conda(推荐,CPU/GPU 都好用)
shell
conda env create -f environment.yml
conda activate yolo_gui
方式 B:pip
shell
pip install -r requirements.txt
requirements.txt 锁定的是 CUDA 11.7 + cuDNN 8.5 组合;若使用其他 CUDA 版本,请按需调整
torch / torchvision / onnxruntime-gpu的版本。
5.2 准备模型权重
将 weights/detection/yolo11n.pt 等文件放到 weights/ 目录下(项目内已附带部分权重)。
如需 ONNX 模型,可使用 yolo export model=yolo11n.pt format=onnx 自行导出。
闯红灯检测要求模型类别中至少包含
car和red_light。仓库默认classes.txt来自 COCO(无
red_light),因此weights/classes-bk.txt留作备份;
weights/classes-ori.txt则是覆盖了red_light类的版本。模型加载时若classes.txt行数与
.pt不一致会自动回退到.pt内置类别,请根据实际模型选好对应的classes.txt。
5.3 运行
shell
python main.py
启动后默认界面布局:
┌───────────────────────────────────────────────────────────────────────┐
│ 智慧交通系列-车辆闯红灯检测告警抓拍系统 │
├──────────────┬────────────────────────────────────┬───────────────────┤
│ 任务选择 │ │ 智能分析 │
│ 模型选择 │ │ ┌─────────────┐ │
│ 跟踪器选择 │ 实时画面 / 视频画面 │ │ 事件日志 │ │
│ 置信度/IoU │ (含多边形覆盖层) │ │实时统计 │ │
│ 帧间隔 │ │ │检测目标 │ │
│ 输入源选择 │ │ └─────────────┘ │
│ 区域绘制 │ │ │
│ 推理控制 │ │ │
├──────────────┴────────────────────────────────────┴───────────────────┤
│ 检测结果表(id/class/conf/bbox) │
└───────────────────────────────────────────────────────────────────────┘
6. 界面说明
6.1 左侧栏
-
任务(单选):
- 目标检测(默认)
- 姿态估计
- 图像分割
当模型为 YOLOv13 时,仅支持目标检测,其它两个单选会自动禁用。
-
模型(下拉):根据任务显示可选模型(yolo11n / yolo11s / ...)。
-
跟踪器(下拉):关闭 / ByteTrack / DeepSort。
-
置信度 / IoU:双数值框 + 滑块双向绑定,实时同步到推理线程。
-
帧间隔(仅文件模式生效):每 N+1 帧推理一次,用于跳帧加速。
-
输入源(高 40px 的两个按钮):
- 选择文件(图片 / 视频)
- 打开摄像头(弹窗输入编号或 RTSP 地址)
-
区域绘制:
- 开始绘制区域 / 结束绘制
- 重置 / 清除
- 显示 / 隐藏
6.2 中间画面
- 上方有渐变蓝色标题栏(
QToolBar风格的浅蓝渐变 + 深蓝粗体字 + 圆角)。 - 实时画面(摄像头 / 视频)按 QLabel 居中等比缩放绘制。
- 闯红灯车辆边框为红色加粗 +
[违章]前缀;左上角红色半透明告警条显示累计违法车辆数。 - 顶部覆盖的
PolygonOverlay在"开始绘制区域"时进入编辑态,可左键加点 / 拖拽顶点 / 右键删除(最少 3 个顶点)。
6.3 右侧"智能分析"侧边栏(EventPanel)
- 事件日志 :表格 5 列(时间 / 类型 / 区域 / 详情 / 抓拍缩略图),闯红灯类目自动用红色背景标出。
缩略图支持点击 → 系统看图工具打开原图。 - 实时统计:事件总数 / 当前检测数 / 当前跟踪数 / 实时 FPS。
- 检测目标 :当前帧所有目标的列表(
#id class conf)。 - 底部按钮:清空事件 、导出 CSV(UTF-8 BOM,Excel 直接可读)。
7. 核心架构
整体架构采用 Qt 的"信号-槽 + QThread"模式,把数据采集、AI 推理、结果渲染分离到不同
线程,避免单帧推理阻塞 UI。
┌────────────────────┐
│ MainWindow (UI) │
└─────────┬──────────┘
│ signal/slot
┌────────────────────┼────────────────────┐
▼ ▼ ▼
CameraCaptureThread AiWorkerThread VideoVisualizationThread
(抓帧) (推理+跟踪) (绘制+展示)
│ ▲
│ violation_event │ violation_hint
▼ │
RedLightViolationDetector ─────┘
│
▼
snapshots/red_light/ ← 抓拍图落盘
(文件模式:用 FileProcessThread 合并 抓帧+推理+绘制,单线程内流水线)
- 摄像头模式:
CameraCaptureThread → AiWorkerThread → VideoVisualizationThread三个 QThread 串行。 - 文件模式:单
FileProcessThread内部循环读帧 + 推理 + 绘制,开销更小。 - 闯红灯检测器
RedLightViolationDetector只有一个实例,由main.py创建后分别注入两个 worker。
8. 闯红灯检测业务详解
8.1 触发条件(每帧)
python
# src/qt/stream/redlight_violation.py
if (has_red_light # 帧中存在 red_light 类目标
and 帧中存在 car
and car.bbox 中心点在用户多边形内): # 重心点 cv2.pointPolygonTest >= 0
trigger_violation()
8.2 多边形
- 由
PolygonOverlay维护,存的是归一化坐标 (0~1),因此与画面分辨率无关; - 窗口 resize / 多边形变化时,
MainWindow._on_polygon_changed会把新坐标同步给检测器; - 顶点最少 3 个,最多 8 个;少于 3 个视为无效,不触发检测。
8.3 去重
| 跟踪器 | 去重策略 |
|---|---|
| 关闭跟踪(id=-1) | 用当前 bbox 与最近 max_recent=50 条记录做 IoU 比对,IoU > 0.5 视为同一辆 |
| ByteTrack / DeepSort | 用 track_id 在 violated_track_ids 集合里查重 |
8.4 抓拍图
violation_id<trackid>_<时间戳>.jpg 命名,存到 snapshots/red_light/:
- 整帧(包含原 YOLO 框、告警 banner 之外 的画面,因为抓拍发生在 draw 之前)
- 红色粗框覆盖到该车辆
- 框顶部
RED LIGHT VIOLATION ID=3 (98.5%)标签 - 左上角时间戳
注:抓拍使用的是
frame(原始 BGR 帧),不包含运行时叠在画面上的 banner / 多边形。
8.5 事件推送
ai_worker.send_violation_event(list)→MainWindow._on_violation_events→EventPanel.add_violation_eventai_worker.send_violation_hint(dict)→VideoVisualizationThread.get_violation_hint→draw_results时读出violation_track_ids和alert_message
8.6 类名约定
| 业务类 | 对应 COCO 类名 | 说明 |
|---|---|---|
car |
car(COCO id=2) |
触发主体 |
red_light |
取决于模型;自定义训练时常作为新增类 | 触发前提 |
若你的模型
red_light类名不同,请修改MainWindow.__init__中的:
pythonself.violation_detector = RedLightViolationDetector( snapshot_dir=_snap_dir, car_class="car", red_light_class="red_light", # ← 改成你的模型中对应类名 )
9. 多线程模型
9.1 摄像头模式
| 线程 | 职责 | 关键信号 |
|---|---|---|
CameraCaptureThread |
cv2.VideoCapture 拉流,emit [frame_id, frame] |
send_frame |
AiWorkerThread |
YOLO 推理 + 跟踪 + 闯红灯业务 | send_ai_output、send_violation_event、send_violation_hint |
VideoVisualizationThread |
等比缩放、绘制、emit QImage | send_displayable_frame、send_ai_output |
三线程通过 Qt 的 pyqtSignal 解耦,UI 线程只在最后一步 update_display_frame 设置 QLabel,
所以即使推理较慢也不会卡 UI。
9.2 文件模式
FileProcessThread 单线程内做:
- 读帧(OpenCV)
- 推理 + 跟踪
- 闯红灯业务
- 绘制(
draw_results) convert_cv_qt(预分配画布,避免每帧 4.6MB 内存分配)emit给 UI
- 暂停 / 继续使用
QMutex + QWaitCondition,不丢帧不撕裂。 - 图片任务一次性推理完会重新自动暂停,等待下一次操作。
- 视频任务结束保留最后一帧,不再清空画面为黑屏。
9.3 跨线程安全
- 多边形更新走
threading.Lock; - 检测器状态对象不暴露原始 list 给外部修改,外部只通过
update_polygon / clear / detect交互; - 跟踪 ID 等值在 emit 前会被
copy.deepcopy,避免主线程渲染时被工作线程改写。
10. 模型与权重
10.1 类别文件
weights/classes.txt:当前默认使用(行数必须与.pt模型类别数一致,否则会自动回退.pt内置)。weights/classes-ori.txt:原始(包含red_light类)备份。weights/classes-bk.txt:更早版本。
模型加载逻辑见
src/models/detection/yolo_detector_pt.py的_resolve_class_names。若训练了包含
red_light类的专用模型 ,请将对应的类别文件改名为classes.txt(或调整代码逻辑)。
10.2 已附带的权重
| 路径 | 说明 |
|---|---|
weights/detection/yolo11n.pt |
YOLO11 nano,COCO 80 类 |
weights/detection/yolo11s.pt |
YOLO11 small |
weights/pose/yolo11n-pose.onnx |
YOLO11 姿态(ONNX) |
weights/segmentation/yolo11s-seg.onnx |
YOLO11 分割(ONNX) |
业务场景下,建议使用专门训练了
red_light类的交通场景模型,否则系统不会自动识别红灯。
10.3 性能开关
- GPU 自动 FP16 :
yolo11n等支持 fp16 的模型在 CUDA 环境下自动启用half=True。 - Conv+BN 融合 :模型加载完成后自动调用
model.fuse()。 - cuDNN benchmark:连续等尺寸输入下会自动选最优卷积算法。
- 画布复用 :
FileProcessThread.convert_cv_qt复用预分配 NumPy 数组 + 零拷贝 QImage 构造。
11. 关键文件清单
| 文件 | 作用 |
|---|---|
| main.py | 程序入口;UI 装配、信号绑定、状态机 |
| src/qt/stream/redlight_violation.py | 闯红灯业务核心:判定 + 去重 + 抓拍 |
| src/qt/stream/ai_worker.py | 摄像头模式下的 AI worker(推理+跟踪+闯红灯) |
| src/qt/stream/video_capture.py | 摄像头/视频流抓帧线程 |
| src/qt/stream/visualize.py | 摄像头模式下的绘制线程 |
| src/qt/video/video_worker.py | 文件模式下的单线程流水线 |
| src/ui/main_window.py | 主窗口 |
| src/ui/event_panel.py | 智能分析侧边栏(事件/统计/目标) |
| src/ui/polygon_overlay.py | 可交互多边形覆盖层(闯红灯区域绘制) |
| src/utils/visualize.py | 通用绘制(框、关键点、违章改色、告警 banner) |
| src/models/detection/yolo_detector_pt.py | 目标检测 ultralytics .pt 后端 |
| requirements.txt | pip 依赖 |
| environment.yml | Conda 环境 |
12. 常见问题
Q1:启动后画面没有目标识别?
确认 classes.txt 行数与所选 .pt 模型类别数一致。yolo_detector_pt.py 加载时若不一致会自动回退 .pt 内置类别并打印警告。
Q2:闯红灯检测不触发?
- 没有画多边形 → 至少 3 个顶点。
- 模型没有
red_light类 → 换用包含红绿灯的专用模型。 - 区域设置好但没出现红灯 → 检查模型是否能识别
red_light类。 - 控制台打印
[RED_LIGHT] 闯红灯检测器已就绪, 抓拍目录: ...表示检测器已注入。
Q3:摄像头 / RTSP 拉不到?
Windows 下某些 USB 摄像头独占,需先关闭其它使用摄像头的程序;
RTSP 地址需包含用户名密码时建议用 rtsp://user:pass@host:port/...。
Q4:抓拍图很大?
默认保存的是 cv2.imwrite 默认质量 (~95)。如需压缩可改 redlight_violation.py 的 cv.imwrite 第三参数。
Q5:多边形顶点超出画面?
PolygonOverlay 已做边界裁剪,鼠标位置会被 clamp 到 [0, w/h]。
Q6:能否拓展其它业务(如违停、压线、车道偏离)?
可以。仿照 RedLightViolationDetector 新建一个业务类,
在 ai_worker.run / video_worker.run 中按相同模式调用并 emit 新的事件信号,
在 EventPanel 增加对应的事件类型即可。EventPanel.add_event 已经是通用接口。
Q7:抓拍目录想换位置?
修改 main.py 中:
python
_snap_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"snapshots", "red_light")
附录 A:信号一览
| 信号 | 发射线程 | 接收端 | 用途 |
|---|---|---|---|
send_frame |
CameraCapture | display/ai | 单帧 BGR 数据 |
send_ai_output |
ai_worker / display / file_worker | MainWindow | 当前帧检测结果(list of dict) |
send_violation_event |
ai_worker / file_worker | MainWindow → EventPanel | 新发生的闯红灯事件(含抓拍路径) |
send_violation_hint |
ai_worker | VideoVisualizationThread | 每帧推送违章 ID + 累计告警文案 |
send_displayable_frame |
VideoVisualizationThread | MainWindow | 已绘制的 QImage |
send_display_frame |
FileProcessThread | MainWindow | 同上(文件模式) |
send_play_progress |
FileProcessThread | MainWindow | 视频播放进度(千分制) |
send_thread_start_stop_flag |
display_thread | MainWindow | 状态机切换 |
send_thread_start_finish_flag |
file_worker | MainWindow | 状态机切换 |
polygon_changed |
PolygonOverlay | MainWindow | 多边形顶点变化(归一化坐标) |
附录 B:检测结果数据结构
python
{
"id": int, # 跟踪 ID(关闭跟踪时为 -1)
"class": str, # 类别名("car" / "red_light" / "person" ...)
"confidence": float, # 0~1
"bbox": [x1, y1, x2, y2], # 像素坐标
"keypoints": np.ndarray, # 姿态任务才有
"segmentation": np.ndarray, # 分割任务才有
}
业务层(闯红灯检测、事件面板、多边形区域绘制)为本项目新增实现。