1. 项目概述与背景
在公共场所安全管理中,快速准确地识别危险物品一直是个重要课题。传统监控系统依赖人工盯防,不仅效率低下,还容易因疲劳导致漏检。我们基于YOLOv8目标检测算法开发了一套智能识别系统,能够自动检测六类常见物品,特别针对pistol(手枪)和knife(刀具)等危险物品进行高亮警示。
这个系统的核心价值在于:
- 实时性:处理速度达到45FPS(在RTX 3060显卡上),满足实时监控需求
- 精准度:通过定制化训练,对危险物品的识别准确率达到92.3%(测试集数据)
- 易用性:提供直观的PyQt6图形界面,支持一键启动检测和报警功能
2. 数据集准备与处理
2.1 数据集构成分析
我们的数据集包含六类物品,具体分布如下:
| 类别(西班牙语) | 英语对应 | 样本数量 | 特点描述 |
|---|---|---|---|
| tarjeta | card | 1,200 | 扁平矩形,反光性强 |
| smartphone | smartphone | 1,500 | 尺寸变化大,有屏幕反光 |
| pistol | pistol | 800 | 金属反光,握把纹理明显 |
| monedero | wallet | 1,000 | 与卡片相似,但厚度更大 |
| billete | bill | 900 | 有特殊纹理图案 |
| knife | knife | 700 | 刀刃反光,握把多样 |
注意:pistol和knife两类样本相对较少,这是实际场景中正样本稀缺的典型情况。我们需要通过数据增强来解决这个问题。
2.2 数据标注检查与修正
使用LabelImg工具检查标注质量时,发现几个常见问题:
- 部分knife样本只标注了刀刃部分,忽略了握把
- 堆叠的卡片(tarjeta)有时被标注为一个整体
- 折叠状态的钱包(monedero)标注框不够精确
修正建议代码:
python复制import cv2
import os
def visualize_annotations(img_path, label_path):
img = cv2.imread(img_path)
dh, dw, _ = img.shape
with open(label_path, 'r') as f:
for line in f.readlines():
cls_id, x, y, w, h = map(float, line.strip().split())
# 转换YOLO格式为像素坐标
x = int(x * dw)
y = int(y * dh)
w = int(w * dw)
h = int(h * dh)
x1, y1 = x - w//2, y - h//2
x2, y2 = x + w//2, y + h//2
# 绘制标注框
color = (0, 255, 0) if cls_id in [2,5] else (255, 0, 0)
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
cv2.imshow('Annotation Check', img)
cv2.waitKey(0)
# 示例调用
visualize_annotations('dataset/train/images/001.jpg',
'dataset/train/labels/001.txt')
2.3 数据增强策略
针对本项目的特殊需求,我们采用以下增强组合:
- MixUp (mixup=0.7):提高模型对重叠物体的识别能力
- 随机旋转 (±30°):增强对倾斜刀具的识别
- HSV色彩抖动:模拟不同光照条件下的物品外观
- 小目标增强:专门提升对小尺寸物品(如卡片)的检测灵敏度
3. 模型训练与优化
3.1 YOLOv8模型选型
我们对比了不同规模的YOLOv8模型:
| 模型类型 | 参数量 | mAP@0.5 | FPS (RTX 3060) | 适用场景 |
|---|---|---|---|---|
| yolov8n | 3.2M | 0.78 | 120 | 边缘设备 |
| yolov8s | 11.4M | 0.84 | 85 | 平衡型 |
| yolov8m | 26.3M | 0.87 | 45 | 本系统选择 |
| yolov8l | 44.1M | 0.88 | 32 | 高精度需求 |
选择yolov8m的原因:
- 公共场所监控通常使用中端GPU服务器
- 需要平衡对小型物品(卡片)和危险物品的识别精度
- 实时性要求不低于30FPS
3.2 关键训练参数配置
python复制from ultralytics import YOLO
model = YOLO('yolov8m.yaml') # 中等规模模型
results = model.train(
data='custom_dataset.yaml',
epochs=150, # 比默认增加50个epoch
imgsz=640,
batch=16,
optimizer='AdamW', # 使用改进版Adam
lr0=0.0012, # 稍大的学习率
lrf=0.01, # 最终学习率
momentum=0.9,
weight_decay=0.0005,
warmup_epochs=3, # 学习率预热
box=7.5, # 调整box loss权重
cls=0.5, # 降低分类loss权重
dfl=1.5, # 增加分布焦点loss
mixup=0.7, # 强数据增强
copy_paste=0.1 # 小比例复制粘贴增强
)
3.3 训练过程监控
使用TensorBoard观察训练指标时,要特别关注:
- val/obj_loss:验证集的目标检测loss,应平稳下降
- val/cls_loss:验证集的分类loss,避免过拟合
- metrics/mAP@0.5:0.95:综合评估指标
常见问题处理:
- 如果val_loss上升而train_loss下降,可能是过拟合,应减小模型规模或增加正则化
- 如果mAP@0.5明显高于mAP@0.5:0.95,说明模型对高IOU阈值样本识别不佳,需调整anchor或增加困难样本
4. 系统实现与部署
4.1 PyQt6界面设计
图形界面主要包含以下功能区域:
- 视频显示区:实时显示检测结果
- 控制面板:开始/停止检测按钮
- 报警状态指示:危险物品检测时的视觉提示
- 日志区域:记录检测事件
python复制from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout,
QPushButton, QLabel, QWidget)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from PyQt6.QtGui import QImage, QPixmap
import cv2
import numpy as np
class VideoThread(QThread):
change_pixmap = pyqtSignal(QImage)
danger_detected = pyqtSignal(bool)
def __init__(self):
super().__init__()
self._run_flag = True
self.model = YOLO('best.pt')
def run(self):
cap = cv2.VideoCapture(0) # 可替换为RTSP流
while self._run_flag:
ret, frame = cap.read()
if ret:
results = self.model.predict(frame, conf=0.65)
annotated_frame = results[0].plot()
# 检查是否有危险物品
danger = any(box.cls in [2,5] for box in results[0].boxes)
self.danger_detected.emit(danger)
# 转换图像格式
rgb_image = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
qt_image = QImage(rgb_image.data, w, h, bytes_per_line,
QImage.Format.Format_RGB888)
self.change_pixmap.emit(qt_image)
def stop(self):
self._run_flag = False
self.wait()
4.2 多线程处理技巧
为避免界面卡顿,我们采用三级线程结构:
- 主线程:处理UI交互
- 检测线程:运行YOLOv8推理
- 视频采集线程:独立处理视频源
重要提示:OpenCV的VideoCapture在某些USB摄像头上会阻塞线程,建议单独使用一个线程处理视频采集,通过队列将帧传递给检测线程。
4.3 报警功能实现
报警系统包含以下组件:
- 视觉报警:红色边框闪烁
- 声音报警:可配置的警示音
- 日志记录:保存检测事件到文件
python复制from PyQt6.QtMultimedia import QSoundEffect
from PyQt6.QtCore import QUrl, QTimer
class AlarmSystem:
def __init__(self):
self.sound_effect = QSoundEffect()
self.sound_effect.setSource(QUrl.fromLocalFile("alarm.wav"))
self.alarm_active = False
self.timer = QTimer()
self.timer.timeout.connect(self._alarm_check)
self.timer.start(100) # 每100ms检查一次
def _alarm_check(self):
if self.alarm_active:
if self.sound_effect.isPlaying():
self.sound_effect.stop()
self.sound_effect.play()
def trigger(self, state):
self.alarm_active = state
5. 性能优化与实际问题解决
5.1 模型推理加速
实测优化方法对比:
| 优化方法 | 推理时间(ms) | 内存占用(MB) | 适用场景 |
|---|---|---|---|
| 原始模型 | 45.2 | 1,200 | 开发环境 |
| TensorRT | 22.1 | 980 | 生产部署 |
| ONNX Runtime | 28.7 | 1,050 | 跨平台 |
| 半精度(FP16) | 19.8 | 650 | NVIDIA GPU |
推荐部署方案:
python复制# 转换为TensorRT引擎
model.export(format='engine', half=True, simplify=True)
# 加载优化后的模型
trt_model = YOLO('best.engine', task='detect')
5.2 典型问题排查
-
误报率高:
- 解决方案:调整conf参数(0.6-0.7为宜),增加危险物品的类别权重
python复制results = model.predict(frame, conf=0.65, classes=[2,5]) -
漏检小物体:
- 解决方案:修改model.yaml中的detect层参数,增加小目标检测头
yaml复制# yolov8m.yaml head: - [-1, 1, nn.Conv2d, [256, 1, 1]] # 新增小目标检测层 - [-1, 1, nn.Upsample, [None, 2, 'nearest']] -
线程冲突:
- 解决方案:使用队列实现线程间通信,避免直接共享资源
python复制from queue import Queue frame_queue = Queue(maxsize=1) # 单帧缓冲
5.3 部署注意事项
-
打包依赖:
bash复制pyinstaller --add-data "best.pt;." --add-data "alarm.wav;." \ --hidden-import "ultralytics.models.yolo" \ --collect-all "PyQt6" \ main.py -
硬件要求:
- 最低配置:Intel i5 + 8GB内存 + NVIDIA GTX 1050
- 推荐配置:Intel i7 + 16GB内存 + NVIDIA RTX 3060
-
摄像头兼容性:
- 测试通过的型号:Logitech C920, Hikvision DS-2CD2系列
- 网络摄像头需支持MJPG编码格式
6. 实际应用效果与改进方向
在测试环境中,系统表现出以下特点:
- 白天光照充足时,pistol识别准确率达94.2%
- 夜间红外模式下,knife识别准确率降至83.5%
- 对折叠状态刀具的识别仍需改进
下一步优化计划:
- 增加红外图像训练数据
- 开发基于注意力机制的改进模型
- 集成人脸识别功能,实现"持械人员"特别预警
- 支持多摄像头协同检测
对于想要扩展功能的开发者,建议从以下几个方面入手:
- 添加RTSP流媒体支持,兼容现有监控系统
- 实现检测结果的上云存储和分析
- 开发移动端监控应用,支持远程报警推送
这个项目最让我惊喜的是YOLOv8对小目标的检测能力——经过适当调优后,即使是信用卡大小的物品也能稳定识别。不过在实际部署中发现,金属反光物品在不同光照条件下的表现差异很大,这提示我们需要更加注重数据集的多样性。
