PyQt5展示YOLOV8检测效果

结果示意:

代码:

import os
import sys
import time
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal, QThread, Qt
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import (QApplication, QMainWindow, QFileDialog,
                             QTableWidgetItem, QMessageBox)
from ultralytics import YOLO


class DetThread(QThread):
    send_imgraw = pyqtSignal(np.ndarray)
    send_imgresult = pyqtSignal(np.ndarray)
    send_result = pyqtSignal(object, dict)  # 修改信号类型

    def __init__(self):
        super().__init__()
        self.running = False
        self.source = None
        self.conf = 0.25
        self.iou = 0.45
        self.model = None
        self.cap = None
        self.mode = 'camera'

    def run(self):
        self.running = True
        try:
            if self.mode == 'image':
                self.process_image()
            else:
                self.process_video()
        except Exception as e:
            print(f"检测错误: {e}")
        finally:
            self.cleanup()

    def process_image(self):
        img = cv2.imread(self.source)
        if img is not None:
            start_time = time.time()
            results = self.model.predict(img, conf=self.conf, iou=self.iou)
            det_info = self._collect_det_info(results, img.shape, start_time)

            self.send_imgraw.emit(img)
            self.send_imgresult.emit(results[0].plot())
            self.send_result.emit(results[0].boxes, det_info)

    def process_video(self):
        self.cap = cv2.VideoCapture(self.source if self.mode == 'video' else 0)
        while self.running and self.cap.isOpened():
            ret, frame = self.cap.read()
            if not ret:
                break

            start_time = time.time()
            results = self.model.predict(frame, conf=self.conf, iou=self.iou)
            det_info = self._collect_det_info(results, frame.shape, start_time)

            self.send_imgraw.emit(frame)
            self.send_imgresult.emit(results[0].plot())
            self.send_result.emit(results[0].boxes, det_info)

            time.sleep(0.03)

    def _collect_det_info(self, results, shape, start_time):
        return {
            'shape': f"{shape[1]}x{shape[0]}",
            'objects': len(results[0]),
            'class_names': results[0].names,
            'speed': results[0].speed,
            'total_time': (time.time() - start_time) * 1000
        }

    def cleanup(self):
        if self.cap and self.cap.isOpened():
            self.cap.release()
        self.running = False

    def stop(self):
        self.running = False


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setup_ui()
        self.init_variables()
        self.connect_signals()
    def setup_ui(self):
        self.setWindowTitle("检测系统")
        self.resize(1200, 800)
        self.setStyleSheet("background-color: #FFFAFA;")

        # 主布局组件
        self.central_widget = QtWidgets.QWidget(self)
        self.setCentralWidget(self.central_widget)
        self.main_layout = QtWidgets.QHBoxLayout(self.central_widget)

        # 左侧控制面板
        self.left_panel = QtWidgets.QWidget()
        self.left_panel.setFixedWidth(400)
        self.left_layout = QtWidgets.QVBoxLayout(self.left_panel)

        # 数据源设置
        self.setup_data_source()
        # 模型参数设置
        self.setup_model_params()
        # 结果显示
        self.setup_results()

        self.main_layout.addWidget(self.left_panel)

        # 右侧显示区域
        self.right_panel = QtWidgets.QWidget()
        self.right_layout = QtWidgets.QVBoxLayout(self.right_panel)

        self.img_label_raw = QtWidgets.QLabel("原始图像")
        self.img_label_raw.setStyleSheet("background: #C0C0C0;")
        self.img_label_raw.setAlignment(Qt.AlignCenter)
        self.img_label_raw.setMinimumSize(640, 360)

        self.img_label_result = QtWidgets.QLabel("检测结果")
        self.img_label_result.setStyleSheet("background: #C0C0C0;")
        self.img_label_result.setAlignment(Qt.AlignCenter)
        self.img_label_result.setMinimumSize(640, 360)

        self.right_layout.addWidget(self.img_label_raw)
        self.right_layout.addWidget(self.img_label_result)
        self.main_layout.addWidget(self.right_panel)

    def setup_data_source(self):
        group = QtWidgets.QGroupBox("数据来源设置")
        layout = QtWidgets.QVBoxLayout()

        # 相机设置
        self.cam_radio_on = QtWidgets.QRadioButton("开启相机")
        self.cam_radio_off = QtWidgets.QRadioButton("关闭相机", checked=True)
        radio_layout = QtWidgets.QHBoxLayout()
        radio_layout.addWidget(self.cam_radio_on)
        radio_layout.addWidget(self.cam_radio_off)
        layout.addLayout(radio_layout)

        # 文件选择
        self.btn_file = QtWidgets.QPushButton("选择图片/视频")
        self.file_path = QtWidgets.QLineEdit()
        self.file_path.setReadOnly(True)
        file_layout = QtWidgets.QHBoxLayout()
        file_layout.addWidget(self.btn_file)
        file_layout.addWidget(self.file_path)
        layout.addLayout(file_layout)

        # 参数设置
        param_layout = QtWidgets.QFormLayout()
        self.pixel_format = QtWidgets.QLineEdit("RGB")
        self.resolution = QtWidgets.QLineEdit("640x640")
        self.exposure = QtWidgets.QLineEdit("1000")
        param_layout.addRow("像素格式:", self.pixel_format)
        param_layout.addRow("标准尺寸:", self.resolution)
        param_layout.addRow("曝光时间:", self.exposure)
        layout.addLayout(param_layout)

        group.setLayout(layout)
        self.left_layout.addWidget(group)

    def setup_model_params(self):
        group = QtWidgets.QGroupBox("模型参数设置")
        layout = QtWidgets.QVBoxLayout()

        # 模型选择
        self.btn_model = QtWidgets.QPushButton("选择模型")
        self.model_path = QtWidgets.QLineEdit()
        self.model_path.setReadOnly(True)
        model_layout = QtWidgets.QHBoxLayout()
        model_layout.addWidget(self.btn_model)
        model_layout.addWidget(self.model_path)
        layout.addLayout(model_layout)

        # 设备选择
        self.cpu_radio = QtWidgets.QRadioButton("CPU")
        self.gpu_radio = QtWidgets.QRadioButton("GPU", checked=True)
        device_layout = QtWidgets.QHBoxLayout()
        device_layout.addWidget(self.cpu_radio)
        device_layout.addWidget(self.gpu_radio)
        layout.addLayout(device_layout)

        # 置信度设置
        self.conf_slider = QtWidgets.QSlider(Qt.Horizontal)
        self.conf_slider.setRange(0, 100)
        self.conf_spin = QtWidgets.QDoubleSpinBox()
        self.conf_spin.setRange(0.0, 1.0)
        conf_layout = QtWidgets.QHBoxLayout()
        conf_layout.addWidget(QtWidgets.QLabel("置信度:"))
        conf_layout.addWidget(self.conf_spin)
        conf_layout.addWidget(self.conf_slider)
        layout.addLayout(conf_layout)

        # IoU阈值设置
        self.iou_slider = QtWidgets.QSlider(Qt.Horizontal)
        self.iou_slider.setRange(0, 100)
        self.iou_spin = QtWidgets.QDoubleSpinBox()
        self.iou_spin.setRange(0.0, 1.0)
        iou_layout = QtWidgets.QHBoxLayout()
        iou_layout.addWidget(QtWidgets.QLabel("IoU阈值:"))
        iou_layout.addWidget(self.iou_spin)
        iou_layout.addWidget(self.iou_slider)
        layout.addLayout(iou_layout)

        group.setLayout(layout)
        self.left_layout.addWidget(group)


    def setup_results(self):
        group = QtWidgets.QGroupBox("检测结果")
        layout = QtWidgets.QVBoxLayout()

        self.result_table = QtWidgets.QTableWidget()
        self.result_table.setColumnCount(4)  # 修改为4列
        self.result_table.setHorizontalHeaderLabels(["类别", "中心坐标", "尺寸", "置信度"])  # 修改表头
        self.result_table.verticalHeader().setVisible(False)
        self.result_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        layout.addWidget(self.result_table)

        # 控制按钮
        self.btn_start = QtWidgets.QPushButton("开始检测")
        self.btn_stop = QtWidgets.QPushButton("停止检测")
        btn_layout = QtWidgets.QHBoxLayout()
        btn_layout.addWidget(self.btn_start)
        btn_layout.addWidget(self.btn_stop)
        layout.addLayout(btn_layout)

        group.setLayout(layout)
        self.left_layout.addWidget(group)

    def init_variables(self):
        self.det_thread = None
        self.current_mode = None
        self.model = None

    def connect_signals(self):
         # 按钮信号
        self.btn_file.clicked.connect(self.select_file)
        self.btn_model.clicked.connect(self.select_model)
        self.btn_start.clicked.connect(self.start_detection)
        self.btn_stop.clicked.connect(self.stop_detection)

            # 参数联动
        self.conf_slider.valueChanged.connect(
            lambda v: self.conf_spin.setValue(v / 100))
        self.conf_spin.valueChanged.connect(
            lambda v: self.conf_slider.setValue(int(v * 100)))
        self.iou_slider.valueChanged.connect(
            lambda v: self.iou_spin.setValue(v / 100))
        self.iou_spin.valueChanged.connect(
            lambda v: self.iou_slider.setValue(int(v * 100)))

        # 相机模式切换
        self.cam_radio_on.toggled.connect(self.update_camera_mode)

    def select_file(self):
        file, _ = QFileDialog.getOpenFileName(
            self, "选择文件", "",
             "图片文件 (*.jpg *.png);;视频文件 (*.mp4 *.avi)")
        if file:
            self.file_path.setText(file)
            if file.lower().endswith(('.jpg', '.png')):
                self.current_mode = 'image'
            else:
                self.current_mode = 'video'

    def select_model(self):
        file, _ = QFileDialog.getOpenFileName(
            self, "选择模型", "", "模型文件 (*.pt)")
        if file:
            try:
                self.model = YOLO(file)
                self.model_path.setText(os.path.basename(file))
                QMessageBox.information(self, "成功", "模型加载成功!")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"模型加载失败: {str(e)}")

    def update_camera_mode(self, checked):
        self.current_mode = 'camera' if checked else None

    def start_detection(self):
        if not self.model:
            QMessageBox.warning(self, "警告", "请先选择模型文件!")
            return

        if self.det_thread and self.det_thread.isRunning():
            self.det_thread.stop()

        self.det_thread = DetThread()
        self.det_thread.model = self.model
        self.det_thread.conf = self.conf_spin.value()
        self.det_thread.iou = self.iou_spin.value()

        if self.current_mode == 'camera':
            self.det_thread.mode = 'camera'
            self.det_thread.source = 0
        elif self.current_mode == 'image':
            self.det_thread.mode = 'image'
            self.det_thread.source = self.file_path.text()
        elif self.current_mode == 'video':
            self.det_thread.mode = 'video'
            self.det_thread.source = self.file_path.text()
        else:
            QMessageBox.warning(self, "警告", "请先选择数据源!")
            return

        # 连接信号
        self.det_thread.send_imgraw.connect(self.show_raw_image)
        self.det_thread.send_imgresult.connect(self.show_result_image)
        self.det_thread.send_result.connect(self.update_results)

        self.det_thread.start()

    def stop_detection(self):
        if self.det_thread and self.det_thread.isRunning():
            self.det_thread.stop()
            self.det_thread.quit()
            self.det_thread.wait()

    def show_raw_image(self, img):
        self.display_image(img, self.img_label_raw)

    def show_result_image(self, img):
        self.display_image(img, self.img_label_result)

    def display_image(self, img, label):
        try:
            h, w, ch = img.shape
            bytes_per_line = ch * w
            q_img = QImage(img.data, w, h, bytes_per_line,
                            QImage.Format_RGB888).rgbSwapped()
            pixmap = QPixmap.fromImage(q_img)
            label.setPixmap(pixmap.scaled(
                label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        except Exception as e:
            print(f"显示图像错误: {e}")



    def update_results(self, boxes, det_info):
        try:
            # 更新状态栏信息
            speed_info = (
                f"Speed: {det_info['speed']['preprocess']:.1f}ms preprocess, "
                f"{det_info['speed']['inference']:.1f}ms inference, "
                f"{det_info['speed']['postprocess']:.1f}ms postprocess"
            )
            main_info = (
                f"{det_info['objects']} objects detected | "
                f"Image size: {det_info['shape']} | "
                f"Total: {det_info['total_time']:.1f}ms"
            )
            self.statusBar().showMessage(f"{main_info} | {speed_info}")

            # 更新表格数据
            self.result_table.setRowCount(det_info['objects'])
            for row, box in enumerate(boxes):
                class_id = int(box.cls[0])
                class_name = det_info['class_names'].get(class_id, "unknown")
                xywh = box.xywh[0].tolist()
                conf = box.conf[0].item()

                self.result_table.setItem(row, 0, QTableWidgetItem(class_name))
                self.result_table.setItem(row, 1, QTableWidgetItem(
                    f"({xywh[0]:.1f}, {xywh[1]:.1f})"))
                self.result_table.setItem(row, 2, QTableWidgetItem(
                    f"{xywh[2]:.1f}x{xywh[3]:.1f}"))
                self.result_table.setItem(row, 3, QTableWidgetItem(f"{conf:.2f}"))

        except Exception as e:
            print(f"更新结果错误: {e}")

    def closeEvent(self, event):
        self.stop_detection()
        super().closeEvent(event)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值