当前位置: 首页 > news >正文

基于多模型AI训练与验证系统开发

YOLOv5-master-int8项目使用说明书

一、项目准备

1. 开发环境搭建

  • 开发工具:推荐使用PyCharm(或其他Python集成开发工具),需支持Python 3.10环境。
  • 项目名称:yolov5-master-int8
  • 核心环境:Python 3.10(必须匹配,避免依赖冲突)。
  • 依赖安装
    项目依赖在requirements.txt中定义,可通过以下命令安装:
    pip install -r requirements.txt
    
    关键依赖包括:PyTorch(用于模型训练/推理)、OpenCV(图像处理)、PySide6(可视化界面)、labelImg(数据标注)等。
    在这里插入图片描述

2. 项目结构说明

项目文件及目录含义如下:

文件/目录名称说明
5n_Int8_Diedai1-5s_Int8_yuanshi存放迭代训练的权重模型,专为C++测试准备的IR格式模型(OpenVINO兼容)。
build系统打包编译的缓存文件,可删除(重新打包时会自动生成)。
classify图像分类任务相关代码,与本项目核心目标检测功能关联较弱,可忽略。
data项目数据配置及资源文件:
- hyps:超参数配置文件(不同数据集/场景的训练参数)
- scripts:数据集下载脚本;
- .yaml文件:数据集配置(定义训练/验证数据路径、类别)。
dist系统构建后的发布文件(打包的EXE及依赖),用于项目分发。
modelsYOLOv5模型架构定义及配置文件如网络层结构、参数初始化。
new_Data自定义训练数据存放目录(标注后的图片及标签文件放入此处)
runs训练过程日志及结果存放目录,其中runs/train/expN/weights保存训练生成的权重(best.pt为最优模型)。
UI可视化界面(main.py)使用的图片资源(如图标、默认背景图)。
utils核心工具函数模块:包含数据处理、模型训练辅助、检测结果可视化等功能。
Demo_int8.py模型量化脚本,支持将模型转换为FP32(单精度)、FP16(半精度)、INT8(整数)格式,减小模型体积并加速推理。
detect.py目标检测测试脚本,加载训练好的模型并对图片/视频进行检测。
使用命令
python detect.py --weights 模型路径 --source 测试数据路径 --device 运行设备
示例:
python detect.py --weights runs/train/exp5/weights/best.pt --source new_Data/images/train/xxx.jpg --device cpu
export.py模型格式转换脚本,支持将PyTorch模型(.pt)转换为ONNX、IR等格式,用于部署。
使用命令
python export.py --weights 模型路径 --imgsz 图像尺寸 --batch-size 批次大小 --include 目标格式
示例(转换为OpenVINO格式):
python export.py --weights runs/train/exp6/weights/best.pt --imgsz 640 --batch-size 1 --include openvino --opset 13
转换后会生成.xml(模型结构)和.bin(权重数据)文件。
json2txt.py标注数据格式转换脚本:将JSON格式标注文件(如labelImg的JSON输出)转换为YOLO训练所需的TXT格式(每行包含类别ID及归一化坐标)。
main.py可视化界面入口脚本,集成数据标注、模型训练、目标检测三大功能,可通过图形界面操作(无需手动输入命令)。
train.py模型训练核心脚本,用于配置训练参数并启动训练。
使用命令
python train.py --imgsz 图像尺寸 --batch 批次大小 --epochs 训练轮次 --data 数据集配置 --weights 预训练权重 --device 训练设备
示例:
python train.py --imgsz 640 --batch 32 --epochs 50 --data continuc_train.yaml --weights runs/train/exp4/weights/best.pt --device 0

二、平台功能使用说明

1. 启动平台

通过main.py启动可视化平台,支持3种启动方式:

  • 点击PyCharm左侧代码行号旁的绿色三角按钮(需确保当前脚本为main.py);
  • 右键main.py,选择“Run ‘main’”;
  • 点击PyCharm右上角的启动按钮(确认下拉框选中main.py)。
    在这里插入图片描述

2. 数据标注功能

界面介绍

界面包含“图片选择”和“标注说明”两部分,核心功能为统一图片格式启动标注工具

在这里插入图片描述

功能详解
  1. 统一图片格式

    • 作用:将不同格式(JPG、PNG、BMP)的图片转换为指定格式(推荐JPG,兼容性好),避免训练时格式冲突。
    • 操作:
      1. 点击“浏览…”选择图片所在文件夹;
      2. 在“统一格式”下拉框选择目标格式(JPG/PNG/BMP);
      3. 点击“转换格式”,转换后的图片会保存在原文件夹下的converted_<格式>子目录中。
  2. 启动标注工具(labelImg)
    在这里插入图片描述

    • 作用:通过labelImg手动标注图片中的目标,生成训练所需的标签文件。

    • 操作:点击“打开标注软件”启动labelImg,界面功能如下:

      功能按钮说明
      Open打开单张图片进行标注。
      Open Dir打开存放图片的文件夹,批量标注该文件夹下所有图片。
      Change Save Dir设置标签文件的保存路径(建议与图片路径对应,方便训练时关联)。
      Save保存当前图片的标注结果(格式由上方格式选择框决定)。
      Create RectBox绘制矩形框标注目标:鼠标拖拽选中目标区域,输入类别名称(建议用英文,避免乱码)。
      Next Image/Prev Image切换到下一张/上一张图片,继续标注。
      格式选择框(YOLO/VOC/ML)选择标签文件格式:
      - YOLO:生成TXT文件(每行含类别ID及归一化坐标,训练必用);
      - VOC:生成XML文件;
      - ML:生成JSON文件。
    • 注意:标注完成后,需将图片和对应的标签文件放入new_Data目录(如new_Data/images/train放图片,new_Data/labels/train放标签)。
      在这里插入图片描述

3. 开始训练功能

界面介绍

界面包含“训练参数设置”和“训练控制按钮”,用于配置训练参数并启动/停止训练。
在这里插入图片描述

功能详解
  1. 训练参数设置

    参数名称说明
    预训练权重训练的初始权重:
    - 首次训练可使用官方权重(如yolov5n.pt,n系列模型轻量快速);
    - 后续训练可使用之前生成的best.pt(位于runs/train/expN/weights),加速收敛并优化特定类别检测效果。
    操作:点击“浏览…”选择权重文件路径。
    训练轮次(epochs)模型训练的迭代次数:每轮次会遍历所有训练数据一次。
    - 默认50次,可在1-2000范围内调整;
    - 建议根据验证精度调整(精度不再提升时可提前停止)。
    批次大小(batch size)每次迭代输入模型的图片数量:
    - 受硬件显存限制(显存越大,可设越大);
    - 默认16,推荐范围:8-64(需根据GPU/CPU性能调整)。
    输入图像尺寸训练时图片的缩放尺寸(默认640x640),需与模型要求匹配(YOLOv5通常支持32的倍数,如320、640、1280)。
    训练设备选择训练运行的硬件:
    - auto:自动选择(优先GPU,无GPU则用CPU);
    - cpu:强制用CPU(速度慢,适合无GPU环境);
    - 0/1:指定GPU编号(多GPU环境)。
    项目名称训练结果存放的根目录(默认runs/train),所有训练实验会保存在该目录下的expN子目录中。
    实验名称单轮训练的标识(默认exp),多次训练会自动递增为exp1exp2等,方便区分不同参数的训练结果。
    恢复训练勾选后可从上次中断的进度继续训练(需确保runs/train/expN中存在中断前的 checkpoint 文件)。
    数据集配置文件指向data目录下的.yaml文件,定义训练/验证数据路径、类别数及类别名称等关键信息。
    操作:点击“浏览…”选择配置文件(如continuc_train.yaml)。
  2. 训练控制

    • 点击“开始训练”:根据配置参数启动训练,训练日志会实时显示在下方文本框中(包括损失值、精度等指标)。
      在这里插入图片描述

    • 点击“停止训练”:中断当前训练(仅在训练中可用),已训练的中间结果会保存在runs/train/expN中。
      在这里插入图片描述

4. 目标检测功能

界面介绍

界面包含“检测模式选择”、“参数调整”、“操作按钮”和“检测结果显示”四部分,支持图片、视频、摄像头三种检测模式。
在这里插入图片描述

功能详解
  1. 检测模式选择

    模式说明
    图片检测对单张图片进行检测:点击“上传图片”选择图片文件,点击“开始检测”生成带标注框的结果图。
    视频检测对本地视频文件进行逐帧检测:点击“上传视频”选择视频文件,可勾选“保存检测结果”并设置输出路径(默认output.mp4),点击“开始检测”后进度条显示处理进度。
    摄像头检测实时检测摄像头画面:选择摄像头ID(默认0为内置摄像头),设置分辨率(如1280x720),点击“开始检测”显示实时检测结果;支持用本地视频或RTSP流模拟摄像头输入(需在“源类型”中切换)。
  2. 参数调整

    参数名称说明
    置信度阈值(conf)目标检测的置信度过滤阈值(0-1):
    - 值越高,检测结果越严格(减少误检,但可能漏检);
    - 默认0.25,可根据需求调整(如提高到0.5过滤低置信度目标)。
    IOU阈值(iou)非极大值抑制(NMS)的IOU阈值(0-1):
    - 用于合并重叠的检测框,值越低,保留的框越少;
    - 默认0.45,值过高可能导致重复标注,值过低可能漏检。
    当前模型显示已加载的检测模型(需先通过“上传模型”选择.pt文件)。
  3. 操作按钮

    • 上传模型:选择训练好的.pt模型文件(如runs/train/exp5/weights/best.pt),加载后可开始检测。
    • 开始检测:根据当前模式启动检测(图片/视频/摄像头)。
    • 停止检测:中断当前检测(仅在视频/摄像头模式中可用)。
      在这里插入图片描述

三、注意事项

  1. 标注时建议使用英文类别名称,避免中文导致的编码问题;
  2. 训练时若出现显存不足,可减小批次大小(batch size)或图像尺寸(imgsz);
  3. 模型转换为OpenVINO格式后,可显著提升在Intel设备上的推理速度,适合部署;
  4. 新标注的数据需放在new_Data目录,并确保data目录下的.yaml文件正确指向该路径。

四、使用建议与最佳实践

1、数据准备

将原始图片放入 new_Data/images 目录。

使用平台工具统一转换为 JPG 格式。

使用 LabelImg 进行标注,保存为 YOLO 格式。

运行 json2txt.py 转换已有 JSON 标注。

2、训练流程

初始训练:

python train.py --weights yolov5n.pt --epochs 100 --data custom_data.yaml

增量训练:

python train.py --weights runs/train/exp1/weights/best.pt --epochs 50

模型验证:

python detect.py --weights runs/train/exp1/weights/best.pt --source test_images/

3、模型部署

导出 OpenVINO 模型:

python export.py --weights best.pt --include openvino

执行 INT8 量化:

python Demo_int8.py --model_path runs/train/exp1/weights/best.xml

五、系统源码

import copy
from datetime import datetime
import os
import shutil
import random
import threading
import sys
import cv2
import torch
import numpy as np
import yaml
import time
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PIL import Image
import re
import subprocess
import argparse
import warningswarnings.filterwarnings("ignore", category=FutureWarning)# 常用的字符串常量
WINDOW_TITLE = "基于多模型AI训练与验证系统"
WELCOME_SENTENCE = "欢迎使用基于YOLOv5的图像识别与物体缺陷检测系统"
ICON_IMAGE = "UI/HuiZanTuBiao.jpg"
IMAGE_LEFT_INIT = "UI/1.png"
IMAGE_RIGHT_INIT = "UI/img_1.png"class TrainingThread(QThread):"""专门用于训练的子线程"""log_signal = Signal(str)finished_signal = Signal(bool)def __init__(self, parent, cmd, cwd, opt):super().__init__(parent)self.cmd = cmdself.cwd = cwdself.opt = optself._is_running = Truedef run(self):try:# 获取当前程序运行的实际路径(关键!)if getattr(sys, 'frozen', False):# 打包后的环境base_path = sys._MEIPASSelse:# 开发环境base_path = os.path.dirname(os.path.abspath(__file__))# 设置环境变量env = os.environ.copy()# 根据用户选择设置设备if self.opt.device == "cpu":env['CUDA_VISIBLE_DEVICES'] = '-1'  # 禁用GPUelse:env['CUDA_VISIBLE_DEVICES'] = self.opt.device# 构建完整的命令行full_cmd = ['python', os.path.join(base_path, 'train.py'),  # 关键:使用实际路径'--imgsz', str(self.opt.imgsz),'--batch', str(self.opt.batch),'--epochs', str(self.opt.epochs),'--data', os.path.join(base_path, self.opt.data),  # 数据集配置也需要动态路径'--weights', os.path.join(base_path, self.opt.weights),  # 权重文件路径'--device', self.opt.device,'--project', self.opt.project,'--name', self.opt.name]if self.opt.resume:full_cmd.append('--resume')self.log_signal.emit(f"[系统] 启动训练命令: {' '.join(full_cmd)}")# 创建进程process = subprocess.Popen(full_cmd,cwd=self.cwd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=False,env=env,creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == 'win32' else 0)self.process = processself.log_signal.emit(f"[系统] 训练进程已启动, PID: {process.pid}")# 常见编码列表,按优先级排序encodings = ['utf-8', 'gbk', 'latin1', 'ascii']while self._is_running:raw_output = process.stdout.readline()# 检查进程是否结束if not raw_output and process.poll() is not None:break# 检查停止标志if not self._is_running:self.log_signal.emit("[系统] 训练被用户终止")self.stop()self.finished_signal.emit(False)returnif raw_output:decoded = Nonefor encoding in encodings:try:decoded = raw_output.decode(encoding)breakexcept UnicodeDecodeError:continueif decoded:self.log_signal.emit(decoded.strip())else:self.log_signal.emit(raw_output.decode('utf-8', errors='replace').strip())time.sleep(0.1)  # 避免CPU占用过高return_code = process.poll()self.finished_signal.emit(return_code == 0)except Exception as e:self.log_signal.emit(f"训练错误: {str(e)}")self.finished_signal.emit(False)def stop(self):"""停止训练"""self._is_running = Falseself.log_signal.emit("[系统] 收到停止训练请求...")# 终止子进程if hasattr(self, 'process') and self.process:try:# Windows系统if sys.platform == 'win32':import ctypesPROCESS_TERMINATE = 1handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.process.pid)if handle:ctypes.windll.kernel32.TerminateProcess(handle, -1)ctypes.windll.kernel32.CloseHandle(handle)else:# Linux/Mac系统import signalos.kill(self.process.pid, signal.SIGKILL)except Exception as e:self.log_signal.emit(f"[错误] 终止进程时出错: {str(e)}")self.terminate()class VideoDetectionThread(QThread):"""视频检测线程"""progress_updated = Signal(int)frame_processed = Signal(str)finished = Signal()error_occurred = Signal(str)def __init__(self, model, video_path, output_path, conf_thres, iou_thres, save_result):super().__init__()self.model = modelself.video_path = video_pathself.output_path = output_pathself.conf_thres = conf_thresself.iou_thres = iou_thresself.save_result = save_resultself._is_running = Truedef run(self):try:# 打开视频文件cap = cv2.VideoCapture(self.video_path)if not cap.isOpened():self.error_occurred.emit("无法打开视频文件")return# 获取视频信息frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))fps = cap.get(cv2.CAP_PROP_FPS)width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))# 设置模型参数self.model.conf = self.conf_thresself.model.iou = self.iou_thres# 准备输出视频out = Noneif self.save_result and self.output_path:fourcc = cv2.VideoWriter_fourcc(*'mp4v')out = cv2.VideoWriter(self.output_path, fourcc, fps, (width, height))# 处理每一帧processed_frames = 0while self._is_running and processed_frames < frame_count:ret, frame = cap.read()if not ret:break# 转换颜色空间并检测frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)results = self.model(frame_rgb, size=640)# 绘制检测结果detected_frame = results.render()[0]detected_frame_bgr = cv2.cvtColor(detected_frame, cv2.COLOR_RGB2BGR)# 保存处理后的帧if out:out.write(detected_frame_bgr)# 保存当前帧用于显示temp_frame_path = os.path.join("data/tmp", "current_frame.jpg")cv2.imwrite(temp_frame_path, detected_frame_bgr)# 发送信号更新UIself.frame_processed.emit(temp_frame_path)progress = int((processed_frames / frame_count) * 100)self.progress_updated.emit(progress)processed_frames += 1# 控制处理速度,避免过快消耗资源time.sleep(0.01)# 释放资源cap.release()if out:out.release()self.finished.emit()except Exception as e:self.error_occurred.emit(str(e))class CameraDetectionThread(QThread):"""摄像头检测线程"""frame_processed = Signal(str)error_occurred = Signal(str)def __init__(self, model, camera_id, conf_thres, iou_thres, resolution):super().__init__()self.model = modelself.camera_id = camera_idself.conf_thres = conf_thresself.iou_thres = iou_thresself.resolution = resolution  # (width, height)self._is_running = Truedef run(self):try:# 打开摄像头cap = cv2.VideoCapture(self.camera_id)if not cap.isOpened():self.error_occurred.emit(f"无法打开摄像头 (ID: {self.camera_id})")return# 设置分辨率cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.resolution[0])cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.resolution[1])# 设置模型参数self.model.conf = self.conf_thresself.model.iou = self.iou_thres# 实时检测while self._is_running:ret, frame = cap.read()if not ret:self.error_occurred.emit("无法获取摄像头帧")break# 转换颜色空间并检测frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)results = self.model(frame_rgb, size=640)# 绘制检测结果detected_frame = results.render()[0]detected_frame_bgr = cv2.cvtColor(detected_frame, cv2.COLOR_RGB2BGR)# 保存当前帧用于显示temp_frame_path = os.path.join("data/tmp", "camera_frame.jpg")cv2.imwrite(temp_frame_path, detected_frame_bgr)# 发送信号更新UIself.frame_processed.emit(temp_frame_path)# 控制帧率time.sleep(0.03)  # 大约30fps# 释放资源cap.release()except Exception as e:self.error_occurred.emit(str(e))def stop(self):self._is_running = Falseself.wait()class MainWindow(QTabWidget):log_signal = Signal(str)training_finished_signal = Signal(bool)training_started_signal = Signal()training_stopped_signal = Signal()def init_training_system(self):"""初始化训练系统"""self.training_thread = Noneself.is_training = Falseself.log_signal.connect(self.update_training_log)self.training_finished_signal.connect(self.handle_training_finished)self.training_started_signal.connect(self.enable_stop_button)self.training_stopped_signal.connect(self.disable_stop_button)def initUI(self):"""图形化界面初始化"""# 设置中文字体font = QFont('SimHei')QApplication.setFont(font)if getattr(sys, 'frozen', False):base_path = sys._MEIPASSelse:base_path = os.path.dirname(os.path.abspath(__file__))# 修正资源路径(根据实际目录结构调整,这里假设 UI 文件夹和代码同级)IMAGE_LEFT_INIT = os.path.join(base_path, "UI/1.jpg")IMAGE_RIGHT_INIT = os.path.join(base_path, "UI/img.png")ICON_IMAGE = os.path.join(base_path, "UI/HuiZanTuBiao.jpg")# ********************* 数据标注功能 *****************************data_annotation_widget = QWidget()data_annotation_layout = QVBoxLayout()annotation_title = QLabel("数据标注功能")annotation_title.setFont(QFont('SimHei', 16, QFont.Bold))annotation_title.setAlignment(Qt.AlignCenter)# 图片选择区域annotation_group = QGroupBox("图片选择")annotation_layout = QVBoxLayout()image_path_layout = QHBoxLayout()image_path_label = QLabel("图片路径:")self.annotation_path_input = QLineEdit()browse_image_button = QPushButton("浏览...")browse_image_button.clicked.connect(self.browse_annotation_images)image_path_layout.addWidget(image_path_label)image_path_layout.addWidget(self.annotation_path_input)image_path_layout.addWidget(browse_image_button)format_layout = QHBoxLayout()format_label = QLabel("统一格式:")self.format_combobox = QComboBox()self.format_combobox.addItems(["JPG", "PNG", "BMP"])format_button = QPushButton("转换格式")format_button.clicked.connect(self.convert_image_format)format_layout.addWidget(format_label)format_layout.addWidget(self.format_combobox)format_layout.addWidget(format_button)label_button = QPushButton("打开标注软件")label_button.setMinimumHeight(40)label_button.clicked.connect(self.open_labeling_tool)annotation_layout.addLayout(image_path_layout)annotation_layout.addLayout(format_layout)annotation_layout.addWidget(label_button)annotation_group.setLayout(annotation_layout)# 标注说明instruction_label = QLabel("""<h3>标注说明</h3><p>1. 点击"浏览..."按钮选择要标注的图片文件夹</p><p>2. 如需统一图片格式,选择目标格式并点击"转换格式"</p><p>3. 点击"打开标注软件"启动labelImg进行标注</p><p>4. 标注文件请保存为YOLO格式(.txt)</p>""")instruction_label.setWordWrap(True)data_annotation_layout.addWidget(annotation_title)data_annotation_layout.addWidget(annotation_group)data_annotation_layout.addWidget(instruction_label)data_annotation_widget.setLayout(data_annotation_layout)# ********************* 开始训练功能 *****************************training_widget = QWidget()training_layout = QVBoxLayout()training_title = QLabel("开始训练功能")training_title.setFont(QFont('SimHei', 16, QFont.Bold))training_title.setAlignment(Qt.AlignCenter)# 训练参数设置training_group = QGroupBox("训练参数设置")training_params_layout = QGridLayout()# 第一行:权重路径weights_layout = QHBoxLayout()weights_label = QLabel("预训练权重:")self.weights_input = QLineEdit()self.weights_input.setText(r"yolov5n.pt")browse_weights_button = QPushButton("浏览...")browse_weights_button.clicked.connect(self.browse_weights)weights_layout.addWidget(weights_label)weights_layout.addWidget(self.weights_input)weights_layout.addWidget(browse_weights_button)# 第二行:训练轮次和批次大小epochs_layout = QHBoxLayout()epochs_label = QLabel("训练轮次 (epochs):")self.epochs_spinbox = QSpinBox()self.epochs_spinbox.setRange(1, 1000)self.epochs_spinbox.setValue(50)epochs_layout.addWidget(epochs_label)epochs_layout.addWidget(self.epochs_spinbox)batch_layout = QHBoxLayout()batch_label = QLabel("批次大小 (batch size):")self.batch_spinbox = QSpinBox()self.batch_spinbox.setRange(1, 128)self.batch_spinbox.setValue(16)batch_layout.addWidget(batch_label)batch_layout.addWidget(self.batch_spinbox)# 第三行:图像大小和设备imgsz_layout = QHBoxLayout()imgsz_label = QLabel("输入图像大小:")self.imgsz_spinbox = QSpinBox()self.imgsz_spinbox.setRange(128, 1024)self.imgsz_spinbox.setValue(640)self.imgsz_spinbox.setSingleStep(32)imgsz_layout.addWidget(imgsz_label)imgsz_layout.addWidget(self.imgsz_spinbox)device_layout = QHBoxLayout()device_label = QLabel("训练设备:")self.device_combobox = QComboBox()self.device_combobox.addItems(["auto", "cpu", "0", "1"])self.device_combobox.setCurrentIndex(0)device_layout.addWidget(device_label)device_layout.addWidget(self.device_combobox)# 第四行:项目名称和实验名称project_layout = QHBoxLayout()project_label = QLabel("项目名称:")self.project_input = QLineEdit("runs/train")project_layout.addWidget(project_label)project_layout.addWidget(self.project_input)name_layout = QHBoxLayout()name_label = QLabel("实验名称:")self.name_input = QLineEdit("exp")name_layout.addWidget(name_label)name_layout.addWidget(self.name_input)# 第五行:恢复训练和数据集路径resume_layout = QHBoxLayout()resume_label = QLabel("恢复训练:")self.resume_checkbox = QCheckBox()resume_layout.addWidget(resume_label)resume_layout.addWidget(self.resume_checkbox)data_path_layout = QHBoxLayout()data_path_label = QLabel("数据集配置文件:")self.data_path_input = QLineEdit()self.data_path_input.setText(r"data.yaml")browse_data_button = QPushButton("浏览...")browse_data_button.clicked.connect(self.browse_data_config)data_path_layout.addWidget(data_path_label)data_path_layout.addWidget(self.data_path_input)data_path_layout.addWidget(browse_data_button)# 将布局添加到网格training_params_layout.addLayout(weights_layout, 0, 0, 1, 2)training_params_layout.addLayout(epochs_layout, 1, 0)training_params_layout.addLayout(batch_layout, 1, 1)training_params_layout.addLayout(imgsz_layout, 2, 0)training_params_layout.addLayout(device_layout, 2, 1)training_params_layout.addLayout(project_layout, 3, 0)training_params_layout.addLayout(name_layout, 3, 1)training_params_layout.addLayout(resume_layout, 4, 0)training_params_layout.addLayout(data_path_layout, 4, 1)# 训练按钮self.train_button = QPushButton("开始训练")self.train_button.setMinimumHeight(40)self.train_button.clicked.connect(self.start_training)# 停止按钮self.stop_train_button = QPushButton("停止训练")self.stop_train_button.setMinimumHeight(40)self.stop_train_button.setEnabled(False)self.stop_train_button.clicked.connect(self.stop_training)# 训练日志self.log_textedit = QTextEdit()self.log_textedit.setReadOnly(True)self.log_textedit.setMinimumHeight(300)# 添加组件到训练布局training_layout.addLayout(training_params_layout)training_layout.addWidget(self.train_button)training_layout.addWidget(self.stop_train_button)training_layout.addWidget(self.log_textedit)training_group.setLayout(training_layout)training_widget.setLayout(QVBoxLayout())training_widget.layout().addWidget(training_title)training_widget.layout().addWidget(training_group)# ********************* 目标检测功能 *****************************detection_widget = QWidget()detection_layout = QVBoxLayout()detection_title = QLabel("目标检测功能")detection_title.setFont(QFont('SimHei', 16, QFont.Bold))detection_title.setAlignment(Qt.AlignCenter)# 检测模式选择mode_group = QGroupBox("检测模式")mode_layout = QHBoxLayout()self.image_mode_radio = QRadioButton("图片检测")self.image_mode_radio.setChecked(True)self.video_mode_radio = QRadioButton("视频检测")self.camera_mode_radio = QRadioButton("摄像头检测")# 连接模式切换信号self.image_mode_radio.toggled.connect(self.switch_detection_mode)self.video_mode_radio.toggled.connect(self.switch_detection_mode)self.camera_mode_radio.toggled.connect(self.switch_detection_mode)mode_layout.addWidget(self.image_mode_radio)mode_layout.addWidget(self.video_mode_radio)mode_layout.addWidget(self.camera_mode_radio)mode_group.setLayout(mode_layout)detection_layout.addWidget(mode_group)# 检测区域detection_area = QSplitter(Qt.Horizontal)# 左侧图像显示区域left_panel = QWidget()left_layout = QVBoxLayout()images_panel = QSplitter(Qt.Vertical)# 图片显示设置self.original_image_label = QLabel()pixmap = QPixmap(IMAGE_LEFT_INIT)scaled_pixmap = pixmap.scaled(786, 428, Qt.KeepAspectRatio, Qt.SmoothTransformation)self.original_image_label.setPixmap(scaled_pixmap)self.original_image_label.setAlignment(Qt.AlignCenter)self.original_image_label.setMinimumSize(400, 300)self.detected_image_label = QLabel()pixmap = QPixmap(IMAGE_RIGHT_INIT)scaled_pixmap = pixmap.scaled(786, 428, Qt.KeepAspectRatio, Qt.SmoothTransformation)self.detected_image_label.setPixmap(scaled_pixmap)self.detected_image_label.setAlignment(Qt.AlignCenter)self.detected_image_label.setMinimumSize(400, 300)# 设置窗口图标self.setWindowIcon(QIcon(ICON_IMAGE))  # 用动态路径images_panel.addWidget(self.original_image_label)images_panel.addWidget(self.detected_image_label)images_panel.setSizes([300, 300])left_layout.addWidget(images_panel)# 操作按钮button_layout = QHBoxLayout()# 图片/视频上传按钮self.upload_media_button = QPushButton("上传图片")self.upload_media_button.setMinimumHeight(35)self.upload_media_button.clicked.connect(self.upload_media)# 开始检测按钮self.start_detect_button = QPushButton("开始检测")self.start_detect_button.setMinimumHeight(35)self.start_detect_button.clicked.connect(self.start_detection)# 停止检测按钮(用于视频和摄像头)self.stop_detect_button = QPushButton("停止检测")self.stop_detect_button.setMinimumHeight(35)self.stop_detect_button.setEnabled(False)self.stop_detect_button.clicked.connect(self.stop_detection)# 添加模型上传按钮upload_model_button = QPushButton("上传模型")upload_model_button.setMinimumHeight(35)upload_model_button.clicked.connect(self.upload_model)button_layout.addWidget(self.upload_media_button)button_layout.addWidget(self.start_detect_button)button_layout.addWidget(self.stop_detect_button)button_layout.addWidget(upload_model_button)left_layout.addLayout(button_layout)# 视频进度条容器self.video_progress_container = QWidget()self.video_progress_layout = QHBoxLayout(self.video_progress_container)self.video_progress_label = QLabel("处理进度:")self.video_progress_bar = QProgressBar()self.video_progress_bar.setValue(0)self.video_progress_layout.addWidget(self.video_progress_label)self.video_progress_layout.addWidget(self.video_progress_bar)self.video_progress_container.setVisible(False)  # 默认隐藏left_layout.addWidget(self.video_progress_container)left_panel.setLayout(left_layout)# 右侧参数调整和结果显示区域right_panel = QWidget()right_layout = QVBoxLayout()# 参数调整params_group = QGroupBox("参数调整")params_layout = QGridLayout()conf_layout = QHBoxLayout()conf_label = QLabel("置信度阈值:")self.conf_slider = QSlider(Qt.Horizontal)self.conf_slider.setRange(0, 100)self.conf_slider.setValue(25)  # 默认0.25self.conf_value_label = QLabel("0.25")# 连接滑块值改变信号到更新函数self.conf_slider.valueChanged.connect(self.update_conf_thres)conf_layout.addWidget(conf_label)conf_layout.addWidget(self.conf_slider)conf_layout.addWidget(self.conf_value_label)iou_layout = QHBoxLayout()iou_label = QLabel("IOU阈值:")self.iou_slider = QSlider(Qt.Horizontal)self.iou_slider.setRange(0, 100)self.iou_slider.setValue(45)  # 默认0.45self.iou_value_label = QLabel("0.45")# 连接滑块值改变信号到更新函数self.iou_slider.valueChanged.connect(self.update_iou_thres)iou_layout.addWidget(iou_label)iou_layout.addWidget(self.iou_slider)iou_layout.addWidget(self.iou_value_label)# 视频保存选项容器self.video_save_container = QWidget()self.video_save_layout = QHBoxLayout(self.video_save_container)self.save_result_checkbox = QCheckBox("保存检测结果")self.save_result_checkbox.setChecked(True)self.output_path_input = QLineEdit("output.mp4")browse_output_button = QPushButton("浏览...")browse_output_button.clicked.connect(self.browse_output_path)self.video_save_layout.addWidget(self.save_result_checkbox)self.video_save_layout.addWidget(self.output_path_input)self.video_save_layout.addWidget(browse_output_button)self.video_save_container.setVisible(False)  # 默认隐藏# 摄像头参数容器self.camera_params_container = QWidget()self.camera_params_layout = QVBoxLayout(self.camera_params_container)camera_id_layout = QHBoxLayout()camera_id_label = QLabel("摄像头ID:")self.camera_id_combobox = QComboBox()# 尝试检测可用摄像头self.detect_available_cameras()camera_id_layout.addWidget(camera_id_label)camera_id_layout.addWidget(self.camera_id_combobox)resolution_layout = QHBoxLayout()resolution_label = QLabel("分辨率:")self.resolution_combobox = QComboBox()self.resolution_combobox.addItems(["640x480", "1280x720", "1920x1080"])self.resolution_combobox.setCurrentIndex(1)  # 默认720presolution_layout.addWidget(resolution_label)resolution_layout.addWidget(self.resolution_combobox)self.camera_params_layout.addLayout(camera_id_layout)self.camera_params_layout.addLayout(resolution_layout)self.camera_params_container.setVisible(False)  # 默认隐藏# 添加模型状态显示model_status_layout = QHBoxLayout()model_label = QLabel("当前模型:")self.model_status_label = QLabel("未加载")self.model_status_label.setStyleSheet("color: gray;")model_status_layout.addWidget(model_label)model_status_layout.addWidget(self.model_status_label)# 添加各布局到参数网格params_layout.addLayout(conf_layout, 0, 0)params_layout.addLayout(iou_layout, 1, 0)params_layout.addWidget(self.video_save_container, 2, 0)  # 视频保存选项params_layout.addWidget(self.camera_params_container, 3, 0)  # 摄像头参数params_layout.addLayout(model_status_layout, 4, 0)params_group.setLayout(params_layout)# 结果显示results_group = QGroupBox("检测结果")results_layout = QVBoxLayout()self.results_textedit = QTextEdit()self.results_textedit.setReadOnly(True)self.results_textedit.setMinimumHeight(200)results_layout.addWidget(self.results_textedit)results_group.setLayout(results_layout)right_layout.addWidget(params_group)right_layout.addWidget(results_group)right_panel.setLayout(right_layout)detection_area.addWidget(left_panel)detection_area.addWidget(right_panel)detection_area.setSizes([800, 400])detection_layout.addWidget(detection_title)detection_layout.addWidget(detection_area)detection_widget.setLayout(detection_layout)# 添加所有选项卡到主窗口self.addTab(data_annotation_widget, "数据标注")self.addTab(training_widget, "开始训练")self.addTab(detection_widget, "目标检测")# 设置选项卡样式self.setStyleSheet("""QTabWidget::pane {border-top: 2px solid #C2C7CB;position: absolute;top: -0.5em;}QTabWidget::tab-bar {alignment: left;}QTabBar::tab {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #E1E1E1, stop: 0.4 #DDDDDD,stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3);border: 2px solid #C4C4C3;border-bottom-color: #C2C7CB;border-top-left-radius: 4px;border-top-right-radius: 4px;min-width: 8ex;padding: 5px;font-size: 14px;font-family: SimHei;}QTabBar::tab:selected, QTabBar::tab:hover {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #fafafa, stop: 0.4 #f4f4f4,stop: 0.5 #e7e7e7, stop: 1.0 #fafafa);}QTabBar::tab:selected {border-color: #9B9B9B;border-bottom-color: #C2C7CB;}QTabBar::tab:!selected {margin-top: 2px;}""")def browse_annotation_images(self):"""浏览并选择要标注的图片文件夹"""directory = QFileDialog.getExistingDirectory(self, "选择图片文件夹", os.getcwd())if directory:self.annotation_path_input.setText(directory)def browse_weights(self):"""浏览并选择预训练权重文件"""fileName, _ = QFileDialog.getOpenFileName(self, "选择预训练权重", os.getcwd(), "PT Files (*.pt)")if fileName:self.weights_input.setText(fileName)def browse_data_config(self):"""浏览并选择数据集配置文件"""fileName, _ = QFileDialog.getOpenFileName(self, "选择数据集配置文件", os.getcwd(), "YAML Files (*.yaml)")if fileName:self.data_path_input.setText(fileName)def convert_image_format(self):"""转换图片格式"""image_path = self.annotation_path_input.text()target_format = self.format_combobox.currentText().lower()if not image_path or not os.path.exists(image_path):QMessageBox.warning(self, "警告", "请选择有效的图片路径")return# 获取所有图片文件image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff']image_files = [f for f in os.listdir(image_path)if os.path.isfile(os.path.join(image_path, f))and os.path.splitext(f)[1].lower() in image_extensions]if not image_files:QMessageBox.warning(self, "警告", "未找到图片文件")return# 创建转换后的文件夹converted_path = os.path.join(image_path, f"converted_{target_format}")os.makedirs(converted_path, exist_ok=True)# 转换图片格式for i, img_file in enumerate(image_files):try:img_path = os.path.join(image_path, img_file)img = Image.open(img_path)# 生成新文件名base_name = os.path.splitext(img_file)[0]new_file = f"{base_name}.{target_format}"new_path = os.path.join(converted_path, new_file)# 保存为新格式img.save(new_path)except Exception as e:print(f"转换失败: {img_file}, 错误: {str(e)}")QMessageBox.information(self, "完成", f"图片格式转换完成\n转换后的图片保存在: {converted_path}")def open_labeling_tool(self):"""打开标注软件"""try:# 尝试启动labelImgimport subprocesssubprocess.Popen(['labelImg'])except Exception as e:QMessageBox.critical(self, "错误", f"无法启动labelImg: {str(e)}\n请确保labelImg已安装")def start_training(self):"""开始训练"""if self.is_training:QMessageBox.warning(self, "警告", "已有训练任务正在进行")return# 获取参数weights = self.weights_input.text()epochs = self.epochs_spinbox.value()batch_size = self.batch_spinbox.value()imgsz = self.imgsz_spinbox.value()data = self.data_path_input.text()device = self.device_combobox.currentText()resume = self.resume_checkbox.isChecked()project = self.project_input.text()name = self.name_input.text()# 验证参数if not all([weights, data]):QMessageBox.warning(self, "警告", "请填写所有必要参数")returnif not os.path.exists(weights):QMessageBox.warning(self, "警告", "权重文件不存在")returnif not os.path.exists(data):QMessageBox.warning(self, "警告", "数据集配置文件不存在")return# 构建 opt 对象opt = argparse.Namespace(weights=weights,epochs=epochs,batch=batch_size,imgsz=imgsz,data=data,device=device,resume=resume,project=project,name=name)# 启动训练self.is_training = Trueself.training_thread = TrainingThread(self, ['python', 'train.py'], os.getcwd(), opt)self.training_thread.log_signal.connect(self.update_training_log)self.training_thread.finished_signal.connect(self.handle_training_finished)self.training_thread.start()# 更新 UI 状态self.update_ui_for_training(True)@Slot(str)def update_training_log(self, message):"""更新训练日志"""self.log_textedit.append(message)# 自动滚动到底部self.log_textedit.verticalScrollBar().setValue(self.log_textedit.verticalScrollBar().maximum())@Slot(bool)def handle_training_finished(self, success):"""处理训练完成"""self.is_training = Falseself.update_ui_for_training(False)# 记录训练完成状态timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")if success:self.log_textedit.append(f"\n[系统] {timestamp} 训练成功完成")QMessageBox.information(self, "完成", "训练成功完成")else:self.log_textedit.append(f"\n[系统] {timestamp} 训练过程中出现错误")QMessageBox.warning(self, "警告", "训练过程中出现错误")def enable_stop_button(self):"""启用停止按钮(线程安全)"""self.stop_train_button.setEnabled(True)def disable_stop_button(self):"""禁用停止按钮(线程安全)"""self.stop_train_button.setEnabled(False)def update_ui_for_training(self, is_training):"""更新UI状态"""self.train_button.setEnabled(not is_training)if is_training:# 记录训练开始timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")self.training_started_signal.emit()self.log_textedit.clear()self.log_textedit.append(f"[系统] {timestamp} 训练开始...")self.log_textedit.append(f"[参数] 权重: {self.weights_input.text()}")self.log_textedit.append(f"[参数] 轮次: {self.epochs_spinbox.value()}")self.log_textedit.append(f"[参数] 批次大小: {self.batch_spinbox.value()}")self.log_textedit.append(f"[参数] 图像尺寸: {self.imgsz_spinbox.value()}")self.log_textedit.append(f"[参数] 数据集: {self.data_path_input.text()}")self.log_textedit.append(f"[参数] 设备: {self.device_combobox.currentText()}")self.log_textedit.append(f"[参数] 项目: {self.project_input.text()}")self.log_textedit.append(f"[参数] 实验: {self.name_input.text()}")else:self.training_stopped_signal.emit()def stop_training(self):"""停止训练"""if self.training_thread and self.is_training:self.log_textedit.append("\n[系统] 正在停止训练...")try:# 停止训练线程self.training_thread.stop()self.is_training = Falseself.update_ui_for_training(False)self.log_textedit.append("[系统] 训练已成功停止")QMessageBox.information(self, "提示", "训练已停止")except Exception as e:self.log_textedit.append(f"[错误] 停止训练时发生异常: {str(e)}")QMessageBox.critical(self, "错误", f"停止训练时发生错误: {str(e)}")def update_conf_thres(self, value):"""更新置信度阈值"""self.conf_thres = value / 100.0self.conf_value_label.setText(f"{self.conf_thres:.2f}")def update_iou_thres(self, value):"""更新IOU阈值"""self.iou_thres = value / 100.0self.iou_value_label.setText(f"{self.iou_thres:.2f}")def upload_model(self):"""上传模型文件"""fileName, _ = QFileDialog.getOpenFileName(self, "选择模型文件", os.getcwd(),"PyTorch模型文件 (*.pt)")if fileName:# 验证模型文件if not os.path.exists(fileName):QMessageBox.warning(self, "警告", "模型文件不存在")return# 获取文件扩展名_, ext = os.path.splitext(fileName)ext = ext.lower()# 验证模型格式if ext != '.pt':QMessageBox.warning(self, "警告", "本系统目前只支持PyTorch模型(.pt),请使用.pt格式的模型")returntry:# 验证模型文件大小file_size = os.path.getsize(fileName) / (1024 * 1024)  # MBif file_size < 1:QMessageBox.warning(self, "警告", "模型文件过小,可能无效")return# 尝试加载模型进行验证model = torch.hub.load('ultralytics/yolov5', 'custom', path=fileName)model.to(self.device)model.eval()# 验证模型结构if not hasattr(model, 'model'):QMessageBox.warning(self, "警告", "无效的PyTorch模型结构")return# 验证通过,设置当前模型self.model_path = fileNameself.model_loaded = False# 启动异步加载模型threading.Thread(target=self.load_model_async, daemon=True).start()# 更新UI状态self.model_status_label.setText(f"加载中... ({os.path.basename(fileName)})")self.model_status_label.setStyleSheet("color: orange;")except Exception as e:QMessageBox.critical(self, "模型验证失败", f"无法加载模型: {str(e)}")self.model_status_label.setText("加载失败")self.model_status_label.setStyleSheet("color: red;")returndef __init__(self):# 初始化界面super().__init__()self.setWindowTitle(WINDOW_TITLE)self.resize(1200, 800)self.setWindowIcon(QIcon(ICON_IMAGE))self.output_size = 480self.img2predict = ""self.video2predict = ""self.video_detection_thread = Noneself.camera_detection_thread = None# 创建临时目录os.makedirs(os.path.join("data", "tmp"), exist_ok=True)# 初始化训练系统self.init_training_system()# 初始化检测参数self.conf_thres = 0.25self.iou_thres = 0.45self.model = Noneself.model_loaded = Falseself.initUI()# 启动异步加载模型threading.Thread(target=self.load_model_async, daemon=True).start()def load_model_async(self):try:self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# ↓↓↓ 新增动态路径获取逻辑 ↓↓↓# 获取当前程序运行的实际路径(关键!)if getattr(sys, 'frozen', False):# 打包后的环境base_path = sys._MEIPASSelse:# 开发环境base_path = os.path.dirname(os.path.abspath(__file__))# 优先用上传的模型路径,没有则用默认self.model_path = getattr(self, 'model_path', os.path.join(base_path, "yolov5n.pt"))# 验证模型文件是否存在if not os.path.exists(self.model_path):self.model_status_label.setText("模型文件不存在")self.model_status_label.setStyleSheet("color: red;")return# 加载PyTorch模型self.model = torch.hub.load('ultralytics/yolov5', 'custom', path=self.model_path)self.model.to(self.device)self.model.eval()# 模型加载成功self.model_loaded = Trueself.model_status_label.setText(f"已加载: {os.path.basename(self.model_path)}")self.model_status_label.setStyleSheet("color: green;")except Exception as e:print(f"模型加载失败: {str(e)}")self.model_status_label.setText(f"加载失败: {os.path.basename(self.model_path) if hasattr(self, 'model_path') else '无'}")self.model_status_label.setStyleSheet("color: red;")def upload_media(self):"""上传图像或视频"""if self.image_mode_radio.isChecked():# 图片模式fileName, _ = QFileDialog.getOpenFileName(self, "选择图像", os.getcwd(),"图像文件 (*.jpg *.jpeg *.png *.bmp)")if fileName:# 保存图像路径self.img2predict = fileNameself.video2predict = ""# 显示原始图像pixmap = QPixmap(fileName)if not pixmap.isNull():scaled_pixmap = pixmap.scaled(self.original_image_label.size(),Qt.KeepAspectRatio, Qt.SmoothTransformation)self.original_image_label.setPixmap(scaled_pixmap)self.detected_image_label.setPixmap(QPixmap(IMAGE_RIGHT_INIT))self.results_textedit.clear()elif self.video_mode_radio.isChecked():# 视频模式fileName, _ = QFileDialog.getOpenFileName(self, "选择视频", os.getcwd(),"视频文件 (*.mp4 *.avi *.mov *.mkv)")if fileName:# 保存视频路径self.video2predict = fileNameself.img2predict = ""# 显示视频第一帧作为预览cap = cv2.VideoCapture(fileName)ret, frame = cap.read()if ret:frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)h, w, c = frame.shapeqimg = QImage(frame_rgb.data, w, h, w * c, QImage.Format_RGB888)pixmap = QPixmap.fromImage(qimg)scaled_pixmap = pixmap.scaled(self.original_image_label.size(),Qt.KeepAspectRatio, Qt.SmoothTransformation)self.original_image_label.setPixmap(scaled_pixmap)self.detected_image_label.setPixmap(QPixmap(IMAGE_RIGHT_INIT))self.results_textedit.clear()cap.release()def start_detection(self):"""开始检测(根据当前模式)"""if self.model is None or not self.model_loaded:QMessageBox.warning(self, "警告", "模型未加载,请先加载模型")returnif self.image_mode_radio.isChecked():# 图片检测self.start_image_detection()elif self.video_mode_radio.isChecked():# 视频检测self.start_video_detection()elif self.camera_mode_radio.isChecked():# 摄像头检测self.start_camera_detection()def start_image_detection(self):"""开始图片检测"""if not self.img2predict or not os.path.exists(self.img2predict):QMessageBox.warning(self, "警告", "请先上传图像")return# 显示处理中self.results_textedit.setText("正在检测...")# 在单独的线程中执行检测self.detection_thread = threading.Thread(target=self.run_detection)self.detection_thread.daemon = Trueself.detection_thread.start()def start_video_detection(self):"""开始视频检测"""if not self.video2predict or not os.path.exists(self.video2predict):QMessageBox.warning(self, "警告", "请先上传视频")return# 获取保存路径output_path = self.output_path_input.text() if self.save_result_checkbox.isChecked() else None# 显示处理中self.results_textedit.setText("正在准备视频检测...")self.video_progress_bar.setValue(0)# 禁用相关按钮self.start_detect_button.setEnabled(False)self.upload_media_button.setEnabled(False)self.stop_detect_button.setEnabled(True)# 创建并启动视频检测线程self.video_detection_thread = VideoDetectionThread(self.model,self.video2predict,output_path,self.conf_thres,self.iou_thres,self.save_result_checkbox.isChecked())# 连接信号self.video_detection_thread.progress_updated.connect(self.update_video_progress)self.video_detection_thread.frame_processed.connect(self.update_video_frame)self.video_detection_thread.finished.connect(self.video_detection_finished)self.video_detection_thread.error_occurred.connect(self.show_detection_error)self.video_detection_thread.start()def start_camera_detection(self):"""开始摄像头检测"""# 获取摄像头IDtry:camera_id = int(self.camera_id_combobox.currentText())except ValueError:QMessageBox.warning(self, "警告", "请选择有效的摄像头ID")return# 获取分辨率resolution_str = self.resolution_combobox.currentText()width, height = map(int, resolution_str.split('x'))# 显示处理中self.results_textedit.setText("正在启动摄像头检测...")# 禁用相关按钮self.start_detect_button.setEnabled(False)self.upload_media_button.setEnabled(False)self.stop_detect_button.setEnabled(True)self.camera_id_combobox.setEnabled(False)self.resolution_combobox.setEnabled(False)# 创建并启动摄像头检测线程self.camera_detection_thread = CameraDetectionThread(self.model,camera_id,self.conf_thres,self.iou_thres,(width, height))# 连接信号self.camera_detection_thread.frame_processed.connect(self.update_camera_frame)self.camera_detection_thread.error_occurred.connect(self.show_detection_error)self.camera_detection_thread.start()def stop_detection(self):"""停止检测(视频或摄像头)"""if self.video_detection_thread and self.video_detection_thread.isRunning():self.video_detection_thread._is_running = Falseself.video_detection_thread.wait()self.video_detection_finished()elif self.camera_detection_thread and self.camera_detection_thread.isRunning():self.camera_detection_thread.stop()self.camera_detection_finished()def run_detection(self):"""执行图像检测"""try:# 读取图像img = cv2.imread(self.img2predict)img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 设置置信度和IOU阈值self.model.conf = self.conf_thres  # 置信度阈值self.model.iou = self.iou_thres  # IOU阈值# 执行检测results = self.model(img, size=640)detected_img = results.render()[0]# 提取检测结果信息result_info = []for box in results.xyxy[0]:class_id = int(box[5])conf = float(box[4])label = self.model.names[class_id]x1, y1, x2, y2 = map(int, box[:4])result_info.append(f"- {label}: 置信度 {conf:.2f}, 位置: ({x1}, {y1}), ({x2}, {y2})")# 保存检测结果result_path = os.path.join("data/tmp", "detection_result.jpg")detected_img = cv2.cvtColor(detected_img, cv2.COLOR_RGB2BGR)cv2.imwrite(result_path, detected_img)# 显示检测结果QMetaObject.invokeMethod(self, "show_detection_result", Qt.QueuedConnection,Q_ARG(str, result_path),Q_ARG(str, "\n".join(result_info) if result_info else "未检测到目标"))except Exception as e:QMetaObject.invokeMethod(self, "show_detection_error", Qt.QueuedConnection,Q_ARG(str, str(e)))@Slot(str, str)def show_detection_result(self, result_path, result_info):"""显示检测结果"""# 显示检测后的图像pixmap = QPixmap(result_path)scaled_pixmap = pixmap.scaled(self.detected_image_label.size(),Qt.KeepAspectRatio, Qt.SmoothTransformation)self.detected_image_label.setPixmap(scaled_pixmap)# 显示检测结果信息self.results_textedit.setText(result_info)@Slot(str)def show_detection_error(self, error_message):"""显示检测错误"""self.results_textedit.setText(f"检测出错: {error_message}")QMessageBox.critical(self, "检测错误", f"检测过程中出错: {error_message}")# 恢复UI状态self.start_detect_button.setEnabled(True)self.upload_media_button.setEnabled(True)self.stop_detect_button.setEnabled(False)self.camera_id_combobox.setEnabled(True)self.resolution_combobox.setEnabled(True)@Slot(int)def update_video_progress(self, progress):"""更新视频处理进度"""self.video_progress_bar.setValue(progress)self.results_textedit.setText(f"视频处理中... {progress}%")@Slot(str)def update_video_frame(self, frame_path):"""更新视频帧显示"""pixmap = QPixmap(frame_path)scaled_pixmap = pixmap.scaled(self.detected_image_label.size(),Qt.KeepAspectRatio, Qt.SmoothTransformation)self.detected_image_label.setPixmap(scaled_pixmap)@Slot(str)def update_camera_frame(self, frame_path):"""更新摄像头帧显示"""pixmap = QPixmap(frame_path)scaled_pixmap = pixmap.scaled(self.detected_image_label.size(),Qt.KeepAspectRatio, Qt.SmoothTransformation)self.detected_image_label.setPixmap(scaled_pixmap)def video_detection_finished(self):"""视频检测完成处理"""self.results_textedit.setText("视频检测完成!")self.video_progress_bar.setValue(100)# 恢复UI状态self.start_detect_button.setEnabled(True)self.upload_media_button.setEnabled(True)self.stop_detect_button.setEnabled(False)if self.save_result_checkbox.isChecked() and self.output_path_input.text():QMessageBox.information(self, "完成", f"视频检测已完成,结果已保存至:\n{self.output_path_input.text()}")def camera_detection_finished(self):"""摄像头检测完成处理"""self.results_textedit.setText("摄像头检测已停止")# 恢复UI状态self.start_detect_button.setEnabled(True)self.upload_media_button.setEnabled(True)self.stop_detect_button.setEnabled(False)self.camera_id_combobox.setEnabled(True)self.resolution_combobox.setEnabled(True)def switch_detection_mode(self):"""切换检测模式"""# 停止当前可能正在进行的检测self.stop_detection()if self.image_mode_radio.isChecked():# 图片模式self.upload_media_button.setText("上传图片")self.video_save_container.setVisible(False)self.camera_params_container.setVisible(False)self.video_progress_container.setVisible(False)self.start_detect_button.setText("开始检测")elif self.video_mode_radio.isChecked():# 视频模式self.upload_media_button.setText("上传视频")self.video_save_container.setVisible(True)self.camera_params_container.setVisible(False)self.video_progress_container.setVisible(True)self.start_detect_button.setText("开始视频检测")elif self.camera_mode_radio.isChecked():# 摄像头模式self.upload_media_button.setText("选择摄像头")self.video_save_container.setVisible(False)self.camera_params_container.setVisible(True)self.video_progress_container.setVisible(False)self.start_detect_button.setText("开始摄像头检测")# 刷新摄像头列表self.detect_available_cameras()def browse_output_path(self):"""浏览视频输出路径"""fileName, _ = QFileDialog.getSaveFileName(self, "保存视频结果", os.getcwd(),"MP4文件 (*.mp4);;AVI文件 (*.avi)")if fileName:self.output_path_input.setText(fileName)def detect_available_cameras(self):"""检测可用摄像头"""self.camera_id_combobox.clear()# 尝试检测前5个摄像头ID,通常足够for i in range(5):cap = cv2.VideoCapture(i)if cap.isOpened():self.camera_id_combobox.addItem(str(i))cap.release()# 如果没有检测到摄像头,添加默认ID 0if self.camera_id_combobox.count() == 0:self.camera_id_combobox.addItem("0")if __name__ == "__main__":app = QApplication(sys.argv)mainWindow = MainWindow()mainWindow.show()sys.exit(app.exec())
http://www.lryc.cn/news/599614.html

相关文章:

  • 移动端设备能部署的llm
  • MC_GearInPos电子齿轮
  • Pytest tmp_path 实战指南:测试中的临时目录管理
  • 基于单片机的数字电压表设计
  • MyBatis-Plus 指南
  • 光耦合器:新能源世界的“绿色信使“
  • 全面解析MySQL(3)——CRUD进阶与数据库约束:构建健壮数据系统的基石
  • Krpano 工具如何调节全景图片切割之后的分辨率
  • 代码随想录算法训练营第三十一天
  • 卡尔曼滤波器噪声方差设置对性能影响的仿真研究
  • MATLAB 设置默认启动路径为上次关闭路径的方法
  • 【优选算法】链表
  • 从 SQL Server 到 KingbaseES V9R4C12,一次“无痛”迁移与深度兼容体验实录
  • UG创建的实体橘黄色实体怎么改颜色?
  • 每日算法-数组合并
  • [RPA] Excel中的字典处理
  • ubuntu22.04.4锁定内核应对海光服务器升级内核无法启动问题
  • CPU(中央处理器)和GPU(图形处理器)的区别
  • 在线事务型的业务、实时分析类业务、离线处理类型的业务
  • 如何提高微信小程序的应用速度
  • 代码随想录算法训练营第五十三天|图论part4
  • 基于spring boot的纺织品企业财务管理系统(源码+论文)
  • vue+iview+i18n国际化
  • Qt:qRegisterMetaType函数使用介绍
  • 如何在 FastAPI 中玩转 GraphQL 和 WebSocket 的实时数据推送魔法?
  • 【数据库】AI驱动未来:电科金仓新一代数据库一体机如何重构性能边界?
  • ESP32学习笔记_Peripherals(4)——MCPWM基础使用
  • 内存优化:从堆分配到零拷贝的终极重构
  • IPv6实战指南:从接入到应用
  • 升级的MS2130S USB3.0高清视频采集芯片