博主介绍
一线CV算法工程师 / 毕设实战派导师 / 踩坑无数但都填平了的老司机
从事计算机视觉与深度学习落地开发多年,主攻目标检测、视频分析、边缘部署方向。做过安防监控、工业质检、智慧交通等多个方向的真实项目,不是只会跑demo的调参侠,是从数据标注、模型训练、推理加速到GUI开发、打包部署全链路都亲手趟过的人。
一句话:从选题到答辩,从代码到论文,从跑通到跑快,都能给你兜底。
前言
这是一篇本科毕业设计的完整实录。项目目标是做一个低成本、多路 RTSP、抗红色误报、支持硬件自动识别与自动加速选择的火焰烟雾检测系统。
硬件环境:AMD Ryzen 5 5600X + AMD RX 7650 GRE 8GB + 16GB 内存 + Windows 10,没有 NVIDIA 显卡,没有服务器
整个项目从零开始,踩了不少坑,也积累了一些经验,写成博客分享给同样在做毕设的同学。
一、项目功能概览
支持多路 RTSP 视频流同时检测(海康、大华、TP-LINK 等)
没有多摄像头时,用 MediaMTX + FFmpeg 把本地视频推成 RTSP 模拟摄像头
硬件自动识别:OS、CPU、内存、GPU、NPU、已安装加速库
自动选最优加速后端,失败自动降级
PySide6 桌面 GUI,多路画面、报警、日志、性能报告一体化
视频区支持动态网格布局(1x2、2x2、2x4、3x3、4x2、4x4)
双击某路画面全屏,ESC 退出
多帧投票 + 双层去抖,降低误报
支持 ONNX Runtime DirectML 加速
全程面向对象开发,工厂 + 策略 + 观察者 + 单例 + 模板方法
二、技术栈
| 类别 | 技术 |
|---|---|
| 检测模型 | YOLOv8n(ultralytics 8.4.176) |
| 推理加速 | ONNX Runtime DirectML |
| GUI | PySide6 |
| 视频流 | OpenCV + FFmpeg + MediaMTX |
| 跟踪 | 自实现轻量 IOU 跟踪器 |
| 日志 | Loguru |
| 可视化 | matplotlib |
| Python | 3.14.3 |
三、项目结构
text
fire_smoke_detection/ ├── configs/ │ ├── cameras.yaml │ ├── accelerate.yaml │ ├── gui.yaml │ └── detection.yaml ├── models/ │ └── best.pt ├── scripts/ │ ├── start_mediamtx.ps1 │ ├── push_streams.ps1 │ ├── stop_streams.ps1 │ ├── export_onnx.py │ └── benchmark.py ├── src/ │ ├── core/ │ │ ├── config_manager.py │ │ ├── logger_manager.py │ │ ├── hardware_probe.py │ │ └── model_loader.py │ ├── accelerators/ │ │ ├── base_accelerator.py │ │ ├── accelerator_factory.py │ │ ├── onnxruntime_accelerator.py │ │ ├── pytorch_accelerator.py │ │ └── ... │ ├── streams/ │ │ ├── base_stream_reader.py │ │ ├── rtsp_stream_reader.py │ │ ├── file_stream_reader.py │ │ └── frame_queue.py │ ├── detection/ │ │ ├── detector.py │ │ ├── tracker.py │ │ └── voting_manager.py │ ├── alarm/ │ │ ├── alarm_event.py │ │ └── alarm_manager.py │ └── gui/ │ ├── pyside6_gui.py │ ├── camera_widget.py │ ├── video_grid_widget.py │ ├── fullscreen_viewer.py │ └── ... ├── tests/ ├── videos/ ├── alarms/ ├── logs/ ├── requirements.txt └── README.md
四、核心模块设计
4.1 硬件自动识别
启动时自动探测本机硬件,输出logs/accelerator_report.json:
python
@dataclass class HardwareInfo: os: str cpu: str memory_gb: float gpus: list[dict] npus: list[str] installed_libs: dict cuda_version: str | None rocm_version: str | None
探测结果示例(我的机器):
json
{ "os": "Windows 10 (10.0.19045)", "cpu": "AMD Ryzen 5 5600X 6-Core", "memory_gb": 15.93, "gpus": [ {"vendor": "AMD", "name": "Radeon RX 7650 GRE", "vram_gb": 7.98} ], "available_backends": [ "ONNXRuntime-DirectML", "ONNXRuntime-CPU", "PyTorch-CPU" ], "selected_backend": "ONNXRuntime-DirectML" }4.2 加速器工厂 + 策略模式
抽象基类定义接口,具体后端继承实现:
python
class BaseAccelerator(ABC): @abstractmethod def load_model(self, path: str) -> None: ... @abstractmethod def infer(self, frame: np.ndarray) -> list: ... @abstractmethod def warmup(self) -> None: ... @abstractmethod def get_name(self) -> str: ... @abstractmethod def is_available(self) -> bool: ... @abstractmethod def get_device(self) -> str: ...
工厂按优先级自动选择,失败自动降级:
python
class AcceleratorFactory: PRIORITY = [ "TensorRT", "ONNXRuntime-DirectML", "ONNXRuntime-CPU", "PyTorch-CPU", ] def auto_select_backend(self) -> str: for name in self.PRIORITY: acc = self.try_init_backend(name) if acc is not None and acc.is_available(): return name raise RuntimeError("没有可用的推理后端")TensorRT、OpenVINO、CoreML 因硬件不匹配,保留占位符,is_available()恒返回False,架构完整但不影响降级链。
4.3 多帧投票 + 双层去抖
第一层(track 级):连续 5 帧中 3 帧命中才报警。
python
class VotingManager: def vote(self, track: Track) -> bool: history = self._history[track.track_id] history.append(1 if track.cls_name in ("fire", "smoke") else 0) if len(history) > self.window: history.popleft() return sum(history) >= self.min_hits第二层(摄像头 × 类别):10 秒冷却,防止刷屏。
python
class AlarmManager: def _should_suppress(self, event: AlarmEvent) -> bool: key = (event.camera_id, event.cls_name) last = self._last_alarm.get(key) if last is None: return False return (datetime.now() - last).total_seconds() < self.cooldown
4.4 动态网格布局
VideoGridWidget预创建 16 个CameraWidget,切换布局只做 show/hide + 重排,零重建、零丢帧:
python
class VideoGridWidget(QWidget): camera_double_clicked = Signal(int) def set_layout(self, rows: int, cols: int): while self.layout.count(): item = self.layout.takeAt(0) w = item.widget() if w: w.setParent(None) total = rows * cols for i in range(total): r, c = divmod(i, self.cols) self.layout.addWidget(self.cameras[i], r, c) self.cameras[i].show() for cam in self.cameras[total:]: cam.hide()
4.5 双击全屏
CameraWidget双击发信号,主窗口打开FullScreenViewer:
python
class CameraWidget(QLabel): double_clicked = Signal(int) def mouseDoubleClickEvent(self, event): self.double_clicked.emit(self.cam_id)
主窗口在update_frame时同时推送网格和全屏窗口,单一数据源,天然同步。
4.6 模拟摄像头
用 MediaMTX + FFmpeg 把本地视频推成 RTSP:
powershell
ffmpeg -re -stream_loop -1 -i videos/fire.mp4 ` -c:v libx264 -preset veryfast -tune zerolatency ` -pix_fmt yuv420p -f rtsp rtsp://localhost:8554/cam1
没有多摄像头也能测试多路并发。
五、踩坑记录
坑 1:Hugging Face 下载超时
国内直连 Hugging Face 基本不可能。解决方法:
python
import os os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
或者直接浏览器手动下载best.pt放到models/。
坑 2:Git Bash 里激活虚拟环境
\.venv\Scripts\activate在 Git Bash 里报错,因为反斜杠是转义符。正确写法:
bash
source .venv/Scripts/activate
坑 3:Python 3.14 + onnxruntime-directml
Python 3.14 太新,某些包没有预编译 wheel。实测onnxruntime-directml能用,但未来如果某个包报ImportError,直接降到 Python 3.11 或 3.12,零成本。
坑 4:DirectML 加速有限
实测 ORT-DirectML 比 ORT-CPU 快约 2 倍,但远不如 NVIDIA + TensorRT 的 5-10 倍。原因是 DirectML 的算子覆盖不全,NMS 等后处理可能落到 CPU。
这本身就是毕设的一个有效结论,论文里可以写"在 AMD 消费级显卡上,DirectML 对 YOLOv8 加速有限,主要瓶颈在算子覆盖和后处理"。
坑 5:PySide6 + matplotlib 兼容性
PySide6 6.12 + matplotlib 3.11 的backend_qtagg偶尔有兼容问题。解决方案是加降级保护:
python
try: from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg HAS_MPL = True except ImportError: HAS_MPL = False # 降级到 QTableWidget
坑 6:多线程更新 UI 崩溃
PySide6 禁止子线程直接操作 UI 控件。必须用信号槽:
python
class DetectionWorker(QThread): frame_ready = Signal(int, np.ndarray, str, bool) alarm_raised = Signal(object) def run(self): # 子线程推理,只发信号 self.frame_ready.emit(cam_id, frame, text, alarm)
六、性能数据
| 后端 | FPS | 延迟(ms) | CPU% |
|---|---|---|---|
| PyTorch-CPU | 12.3 | 81.3 | 65 |
| ONNXRuntime-CPU | 18.7 | 53.5 | 58 |
| ONNXRuntime-DirectML | 45.2 | 22.1 | 35 |
测试环境:AMD RX 7650 GRE + Ryzen 5 5600X,单路 1080P 子码流,imgsz=640,batch=1。
DirectML 相比 CPU 提升约 2.4 倍,相比 PyTorch-CPU 提升约 3.7 倍。
七、误报优化
针对"红色衣服人行走"的误报问题,采取三层策略:
数据层:加入红色衣服、红车、夕阳等负样本,标注为
other类推理层:提高 fire_conf 阈值到 0.5-0.6,smoke_conf 到 0.4-0.45
后处理层:多帧投票 + 双层去抖 + IOU 跟踪
合成测试视频上的误报率从优化前的 12% 降到 2% 以下。
八、经验总结
8.1关于面向对象
每个类单独文件,职责单一,虽然文件多,但:
论文里能画清晰的类图
答辩时能讲设计模式
后续改功能不用动其他文件
8.2关于硬件加速
AMD 显卡在 Windows 上跑深度学习,DirectML 是唯一现实的选择。ROCm 对消费级显卡支持有限,不要在上面浪费时间。
如果预算允许,二手 NVIDIA 显卡(如 RTX 3060 12GB)在深度学习上体验会好很多,TensorRT 生态成熟。
九、后续改进方向
支持更多类别(加入
other类,进一步降低误报)模型轻量化(剪枝、蒸馏)
部署到边缘设备(Jetson、RK3588)
增加 Web 端远程查看
支持报警推送(邮件、微信、Webhook)
十、结语
整个项目从零开始,踩了不少坑,但最终跑通了完整流程。
如果这篇文章对你有帮助,欢迎点赞收藏。有问题欢迎评论区交流。
项目地址:[GitHub 链接]
参考:
Ultralytics YOLOv8 官方文档
ONNX Runtime DirectML
MediaMTX
PySide6 官方文档
标签:YOLOv8 火焰检测 烟雾检测 PySide6 ONNX Runtime DirectML 本科毕设 计算机视觉