YOLOv8无人机可视化界面开发

仅需在源码中添加一个py文件,就可实现无人机图像视频的可视化检测
 

所需环境:YOLO适配环境,安装PYQT5工具
 

安装pyqt5

进入终端进入环境输入
 

pip install PyQt5

再安装pyqt5的适配工具
 

pip install pyqt5-tools

安装成功后,再YOLOv8项目中,新建一个pyqt.py文件
将下面内容进行复制粘贴进去
 

import sys
import cv2
import numpy as np
import torch
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel,
                             QScrollArea, QFileDialog, QMessageBox, QHBoxLayout,
                             QAction, QToolBar, QPushButton, QProgressBar,
                             QComboBox, QVBoxLayout, QSlider)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt, QThread, pyqtSignal, pyqtSlot,QTimer
from ultralytics import YOLO


class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)
    finished_signal = pyqtSignal()
    progress_updated = pyqtSignal(int)

    def __init__(self, model, video_path, save_path=None, device='cpu',iou=0.7):
        super().__init__()
        self.model = model
        self.video_path = video_path
        self.save_path = save_path
        self.device = device
        self.iou = iou
        self._run_flag = True
        self._pause_flag = False
        self.total_frames = 0

    def run(self):
        cap = cv2.VideoCapture(self.video_path)
        if not cap.isOpened():
            self.finished_signal.emit()
            return

        self.total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        fps = cap.get(cv2.CAP_PROP_FPS)

        self.progress_updated.emit(0)

        if self.save_path:
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            self.out = cv2.VideoWriter(self.save_path, fourcc, fps,
                                       (frame_width, frame_height))

        current_frame = 0
        while self._run_flag and cap.isOpened():
            if not self._pause_flag:
                ret, frame = cap.read()
                if not ret:
                    break

                current_frame += 1
                progress = int((current_frame / self.total_frames) * 100) if self.total_frames > 0 else 0
                self.progress_updated.emit(progress)

                # 使用指定设备进行预测
                results = self.model.predict(frame, device=self.device,iou=self.iou)
                annotated_frame = results[0].plot()

                self.change_pixmap_signal.emit(annotated_frame)

                if self.save_path:
                    self.out.write(annotated_frame)

        cap.release()
        if self.save_path:
            self.out.release()
        self.finished_signal.emit()

    def pause(self):
        self._pause_flag = True

    def resume(self):
        self._pause_flag = False

    def stop(self):
        self._run_flag = False
        self.wait()


class YOLOv8GUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.model = None
        self.cv_image = None
        self.result_image = None
        self.video_thread = None
        self.iou_threshold = 0.7
        self.initUI()
        self.setWindowTitle("YOLOv8检测器")
        self.setGeometry(100, 100, 1280, 720)


    def initUI(self):
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu('文件')
        open_image_action = QAction('打开图片', self)
        open_image_action.triggered.connect(self.open_image)
        file_menu.addAction(open_image_action)

        open_video_action = QAction('打开视频', self)
        open_video_action.triggered.connect(self.open_video)
        file_menu.addAction(open_video_action)

        # 模型菜单
        model_menu = menubar.addMenu('模型')
        load_model_action = QAction('加载模型', self)
        load_model_action.triggered.connect(self.load_model)
        model_menu.addAction(load_model_action)

        # 工具栏
        toolbar = QToolBar("主工具栏")
        self.addToolBar(toolbar)

        # 设备选择组件
        toolbar.addWidget(QLabel("  设备:"))
        self.device_combo = QComboBox()
        self.device_combo.addItem("自动选择")
        self.device_combo.addItem("CPU")
        if torch.cuda.is_available():
            self.device_combo.addItem("GPU")
        toolbar.addWidget(self.device_combo)

        # 图片检测按钮
        self.detect_image_action = QAction('检测图片', self)
        self.detect_image_action.triggered.connect(self.detect_image)
        toolbar.addAction(self.detect_image_action)

        # 视频控制按钮
        self.start_btn = QPushButton("开始", self)
        self.start_btn.clicked.connect(self.start_video)
        self.pause_btn = QPushButton("暂停", self)
        self.pause_btn.clicked.connect(self.pause_video)
        self.stop_btn = QPushButton("停止", self)
        self.stop_btn.clicked.connect(self.stop_video)

        self.set_video_buttons(False)

        toolbar.addWidget(self.start_btn)
        toolbar.addWidget(self.pause_btn)
        toolbar.addWidget(self.stop_btn)

        # 保存按钮
        self.save_action = QAction('保存结果', self)
        self.save_action.triggered.connect(self.save_result)
        self.save_action.setEnabled(False)
        toolbar.addAction(self.save_action)

        # 主界面布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # 左侧原始图像区域
        left_layout = QVBoxLayout()
        self.orig_label = QLabel()
        self.orig_label.setAlignment(Qt.AlignCenter)
        self.orig_scroll = QScrollArea()
        self.orig_scroll.setWidget(self.orig_label)
        self.orig_scroll.setWidgetResizable(True)
        left_layout.addWidget(self.orig_scroll)
        main_layout.addLayout(left_layout, stretch=2)

        # 右侧控制面板
        right_layout = QVBoxLayout()

        # IoU控制区域
        iou_control = QVBoxLayout()
        self.iou_label = QLabel(f"IoU阈值: {self.iou_threshold:.2f}")
        self.iou_slider = QSlider(Qt.Horizontal)
        self.iou_slider.setRange(0, 100)
        self.iou_slider.setValue(70)
        self.iou_slider.setTickInterval(10)
        self.iou_slider.setTickPosition(QSlider.TicksBelow)
        self.iou_slider.valueChanged.connect(self.update_iou_threshold)

        iou_control.addWidget(self.iou_label)
        iou_control.addWidget(self.iou_slider)
        right_layout.addLayout(iou_control)

        # 检测结果显示区域
        self.result_label = QLabel()
        self.result_label.setAlignment(Qt.AlignCenter)
        self.result_scroll = QScrollArea()
        self.result_scroll.setWidget(self.result_label)
        self.result_scroll.setWidgetResizable(True)
        right_layout.addWidget(self.result_scroll, stretch=1)

        main_layout.addLayout(right_layout, stretch=3)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setMaximum(100)
        self.progress_bar.setVisible(False)
        self.progress_bar.setFormat("处理进度: %p%")
        self.statusBar().addPermanentWidget(self.progress_bar)

        # 防抖定时器
        self.detect_timer = QTimer()
        self.detect_timer.setSingleShot(True)
        self.detect_timer.timeout.connect(self.detect_image)

    def get_selected_device(self):
        """获取用户选择的设备"""
        selected = self.device_combo.currentText()
        if selected == "GPU":
            if torch.cuda.is_available():
                return "cuda:0"
            else:
                QMessageBox.warning(self, '警告', 'GPU不可用,已自动切换到CPU')
                return "cpu"
        elif selected == "CPU":
            return "cpu"
        else:  # 自动选择
            return "cuda:0" if torch.cuda.is_available() else "cpu"

    def set_video_buttons(self, enabled=True):
        self.start_btn.setEnabled(enabled)
        self.pause_btn.setEnabled(enabled)
        self.stop_btn.setEnabled(enabled)

    def update_iou_threshold(self, value):
        """更新IoU阈值并触发检测"""
        self.iou_threshold = value / 100.0
        self.iou_label.setText(f"IoU阈值: {self.iou_threshold:.2f}")

        # 防抖处理:500ms后执行检测
        if self.cv_image is not None and self.model is not None:
            self.detect_timer.start(500)

    def open_image(self):
        filename, _ = QFileDialog.getOpenFileName(
            self, '打开图片', '', '图片文件 (*.jpg *.jpeg *.png)')
        if filename:
            self.cv_image = cv2.imread(filename)
            if self.cv_image is not None:
                rgb_image = cv2.cvtColor(self.cv_image, cv2.COLOR_BGR2RGB)
                self._display_image(rgb_image, self.orig_label)
                self.save_action.setEnabled(False)
                self.set_video_buttons(False)

    def open_video(self):
        filename, _ = QFileDialog.getOpenFileName(
            self, '打开视频', '', '视频文件 (*.mp4 *.avi *.mov)')
        if filename:
            self.video_path = filename
            self.set_video_buttons(True)
            self.save_action.setEnabled(False)
            self._display_video_info(filename)

    def _display_video_info(self, path):
        cap = cv2.VideoCapture(path)
        if cap.isOpened():
            fps = cap.get(cv2.CAP_PROP_FPS)
            frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
            duration = frame_count / fps if fps > 0 else 0
            info = f"视频信息:\n分辨率: {int(cap.get(3))}x{int(cap.get(4))}\n" \
                   f"时长: {duration:.1f}秒\n帧率: {fps:.1f}FPS"
            cap.release()
            self.orig_label.setText(info)
            self.result_label.clear()

    def load_model(self):
        model_path, _ = QFileDialog.getOpenFileName(
            self, '加载模型', '', '模型文件 (*.pt)')
        if model_path:
            try:
                self.model = YOLO(model_path)
                QMessageBox.information(self, '成功', '模型加载成功!')
            except Exception as e:
                QMessageBox.critical(self, '错误', f'模型加载失败: {str(e)}')

    def detect_image(self):
        if self.model is None:
            QMessageBox.warning(self, '警告', '请先加载模型!')
            return
        if self.cv_image is None:
            QMessageBox.warning(self, '警告', '请先打开图片!')
            return

        device = self.get_selected_device()
        results = self.model.predict(self.cv_image, device=device,iou=self.iou_threshold)
        res_plotted = results[0].plot()
        self.result_image = res_plotted

        rgb_image = cv2.cvtColor(res_plotted, cv2.COLOR_BGR2RGB)
        self._display_image(rgb_image, self.result_label)
        self.save_action.setEnabled(True)

    def start_video(self):
        if not self.model:
            QMessageBox.warning(self, '警告', '请先加载模型!')
            return

        save_path, _ = QFileDialog.getSaveFileName(
            self, '保存视频', '', 'MP4视频 (*.mp4)')
        if not save_path:
            return

        device = self.get_selected_device()
        self.video_thread = VideoThread(self.model, self.video_path,
                                        save_path, device, self.iou_threshold)
        self.video_thread.change_pixmap_signal.connect(self.update_video_frame)
        self.video_thread.finished_signal.connect(self.video_finished)
        self.video_thread.progress_updated.connect(self.update_progress)
        self.video_thread.start()

        self.progress_bar.setVisible(True)
        self.start_btn.setEnabled(False)
        self.pause_btn.setEnabled(True)
        self.stop_btn.setEnabled(True)

    def pause_video(self):
        if self.video_thread._pause_flag:
            self.video_thread.resume()
            self.pause_btn.setText("暂停")
        else:
            self.video_thread.pause()
            self.pause_btn.setText("继续")

    def stop_video(self):
        if self.video_thread:
            self.video_thread.stop()
            self.video_thread = None

        self.progress_bar.setVisible(False)
        self.progress_bar.setValue(0)
        self.set_video_buttons(False)
        self.orig_label.clear()
        self.result_label.clear()
        self.pause_btn.setText("暂停")

    @pyqtSlot(np.ndarray)
    def update_video_frame(self, frame):
        rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        self._display_image(rgb_image, self.result_label)

    @pyqtSlot(int)
    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def video_finished(self):
        self.progress_bar.setVisible(False)
        self.progress_bar.setValue(0)
        self.set_video_buttons(False)
        QMessageBox.information(self, '完成', '视频处理已完成!')
        self.save_action.setEnabled(True)

    def save_result(self):
        if self.result_image is not None:
            filename, _ = QFileDialog.getSaveFileName(
                self, '保存结果', '', '图片文件 (*.png *.jpg *.jpeg)')
            if filename:
                try:
                    if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
                        filename += '.png'
                    cv2.imwrite(filename, self.result_image)
                    QMessageBox.information(self, '成功', f'结果已保存至:\n{filename}')
                except Exception as e:
                    QMessageBox.critical(self, '错误', f'保存失败: {str(e)}')

    def _display_image(self, image, label):
        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)
        label.setPixmap(pixmap.scaled(
            label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))


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

点击运行,进入到UI界面

先点击选择GPU,再点击模型加载,选择你目标下的模型,随后点击文件,选择需要检测的图片

点击检测图片,就可以进行实时检测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值