基于YOLOv8的安全手套佩戴识别系统开发实战
在工业安全生产监管中,手套佩戴识别一直是重要的安全防护环节。传统的人工巡检方式效率低下且容易遗漏,而基于深度学习的自动化检测系统能够有效解决这一问题。本文将详细介绍如何基于YOLOv8算法构建一套完整的安全手套佩戴识别检测系统,包含从环境配置、模型训练到UI界面开发的完整流程。
1. 项目背景与核心概念
1.1 安全手套识别的重要性
在建筑工地、工厂车间、电力作业等高危工作环境中,规范佩戴安全手套是防止手部伤害的基本要求。传统的人工监控存在以下痛点:
- 监控盲区难以覆盖
- 人工巡检成本高
- 实时性差,无法及时预警
- 主观判断标准不一致
基于YOLOv8的自动识别系统能够实现7×24小时不间断监控,准确识别作业人员是否规范佩戴手套,有效提升安全生产管理的智能化水平。
1.2 YOLOv8算法简介
YOLOv8是Ultralytics公司推出的最新一代目标检测算法,相比前代版本在精度和速度上都有显著提升。其主要特点包括:
- 骨干网络优化 :采用更高效的CSPDarknet53结构
- 特征金字塔增强 :改进的PAN-FPN结构提升多尺度检测能力
- 损失函数改进 :使用CIoU Loss提高边界框回归精度
- 训练策略优化 :引入Mosaic数据增强和自适应锚框计算
1.3 系统架构概述
整个系统采用模块化设计,主要包括:
- 数据预处理模块 :图像增强、标注格式转换
- 模型训练模块 :YOLOv8模型训练与优化
- 推理检测模块 :实时目标检测与结果可视化
- UI交互模块 :基于PyQt5的用户界面
2. 环境配置与依赖安装
2.1 系统环境要求
推荐使用以下环境配置:
- 操作系统:Windows 10/11 或 Ubuntu 18.04+
- Python版本:3.8-3.10
- 深度学习框架:PyTorch 1.12+
- GPU支持:NVIDIA GPU(可选,推荐GTX 1060以上)
2.2 创建虚拟环境
使用Anaconda创建独立的Python环境,避免依赖冲突:
# 创建名为yolov8的虚拟环境
conda create -n yolov8 python=3.9
# 激活环境
conda activate yolov8
2.3 安装核心依赖库
创建requirements.txt文件,包含项目所需的所有依赖:
# requirements.txt
ultralytics==8.0.0
torch>=1.12.0
torchvision>=0.13.0
opencv-python>=4.5.0
PyQt5>=5.15.0
numpy>=1.21.0
pillow>=8.0.0
matplotlib>=3.5.0
seaborn>=0.11.0
pandas>=1.3.0
使用pip批量安装依赖:
pip install -r requirements.txt
2.4 PyTorch GPU版本安装(可选)
如果使用GPU加速训练,需要安装CUDA版本的PyTorch:
# 根据CUDA版本选择对应的PyTorch
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
3. 数据集准备与处理
3.1 数据集结构设计
安全手套识别数据集包含两个类别:
- Gloves :佩戴手套
- NO-Gloves :未佩戴手套
数据集按照YOLO格式组织:
datasets/
├── images/
│ ├── train/ # 训练集图像
│ ├── val/ # 验证集图像
│ └── test/ # 测试集图像
└── labels/
├── train/ # 训练集标注
├── val/ # 验证集标注
└── test/ # 测试集标注
3.2 数据标注规范
使用LabelImg等工具进行标注,标注文件为YOLO格式的txt文件:
# 标注格式:class_id x_center y_center width height
0 0.512 0.634 0.124 0.256
1 0.723 0.445 0.089 0.167
3.3 数据集配置文件
创建data.yaml配置文件,定义数据集路径和类别信息:
# data.yaml
path: F:\安全手套佩戴识别检测数据集
train: images/train
val: images/val
test: images/test
nc: 2 # 类别数量
names: ['Gloves', 'NO-Gloves'] # 类别名称
# 下载命令/URL(可选)
download: None
3.4 数据增强策略
为提高模型泛化能力,采用以下数据增强技术:
# 数据增强配置示例
augmentation = {
'hsv_h': 0.015, # 色调调整
'hsv_s': 0.7, # 饱和度调整
'hsv_v': 0.4, # 明度调整
'translate': 0.1, # 平移
'scale': 0.5, # 缩放
'flipud': 0.0, # 上下翻转
'fliplr': 0.5, # 左右翻转
'mosaic': 1.0, # 马赛克增强
'mixup': 0.0 # MixUp增强
}
4. 模型训练与优化
4.1 模型选择策略
YOLOv8提供多种规模的预训练模型,根据实际需求选择:
# 模型规模对比
model_configs = {
'yolov8n': {'参数': 3.2M, '速度': 最快, '精度': 基础},
'yolov8s': {'参数': 11.2M, '速度': 快, '精度': 平衡},
'yolov8m': {'参数': 25.9M, '速度': 中等, '精度': 较好},
'yolov8l': {'参数': 43.7M, '速度': 较慢, '精度': 高},
'yolov8x': {'参数': 68.2M, '速度': 慢, '精度': 最高}
}
4.2 训练代码实现
创建完整的训练脚本:
# train.py
from ultralytics import YOLO
import argparse
def main():
parser = argparse.ArgumentParser(description='YOLOv8安全手套识别训练脚本')
parser.add_argument('--model', type=str, default='yolov8s.pt', help='预训练模型路径')
parser.add_argument('--data', type=str, default='datasets/data.yaml', help='数据集配置文件')
parser.add_argument('--epochs', type=int, default=500, help='训练轮数')
parser.add_argument('--batch', type=int, default=64, help='批次大小')
parser.add_argument('--imgsz', type=int, default=640, help='输入图像尺寸')
parser.add_argument('--device', type=str, default='0', help='训练设备')
args = parser.parse_args()
# 加载模型
model = YOLO(args.model)
# 开始训练
results = model.train(
data=args.data,
epochs=args.epochs,
batch=args.batch,
imgsz=args.imgsz,
device=args.device,
workers=0,
project='runs/detect',
name='gloves_detection',
save=True,
exist_ok=True
)
print("训练完成!")
if __name__ == '__main__':
main()
4.3 训练参数调优
关键训练参数配置说明:
training_config = {
'lr0': 0.01, # 初始学习率
'lrf': 0.01, # 最终学习率
'momentum': 0.937, # 动量
'weight_decay': 0.0005, # 权重衰减
'warmup_epochs': 3.0, # 热身轮数
'warmup_momentum': 0.8,# 热身动量
'warmup_bias_lr': 0.1, # 热身偏置学习率
'box': 7.5, # 边界框损失权重
'cls': 0.5, # 分类损失权重
'dfl': 1.5, # DFL损失权重
'close_mosaic': 10 # 关闭马赛克增强的轮数
}
4.4 训练过程监控
使用TensorBoard监控训练过程:
# 启动TensorBoard
tensorboard --logdir runs/detect
关键监控指标:
- 损失函数变化(box_loss, cls_loss, dfl_loss)
- 验证集精度(precision, recall, mAP50, mAP50-95)
- 学习率变化曲线
5. 模型评估与测试
5.1 评估指标解读
# 模型评估脚本
from ultralytics import YOLO
def evaluate_model():
# 加载最佳模型
model = YOLO('runs/detect/gloves_detection/weights/best.pt')
# 在验证集上评估
metrics = model.val(
data='datasets/data.yaml',
split='val',
batch=16,
imgsz=640,
conf=0.25,
iou=0.45
)
# 输出评估结果
print(f"mAP50: {metrics.box.map50:.4f}")
print(f"mAP50-95: {metrics.box.map:.4f}")
print(f"Precision: {metrics.box.precision:.4f}")
print(f"Recall: {metrics.box.recall:.4f}")
if __name__ == '__main__':
evaluate_model()
5.2 混淆矩阵分析
通过混淆矩阵分析模型在不同类别上的表现:
# 生成混淆矩阵
from ultralytics import YOLO
import matplotlib.pyplot as plt
model = YOLO('runs/detect/gloves_detection/weights/best.pt')
model.confusion_matrix()
5.3 测试集性能验证
在独立测试集上验证模型泛化能力:
def test_model():
model = YOLO('runs/detect/gloves_detection/weights/best.pt')
# 测试集评估
test_metrics = model.val(
data='datasets/data.yaml',
split='test',
batch=16
)
return test_metrics
6. UI界面开发
6.1 PyQt5界面设计
创建主界面类,实现完整的用户交互功能:
# main_window.py
import sys
import os
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap, QIcon, QFont
from PyQt5.QtWidgets import (QMainWindow, QFileDialog, QMessageBox,
QTableWidgetItem, QHeaderView, QProgressBar)
from ultralytics import YOLO
class DetectionThread(QtCore.QThread):
"""检测线程类,避免界面卡顿"""
finished_signal = pyqtSignal(object)
error_signal = pyqtSignal(str)
def __init__(self, model, image, conf_threshold, iou_threshold):
super().__init__()
self.model = model
self.image = image
self.conf_threshold = conf_threshold
self.iou_threshold = iou_threshold
def run(self):
try:
results = self.model.predict(
self.image,
conf=self.conf_threshold,
iou=self.iou_threshold,
verbose=False
)
self.finished_signal.emit(results)
except Exception as e:
self.error_signal.emit(str(e))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.model = None
self.current_image = None
self.current_results = None
self.cap = None
self.timer = QTimer()
self.video_writer = None
self.output_dir = "output"
# 创建输出目录
if not os.path.exists(self.output_dir):
os.makedirs(self.output_dir)
self.setup_ui()
self.connect_signals()
def setup_ui(self):
"""设置用户界面"""
self.setWindowTitle("YOLOv8安全手套识别系统")
self.setGeometry(100, 100, 1600, 900)
# 设置窗口图标
self.setWindowIcon(QIcon('icon.ico'))
# 创建中央部件
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
# 主布局
main_layout = QtWidgets.QHBoxLayout(central_widget)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(15)
# 左侧图像显示区域
left_layout = self.create_image_display()
main_layout.addLayout(left_layout, 3)
# 右侧控制面板
right_layout = self.create_control_panel()
main_layout.addLayout(right_layout, 1)
# 状态栏
self.setup_statusbar()
def create_image_display(self):
"""创建图像显示区域"""
layout = QtWidgets.QVBoxLayout()
layout.setSpacing(15)
# 原始图像显示
original_group = QtWidgets.QGroupBox("原始图像")
original_group.setMinimumHeight(400)
self.original_label = QtWidgets.QLabel()
self.original_label.setAlignment(Qt.AlignCenter)
self.original_label.setText("等待加载图像...")
self.original_label.setStyleSheet("border: 1px solid #cccccc; background-color: #f8f9fa;")
original_layout = QtWidgets.QVBoxLayout()
original_layout.addWidget(self.original_label)
original_group.setLayout(original_layout)
layout.addWidget(original_group)
# 检测结果图像显示
result_group = QtWidgets.QGroupBox("检测结果")
result_group.setMinimumHeight(400)
self.result_label = QtWidgets.QLabel()
self.result_label.setAlignment(Qt.AlignCenter)
self.result_label.setText("检测结果将显示在这里")
self.result_label.setStyleSheet("border: 1px solid #cccccc; background-color: #f8f9fa;")
result_layout = QtWidgets.QVBoxLayout()
result_layout.addWidget(self.result_label)
result_group.setLayout(result_layout)
layout.addWidget(result_group)
return layout
def create_control_panel(self):
"""创建控制面板"""
layout = QtWidgets.QVBoxLayout()
layout.setSpacing(15)
# 模型设置组
model_group = self.create_model_group()
layout.addWidget(model_group)
# 参数设置组
param_group = self.create_parameter_group()
layout.addWidget(param_group)
# 功能按钮组
func_group = self.create_function_group()
layout.addWidget(func_group)
# 检测结果表格
table_group = self.create_result_table()
layout.addWidget(table_group, 1)
return layout
def create_model_group(self):
"""创建模型设置组"""
group = QtWidgets.QGroupBox("模型设置")
layout = QtWidgets.QVBoxLayout()
# 模型选择
self.model_combo = QtWidgets.QComboBox()
self.model_combo.addItems(["yolov8n.pt", "yolov8s.pt", "yolov8m.pt", "best.pt"])
# 加载模型按钮
self.load_btn = QtWidgets.QPushButton("加载模型")
self.load_btn.setStyleSheet(self.get_button_style("#4CAF50"))
layout.addWidget(QtWidgets.QLabel("选择模型:"))
layout.addWidget(self.model_combo)
layout.addWidget(self.load_btn)
group.setLayout(layout)
return group
def create_parameter_group(self):
"""创建参数设置组"""
group = QtWidgets.QGroupBox("检测参数")
layout = QtWidgets.QFormLayout()
# 置信度阈值
self.conf_slider = QtWidgets.QSlider(Qt.Horizontal)
self.conf_slider.setRange(1, 99)
self.conf_slider.setValue(25)
self.conf_label = QtWidgets.QLabel("0.25")
# IoU阈值
self.iou_slider = QtWidgets.QSlider(Qt.Horizontal)
self.iou_slider.setRange(1, 99)
self.iou_slider.setValue(45)
self.iou_label = QtWidgets.QLabel("0.45")
layout.addRow("置信度阈值:", self.conf_slider)
layout.addRow("当前值:", self.conf_label)
layout.addRow("IoU阈值:", self.iou_slider)
layout.addRow("当前值:", self.iou_label)
group.setLayout(layout)
return group
def create_function_group(self):
"""创建功能按钮组"""
group = QtWidgets.QGroupBox("检测功能")
layout = QtWidgets.QVBoxLayout()
# 功能按钮
self.image_btn = QtWidgets.QPushButton("图片检测")
self.video_btn = QtWidgets.QPushButton("视频检测")
self.camera_btn = QtWidgets.QPushButton("摄像头检测")
self.stop_btn = QtWidgets.QPushButton("停止检测")
self.save_btn = QtWidgets.QPushButton("保存结果")
# 设置按钮样式
buttons = [self.image_btn, self.video_btn, self.camera_btn,
self.stop_btn, self.save_btn]
colors = ["#2196F3", "#2196F3", "#2196F3", "#f44336", "#ff9800"]
for btn, color in zip(buttons, colors):
btn.setStyleSheet(self.get_button_style(color))
layout.addWidget(btn)
# 初始状态设置
self.stop_btn.setEnabled(False)
self.save_btn.setEnabled(False)
group.setLayout(layout)
return group
def create_result_table(self):
"""创建结果表格"""
group = QtWidgets.QGroupBox("检测结果详情")
layout = QtWidgets.QVBoxLayout()
self.result_table = QtWidgets.QTableWidget()
self.result_table.setColumnCount(5)
self.result_table.setHorizontalHeaderLabels(["ID", "类别", "置信度", "位置", "状态"])
self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
layout.addWidget(self.result_table)
group.setLayout(layout)
return group
def setup_statusbar(self):
"""设置状态栏"""
self.status_bar = self.statusBar()
self.status_bar.showMessage("就绪")
# 添加进度条
self.progress_bar = QProgressBar()
self.progress_bar.setMaximumWidth(200)
self.progress_bar.setVisible(False)
self.status_bar.addPermanentWidget(self.progress_bar)
def get_button_style(self, color):
"""获取按钮样式"""
return f"""
QPushButton {{
padding: 10px;
background-color: {color};
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
}}
QPushButton:hover {{
background-color: {self.darken_color(color)};
}}
QPushButton:disabled {{
background-color: #cccccc;
}}
"""
def darken_color(self, color):
"""颜色变暗"""
return f"#{hex(int(color[1:3], 16) - 30)[2:]:>02}{hex(int(color[3:5], 16) - 30)[2:]:>02}{hex(int(color[5:7], 16) - 30)[2:]:>02}"
def connect_signals(self):
"""连接信号槽"""
self.load_btn.clicked.connect(self.load_model)
self.image_btn.clicked.connect(self.detect_image)
self.video_btn.clicked.connect(self.detect_video)
self.camera_btn.clicked.connect(self.detect_camera)
self.stop_btn.clicked.connect(self.stop_detection)
self.save_btn.clicked.connect(self.save_result)
self.conf_slider.valueChanged.connect(self.update_conf_value)
self.iou_slider.valueChanged.connect(self.update_iou_value)
self.timer.timeout.connect(self.update_frame)
def load_model(self):
"""加载模型"""
try:
model_path = self.model_combo.currentText()
self.model = YOLO(model_path)
self.status_bar.showMessage(f"模型 {model_path} 加载成功")
# 启用检测按钮
self.image_btn.setEnabled(True)
self.video_btn.setEnabled(True)
self.camera_btn.setEnabled(True)
except Exception as e:
QMessageBox.critical(self, "错误", f"模型加载失败: {str(e)}")
def update_conf_value(self):
"""更新置信度阈值显示"""
conf = self.conf_slider.value() / 100
self.conf_label.setText(f"{conf:.2f}")
def update_iou_value(self):
"""更新IoU阈值显示"""
iou = self.iou_slider.value() / 100
self.iou_label.setText(f"{iou:.2f}")
def detect_image(self):
"""图片检测"""
if not self.model:
QMessageBox.warning(self, "警告", "请先加载模型")
return
file_path, _ = QFileDialog.getOpenFileName(
self, "选择图片", "",
"图片文件 (*.jpg *.jpeg *.png *.bmp);;所有文件 (*)"
)
if file_path:
self.process_image(file_path)
def process_image(self, file_path):
"""处理单张图片"""
try:
# 读取图片
img = cv2.imread(file_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 显示原始图片
self.display_image(img_rgb, self.original_label)
self.current_image = img_rgb.copy()
# 开始检测
self.start_detection(img_rgb, file_path)
except Exception as e:
QMessageBox.critical(self, "错误", f"图片处理失败: {str(e)}")
def start_detection(self, image, source_name):
"""开始目标检测"""
self.progress_bar.setVisible(True)
self.status_bar.showMessage(f"正在检测: {os.path.basename(source_name)}...")
# 创建检测线程
self.detection_thread = DetectionThread(
self.model, image,
self.conf_slider.value() / 100,
self.iou_slider.value() / 100
)
self.detection_thread.finished_signal.connect(self.on_detection_finished)
self.detection_thread.error_signal.connect(self.on_detection_error)
self.detection_thread.start()
def on_detection_finished(self, results):
"""检测完成回调"""
self.progress_bar.setVisible(False)
try:
# 获取检测结果
result_img = results[0].plot()
self.display_image(result_img, self.result_label)
self.current_results = results[0]
# 更新结果表格
self.update_result_table(results[0])
self.save_btn.setEnabled(True)
self.status_bar.showMessage("检测完成")
except Exception as e:
QMessageBox.critical(self, "错误", f"结果处理失败: {str(e)}")
def on_detection_error(self, error_msg):
"""检测错误回调"""
self.progress_bar.setVisible(False)
QMessageBox.critical(self, "错误", f"检测过程出错: {error_msg}")
def display_image(self, image, label):
"""在QLabel中显示图像"""
h, w, ch = image.shape
bytes_per_line = ch * w
q_img = QImage(image.data, w, h, bytes_per_line, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(q_img)
# 缩放适应标签大小
scaled_pixmap = pixmap.scaled(
label.width() - 10,
label.height() - 10,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
label.setPixmap(scaled_pixmap)
def update_result_table(self, results):
"""更新结果表格"""
self.result_table.setRowCount(0)
if hasattr(results, 'boxes') and results.boxes is not None:
boxes = results.boxes
for i, (xyxy, conf, cls) in enumerate(zip(boxes.xyxy, boxes.conf, boxes.cls)):
self.result_table.insertRow(i)
# 类别名称
class_name = results.names[int(cls)]
# 位置信息
x1, y1, x2, y2 = map(int, xyxy)
position = f"({x1}, {y1}) - ({x2}, {y2})"
# 状态判断
status = "合规" if class_name == "Gloves" else "违规"
# 填充表格
self.result_table.setItem(i, 0, QTableWidgetItem(str(i+1)))
self.result_table.setItem(i, 1, QTableWidgetItem(class_name))
self.result_table.setItem(i, 2, QTableWidgetItem(f"{conf:.3f}"))
self.result_table.setItem(i, 3, QTableWidgetItem(position))
self.result_table.setItem(i, 4, QTableWidgetItem(status))
def detect_video(self):
"""视频检测"""
if not self.model:
QMessageBox.warning(self, "警告", "请先加载模型")
return
file_path, _ = QFileDialog.getOpenFileName(
self, "选择视频", "",
"视频文件 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)"
)
if file_path:
self.start_video_detection(file_path)
def start_video_detection(self, file_path):
"""开始视频检测"""
try:
self.cap = cv2.VideoCapture(file_path)
if not self.cap.isOpened():
raise Exception("无法打开视频文件")
# 获取视频信息
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 设置按钮状态
self.stop_btn.setEnabled(True)
self.image_btn.setEnabled(False)
self.video_btn.setEnabled(False)
self.camera_btn.setEnabled(False)
# 开始定时器
self.timer.start(int(1000 / self.fps))
self.status_bar.showMessage(f"正在处理视频: {os.path.basename(file_path)}")
except Exception as e:
QMessageBox.critical(self, "错误", f"视频检测失败: {str(e)}")
def detect_camera(self):
"""摄像头检测"""
if not self.model:
QMessageBox.warning(self, "警告", "请先加载模型")
return
try:
# 尝试打开默认摄像头
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
raise Exception("无法打开摄像头")
# 设置按钮状态
self.stop_btn.setEnabled(True)
self.image_btn.setEnabled(False)
self.video_btn.setEnabled(False)
self.camera_btn.setEnabled(False)
# 开始定时器
self.timer.start(30) # 约30fps
self.status_bar.showMessage("摄像头实时检测中...")
except Exception as e:
QMessageBox.critical(self, "错误", f"摄像头检测失败: {str(e)}")
def update_frame(self):
"""更新视频/摄像头帧"""
if self.cap and self.cap.isOpened():
ret, frame = self.cap.read()
if ret:
# 转换颜色空间
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 显示原始帧
self.display_image(frame_rgb, self.original_label)
# 进行检测
results = self.model.predict(
frame_rgb,
conf=self.conf_slider.value() / 100,
iou=self.iou_slider.value() / 100,
verbose=False
)
# 显示检测结果
result_frame = results[0].plot()
self.display_image(result_frame, self.result_label)
# 更新结果表格
self.update_result_table(results[0])
def stop_detection(self):
"""停止检测"""
self.timer.stop()
if self.cap:
self.cap.release()
self.cap = None
# 恢复按钮状态
self.stop_btn.setEnabled(False)
self.image_btn.setEnabled(True)
self.video_btn.setEnabled(True)
self.camera_btn.setEnabled(True)
self.status_bar.showMessage("检测已停止")
def save_result(self):
"""保存检测结果"""
if self.current_results is None:
QMessageBox.warning(self, "警告", "没有可保存的检测结果")
return
try:
file_path, _ = QFileDialog.getSaveFileName(
self, "保存结果",
os.path.join(self.output_dir, "detection_result.jpg"),
"图片文件 (*.jpg *.png)"
)
if file_path:
# 保存结果图像
result_img_bgr = cv2.cvtColor(self.current_results.plot(), cv2.COLOR_RGB2BGR)
cv2.imwrite(file_path, result_img_bgr)
# 保存检测信息到文本文件
info_file = file_path.replace('.jpg', '_info.txt').replace('.png', '_info.txt')
with open(info_file, 'w', encoding='utf-8') as f:
f.write("安全手套检测结果报告\n")
f.write("=" * 50 + "\n")
if hasattr(self.current_results, 'boxes') and self.current_results.boxes is not None:
boxes = self.current_results.boxes
for i, (xyxy, conf, cls) in enumerate(zip(boxes.xyxy, boxes.conf, boxes.cls)):
class_name = self.current_results.names[int(cls)]
status = "合规" if class_name == "Gloves" else "违规"
f.write(f"检测目标 {i+1}:\n")
f.write(f" 类别: {class_name}\n")
f.write(f" 置信度: {conf:.3f}\n")
f.write(f" 状态: {status}\n")
f.write(f" 位置: {xyxy.tolist()}\n\n")
QMessageBox.information(self, "成功", f"结果已保存到:\n{file_path}")
except Exception as e:
QMessageBox.critical(self, "错误", f"保存失败: {str(e)}")
def main():
app = QtWidgets.QApplication(sys.argv)
app.setFont(QFont("Microsoft YaHei", 9))
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
6.2 界面样式优化
创建专业的CSS样式表提升用户体验:
/* styles.css */
QMainWindow {
background-color: #f5f5f5;
font-family: "Microsoft YaHei", sans-serif;
}
QGroupBox {
border: 2px solid #e0e0e0;
border-radius: 8px;
margin-top: 10px;
padding-top: 15px;
background-color: white;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 8px;
background-color: white;
color: #333;
font-weight: bold;
}
QLabel {
color: #333;
font-size: 9pt;
}
QPushButton {
padding: 8px 16px;
border: none;
border-radius: 4px;
font-weight: bold;
min-height: 20px;
}
QPushButton:hover {
opacity: 0.9;
}
QPushButton:disabled {
background-color: #cccccc;
color: #666666;
}
QTableWidget {
border: 1px solid #e0e0e0;
alternate-background-color: #f9f9f9;
selection-background-color: #e3f2fd;
}
QHeaderView::section {
background-color: #2196F3;
color: white;
padding: 6px;
border: none;
font-weight: bold;
}
QSlider::groove:horizontal {
height: 6px;
background: #e0e0e0;
border-radius: 3px;
}
QSlider::handle:horizontal {
width: 16px;
height: 16px;
margin: -5px 0;
background: #2196F3;
border-radius: 8px;
}
QSlider::sub-page:horizontal {
background: #2196F3;
border-radius: 3px;
}
QProgressBar {
border: 1px solid #ccc;
border-radius: 3px;
text-align: center;
}
QProgressBar::chunk {
background-color: #4CAF50;
width: 10px;
}
7. 系统部署与优化
7.1 模型量化与加速
为提升推理速度,可以对模型进行量化:
# 模型量化脚本
from ultralytics import YOLO
def quantize_model():
# 加载训练好的模型
model = YOLO('runs/detect/gloves_detection/weights/best.pt')
# 导出为ONNX格式(包含量化)
model.export(
format='onnx',
dynamic=True,
simplify=True,
opset=12
)
更多推荐



所有评论(0)