可在过往博客查看,YOLO原理,以及具体训练过程 , 这篇文章是继续完善YOLO模型的使用,即将控制台cmd交互的YOLO5模型实现为交互界面可视化操作。我们前期已经搭建了一个QT框架,现在只要将具体函数与QT框架进行绑定即可。
个人建议直接将整个ui文件放置在YOLO5文件夹下,方便后期整个项目的文件关系关联使用
原始的QT 框架长这样,但是还没进行具体的操作功能实现
打开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等。
在QT可视化操作界面中的控件与对应的标签
上述图片中的几个控件是我们接下来需要进行函数编辑绑定的重要控件
以往参考的一些YOLO交互界面设计,都是将YOLOdetect脚本等脚本的方法写在主脚本中。这样不但需要理清很多YOLO脚本中的代码,而且不方便后期的维护。通过观察YOLO的detect.py脚本,发现其自身逻辑框架已经很完备了。我们只需要在detect.py脚本中结合具体应用场景进行一定的调整,就可以作为一个包进行导入。
然后我的YOLO模型就简单应用为图片监测,其他的应用场景需求也可以自行调整。
在run方法中观察到run函数并没有返回具体的文件保存目录,而是隐式的将图片保存到指定文件夹中
故
return save_path
来返回保存图片的具体目录return run(**vars(opt))
来返回这个变量parse_opt方法中有几个参数是重要的,需要留意,为后续的函数绑定做准备。
最后将detect.py文件另存为detect_adjust.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_())
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
def __init__(self,parent=None):
super(UI_Logic_Window, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.init_slots()
self
必须写,但父类方法 parent
中不能写selfUi_MainWindow
类,并调用其 setupUi
方法来初始化UI界面,如:self.ui = Ui_MainWindow()
self.ui.setupUi(self)
这段代码的作用是创建一个名为 ui
的实例变量,并将其设置为 Ui_MainWindow
类的实例。然后,使用 ui
实例来调用 setupUi
方法,该方法会接收一个 self
参数,将其用于初始化UI界面。这样,就完成了UI界面的加载和初始化,并可以在应用程序中使用了。
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
函数上。这样,当用户点击对应的按钮时,程序就会自动调用相应的槽函数,从而实现各自的功能。
这是一个自定义槽函数 button_image_open(self)
,在该函数中实现了打开图片的功能。当用户点击 pushButton
按钮时,程序会自动调用该函数。
接下来,这段代码主要的功能是通过 QtWidgets.QFileDialog
弹出文件选择对话框,让用户选择要打开的图片文件。如果用户成功打开了一个图片文件,则会将其路径保存到变量 self.img_name
中。同时,还针对打开文件出现异常的情况进行了异常处理,保证程序的健壮性。
如果成功打开了一个图片文件,这里还使用了 QtWidgets.QMessageBox
显示一个提示信息,以告诉用户操作是否成功。接着,将选择的图片文件显示在界面上。具体地,代码将图片文件路径转换为 QPixmap
对象,调整其大小,并将其设置为 label_3
控件的背景。
最后,通过设置 setScaledContents(True)
来保持图片的纵横比例。这样,当 label_3
控件的大小改变时,图片也能保持其比例不变。
button_image_detect_save(self)
是一个自定义的槽函数,主要实现了检测图片并保存的功能。当用户点击 pushButton_2
按钮时,程序会自动调用该函数。
该函数的主要功能是通过 QtWidgets.QFileDialog
弹出文件夹选择对话框,让用户选择要保存检测结果的文件夹。如果用户成功选择了一个文件夹,则会将其路径保存到变量 self.save_dir
中。通过打印信息可以方便地查看选择的文件夹路径。
需要注意的是,该函数并没有实现具体的检测功能和保存功能,而只是实现了路径的选择和保存。如果想要实现具体的功能,还需要在detect
函数中实现
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
。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
语句来退出整个函数。这种做法是正确的,可以保证在出现错误时停止程序的继续执行。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
退出整个函数以防止程序继续执行可能会出错的代码。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 程序必不可少的一部分。