可在过往博客查看,YOLO原理,以及具体训练过程 , 这篇文章是继续完善YOLO模型的使用,即将控制台cmd交互的YOLO5模型实现为交互界面可视化操作。我们前期已经搭建了一个QT框架,现在只要将具体函数与QT框架进行绑定即可。
1. 将.ui文件转换为.py文件
1.1 文件放置及QT框架预览
个人建议直接将整个ui文件放置在YOLO5文件夹下,方便后期整个项目的文件关系关联使用
原始的QT 框架长这样,但是还没进行具体的操作功能实现
1.2 将detect.ui文件转换为detect.py文件
1.3 查看具体控件名称
打开detect.py文件,查看具体的控件名称。控件名称在后续开发工作中需要用于具体函数的绑定
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "操作区"))
self.label.setText(_translate("MainWindow", "功能选择"))
self.label_2.setText(_translate("MainWindow", "检测结果"))
self.pushButton.setText(_translate("MainWindow", "图片加载地址"))
self.pushButton_2.setText(_translate("MainWindow", "检测保存地址"))
self.pushButton_3.setText(_translate("MainWindow", "开始检测"))
self.label_3.setText(_translate("MainWindow", "待检测区"))
self.label_7.setText(_translate("MainWindow", "检测区"))
self.label_8.setText(_translate("MainWindow", "欢迎使用狗嘴套检测系统"))
在detect.py文件中的retranslateUi方法中,可以查看到具体的控件名称。
-
控件名:
比如label, label_2, label_3, pushButton, pushButton_2, pushButton_3等。 -
操作界面显示便签:
这些self后面的第一个名称就是对应的控件变量名,其具体的可视化展现是其对应最后的文字
在QT可视化操作界面中的控件与对应的标签
上述图片中的几个控件是我们接下来需要进行函数编辑绑定的重要控件
2. 修改detect.py文件
以往参考的一些YOLO交互界面设计,都是将YOLOdetect脚本等脚本的方法写在主脚本中。这样不但需要理清很多YOLO脚本中的代码,而且不方便后期的维护。通过观察YOLO的detect.py脚本,发现其自身逻辑框架已经很完备了。我们只需要在detect.py脚本中结合具体应用场景进行一定的调整,就可以作为一个包进行导入。
然后我的YOLO模型就简单应用为图片监测,其他的应用场景需求也可以自行调整。
2.1 run方法
在run方法中观察到run函数并没有返回具体的文件保存目录,而是隐式的将图片保存到指定文件夹中
故
- 在run方法最后加上
return save_path
来返回保存图片的具体目录 - 在main方法最后加上
return run(**vars(opt))
来返回这个变量
2.2 parse_opt方法
parse_opt方法中有几个参数是重要的,需要留意,为后续的函数绑定做准备。
- weights: 使用权重的目录
- source:需要检测的文件夹、文件
- roject:需要保存到的文件夹
最后将detect.py文件另存为detect_adjust.py文件
3. 编写main.py文件
以下是main.py的具体代码实现
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import os
# 导入模型
from ui.detect import Ui_MainWindow # 导入detec_ui的界面
from detect_adjust import parse_opt, main
class UI_Logic_Window(QtWidgets.QMainWindow):
def __init__(self,parent=None):
super(UI_Logic_Window, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.init_slots()
# 控件绑定相关操作
def init_slots(self):
self.ui.pushButton.clicked.connect(self.button_image_open)
self.ui.pushButton_2.clicked.connect(self.button_image_detect_save)
self.ui.pushButton_3.clicked.connect(self.detect)
# 设置需要检测的图片
def button_image_open(self):
name_list = []
try:
self.img_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开图片", r"D:\dog_picture\YOLO_dog_mask\images\test",
"*.jpg;;*.png;;All Files(*)")
except OSError as reason:
print('文件打开出错啊!核对路径是否正确' + str(reason))
else:
# 判断图片是否为空
if not self.img_name:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打开图片失败", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
# 打印检测图片,与label_3绑定起来
jpg = QtGui.QPixmap(self.img_name).scaled(self.ui.label_3.width(), self.ui.label_3.height())
self.ui.label_3.setPixmap(jpg)
self.ui.label_3.setScaledContents(True)
# 设置需要保存的地址
def button_image_detect_save(self):
print('button_image_save')
self.save_dir = QtWidgets.QFileDialog.getExistingDirectory(self, "选择文件夹", "runs/detect")
print("选择的文件夹路径:", self.save_dir)
# 检测函数
def detect(self):
try:
source = self.img_name.replace('/', '\\')
except:
QtWidgets.QMessageBox.warning(self, u"Warning", u"请先选择要检测的图片文件",
buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
pass
try:
save_dir = self.save_dir
except:
QtWidgets.QMessageBox.warning(self, u"Warning", u"请先选择要保存的图片文件夹",
buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
return
# 构造参数,设置参数值
opt = parse_opt()
weights_path = 'runs\\train\exp4\weights\last.pt'
opt.weights = weights_path
opt.source = source
opt.project = save_dir
save_img = main(opt)
print(save_img)
jpg = QtGui.QPixmap(save_img).scaled(self.ui.label_7.width(), self.ui.label_7.height())
self.ui.label_7.setPixmap(jpg)
self.ui.label_7.setScaledContents(True)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
current_ui = UI_Logic_Window()
current_ui.show()
sys.exit(app.exec_())
3.1 import处需要注意的是:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import os
# 导入模型
from ui.detect import Ui_MainWindow # 导入detec_ui的界面
from detect_adjust import parse_opt, main
- import sys —— from PyQt5.QtWidgets import *
引入PyQt5所需要依赖的包 - from ui.detect import Ui_MainWindow
导入detec_ui的界面,其中ui是与我们main.py同级文件下的ui文件,也就是我们之前将detect.ui保存到的ui文件夹,对应的ui.detect也就是将ui文件夹下的detect.py作为一个包进行导入,将使用Ui_MainWindow作为父类 - from detect_adjust import parse_opt, main
导入detect_adjust包的parse_opt方法和main方法
3.2 init 初始化函数定义
def __init__(self,parent=None):
super(UI_Logic_Window, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.init_slots()
- def init(self,parent=None):
parent=None是定义UI_Logic_Window类时声明的一个默认参数(也就是在未传递参数情况下默认值为None)。
这个默认参数可以被覆盖,例如,在创建UI_Logic_Window实例时如果需要指定父级窗口,则可以在传递实参时传递一个有效的窗口对象。 - super(UI_Logic_Window, self).init(parent)
这里应该是继承了QtWidgets.Qmainwindow的方法super(子类,self).父类的方法(self, 参数)
super中的self
必须写,但父类方法parent
中不能写self
使用super()函数调用父类的构造函数,不仅使代码更加通用和灵活,同时也可以避免出现硬编码的问题, 并且处理多重继承也更加方便。因此,在子类的__init__()方法中使用super()函数来调用父类的构造函数通常是一种更好的实践。 - self.ui = Ui_MainWindow()
self.ui.setupUi(self)
在应用程序中,可以在初始化子类时实例化这个Ui_MainWindow
类,并调用其setupUi
方法来初始化UI界面,如:
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
这段代码的作用是创建一个名为 ui
的实例变量,并将其设置为 Ui_MainWindow
类的实例。然后,使用 ui
实例来调用 setupUi
方法,该方法会接收一个 self
参数,将其用于初始化UI界面。这样,就完成了UI界面的加载和初始化,并可以在应用程序中使用了。
- self.init_slots()
这段代码是在一个Python类中初始化UI界面,并且调用了一个名为"init_slots"的方法,可能是用来连接信号和槽的。通常,在PyQt或是PySide的GUI编程中,先使用Qt Designer进行UI的设计,然后利用相关的类库将UI装载到程序中,并添加适当的逻辑和事件处理。而接下来的init_slots函数就会定义相应的逻辑和事件处理
3.3 init_slots 连接信号和槽
init_slots(self)
是一个自定义函数,用于将UI界面中各个控件的信号与槽函数进行连接。
在PyQt或PySide中,信号和槽机制是一个结合了事件驱动和消息传递机制的重要特性。
通过连接信号和槽,可以实现控件之间的交互和响应,从而实现应用程序的功能。
在这个函数中,有三个连接操作:
self.ui.pushButton.clicked.connect(self.button_image_open)
self.ui.pushButton_2.clicked.connect(self.button_image_detect_save)
self.ui.pushButton_3.clicked.connect(self.detect)
这三个操作分别连接了三个控件的信号和槽函数,通过点击按钮触发槽函数实现各自的功能。具体来说,这三个操作的意义分别为:
-
self.ui.pushButton.clicked.connect(self.button_image_open)
将 QPushButton 控件pushButton
的clicked
信号连接到self.button_image_open
函数上。 -
self.ui.pushButton_2.clicked.connect(self.button_image_detect_save)
将另一个 QPushButton 控件pushButton_2
的clicked
信号连接到self.button_image_detect_save
函数上。 -
self.ui.pushButton_3.clicked.connect(self.detect)
将第三个 QPushButton 控件pushButton_3
的clicked
信号连接到self.detect
函数上。
下图显示了对应的槽函数绑定关系
这样,当用户点击对应的按钮时,程序就会自动调用相应的槽函数,从而实现各自的功能。
3.4 button_image_open(self) 选择检测图片槽函数
这是一个自定义槽函数 button_image_open(self)
,在该函数中实现了打开图片的功能。当用户点击 pushButton
按钮时,程序会自动调用该函数。
接下来,这段代码主要的功能是通过 QtWidgets.QFileDialog
弹出文件选择对话框,让用户选择要打开的图片文件。如果用户成功打开了一个图片文件,则会将其路径保存到变量 self.img_name
中。同时,还针对打开文件出现异常的情况进行了异常处理,保证程序的健壮性。
如果成功打开了一个图片文件,这里还使用了 QtWidgets.QMessageBox
显示一个提示信息,以告诉用户操作是否成功。接着,将选择的图片文件显示在界面上。具体地,代码将图片文件路径转换为 QPixmap
对象,调整其大小,并将其设置为 label_3
控件的背景。
最后,通过设置 setScaledContents(True)
来保持图片的纵横比例。这样,当 label_3
控件的大小改变时,图片也能保持其比例不变。
3.5 button_image_detect_save(self) 图片保存目录槽函数
button_image_detect_save(self)
是一个自定义的槽函数,主要实现了检测图片并保存的功能。当用户点击 pushButton_2
按钮时,程序会自动调用该函数。
该函数的主要功能是通过 QtWidgets.QFileDialog
弹出文件夹选择对话框,让用户选择要保存检测结果的文件夹。如果用户成功选择了一个文件夹,则会将其路径保存到变量 self.save_dir
中。通过打印信息可以方便地查看选择的文件夹路径。
需要注意的是,该函数并没有实现具体的检测功能和保存功能,而只是实现了路径的选择和保存。如果想要实现具体的功能,还需要在detect
函数中实现
3.6 detect(self) 图片检测槽函数
detect(self)
是一个自定义的槽函数,主要实现了图片检测的功能。当用户点击 pushButton_3
按钮时,程序会自动调用该函数。
def detect(self):
# 构造参数,设置参数值
opt = parse_opt()
weights_path = 'runs\\train\exp4\weights\last.pt'
opt.weights = weights_path
opt.source = self.img_name.replace('/','\\')
save_img = main(opt)
print(save_img)
jpg = QtGui.QPixmap(save_img).scaled(self.ui.label_7.width(), self.ui.label_7.height())
self.ui.label_7.setPixmap(jpg)
self.ui.label_7.setScaledContents(True)
该函数的主要流程如下:
- 调用
parse_opt()
函数创建程序运行参数opt
。 - 设置权重文件的路径、检测的图片路径和检测结果保存的路径等参数。
weights:保存的权重文件,这里使用已经训练好的小狗嘴套佩戴模型权重的last版本
opt.weights: 调用opt的weights属性并更新
opt.source: 调用opt的source属性并更新,需要注意的是:更新数据的格式,字符串而且是反斜号 - 调用
main(opt)
,进行图片检测,返回保存的检测结果。 - 将检测结果显示在界面上。
在具体实现中,函数使用 parse_opt()
函数构造了程序运行参数 opt
,并设置了权重文件的路径、检测的图片路径等参数。然后,使用 opt
参数调用 main(opt)
函数进行检测,返回检测结果。将检测结果显示在 label_7
控件上,从而完成了检测和结果显示的功能。
需要注意的是,该函数中的 self.img_name
变量,实际上是在 button_image_open(self)
函数中赋值得到的。因此,在该函数中需要先调用 button_image_open
函数,让用户选择一个图片文件。
如果用户没有选择图片文件,则 self.img_name
变量为空,会导致程序出错。
解决方法:
- 使用了两个
try ... except ...
块来处理self.img_name.replace('/', '\\')
和self.save_dir
的值。在出现异常的情况下,使用QtWidgets.QMessageBox
显示警告信息,帮助用户进行正确的操作。
在self.img_name.replace('/', '\\')
处理异常时,使用的是pass
语句,这样程序会继续执行后续代码。这种用法可能导致后续代码中出现错误或异常,从而导致程序运行不正确。如果在这里的异常需要终止整个程序的执行,应该使用return
退出函数。
而在self.save_dir
处理异常时,确实使用了return
语句来退出整个函数。这种做法是正确的,可以保证在出现错误时停止程序的继续执行。
类似地,可以在最开始的__init__部分就定义self.save_dir =None, self.img_name = None
,然后使用if not None: return
的结构来进行状态判断
if not self.img_name:
QtWidgets.QMessageBox.warning(self, u"Warning", u"请先选择要检测的图片文件", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
return
- 总体上来说,这段代码可以帮助用户输入参数并正确进行图像处理,但是在处理异常时需要注意使用
return
退出整个函数以防止程序继续执行可能会出错的代码。
3.7 if __name == ‘__main’:
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
current_ui = UI_Logic_Window()
current_ui.show()
sys.exit(app.exec_())
这段代码是一个 main.py 程序的入口,包括如下几个步骤:
-
创建一个
QApplication
对象,用于管理 GUI 应用程序的事件循环和其他系统级操作。 -
创建一个
UI_Logic_Window
对象并调用其show
方法,将窗口显示出来。 -
执行 GUI 应用程序的事件循环,等待用户输入、响应事件等操作。
-
用户退出应用程序或事件循环结束时,调用
sys.exit
方法退出程序。
sys.exit(app.exec_()) # 程序退出机制 app.exec_()的作用是退出主循环
在这里,程序会先创建 QApplication
对象,然后创建 UI_Logic_Window
对象并显示窗口。在窗口显示之后,程序进入 GUI 应用程序的事件循环中并等待用户输入或响应事件等操作。在用户退出应用程序或事件循环结束时,调用 sys.exit
方法退出程序。
这段代码是整个程序的主入口,是 main.py 程序必不可少的一部分。
4. 界面交互系统
4.1 使用步骤
文章来源:https://www.toymoban.com/news/detail-721014.html
4.2 具体交互效果展示
文章来源地址https://www.toymoban.com/news/detail-721014.html
4.3 不足之处
- 该界面操作系统目前只支持对图片的检测,不能很好地支持其他类型的待检测文件
- 对输出的显著性并没有通过具体的对话框进行文本展示
- 整体的界面操作系统仍比较粗糙,需要进一步地打磨优化
到了这里,关于使用PyQt简单实现YOLOv5交互界面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!