这里跟随博主学习开发利用PyQT5搭建YOLOv5可视化界面,并打包成exe程序。
博主原文:【目标检测】利用PyQT5搭建YOLOv5可视化界面_return _vf.meshgrid(tensors, **kwargs) # type: ign_zstar-_的博客-CSDN博客
这里要用到yolov5的模型和权重,如下图
这些文件夹直接从配置好的yolov5中复制过来
下面解释一下各文件夹的用途
models:存放模型构建相关程序
utils:存放绘图、数据加载等相关工具,直接从yolov5-5.0版本中clone过来
UI:存放软件图标
result:存放预测之后的图片或视频
weights:模型权重,默认使用YOLOv5官方提供的yolov5s.pt
import os
import sys
import cv2
import random
import torch
import numpy as np
import torch.backends.cudnn as cudnn
import qdarkstyle
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon, QPixmap
from models.experimental import attempt_load
from utils.general import check_img_size, non_max_suppression, scale_coords
from utils.datasets import letterbox
from utils.plots import plot_one_box
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Ui_MainWindow, self).__init__(parent)
self.timer_video = QtCore.QTimer()
self.setupUi(self)
self.init_logo()
self.init_slots()
self.cap = cv2.VideoCapture()
self.out = None
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
self.half = self.device.type != 'cpu' # half precision only supported on CUDA
cudnn.benchmark = True
weights = 'D:\Qt\QtData\PyQt\YOLOv5_visualization_interface\weights\yolov5s.pt' # 模型加载路径
imgsz = 640 # 预测图尺寸大小
self.conf_thres = 0.25 # NMS置信度
self.iou_thres = 0.45 # IOU阈值
# 载入模型
self.model = attempt_load(weights, map_location=self.device)
stride = int(self.model.stride.max())
self.imgsz = check_img_size(imgsz, s=stride)
if self.half:
self.model.half() # to FP16
# 从模型中获取各类别名称
self.names = self.model.module.names if hasattr(self.model, 'module') else self.model.names
# 给每一个类别初始化颜色
self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in self.names]
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(900, 600)
# MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
# self.centralwidget.setStyleSheet("border: 1px solid white;")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint)
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setContentsMargins(0, 0, 0, 0) # 布局的左、上、右、下到窗体边缘的距离
# self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
# 打开图片按钮
self.pushButton_img = QtWidgets.QPushButton(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton_img.sizePolicy().hasHeightForWidth())
self.pushButton_img.setSizePolicy(sizePolicy)
self.pushButton_img.setMinimumSize(QtCore.QSize(150, 40))
self.pushButton_img.setMaximumSize(QtCore.QSize(150, 40))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(12)
self.pushButton_img.setFont(font)
self.pushButton_img.setObjectName("pushButton_img")
self.verticalLayout.addWidget(self.pushButton_img, 0, QtCore.Qt.AlignHCenter)
self.verticalLayout.addStretch(5) # 增加垂直盒子内部对象间距
# 打开摄像头按钮
self.pushButton_camera = QtWidgets.QPushButton(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton_camera.sizePolicy().hasHeightForWidth())
self.pushButton_camera.setSizePolicy(sizePolicy)
self.pushButton_camera.setMinimumSize(QtCore.QSize(150, 40))
self.pushButton_camera.setMaximumSize(QtCore.QSize(150, 40))
self.pushButton_camera.setFont(font)
self.pushButton_camera.setObjectName("pushButton_camera")
self.verticalLayout.addWidget(self.pushButton_camera, 0, QtCore.Qt.AlignHCenter)
self.verticalLayout.addStretch(5)
# 打开视频按钮
self.pushButton_video = QtWidgets.QPushButton(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton_video.sizePolicy().hasHeightForWidth())
self.pushButton_video.setSizePolicy(sizePolicy)
self.pushButton_video.setMinimumSize(QtCore.QSize(150, 40))
self.pushButton_video.setMaximumSize(QtCore.QSize(150, 40))
self.pushButton_video.setFont(font)
self.pushButton_video.setObjectName("pushButton_video")
self.verticalLayout.addWidget(self.pushButton_video, 0, QtCore.Qt.AlignHCenter)
self.verticalLayout.addStretch(50)
# 显示导出文件夹按钮
self.pushButton_showdir = QtWidgets.QPushButton(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton_showdir.sizePolicy().hasHeightForWidth())
self.pushButton_showdir.setSizePolicy(sizePolicy)
self.pushButton_showdir.setMinimumSize(QtCore.QSize(150, 50))
self.pushButton_showdir.setMaximumSize(QtCore.QSize(150, 50))
self.pushButton_showdir.setFont(font)
self.pushButton_showdir.setObjectName("pushButton_showdir")
self.verticalLayout.addWidget(self.pushButton_showdir, 0, QtCore.Qt.AlignHCenter)
# 右侧图片/视频填充区域
self.verticalLayout.setStretch(2, 1)
self.horizontalLayout.addLayout(self.verticalLayout)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 3)
self.horizontalLayout_2.addLayout(self.horizontalLayout)
self.label.setStyleSheet("border: 1px solid white;") # 添加显示区域边框
# 底部美化导航条
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "YOLOv5目标检测平台"))
self.pushButton_img.setText(_translate("MainWindow", "图片检测"))
self.pushButton_camera.setText(_translate("MainWindow", "摄像头检测"))
self.pushButton_video.setText(_translate("MainWindow", "视频检测"))
self.pushButton_showdir.setText(_translate("MainWindow", "打开输出文件夹"))
self.label.setText(_translate("MainWindow", "TextLabel"))
def init_slots(self):
self.pushButton_img.clicked.connect(self.button_image_open)
self.pushButton_video.clicked.connect(self.button_video_open)
self.pushButton_camera.clicked.connect(self.button_camera_open)
self.pushButton_showdir.clicked.connect(self.button_show_dir)
self.timer_video.timeout.connect(self.show_video_frame)
def init_logo(self):
pix = QtGui.QPixmap('') # 绘制初始化图片
self.label.setScaledContents(True)
self.label.setPixmap(pix)
def button_image_open(self):
print('打开图片')
name_list = []
img_name, _ = QtWidgets.QFileDialog.getOpenFileName(
self, "打开图片", "", "*.jpg;;*.png;;All Files(*)")
if not img_name:
return
img = cv2.imread(img_name)
print(img_name)
showimg = img
with torch.no_grad():
img = letterbox(img, new_shape=self.imgsz)[0]
# Convert
# BGR to RGB, to 3x416x416
img = img[:, :, ::-1].transpose(2, 0, 1)
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(self.device)
img = img.half() if self.half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
pred = self.model(img)[0]
# Apply NMS
pred = non_max_suppression(pred, self.conf_thres, self.iou_thres)
# Process detections
for i, det in enumerate(pred):
if det is not None and len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(
img.shape[2:], det[:, :4], showimg.shape).round()
for *xyxy, conf, cls in reversed(det):
label = '%s %.2f' % (self.names[int(cls)], conf)
# print(label.split()[0]) # 打印各目标名称
name_list.append(self.names[int(cls)])
plot_one_box(xyxy, showimg, label=label,
color=self.colors[int(cls)], line_thickness=2)
cv2.imwrite('result/prediction.jpg', showimg)
self.result = cv2.cvtColor(showimg, cv2.COLOR_BGR2BGRA)
self.result = cv2.resize(self.result, (640, 480), interpolation=cv2.INTER_AREA)
self.QtImg = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0], QtGui.QImage.Format_RGB32)
self.label.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))
def button_video_open(self):
video_name, _ = QtWidgets.QFileDialog.getOpenFileName(
self, "打开视频", "", "*.mp4;;*.avi;;All Files(*)")
if not video_name:
return
flag = self.cap.open(video_name)
if flag == False:
QtWidgets.QMessageBox.warning(
self, u"Warning", u"打开视频失败", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok)
else:
self.out = cv2.VideoWriter('result/vedio_prediction.avi', cv2.VideoWriter_fourcc(
*'MJPG'), 20, (int(self.cap.get(3)), int(self.cap.get(4))))
self.timer_video.start(30)
self.pushButton_video.setDisabled(True)
self.pushButton_img.setDisabled(True)
self.pushButton_camera.setDisabled(True)
def button_camera_open(self):
if not self.timer_video.isActive():
# 默认使用第一个本地camera
flag = self.cap.open(0)
if flag == False:
QtWidgets.QMessageBox.warning(
self, u"Warning", u"打开摄像头失败", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok)
else:
self.out = cv2.VideoWriter('result/camera_prediction.avi', cv2.VideoWriter_fourcc(
*'MJPG'), 20, (int(self.cap.get(3)), int(self.cap.get(4))))
self.timer_video.start(30)
self.pushButton_video.setDisabled(True)
self.pushButton_img.setDisabled(True)
self.pushButton_camera.setText(u"关闭摄像头")
else:
self.timer_video.stop()
self.cap.release()
self.out.release()
self.label.clear()
self.init_logo()
self.pushButton_video.setDisabled(False)
self.pushButton_img.setDisabled(False)
self.pushButton_camera.setText(u"摄像头检测")
def show_video_frame(self):
name_list = []
flag, img = self.cap.read()
if img is not None:
showimg = img
with torch.no_grad():
img = letterbox(img, new_shape=self.imgsz)[0]
# Convert
# BGR to RGB, to 3x416x416
img = img[:, :, ::-1].transpose(2, 0, 1)
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(self.device)
img = img.half() if self.half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
pred = self.model(img)[0]
# Apply NMS
pred = non_max_suppression(pred, self.conf_thres, self.iou_thres)
# Process detections
for i, det in enumerate(pred): # detections per image
if det is not None and len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(
img.shape[2:], det[:, :4], showimg.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
label = '%s %.2f' % (self.names[int(cls)], conf)
name_list.append(self.names[int(cls)])
# print(label) # 打印各目标+置信度
plot_one_box(
xyxy, showimg, label=label, color=self.colors[int(cls)], line_thickness=2)
self.out.write(showimg)
show = cv2.resize(showimg, (640, 480))
self.result = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
showImage = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB888)
self.label.setPixmap(QtGui.QPixmap.fromImage(showImage))
else:
self.timer_video.stop()
self.cap.release()
self.out.release()
self.label.clear()
self.pushButton_video.setDisabled(False)
self.pushButton_img.setDisabled(False)
self.pushButton_camera.setDisabled(False)
self.init_logo()
def button_show_dir(self):
path = os.getcwd() + '\\' + 'result'
os.system(f"start explorer {path}")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
ui = Ui_MainWindow()
# 设置窗口透明度
# ui.setWindowOpacity(0.93)
# 去除顶部边框
# ui.setWindowFlags(Qt.FramelessWindowHint)
# 设置窗口图标
icon = QIcon()
icon.addPixmap(QPixmap("./UI/icon.ico"), QIcon.Normal, QIcon.Off)
ui.setWindowIcon(icon)
ui.show()
sys.exit(app.exec_())
打包通常采用的是Pyinstaller这个工具库,本次打包使用一个新的工具叫Auto Py to Exe,该工具仍是调用Pyinstaller进行打包,不过对选项进行了可视化,操作更加便捷。
git clone https://github.com/brentvollebregt/auto-py-to-exe.git python setup.py install
1.这里有git clone用法,需要安装git,安装教程见本人图文安装教程
Git 安装图文教程_机械刘怀洋的博客-CSDN博客
2.进入项目文件夹,cmd打开终端输入上述命令
git clone GitHub - brentvollebregt/auto-py-to-exe: Converts .py to .exe using a simple graphical interface
3.安装好Auto Py to Exe后文件夹中出现setup.py
cmd进入文件夹后执行命令
python setup.py install
4.安装好之后,在终端输入auto-py-to-exe,报错没有No module named 'bottle'
安装缺失的包
再来一次,又报错
再次安装缺失的包,注意geventwebsocket库要这么安装
再来一次这次成了
脚本位置选择main.py,选择单目录模式,隐藏控制台,并选择图标和输出路径,然后就可以一键进行打包
这里提供一个下载.ico文件的网站
Free RemixIcon icons - Iconfinder
5.开始输出
点击convert开始输出,这需要一段时间
完成后会出现如下提示
6.可视化界面
输出的main文件中会出现一个main.exe,如下图
双击main.exe,发现报错,找不到权重文件
原来是在运行这个程序之前要先把下面这些文件夹丢进去
再次运行,已经成功