智慧校园-校园暴力目标检测数据集

包含[ ‘fight02’, ‘fighter01’, ‘fighter02’, ‘fighter03’, ‘fighter04’, ‘ousider02’, ‘ousider03’, ‘outsider01’, ‘outsider02’, ‘outsider03’, ‘outsider04’, ‘outsider05’, ‘threatener01’, ‘threatener02’, ‘threatener03’, ‘threatener04’, ‘victim01’, ‘victim02’, ‘victim03’, ‘victim04’]几个种类。

数据集包含图片共4339张。
可提供各种YOLO格式

1

构建一个基于 YOLOv8 和 PyQt5 的校园暴力检测系统,核心在于利用 YOLOv8 强大的多目标检测能力识别出 fighter (打斗者), victim (受害者), threatener (威胁者) 等角色,并通过 PyQt5 构建一个可视化的监控界面,实现实时报警和视频回放。
以下是完整的实施步骤,包含数据预处理、模型训练、PyQt5 界面开发及核心逻辑代码。
🛠️ 1. 数据预处理与配置
由于你的数据集中存在拼写错误 (ousider vs outsider) 且类别过于细碎 (01-04),为了提升模型的泛化能力和训练稳定性,建议进行类别合并。
A. 类别合并策略 (推荐)
将 20 个细粒度类别合并为 4 个核心行为类别:
fighter: 合并fighter01~fighter04,fight02victim: 合并victim01~victim04threatener: 合并threatener01~threatener04outsider: 合并outsider01~outsider05,ousider02,ousider03
如果不合并:模型需要学习区分“第一个打斗者”和“第二个打斗者”,这在逻辑上是不必要的,且会导致样本分散,降低精度。强烈建议合并。
B. 数据集目录结构
violence_dataset/
├── images/
│ ├── train/
│ ├── val/
│ └── test/
├── labels/ # 确保这里的 txt 文件中的类别 ID 已根据合并策略更新 (0-3)
│ ├── train/
│ ├── val/
│ └── test/
└── violence_data.yaml
C. 配置文件 violence_data.yaml
path: ./violence_dataset
train: images/train
val: images/val
test: images/test
nc: 4 # 合并后的类别数
names:
0: fighter # 打斗者 (红色预警)
1: victim # 受害者 (橙色预警)
2: threatener # 威胁者 (黄色预警)
3: outsider # 旁观者 (蓝色标记,通常不触发强报警)
🚀 2. YOLOv8 模型训练
使用 Python 脚本进行训练,针对暴力行为特点调整超参数。
from ultralytics import YOLO
import torch
def train_violence_model():
print("🥊 开始训练校园暴力检测模型...")
# 加载预训练权重 (建议使用 m 或 l 版本以捕捉细微动作)
model = YOLO('yolov8m.pt')
results = model.train(
data='violence_data.yaml',
epochs=100,
imgsz=640,
batch=16,
device='0' if torch.cuda.is_available() else 'cpu',
workers=4,
# 针对暴力行为的增强
augment=True,
hsv_h=0.015,
hsv_s=0.7,
hsv_v=0.4,
degrees=0.0, # 暴力场景通常水平,禁止旋转
translate=0.1,
scale=0.5,
shear=0.0,
perspective=0.0,
flipud=0.0, # 禁止上下翻转
fliplr=0.5,
mosaic=1.0, # Mosaic 对拥挤场景(多人打架)非常有效
mixup=0.1,
copy_paste=0.2, # 增加打斗者的出现频率
# 优化器设置
optimizer='SGD',
lr0=0.01,
lrf=0.01,
patience=30,
project='runs/violence_detect',
name='violence_yolov8m_v1',
exist_ok=False,
verbose=True
)
print(f"✅ 训练完成!模型保存于:{results.save_dir}/weights/best.pt")
# 验证模型
model.val(split='test')
if __name__ == '__main__':
train_violence_model()
🖥️ 3. 构建 PyQt5 暴力检测系统
这是一个完整的单文件 PyQt5 应用程序,集成了视频流读取、YOLOv8 推理、实时绘图、报警逻辑和日志记录。
前置安装
pip install ultralytics opencv-python PyQt5 numpy
核心代码 violence_monitor.py
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton,
QVBoxLayout, QWidget, QMessageBox, QTextEdit, QGroupBox)
from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap, QFont, QColor
from ultralytics import YOLO
from datetime import datetime
# --- 配置区域 ---
MODEL_PATH = 'runs/violence_detect/violence_yolov8m_v1/weights/best.pt'
CONF_THRESHOLD = 0.4
IOU_THRESHOLD = 0.5
ALERT_CLASSES = ['fighter', 'victim', 'threatener'] # 触发报警的类别
COLORS = {
'fighter': (0, 0, 255), # Red (BGR)
'victim': (0, 165, 255), # Orange
'threatener': (0, 255, 255), # Yellow
'outsider': (255, 255, 0) # Cyan
}
class DetectionThread(QThread):
"""后台检测线程,避免阻塞 UI"""
frame_signal = pyqtSignal(QImage, list, float) # 图像, 检测结果, FPS
def __init__(self, video_source, model_path):
super().__init__()
self.video_source = video_source
self.model = YOLO(model_path)
self.running = True
def run(self):
cap = cv2.VideoCapture(self.video_source)
if not cap.isOpened():
print("无法打开摄像头/视频")
return
while self.running:
ret, frame = cap.read()
if not ret:
break
start_time = cv2.getTickCount()
# YOLO 推理
results = self.model(frame, conf=CONF_THRESHOLD, iou=IOU_THRESHOLD, verbose=False)
result = results[0]
detections = []
boxes = result.boxes
if boxes is not None:
for i in range(len(boxes)):
cls_id = int(boxes.cls[i])
conf = float(boxes.conf[i])
class_name = self.model.names[cls_id]
xyxy = boxes.xyxy[i].cpu().numpy()
detections.append({
'class': class_name,
'conf': conf,
'box': xyxy
})
# 计算 FPS
end_time = cv2.getTickCount()
fps = cv2.getTickFrequency() / (end_time - start_time)
# 绘制结果到帧上 (为了在 UI 显示)
display_frame = self.draw_detections(frame, detections)
# 转换格式 BGR -> RGB -> QImage
h, w, ch = display_frame.shape
bytes_per_line = ch * w
qt_image = QImage(display_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
self.frame_signal.emit(qt_image, detections, fps)
# 控制帧率 (可选,防止过快)
# self.msleep(30)
cap.release()
def draw_detections(self, frame, detections):
for det in detections:
x1, y1, x2, y2 = map(int, det['box'])
label = f"{det['class']} {det['conf']:.2f}"
color = COLORS.get(det['class'], (0, 255, 0))
# 画框
cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
# 画背景文字
(w, h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
cv2.rectangle(frame, (x1, y1 - 20), (x1 + w, y1), color, -1)
cv2.putText(frame, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)
return frame
def stop(self):
self.running = False
self.wait()
class ViolenceMonitorSystem(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("🏫 校园暴力智能监控系统 (YOLOv8 + PyQt5)")
self.setGeometry(100, 100, 1200, 800)
# 加载模型检查
try:
self.model_path = MODEL_PATH
# 简单检查文件是否存在,实际加载在线程中
import os
if not os.path.exists(self.model_path):
QMessageBox.critical(self, "错误", f"未找到模型文件:\n{self.model_path}\n请先运行训练脚本!")
sys.exit(1)
except Exception as e:
QMessageBox.critical(self, "错误", str(e))
sys.exit(1)
self.init_ui()
self.thread = None
self.alert_count = 0
self.last_alert_time = 0
def init_ui(self):
# 主布局
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout()
# 1. 视频显示区域
self.video_label = QLabel("等待视频流...")
self.video_label.setAlignment(Qt.AlignCenter)
self.video_label.setStyleSheet("background-color: #000; border: 2px solid #333; min-height: 480px;")
main_layout.addWidget(self.video_label, stretch=3)
# 2. 控制与日志区域
control_layout = QVBoxLayout()
# 状态面板
status_group = QGroupBox("🚨 实时状态")
status_layout = QVBoxLayout()
self.status_label = QLabel("状态: ⭕ 监测中")
self.status_label.setFont(QFont("Arial", 14, QFont.Bold))
self.status_label.setStyleSheet("color: green;")
self.fps_label = QLabel("FPS: 0.0")
status_layout.addWidget(self.status_label)
status_layout.addWidget(self.fps_label)
status_group.setLayout(status_layout)
control_layout.addWidget(status_group)
# 日志面板
log_group = QGroupBox("📝 报警日志")
log_layout = QVBoxLayout()
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
self.log_text.setFont(QFont("Consolas", 10))
self.log_text.setStyleSheet("background-color: #f0f0f0;")
log_layout.addWidget(self.log_text)
log_group.setLayout(log_layout)
control_layout.addWidget(log_group, stretch=1)
# 按钮
btn_layout = QVBoxLayout()
self.start_btn = QPushButton("▶ 启动监控 (摄像头)")
self.start_btn.clicked.connect(self.start_monitoring)
self.start_btn.setStyleSheet("background-color: #28a745; color: white; font-size: 16px; padding: 10px;")
self.stop_btn = QPushButton("⏹ 停止监控")
self.stop_btn.clicked.connect(self.stop_monitoring)
self.stop_btn.setEnabled(False)
self.stop_btn.setStyleSheet("background-color: #dc3545; color: white; font-size: 16px; padding: 10px;")
btn_layout.addWidget(self.start_btn)
btn_layout.addWidget(self.stop_btn)
control_layout.addLayout(btn_layout)
# 组合左右布局 (视频左,控制右)
# 这里简化为上下布局,实际可改为 QHBoxLayout 分左右
main_layout.addLayout(control_layout, stretch=1)
central_widget.setLayout(main_layout)
def start_monitoring(self):
self.start_btn.setEnabled(False)
self.stop_btn.setEnabled(True)
self.log_text.append(f"[{datetime.now().strftime('%H:%M:%S')}] 系统启动,加载模型中...")
# 0 代表默认摄像头,也可替换为视频路径 'violence_test.mp4'
self.thread = DetectionThread(0, self.model_path)
self.thread.frame_signal.connect(self.update_frame)
self.thread.start()
def stop_monitoring(self):
if self.thread:
self.thread.stop()
self.thread = None
self.start_btn.setEnabled(True)
self.stop_btn.setEnabled(False)
self.video_label.clear()
self.video_label.setText("监控已停止")
self.status_label.setText("状态: ⭕ 已停止")
self.status_label.setStyleSheet("color: gray;")
self.log_text.append(f"[{datetime.now().strftime('%H:%M:%S')}] 系统停止")
def update_frame(self, q_image, detections, fps):
# 更新视频
pixmap = QPixmap.fromImage(q_image)
self.video_label.setPixmap(pixmap.scaled(self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
# 更新 FPS
self.fps_label.setText(f"FPS: {fps:.1f}")
# 报警逻辑
alert_triggered = False
alert_msgs = []
for det in detections:
if det['class'] in ALERT_CLASSES:
alert_triggered = True
alert_msgs.append(f"{det['class']} (置信度: {det['conf']:.2f})")
if alert_triggered:
current_time = datetime.now().timestamp()
# 防抖动:每秒只记录一次详细日志,但状态实时变红
self.status_label.setText("状态: 🔴 检测到暴力行为!")
self.status_label.setStyleSheet("color: red; font-weight: bold; font-size: 18px;")
if current_time - self.last_alert_time > 1.0:
msg = ", ".join(alert_msgs)
self.log_text.append(f"[{datetime.now().strftime('%H:%M:%S')}] ⚠️ 警报: 发现 {msg}")
self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())
self.last_alert_time = current_time
# 这里可以添加播放声音代码: playsound('alarm.wav')
else:
self.status_label.setText("状态: 🟢 正常监测中")
self.status_label.setStyleSheet("color: green;")
def closeEvent(self, event):
self.stop_monitoring()
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = ViolenceMonitorSystem()
window.show()
sys.exit(app.exec_())
💡 4. 系统功能亮点与优化
-
细粒度行为分析:
- 系统不仅报告“有异常”,还能明确指出是
fighter(正在打人) 还是victim(正在被打),甚至能识别出threatener(潜在威胁),这对于校园安保人员提前介入至关重要。
- 系统不仅报告“有异常”,还能明确指出是
-
多线程架构:
- 使用
QThread将耗时的 YOLO 推理放在后台线程,确保 PyQt5 的主界面(UI)永远流畅,不会出现“未响应”的情况。
- 使用
-
智能防误报:
- 代码中加入了时间戳判断 (
last_alert_time),避免同一秒内日志刷屏,同时保持状态栏实时红色高亮。 - 可以通过增加逻辑:只有连续 N 帧检测到
fighter才触发最高级别警报。
- 代码中加入了时间戳判断 (
-
可视化增强:
- 不同类别使用不同颜色的边框(红、橙、黄),一目了然。
- 实时显示 FPS,方便评估硬件性能。
208

被折叠的 条评论
为什么被折叠?



