仅需在源码中添加一个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,再点击模型加载,选择你目标下的模型,随后点击文件,选择需要检测的图片
点击检测图片,就可以进行实时检测。