欢迎关注『youcans动手学模型』系列
本专栏内容和资源同步到 GitHub/youcans
【YOLO5 项目实战】(8)PyQt5 图形界面—PCB缺陷检测系统
- 1. PyQt5 图形界面开发工具
- 1.1 PyQt5 的安装
- 1.2 在 PyCharm 集成 QtDesigner 和 PyUIC
- 1.3 使用 QtDesigner 开发 PyQt5 图形界面
- 2. PCB 缺陷检测系统的图形界面设计
- 2.1 PCB 缺陷检测系统需求分析
- 2.2 创建图形窗口(MainWindow)
- 2.3 创建菜单对象(menu)
- 2.4 布局管理(Label)
- 2.5 创建内容显示控件(Label)
- 2.6 创建按钮控件(PushButton)
- 3. GUI 主程序开发
- 3.1 从零开始编写主程序
- 3.2 信号与槽的连接
- 3.3 导入图像文件子程序
- 3.4 保存图像文件子程序
- 3.5 YOLOv5 图像检测子程序
- 3.6 OpenCV 图像文件转换为 QImage 子程序
- 3.7 帮助菜单子程序
- 4. 调用YOLO5 模型进行目标检测
- 4.1 安装 ultralytics 模块
- 4.2 ultralytics 的导入和初始化
- 4.3 使用 YOLO 模型进行检测
- 5. PCB缺陷检测系统的使用
本节以基于 YOLOv5 的PCB 缺陷检测任务为例,详细介绍使用 Pyqt5 设计 YOLOv5 图形用户界面,实现人机交互的 YOLOv5 PCB 缺陷检测系统。
关于YOLOv5 的PCB 缺陷检测前文已有介绍,如需了解如何使用 PCB 缺陷数据集训练模型,可以阅读我的博客 【YOLO5 项目实战】(3)PCB 缺陷检测 。
1. PyQt5 图形界面开发工具
1.1 PyQt5 的安装
Qt 库是跨平台的 C++ 库的集合,是最强大的 GUI 库之一,可以实现高级 API 来访问桌面和移动系统的各种服务。
安装了 PyQt5 就可以用 Python 语言编写 Qt 程序。
pip install pyqt5 -i https://mirrors.aliuyun.com/pypi/simple
虽然安装 PyQt5 就可以编程实现 GUI,但是学习、编程、调试、修改都是相当复杂和繁琐的。而 Qt Designer 基本是通过人机交互的排版方式进行界面设计,非常方便、直观。所以我在使用 Qt Designer 图形界面设计工具之后,就再也不愿意编写 Python 程序来实现 GUI 了。
Qt Tools 包含了两个重要的工具:
- 图形界面设计工具 Qt Designer,用于设计图形界面,生成 .ui文件,以 xml 格式存储界面和控件的属性;
- UI 文件转换工具 PyUic,用于将 .ui 文件解析为 .py 文件的工具。
Qt Tools 工具可以直接使用 pip 方式安装:
pip install pyqt5-tools
或:
pip install pyqt5-tools -i https://pypi.douban.com/simple/
1.2 在 PyCharm 集成 QtDesigner 和 PyUIC
使用 PyCharm 集成开发工具的小白,在安装 QtTools 库以后,还要对 QtDesigner 和 PyUIC 进行环境配置,将其集成到 PyCharm 中。
- 运行 PyCharm;
- 从顶部菜单栏选择:File -> Settings,弹出 Seetings 窗口;
- 从左侧菜单栏中选择:Tools -> ExternalTools,在右侧点击 “+” 弹出 CreateTool 窗口;
(1)添加 QtDesigner 工具
在 CreateTool 窗口依次填写:
- Name:填写 “QtDesigner”
- Program:填写 designer.exe 的路径,例如:
C:\Python\Anaconda3\Lib\site-packages\qt5_applications\Qt\bin\designer.exe
注意:此处填写刚才 pip 安装的 pyqt5-tools 工具包的路径。如果小白的 Python 或 Anaconda3 安装在其他路径下,则从对应的目录找到 “qt5_applications\Qt\bin\designer.exe”,或者在资源管理器中搜索 “designer.exe” 文件找到安装路径。
- Arguments:不用填写
- Working directory:填写生成 UI 文件的保存路径
例如,要将 .ui 文件保存在当前 Project 的路径下,则填写 “$ProjectFileDir$”;要将 .ui 文件保存在当前 Project 路径下的 \program 子目录中,则填写 “$ProjectFileDir$\program”。
填好 CreateTool 窗口后,点击 “OK” 即可完成 QtDesigner 工具的添加。
(2)添加 PyUIC 工具
在 CreateTool 窗口依次填写:
- Name:填写 “PyUIC”
- Program:填写 python.exe 的路径,例如:
C:\Python\Anaconda3\python.exe
注意:此处填写 IDE 使用的 Python Interpreter的路径。如果小白的 Python 或 Anaconda3 安装在其他路径下,或者选择其它路径中的 python.exe 作为 Python Interpreter,可以从 Pycharm -> Settings -> Project -> Python Interpreter 打开配置窗口,从右侧上方 “Python Interpreter:” 选项框找到 python.exe 的路径。
- Arguments:填写"-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py”
- Working directory:填写将 .ui 文件转换为 .py 文件的保存路径
例如,要将 .py 文件保存在当前 Project 的路径下,则填写 “$ProjectFileDir$”;要将 .py 文件保存在当前 Project 路径下的 \program 子目录中,则填写 “$ProjectFileDir$\program”。
填好 CreateTool 窗口后,点击 “OK” 即可完成 QtDesigner 工具的添加。
1.3 使用 QtDesigner 开发 PyQt5 图形界面
使用 QtDesigner 开发 PyQt5 图形界面的基本步骤是:
(1)使用图形界面设计工具 QtDesigner 进行图形界面设计,生成 .ui 文件;
(2)使用 UI 转换工具 PyUIC 将 .ui 文件转换为 .py 文件;
(3)编写一个 Python 应用程序调用 .py 界面文件,就可以实现 Python 平台的 GUI 编程。
具体测试方法可以参见我的博客 【OpenCV-PyQT项目实战(1)安装与环境配置】
2. PCB 缺陷检测系统的图形界面设计
2.1 PCB 缺陷检测系统需求分析
- 导入待检测文件(图像,视频,摄像头)
- 显示检测图像(待检测图像,检测结果图像)
- 显示检测结果(图像检测结果,目标检测数据)
- 保存检测结果
- 运行控制(检测,保存,帮助)
- 帮助
- 关闭
2.2 创建图形窗口(MainWindow)
- 运行 PyCharm,打开建立的 Python Project,例如 Project 为 YOLOv5UI_PCB。
- 从顶部菜单栏选择:Tools -> ExternalTools -> QtDesigner,打开 QtDesigner。
- 在 “新建窗体” 窗口的左侧菜单选择 “MainWindow” 新建一个图形窗口。
- 在左侧上方 “对象查看器” 窗口选中 “MainWindow” 对象,在左侧中部 “属性编辑器” 编辑:
(1)将 “宽度” 修改为 1080, “高度” 修改为 720,也可以直接使用鼠标拉伸来调整 “MainWindow” 大小。
(2)将 “WindowTitle” 属性修改为:YOLOv5目标检测。
(3)将 “WindowIcon” 属性修改为:用户选择的图标,例如 icons\youcans.png。
2.3 创建菜单对象(menu)
QtDesigner 创建的 “MainWindow” 图形窗口,自动生成了顶部菜单栏 menubar,在图形窗口左上角显示有文本输入框 “在这里输入”。
输入菜单对象(menu)的标题(title):
- 鼠标点击文本输入框 “在这里输入”,选中文本输入框,控件的边框变为紫色;
- 再双击选中的控件,出现激活的文本输入框,就可以输入所要建立菜单的标题;
- 输入菜单标题后回车结束,就建立了一个一级菜单,例如:将菜单标题设为 “文件”。
输入菜单对象的标题,以及修改菜单对象的属性,更通用的方法是:
- 在 QtDesigner 右侧的 “对象查看器” 中选中对象 “menu”,此时右侧中部的 “属性编辑器” 将显示对象 “menu” 的属性。
- 在 “属性编辑器” 内选择 “title” 属性,将其修改为菜单标题:“文件”。
建立一级菜单 “文件” 后,菜单栏中在 “文件” 右侧又出现新的文本输入框 “在这里输入” ,按照以上操作可以接着建立更多的菜单对象。
QtDesigner 创建二级菜单,实际上是将动作(action)添加到一级菜单。
2.4 布局管理(Label)
布局管理就是管理图形窗口中各个部件的位置和排列。图形窗口中的大量部件也需要通过布局管理,对部件进行整理分组、排列定位,才能使界面整齐有序、美观大方。
在 QtDesigner 左侧工具栏中,第一组工具 “Layout” 就是布局管理器,分别提供了水平布局、垂直布局、栅格布局和表单布局 4种布局管理的工具按钮。
容器布局将容器控件(Container)与布局管理器结合,先用容器控件将窗口分为若干区域,再在每个区域内加入布局管理器。建立容器控件后,可以直接将其它控件加入容器控件内;也可以在容器控件加入布局管理器,再向布局管理器加入多个控件,使多个控件按布局要求放在容器中。
首先在 QtDesigner 左侧工具栏的 “Containers” 类中,选择 “Frame” 控件或 “Widget” 控件将其拖动至中间的图形窗口中,创建容器控件。
对图形窗口中的容器控件 “Frame” 或 “Widget”,可以选中后用鼠标拖动、拉伸来调整控件的位置和大小,或者在 “属性编辑器” 中设置 (X, Y)、宽度、高度属性。
对于需要进行布局管理的容器控件,从在 QtDesigner 左侧工具栏的 “Laytout” 类中选择所需的布局管理器控件,将其拖动至容器控件中,创建容器控件的布局管理器。
容器布局就像网站、报刊中的栏目、子版,可以按照编辑的要求便捷、自由地进行布局。例如,我们要将程序窗口按照十字分割方案分为上下和左右四个部分,就在窗口先创建四个 “Frame” 容器控件,并调整其位置和大小,然后向一个或几个 “Frame” 容器控件加入所需的布局管理控件。如下图所示,可以实现对程序窗口的自由分割和布局。
2.5 创建内容显示控件(Label)
在 QtDesigner 左侧的 “WidgetBox” 工具栏中,将常用的控件按类别进行分组。用鼠标将工具栏中的控件图标拖拽到 QtDesigner 中间的图形界面编辑窗口,就在图像界面创建了一个所选择的控件。
内容显示控件QLabel(显示框)是一个只读显示的简易控件,用于显示不可编辑的文本或图像,不提供用户交互功能。
- 从左侧控件栏的 “DisplayWidget” 中选择 Label 控件,移动鼠标将 Label 控件拖动到新建图形窗口内的任意位置,就在图形窗口位置生成了一个 Label 控件对象。
- 鼠标左键选中图形窗口中的这个 Label 控件对象,拖动鼠标可以调整控件的位置,对于其它控件也可以通过鼠标拖动来调整位置。
- 对于添加到 QLabel 显示框,可以通过属性编辑器修改属性,例如通过 “Pixmap” 选择显示的图像文件。默认情况下,QLabel 会对 文本和图像 内容 左对齐、垂直居中显示,也可以通过属性修改进行设置。
类似地,建立 Label 控件对象 “label_2”,但没有为其设置显示的图像文件,因此该区域看起来是空白的。
2.6 创建按钮控件(PushButton)
按钮是最常用的控件类型。在 QtDesigner 左侧的 “WidgetBox” 工具栏中的"Buttons" 组,设有多种不同类型的按钮控件。
QPushButton(按键按钮)是最常用的按钮,按下(或者单击)按钮可以执行某个操作或回答问题,例如:确定,应用,取消,关闭,是,否和帮助。
- 按钮控件通常显示一个文本标签(text),可以为按钮选择一个图标(icon),还可以选择设置快捷键(shortcut)。
- 按键按钮的上述属性,都可以在 “属性编辑器” 中相应的属性行中进行编辑修改。
- 当按键按钮被鼠标或快捷键激活时,按钮会发出 clicked() 信号,可以通过连接槽函数来触发特定的操作。
用鼠标将工具栏中的按钮控件拖拽到 QtDesigner 中间的图形界面编辑窗口,就在图像界面创建了一个按钮控件。从左侧控件栏的 “Buttons” 中选择 PushButton 按钮,移动鼠标将 PushButton 按钮拖动到新建图形窗口内的任意位置,就在图形窗口位置生成了一个 PushButton 按钮对象。
- 鼠标左键选中图形窗口中的这个 PushButton 按钮对象,拖动鼠标可以调整控件的位置。
- 鼠标选中 PushButton 按钮对象,控件周围的边界位置上就出现 8个蓝色的点,表示控件被选中,这时可以在右侧的 “属性编辑器” 内对对象的属性进行编辑和修改,例如:
- 将 PushButton 对象的高度修改为 80,宽度修改为 40;
- 将 PushButton 对象的 “QAbstractButton->text” 修改为 “1 打开”。
于是,我们就完成了本项目的图形界面设计,将其保存为 uiYOLO5_2.ui文件。
在 PyCharm中,使用 PyUIC 将选中的 uiYOLO5_2.ui 文件转换为 .py 文件,就得到了 uiYOLO5_2.py 文件。
3. GUI 主程序开发
3.1 从零开始编写主程序
我们编写一个主程序调用设计的图形界面 YOLOv5PCB_Main1.py,就可以完成一个图形界面应用程序。
在 QtDesigner 将设计的图形界面保存为 uiYOLO5_2.ui。在 PyCharm 选中 uiYOLO5_2.ui 文件,点击鼠标右键唤出下拉菜单,选择:ExternalTools -> PyUIC,点击鼠标左键运行 PyUIC 将选中的 .ui 文件转换为 .py 文件,在该路径生成 uiYOLO5_2.py 文件。
面向对象的程序设计使程序的结构更加清晰,从而易于阅读、理解、开发和维护,非常适合开发大规模、多任务的图形界面应用软件。PyQt5 中的类、对象、控件和方法,都是面向对象的程序设计的概念。
我们采用面向对象的程序设计方法,编写第一个例程 YOLOv5_Main1.py 内容如下。其中,导入模块 uiYOLO5_2 是刚才建立的 uiYOLO5_2.py 文件,要注意输入正确路径和文件名。
# -*- coding: utf-8 -*-
# YOLOv5_Main1.py
# GUI of YOLOv5
# Copyright 2024 Youcans, Xidian
# Crated:2024-08-28
import sys, time
from PyQt5.QtWidgets import QApplication, QMainWindow
from uiYOLO5_2 import Ui_MainWindow # 导入 uiYOLO5_2.py 中的 Ui_MainWindow 界面类
class MyMainWindow(QMainWindow): # 继承 QMainWindow类
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent) # 初始化父类
self.ui = Ui_MainWindow() # 继承 Ui_MainWindow 界面类
self.ui.setupUi(self) # 继承 Ui_MainWindow 界面类
# self.initGUI()
# self.initMain()
if __name__ == '__main__':
app = QApplication(sys.argv) # 在 QApplication 方法中使用,创建应用程序对象
myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口
myWin.show() # 在桌面显示控件 myWin
sys.exit(app.exec_()) # 结束进程,退出程序
在这个例程中,我们创建了一个类 MyMainWindow(),它继承了 QtWidgets.QMainWindow 类方法和导入的 uiDemo2.py 中的 Ui_MainWindow 界面类。
编译运行这个程序,生成如下图像界面,与我们在 QtDesigner 设计的 GUI 是一致的。但我们还没有对其中的对象进行定义和关联,所以点击按键并不会动作。
3.2 信号与槽的连接
在上节的例程 YOLOv5_Main1.py 中,我们使用 QtDesigner 设计了一个简单的图像界面 ,包括菜单、工具栏、图像和按钮。但是,这些按钮和菜单项还是无效的,点击后不会调用相应的处理函数,这是因为我们还没有为这些控件关联动作程序(连接槽函数)。
信号与槽机制是 PyQt 的核心机制,用于对象之间的通信,也就是实现函数之间的自动调用。关于信号与槽的具体内容,请参考我的博客【OpenCV-PyQT项目实战(3)信号与槽机制】。
在 initGUI(self) 子程序中,定义信号与槽的连接。将自定义的槽函数 openImage()、openVideo()、runSlot() 等添加到主程序中,编写第二个例程 YOLOv5_Main2.py 内容如下:
# -*- coding: utf-8 -*-
# YOLOv5_Main2.py
# GUI of YOLOv5
# Copyright 2024 Youcans, Xidian
# Crated:2024-08-28
import sys, time
import numpy as np
import cv2 as cv
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from uiYOLO5_2 import Ui_MainWindow # 导入 uiYOLO5_2.py 中的 Ui_MainWindow 界面类
class MyMainWindow(QMainWindow): # 继承 QMainWindow类
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent) # 初始化父类
self.ui = Ui_MainWindow() # 继承 Ui_MainWindow 界面类
self.ui.setupUi(self) # 继承 Ui_MainWindow 界面类
self.initGUI()
self.initMain()
def initGUI(self):
# --- GUI 菜单栏定义 ---
# 菜单栏 动作定义
self.ui.actionOpen.triggered.connect(self.openImage) # 连接并执行 openImage 子程序
self.ui.actionRun.triggered.connect(self.runSlot) # 连接并执行 runRun 子程序
self.ui.actionSave.triggered.connect(self.saveSlot) # 连接并执行 saveSlot 子程序
self.ui.actionHelp.triggered.connect(self.actHelp) # 连接并执行 actHelp 子程序
self.ui.actionQuit.triggered.connect(self.close) # 连接并执行 退出
# 通过 connect 建立信号/槽连接
# 点击按钮事件时,发射triggered信号,执行相应的子程序 click_pushButton
self.ui.btnPic_1.clicked.connect(self.openImage) # 点击 btnPic_1/导入图片 触发
self.ui.btnPic_2.clicked.connect(self.openVideo) # 点击 btnPic_2/导入视频 触发
self.ui.btnPic_3.clicked.connect(self.openImage) # 点击 btnPic_3/开启摄像头 触发
self.ui.Button_1.clicked.connect(self.runSlot) # 点击 Button_1/目标检测 触发
self.ui.Button_2.clicked.connect(self.saveSlot) # 点击 Button_2/保存结果 触发
self.ui.Button_3.clicked.connect(self.close) # 点击 Button_3/退出 触发
# 通过 connect 建立信号/槽连接
self.ui.spinBox.valueChanged.connect(self.on_spinBox_valueChanged)
def initMain(self):
self.mode = "image"
self.org_path = None
self.draw_path = None
self.org_img = np.empty((1,1,3))
self.numTarget = 0
self.is_camera_open = False
# 初始化
self.ui.spinBox.setRange(0, 0) # 初始化 目标编号的可选范围
self.ui.spinBox.setValue(0) # 初始化 spinbox 的初值为 0
self.org_img = tools.img_cvread("PCB00.png") # OpenCV 读取图像文件
# self.ui.label.setPixmap(self.cvToQImage(self.org_img)) # 刷新显示图片
def openImage(self): # 读取图像文件,动作 actionOpen 触发
return
def openVideo(self): # 读取视频文件, 点击 Button_2 触发
return
def runSlot(self): # 动作 actionRun 触发
return
def saveSlot(self): # 动作 actionSave 触发
return
def on_spinBox_valueChanged(self): # 点击目标编号, 切换显示第 index 个目标的数据
return
def actHelp(self): # 帮助子程序, 动作 actHelp 触发
QMessageBox.about(self, "About",
"""YOLO5 目标检测系统 v1.0\nCopyright YouCans, IMIU Xidian 2024""")
return
if __name__ == '__main__':
app = QApplication(sys.argv) # 在 QApplication 方法中使用,创建应用程序对象
myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口
myWin.show() # 在桌面显示控件 myWin
sys.exit(app.exec_()) # 结束进程,退出程序
编译运行这个程序,生成图像界面与例程 YOLOv5_Main1.py相同,但现在按键可以点击,并执行相应的子程序。例如点击菜单中的“帮助”可以弹出帮助窗口,点击“退出”按键会退出程序。但是,点击其它按钮和菜单项还是没有执行相应的内容,这是因为我们的任务处理子程序(槽函数)还是空的。
3.3 导入图像文件子程序
从菜单选择“打开”,或点击按键“导入图片”,进入导入图像文件子程序,读取待处理的图像文件,并刷新图像显示,清空检测数据显示区。
用 QFileDialog.getOpenFileName 函数交互式选择要打开的文件,用 cv.imread函数读取图像文件。
def openImage(self): # 读取图像文件,动作 actionOpen 触发
self.mode = "image" # 图像文件模式
self.file_path, _ = QFileDialog.getOpenFileName(None, '打开图片', './', "Image files (*.jpg *.jepg *.png)")
print(self.file_path)
self.org_path = self.file_path
# self.org_img = tools.img_cvread(self.org_path) # OpenCV 读取图像文件
self.org_img = cv.imdecode(np.fromfile(self.org_path, dtype=np.uint8), cv.IMREAD_COLOR)
imgPix = self.cvToQImage(self.org_img) # OpenCV 转为 PyQt 图像格式
self.ui.label.setPixmap(imgPix) # 刷新显示图片
self.ui.label.setAlignment(Qt.AlignCenter)
self.ui.lineEdit_1.setText(self.file_path) # 显示导入文件路径
self.targetUpdate(0) # 清空检测数据显示区
return
3.4 保存图像文件子程序
用 cv.imwrite函数写入检测结果图像文件。
def saveSlot(self): # 动作 actionSave 触发
if not self.org_path: # 不存在需要检测的图像
print("Org_img not existed.")
return
if not self.numTarget: # 不存在需要保存的图像
print("Draw_img not existed.")
return
save_name = (self.org_path.split("/")[-1]).split(".")[0] + "_out.png"
print("save_name", save_name)
self.save_path = "..\\runs\\detect\\" + save_name
if self.mode == "image":
cv.imwrite(self.save_path, self.draw_img)
print("{} saved:".format(self.save_path))
return
3.5 YOLOv5 图像检测子程序
def runSlot(self): # 动作 actionRun 触发
print(self.org_img.size)
print("Detect image: ", self.org_path)
# 调用 YOLOv5 预训练模型检测图片
t1 = time.time()
img_path = self.org_path # 需要检测的图片地址
self.results = self.model(img_path)[0] # YOLO 检测结果
t2 = time.time()
print("Detect time: {:.3f} s".format(t2 - t1))
# 显示检测结果图片
now_img = self.results.plot() # 检测结果图片, CV格式
self.draw_img = now_img
imgPix = self.cvToQImage(now_img) # OpenCV 转为 PyQt 图像格式
self.ui.label.setPixmap(imgPix) # 刷新显示图片
self.ui.label.setAlignment(Qt.AlignCenter)
# 目标类别
cls_list = self.results.boxes.cls.tolist()
self.numTarget = len(cls_list) # 目标数量, 检测结果是多目标的数组
print("number of target:", self.numTarget) # 目标数量, 1
self.cls_list = [int(i) for i in cls_list]
print("cls_list:",self.cls_list) # 目标类别, [1]
# 目标位置 [xmin, xmax, ymin, ymax]
location_list = self.results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
print("location_list:",self.location_list) # [[599, 1044, 642, 1080]]
# 置信度
self.conf_list = self.results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each*100) for each in self.conf_list]
print("conf_list:", self.conf_list) # ['53.12 %']
self.ui.spinBox.setRange(1, self.numTarget) # 设置目标编号的 可选范围
self.targetUpdate(1) # 默认显示第 1 个目标的数据
return
3.6 OpenCV 图像文件转换为 QImage 子程序
def cvToQImage(self, image):
# 8-bits unsigned, NO. OF CHANNELS=1
if image.dtype == np.uint8:
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 3: # CV_8UC3
# Create QImage with same dimensions as input Mat
qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)
return qImg.rgbSwapped()
elif channels == 1:
# Create QImage with same dimensions as input Mat
qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)
return qImg
else:
QtCore.qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
return QImage()
3.7 帮助菜单子程序
def actHelp(self): # 帮助子程序, 动作 actHelp 触发
QMessageBox.about(self, "About",
"""YOLO5 目标检测系统 v1.0\nCopyright YouCans, IMIU Xidian 2024""")
return
至此,我们完成了 GUI 程序的开发,可以实现 GUI 中的各种基本功能。
4. 调用YOLO5 模型进行目标检测
在上一章中,我们介绍的其实都是基于 PyQt 的 GUI 相关操作的子程序的实现,例如读入和保存文件,显示图像和数据,与 YOLO5 模型的使用没有直接关系。
本章以 PCB 缺陷检测任务为例,具体介绍如何在这个框架上应用 YOLOv5 进行 PCB 缺陷检测。
4.1 安装 ultralytics 模块
本文使用 ultralytics 模块实现 YOLO 模型和方法。
Ultralytics是一个开源的计算机视觉和深度学习框架,旨在简化训练、评估和部署视觉模型的过程。该框架提供了一系列流行的视觉模型,包括YOLOv5、YOLOv4、YOLOv3、YOLOv3-tiny、YOLOv5-tiny、EfficientDet、PAN、PP-YOLO等,并提供了训练、评估和推理的工具和实用程序。
Ultralytics 提供了多种安装方法,包括 pip、conda 和 Docker。推荐采用 Pip 安装:
# Install the ultralytics package from PyPI
pip install ultralytics
注意要把 ultralytics 安装在激活环境,并在该激活环境建立和运行本项目。
YOLO Python 界面可无缝集成到项目中,从而轻松加载、运行和处理模型输出。 界面的设计以简单易用为宗旨,用户可以在自己的项目中快速实现对象检测、分割和分类。这使得 的 界面成为任何希望将这些功能纳入其 项目的人的宝贵工具。
例如,用户只需几行代码就可以加载模型、训练模型、评估模型在验证集上的性能,甚至将模型导出为ONNX 格式。
from ultralytics import YOLO
# Create a new YOLO model from scratch
model = YOLO("yolov8n.yaml")
# Load a pretrained YOLO model (recommended for training)
model = YOLO("yolov8n.pt")
# Train the model using the 'coco8.yaml' dataset for 3 epochs
results = model.train(data="coco8.yaml", epochs=3)
# Evaluate the model's performance on the validation set
results = model.val()
# Perform object detection on an image using the model
results = model("https://ultralytics.com/images/bus.jpg")
# Export the model to ONNX format
success = model.export(format="onnx")
4.2 ultralytics 的导入和初始化
主程序 YOLOv5_Main*.py 的开头导入所需的模块时,除了前面例程中导入的系统相关和 GUI 相关模块之外,还要导入 ultralytics 模块如下:
import numpy as np
import cv2 as cv
from ultralytics import YOLO
为了快速对输入图像进行目标检测,主程序初始化时加载预训练模型。在
def initMain(self):
# 加载预训练模型
modelPath = '../models/Yolov5sPCB100.pt' # 加载的预训练模型路径
self.model = YOLO(modelPath, task='detect')
self.model(np.zeros((48, 48, 3))) #预先加载推理模型
其中,modelPath 是训练好的 PCB缺陷检测模型的路径,可以根据该模型文件的路径和文件名进行设置。注意对于 PCB 缺陷检测项目,该模型并不是官方下载的 YOLOv5s.pt 预训练模型,而是在 PCB 缺陷数据集训练的专用模型。
4.3 使用 YOLO 模型进行检测
如前所述,ultralytics 使用 YOLO 模型检测图片的程序非常简单:
# Perform object detection on an image using the model
results = model(“https://ultralytics.com/images/bus.jpg”)
模型检测调用返回一个 Results 对象。
Results 对象的属性如下(属性,类型,描述):
- orig_img,numpy.ndarray,原始图像,以numpy数组表示。
- orig_shape,tuple,原始图像的形状,以(height, width)格式表示。
- boxes,Boxes, optional,包含检测边界框的 Boxes 对象。
- masks,Masks, optional,包含检测掩码的 Masks 对象。
- probs,Probs, optional,包含每个类的概率的 Probs 对象,用于分类任务。
- keypoints,Keypoints, optional,包含每个对象检测关键点的 Keypoints 对象。
- obb,OBB, optional,包含定向边界框的 OBB 对象。
- speed,dict,一个字典,包含预处理、推理和后处理的速度(以毫秒为单位,针对每张图像)。
- names,dict,一个字典,包含类名。
- path,str,图像文件的路径。
Results 对象的方法如下(方法,返回类型,描述):
- update(),None,更新 Results 对象的 boxes、masks 和 probs 属性。
- cpu(),Results,返回一个 Results 对象的副本,所有张量都在CPU内存中。
- numpy(),Results,返回一个 Results 对象的副本,所有张量都转换为numpy数组。
- cuda(),Results,返回一个 Results 对象的副本,所有张量都在GPU内存中。
- to(),Results,返回一个 Results 对象的副本,张量在指定的设备和数据类型上。
- new(),Results,返回一个新的 Results 对象,具有相同的图像、路径和名称。
- plot(),numpy.ndarray,绘制检测结果。返回标注图像的numpy数组。
- show(),None,在屏幕上显示标注结果。
- save(),None,将标注结果保存到文件。
- verbose(),str,返回每个任务的日志字符串。
- save_txt(),None,将预测结果保存到txt文件中。
- save_crop(),None,将裁剪的预测结果保存到 save_dir/cls/file_name.jpg 中。
- tojson(),str,将对象转换为JSON格式。
在本项目中,通过 img_path 传递被检测的图片路径,model(img_path)[0] 返回检测结果的 Results 对象。
需要注意的是,不论被检测图片是一幅图像,还是多幅图像或图像序列,返回值都是一个 list 数组,通过 [0] 得到第一幅图像的检测结果。
# 调用 YOLOv5 预训练模型检测图片
img_path = self.org_path # 需要检测的图片地址
self.results = self.model(img_path)[0] # YOLO 检测结果
进一步地,
(1)使用 Results 对象的方法 results.plot() 得到检测结果的标注图像(numpy数组格式,与 OpenCV图像格式相同,需要转化为 PyQt 图像格式才能显示).
(2)使用 Results 对象的属性 Boxes 对象,其属性 cls 返回边界框的类别值,属性 xyxy 返回xyxy格式的边界框坐标,属性 conf 返回边界框的置信度。
需要注意的是,检测结果可能包括零个、一个或多个目标,通过 .tolist 转换为 list 数组,以方便对每一个目标进行操作。
# 显示检测结果图片
now_img = self.results.plot() # 检测结果图片, CV格式
self.draw_img = now_img
imgPix = self.cvToQImage(now_img) # OpenCV 转为 PyQt 图像格式
# 目标类别
cls_list = self.results.boxes.cls.tolist()
self.numTarget = len(cls_list) # 目标数量, 检测结果是多目标的数组
self.cls_list = [int(i) for i in cls_list]
# 目标位置 [xmin, xmax, ymin, ymax]
location_list = self.results.boxes.xyxy.tolist()
self.location_list = [list(map(int, e)) for e in location_list]
# 置信度
self.conf_list = self.results.boxes.conf.tolist()
self.conf_list = ['%.2f %%' % (each*100) for each in self.conf_list]
如何使用这些检测结果数据,例如显示、保存,是在 GUI 程序中实现,在此不做赘述。
5. PCB缺陷检测系统的使用
运行 PCB缺陷检测系统,主要操作如下:
(1)通过菜单栏或“导入”按键 导入被检测图片,主图显示被检测图片。
(2)通过菜单栏或“检测”按键 调用YOLOv5 模型对图像进行目标检测,主图显示检测后的标注图像,检测结果区显示被检测目标的列表,检测数据区显示第1个检测目标的数据。
(3)在检测数据区选择“目标编号”,可以显示对应的目标的检测数据。
(4)点击保存,可以保存检测结果图像和数据文件。
(5)通过菜单栏或“退出”按键,退出系统。
说明:
本系统 GUI 预留了导入视频文件或开启摄像头进行检测,但由于本项目内容太多,本文并未涉及这些功能,将在后续文中进行介绍。有兴趣的读者也可以参考我的博客:
OpenCV-PyQT项目实战(9)项目案例04:视频播放
OpenCV-PyQT项目实战(12)项目案例08:多线程视频播放。
【本节完】
版权声明:
欢迎关注『youcans动手学模型』系列
转发请注明原文链接:
【YOLO5 项目实战】(8)PyQt5 图形界面—PCB缺陷检测系统
Copyright 2024 youcans, XUPT
Crated:2024-08-28