1 Star 0 Fork 1

重楼/YOLOv8-PySide6-GUI-main

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
main.py 19.73 KB
一键复制 编辑 原始数据 按行查看 历史
byczyz 提交于 2023-09-10 17:43 . 提交一份备用代码————B
from ultralytics.yolo.engine.predictor import BasePredictor
from ultralytics.yolo.engine.results import Results
from ultralytics.yolo.utils import DEFAULT_CFG, LOGGER, SETTINGS, callbacks, ops
from ultralytics.yolo.utils.plotting import Annotator, colors, save_one_box
from ultralytics.yolo.utils.torch_utils import smart_inference_mode
from ultralytics.yolo.utils.files import increment_path
from ultralytics.yolo.utils.checks import check_imshow
from ultralytics.yolo.cfg import get_cfg
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMenu
from PySide6.QtGui import QImage, QPixmap, QColor
from PySide6.QtCore import QTimer, QThread, Signal, QObject, QPoint, Qt
from ui.CustomMessageBox import MessageBox
from ui.home import Ui_MainWindow
from UIFunctions import *
from collections import defaultdict
from pathlib import Path
from utils.capnums import Camera
from utils.rtsp_win import Window
import numpy as np
import time
import json
import torch
import sys
import cv2
import os
from CameraViewer import CameraViewer
from yoloPre import YoloPredictor
class MainWindow(QMainWindow, Ui_MainWindow):
# 这是一个 PySide6 的信号(Signal)对象,
# 用于在应用程序中传递消息。在这个场景中,它被用于在主窗口和 YOLO 实例之间传递消息。
main2yolo_begin_sgl = Signal() # The main window sends an execution signal to the yolo instance
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# basic interface 【UI初始化!】
self.setupUi(self)
self.setAttribute(Qt.WA_TranslucentBackground) # rounded transparent
self.setWindowFlags(Qt.FramelessWindowHint) # Set window flag: hide window borders
UIFuncitons.uiDefinitions(self)
# Show module shadows
UIFuncitons.shadow_style(self, self.Class_QF, QColor(162, 129, 247))
UIFuncitons.shadow_style(self, self.Target_QF, QColor(251, 157, 139))
UIFuncitons.shadow_style(self, self.Fps_QF, QColor(170, 128, 213))
UIFuncitons.shadow_style(self, self.Model_QF, QColor(64, 186, 193))
# read model folder 加载模型
self.pt_list = os.listdir('./models')
self.pt_list = [file for file in self.pt_list if file.endswith('.pt')]
self.pt_list.sort(key=lambda x: os.path.getsize('./models/' + x)) # sort by file size
self.model_box.clear()
self.model_box.addItems(self.pt_list)
self.Qtimer_ModelBox = QTimer(self) # Timer: Monitor model file changes every 2 seconds
self.Qtimer_ModelBox.timeout.connect(self.ModelBoxRefre)
self.Qtimer_ModelBox.start(2000)
# Yolo-v8 thread 初始化
'''
这段代码是 Yolo 模型的初始化过程,
在这里主要是负责设置模型名称、创建 Yolo 线程、将 Yolo 信号连接到主线程的槽函数上,
并将主线程的信号连接到 Yolo 类的槽函数上,并启动 Yolo 线程。
'''
self.yolo_predict = YoloPredictor() # Create a Yolo instance
self.select_model = self.model_box.currentText() # default model
self.yolo_predict.new_model_name = "./models/%s" % self.select_model
self.yolo_thread = QThread() # Create yolo thread
# 将 Yolo 类中的信号绑定到主线程的槽函数上
self.yolo_predict.yolo2main_pre_img.connect(lambda x: self.show_image(x, self.pre_video))
self.yolo_predict.yolo2main_res_img.connect(lambda x: self.show_image(x, self.res_video))
self.yolo_predict.yolo2main_status_msg.connect(lambda x: self.show_status(x))
self.yolo_predict.yolo2main_fps.connect(lambda x: self.fps_label.setText(x))
# self.yolo_predict.yolo2main_labels.connect(self.show_labels)
self.yolo_predict.yolo2main_class_num.connect(lambda x: self.Class_num.setText(str(x)))
self.yolo_predict.yolo2main_target_num.connect(lambda x: self.Target_num.setText(str(x)))
self.yolo_predict.yolo2main_progress.connect(lambda x: self.progress_bar.setValue(x))
# 将主线程的信号绑定到 Yolo 类的槽函数上,并启动 Yolo 线程
self.main2yolo_begin_sgl.connect(self.yolo_predict.run)
self.yolo_predict.moveToThread(self.yolo_thread)
# Model parameters
self.model_box.currentTextChanged.connect(self.change_model)
self.iou_spinbox.valueChanged.connect(lambda x: self.change_val(x, 'iou_spinbox')) # iou box
self.iou_slider.valueChanged.connect(lambda x: self.change_val(x, 'iou_slider')) # iou scroll bar
self.conf_spinbox.valueChanged.connect(lambda x: self.change_val(x, 'conf_spinbox')) # conf box
self.conf_slider.valueChanged.connect(lambda x: self.change_val(x, 'conf_slider')) # conf scroll bar
self.speed_spinbox.valueChanged.connect(lambda x: self.change_val(x, 'speed_spinbox')) # speed box
self.speed_slider.valueChanged.connect(lambda x: self.change_val(x, 'speed_slider')) # speed scroll bar
# Prompt window initialization
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
self.Model_name.setText(self.select_model)
# Select detection source
self.src_file_button.clicked.connect(self.open_src_file) # select local file
self.src_cam_button.clicked.connect(self.chose_cam) # chose_camera
# self.src_rtsp_button.clicked.connect(self.show_status("The function has not yet been implemented."))#chose_rtsp
# start testing button
self.run_button.clicked.connect(self.run_or_continue) # pause/start
self.stop_button.clicked.connect(self.stop) # termination
# Other function buttons
self.save_res_button.toggled.connect(self.is_save_res) # save image option
self.save_txt_button.toggled.connect(self.is_save_txt) # Save label option
self.ToggleBotton.clicked.connect(lambda: UIFuncitons.toggleMenu(self, True)) # left navigation button
self.settings_button.clicked.connect(lambda: UIFuncitons.settingBox(self, True)) # top right settings button
# initialization
self.load_config()
# The main window displays the original image and detection results
@staticmethod
def show_image(img_src, label):
try:
ih, iw, _ = img_src.shape # 获取原始图像的高度和宽度
w = label.geometry().width() # 获取 QLabel 组件的当前宽度
h = label.geometry().height() # 获取 QLabel 组件的当前高度
# 根据原始图像和 QLabel 组件的大小,等比例缩放原始图像
if iw / w > ih / h: # 如果原始图像的宽度比高度大,按照宽度比例缩放
scal = w / iw
nw = w
nh = int(scal * ih)
img_src_ = cv2.resize(img_src, (nw, nh))
else: # 如果原始图像的高度比宽度大,按照高度比例缩放
scal = h / ih
nw = int(scal * iw)
nh = h
img_src_ = cv2.resize(img_src, (nw, nh))
# 将 OpenCV 图像转换为 QImage 对象,并将其显示在 QLabel 组件中
frame = cv2.cvtColor(img_src_, cv2.COLOR_BGR2RGB) # 将图像的颜色空间从 BGR 转换为 RGB
img = QImage(frame.data, frame.shape[1], frame.shape[0], frame.shape[2] * frame.shape[1],
QImage.Format_RGB888) # 创建 QImage 对象
label.setPixmap(QPixmap.fromImage(img)) # 将 QImage 对象转换为 QPixmap 对象,并将其显示在 QLabel 组件中
except Exception as e:
print(repr(e))
# Control start/pause
def run_or_continue(self):
if self.yolo_predict.source == '':
print('Please select a video source')
self.show_status('Please select a video source before starting detection...')
self.run_button.setChecked(False)
else:
self.yolo_predict.stop_dtc = False
print('start')
if self.run_button.isChecked():
self.run_button.setChecked(True) # start button
self.save_txt_button.setEnabled(False) # It is forbidden to check and save after starting the detection
self.save_res_button.setEnabled(False)
self.show_status('Detecting...')
self.yolo_predict.continue_dtc = True # Control whether Yolo is paused
if not self.yolo_thread.isRunning():
self.yolo_thread.start()
self.main2yolo_begin_sgl.emit()
else:
self.yolo_predict.continue_dtc = False
self.show_status("Pause...")
self.run_button.setChecked(False) # start button
# bottom status bar information
def show_status(self, msg):
self.status_bar.setText(msg)
print(msg)
if msg == 'Detection completed' or msg == '检测完成':
self.save_res_button.setEnabled(True)
self.save_txt_button.setEnabled(True)
self.run_button.setChecked(False)
self.progress_bar.setValue(0)
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end process
elif msg == 'Detection terminated!' or msg == '检测终止':
self.save_res_button.setEnabled(True)
self.save_txt_button.setEnabled(True)
self.run_button.setChecked(False)
self.progress_bar.setValue(0)
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end process
self.pre_video.clear() # clear image display
self.res_video.clear()
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
# select local file
def open_src_file(self):
print('local file')
config_file = 'config/fold.json'
config = json.load(open(config_file, 'r', encoding='utf-8'))
open_fold = config['open_fold']
if not os.path.exists(open_fold):
open_fold = os.getcwd()
# 这段代码是 PySide6 中使用 QFileDialog 对话框进行文件选择的示例。
# 如果你单击了窗口中的某个按钮,会打开一个文件对话框,允许用户选择一个视频或图像文件。
# QFileDialog.getOpenFileName() 是一个静态方法,返回一个元组。
# 这个元组包含用户选择的文件的完整路径和一个空字符串。
# 使用的是解包(unpacking)语法,将返回的元组拆分为两个变量 name 和 _。
# name 变量包含用户选择的文件的完整路径,
# 而 _ 变量包含一个空字符串。由于我们不使用空字符串,
# 因此可以将其赋给一个无用且未使用的变量 _,以避免不必要的警告。
name, _ = QFileDialog.getOpenFileName(self, 'Video/image', open_fold,
"Pic File(*.mp4 *.mkv *.avi *.flv *.jpg *.png)")
if name:
self.yolo_predict.source = name
self.show_status('Load File:{}'.format(os.path.basename(name)))
config['open_fold'] = os.path.dirname(name)
config_json = json.dumps(config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(config_json)
self.stop()
# Select camera source---- have one bug
def chose_cam(self):
try:
print('open camera')
self.yolo_predict.camera_run()
except Exception as e:
print(e)
self.show_status('%s' % e)
# select network source
def chose_rtsp(self):
self.rtsp_window = Window()
config_file = 'config/ip.json'
if not os.path.exists(config_file):
ip = "rtsp://admin:[email protected]:555"
new_config = {"ip": ip}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(new_json)
else:
config = json.load(open(config_file, 'r', encoding='utf-8'))
ip = config['ip']
self.rtsp_window.rtspEdit.setText(ip)
self.rtsp_window.show()
self.rtsp_window.rtspButton.clicked.connect(lambda: self.load_rtsp(self.rtsp_window.rtspEdit.text()))
# load network sources
def load_rtsp(self, ip):
try:
self.stop()
MessageBox(
self.close_button, title='提示', text='加载 rtsp...', time=1000, auto=True).exec()
self.yolo_predict.source = ip
new_config = {"ip": ip}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open('config/ip.json', 'w', encoding='utf-8') as f:
f.write(new_json)
self.show_status('Loading rtsp:{}'.format(ip))
self.rtsp_window.close()
except Exception as e:
self.show_status('%s' % e)
# Save test result button--picture/video
def is_save_res(self):
if self.save_res_button.checkState() == Qt.CheckState.Unchecked:
self.show_status('NOTE: Run image results are not saved.')
self.yolo_predict.save_res = False
elif self.save_res_button.checkState() == Qt.CheckState.Checked:
self.show_status('NOTE: Run image results will be saved.')
self.yolo_predict.save_res = True
# Save test result button -- label (txt)
def is_save_txt(self):
if self.save_txt_button.checkState() == Qt.CheckState.Unchecked:
self.show_status('NOTE: Labels results are not saved.')
self.yolo_predict.save_txt = False
elif self.save_txt_button.checkState() == Qt.CheckState.Checked:
self.show_status('NOTE: Labels results will be saved.')
self.yolo_predict.save_txt = True
# Configuration initialization ~~~wait to change~~~
def load_config(self):
config_file = 'config/setting.json'
if not os.path.exists(config_file):
iou = 0.26
conf = 0.33
rate = 10
save_res = 0
save_txt = 0
new_config = {"iou": iou,
"conf": conf,
"rate": rate,
"save_res": save_res,
"save_txt": save_txt
}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(new_json)
else:
config = json.load(open(config_file, 'r', encoding='utf-8'))
if len(config) != 5:
iou = 0.26
conf = 0.33
rate = 10
save_res = 0
save_txt = 0
else:
iou = config['iou']
conf = config['conf']
rate = config['rate']
save_res = config['save_res']
save_txt = config['save_txt']
self.save_res_button.setCheckState(Qt.CheckState(save_res))
self.yolo_predict.save_res = (False if save_res == 0 else True)
self.save_txt_button.setCheckState(Qt.CheckState(save_txt))
self.yolo_predict.save_txt = (False if save_txt == 0 else True)
self.run_button.setChecked(False)
self.show_status("Welcome~")
# Terminate button and associated state
def stop(self):
print('stop')
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end thread
self.yolo_predict.stop_dtc = True
self.run_button.setChecked(False) # start key recovery
self.save_res_button.setEnabled(True) # Ability to use the save button
self.save_txt_button.setEnabled(True) # Ability to use the save button
self.pre_video.clear() # clear image display
self.res_video.clear() # clear image display
self.progress_bar.setValue(0)
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
# Change detection parameters
def change_val(self, x, flag):
if flag == 'iou_spinbox':
self.iou_slider.setValue(int(x * 100)) # The box value changes, changing the slider
elif flag == 'iou_slider':
self.iou_spinbox.setValue(x / 100) # The slider value changes, changing the box
self.show_status('IOU Threshold: %s' % str(x / 100))
self.yolo_predict.iou_thres = x / 100
elif flag == 'conf_spinbox':
self.conf_slider.setValue(int(x * 100))
elif flag == 'conf_slider':
self.conf_spinbox.setValue(x / 100)
self.show_status('Conf Threshold: %s' % str(x / 100))
self.yolo_predict.conf_thres = x / 100
elif flag == 'speed_spinbox':
self.speed_slider.setValue(x)
elif flag == 'speed_slider':
self.speed_spinbox.setValue(x)
self.show_status('Delay: %s ms' % str(x))
self.yolo_predict.speed_thres = x # ms
# change model 【更换模型】
def change_model(self, x):
self.select_model = self.model_box.currentText()
self.yolo_predict.new_model_name = "./models/%s" % self.select_model
self.show_status('Change Model:%s' % self.select_model)
self.Model_name.setText(self.select_model)
# label result
# def show_labels(self, labels_dic):
# try:
# self.result_label.clear()
# labels_dic = sorted(labels_dic.items(), key=lambda x: x[1], reverse=True)
# labels_dic = [i for i in labels_dic if i[1]>0]
# result = [' '+str(i[0]) + ':' + str(i[1]) for i in labels_dic]
# self.result_label.addItems(result)
# except Exception as e:
# self.show_status(e)
# Cycle monitoring model file changes
def ModelBoxRefre(self):
pt_list = os.listdir('./models')
pt_list = [file for file in pt_list if file.endswith('.pt')]
pt_list.sort(key=lambda x: os.path.getsize('./models/' + x))
# It must be sorted before comparing, otherwise the list will be refreshed all the time
if pt_list != self.pt_list:
self.pt_list = pt_list
self.model_box.clear()
self.model_box.addItems(self.pt_list)
# Get the mouse position (used to hold down the title bar and drag the window)
def mousePressEvent(self, event):
p = event.globalPosition()
globalPos = p.toPoint()
self.dragPos = globalPos
# Optimize the adjustment when dragging the bottom and right edges of the window size
def resizeEvent(self, event):
# Update Size Grips
UIFuncitons.resize_grips(self)
# Exit Exit thread, save settings
def closeEvent(self, event):
config_file = 'config/setting.json'
config = dict()
config['iou'] = self.iou_spinbox.value()
config['conf'] = self.conf_spinbox.value()
config['rate'] = self.speed_spinbox.value()
config['save_res'] = (0 if self.save_res_button.checkState() == Qt.Unchecked else 2)
config['save_txt'] = (0 if self.save_txt_button.checkState() == Qt.Unchecked else 2)
config_json = json.dumps(config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(config_json)
# Exit the process before closing
if self.yolo_thread.isRunning():
self.yolo_predict.stop_dtc = True
self.yolo_thread.quit()
MessageBox(
self.close_button, title='Note', text='Exiting, please wait...', time=3000, auto=True).exec()
sys.exit(0)
else:
sys.exit(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
Home = MainWindow()
Home.show()
sys.exit(app.exec())
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/chong_lou/yolov8-py-side6-gui-main.git
[email protected]:chong_lou/yolov8-py-side6-gui-main.git
chong_lou
yolov8-py-side6-gui-main
YOLOv8-PySide6-GUI-main
master

搜索帮助