个人打卡,慎看。
指路大佬:【手把手带你实战YOLOv5-入门篇】YOLOv5 Pyside6可视化界面_哔哩哔哩_bilibili
零、虚拟环境迁移路径后pip报错解决
yolov5-master文件夹我换位置后,无法pip install了。解决如下:
- activate.bat中修改:@set "VIRTUAL_ENV=D:\yolov5-master\venv"
- activate中修改:VIRTUAL_ENV='D:\yolov5-master\venv'
- 更新pip:python -m pip install --upgrade pip
参考:Python 虚拟环境迁移路径后pip报错解决记录_pybot移动路径后-CSDN博客
【建议挂v,速度会快】
一、环境安装
- 下载Pyside6: pip install pyside6
我的pyside6不在Python.exe的上级文件夹的子目录下,packages和python.exe的位置也不同,发现解释器的位置设置错了,之前一直提示我Invalid,我也没有注意。
一般python.exe和site-packages的位置关系是,python.exe和它的上级在同一文件夹下。
比如anaconda下python.exe, lib
lib下site-packages ,也可以输入以下命令,查找site-packages的位置。
import site
print(site.getsitepackages())
以后安装了包,但是无法import,也可以查找site-packages的位置,很有可能这里面没有那个包。是python.exe选取的错误,修改为对应的python.exe即可。
- 找到pyside6/designer.exe,将快捷方式发送到桌面。
- up主用vscode安装了qt for python插件,但是我使用的是Pycharm,vscode不熟练,在pycharm中,设置setting->tools->external tools,点击+ ,添加,在Program中,输入designer.exe的位置。name和group随便设置,点击ok。
二、设计UI
- 新建mainwindow
- 把两个textlabel拖到mainwindow中【存放图片】,用vertical line隔离他们。拖两个pushbutton【按钮】。
- 勾选scaledcontents可以实现label中存放内容缩放。将alignment设置水平中心对齐。
- 修改对象的名字,便于记忆。比如label1改成input
- 保存到yolov5-master文件夹下,修改名字为main_window
三、将ui转换成py
添加external tool
我的血泪教训,改这个错改了40min:
- program不要选site-packages\Pyside6\uic.exe 这样转换出来的结果可能是C++代码。
- 选\Scripts\pyside6-uic.exe这个文件
- Program: D:\anaconda\Scripts\pyside6-uic.exe 【输入自己的pyside6-uic.exe文件路径】
- Arguments: $FileName$ -o $FileNameWithoutExtension$.py
- working directory: $FileDir$
四、调用mainwindow
import sys
from PySide6.QtWidgets import QMainWindow, QApplication
from main_window import Ui_MainWindow
#这里的main_window是你的ui转换成的py名字
#Ui_MainWindow是main_window里的class名字
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__() #self后不要加(),我加了()报错无法调用mainwindow
self.setupUi(self)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
运行上面的指令即可弹出UI设计界面。
五、关联按钮和代码
bing_slots()函数负责绑定。det_image和pushButton_2【忘改了】是那两个按钮的名字。
import sys
from PySide6.QtWidgets import QMainWindow, QApplication,QFileDialog
#QFileDialog可以访问文件资源管理器
from main_window import Ui_MainWindow
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.bind_slots() #不要忘记调用,不写这句话无法显示。
def open_image(self):
print("点击显示图片!")
def open_video(self):
print("点击显示视频!")
def bind_slots(self):
#绑定
self.det_image.clicked.connect(self.open_image)
self.pushButton_2.clicked.connect(self.open_video)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
六、获取图片
- 一开始无法显示,我设置QPixmap("./datasets/images/train/30.jpg")发现可以显示,于是判断是filepath格式的问题;
- QPixmap的参数是string类型的。打印file_path[0]发现打印出来的仍然是list格式。
- 于是转换list为string类型。filePath=','.join(file_path[0])
#与上个代码省略了相同的部分,方便理解。
from PySide6.QtGui import QPixmap
#显示图片
def open_image(self):
file_path=QFileDialog.getOpenFileNames(self,dir="./datasets/images/train",filter="*.jpg;*.png")
#设置从dir里找图片, filter分号隔离不同格式
#file_path返回的是有两个元素的数组,我们运行后,发现第一个元素是路径,第二个元素是格式。我们只输出路径。
if file_path[0]:
filePath=','.join(file_path[0])
print(file_path[0])
print(filePath)
self.input.setPixmap(QPixmap(filePath)) #图片显示
七、训练图片
- 首先,遇到了问题,我运行页面卡住了,一直在下载某个包,经查找,发现是下面这行代码的问题。我运行提示卡在了“'Downloading torch-2.2.2-cp310-cp310-win_amd64.whl (198.6 MB)”,
- model = torch.hub.load("./","custom",path="runs/train/exp2/weights/best.pt",trust_repo=True, source="local") 运行这行代码时卡住了,其他代码不会卡。
- 已解决:下载几个包即可:pip install torch torchvision torchaudio
- 参考:通过pip安装pytorch超时问题解决 - 知乎 (zhihu.com)
运行代码:
import sys
import torch
from PySide6.QtWidgets import QMainWindow, QApplication,QFileDialog
#QFileDialog可以访问文件资源管理器
from PySide6.QtGui import QPixmap,QImage
#显示图片
from main_window import Ui_MainWindow
def convert2QImage(img):
height,width,channel=img.shape
return QImage(img,width,height,width*channel,QImage.Format_RGB888)
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.model=torch.hub.load("./","custom",path="runs/train/exp2/weights/best.pt",source="local")
self.bind_slots() #不要忘记调用,不写这句话无法显示
def image_pred(self,filePath):
results=self.model(filePath)
image=results.render()[0]
return convert2QImage(image)
#检测后的图片
def open_image(self):
file_path=QFileDialog.getOpenFileNames(self,dir="./datasets/images/train",filter="*.jpg;*.png")
#设置从dir里找图片, filter分号隔离不同格式
#file_path返回的是有两个元素的数组,我们运行后,发现第一个元素是路径,第二个元素是格式。我们只输出路径。
if file_path[0]:
filePath=','.join(file_path[0])
qimage=self.image_pred(filePath)
self.input.setPixmap(QPixmap(filePath))
self.output.setPixmap(QPixmap.fromImage(qimage))
def open_video(self):
print("点击显示视频!")
def bind_slots(self):
#绑定
self.det_image.clicked.connect(self.open_image)
self.pushButton_2.clicked.connect(self.open_video)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
八、训练视频
仍然遇到了报错:Traceback (most recent call last):
File "D:\yolov5-master\yolov5-master\base_ui.py", line 57, in open_video
self.input.setPixmap(QPixmap(convert2QImage(filePath)))
File "D:\yolov5-master\yolov5-master\base_ui.py", line 13, in convert2QImage
height,width,channel=imag.shape
AttributeError: 'str' object has no attribute 'shape'
但是我训练图片时, height,width,channel=imag.shape这行代码就没有出现问题,于是查找包含改行代码的函数调用的参数,果然写错了self.input.setPixmap(QPixmap(convert2QImage(frame)))是frame不是filePath。 一定要细心!!!
import sys
import cv2
import torch
from PySide6.QtWidgets import QMainWindow, QApplication,QFileDialog
#QFileDialog可以访问文件资源管理器
from PySide6.QtGui import QPixmap,QImage
#显示图片
from main_window import Ui_MainWindow
def convert2QImage(imag):
height,width,channel=imag.shape
return QImage(imag,width,height,width*channel,QImage.Format_RGB888)
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.model=torch.hub.load("./","custom",path="runs/train/exp2/weights/best.pt",source="local")
self.bind_slots() #不要忘记调用,不写这句话无法显示
def image_pred(self,filePath):
results=self.model(filePath)
image=results.render()[0]
return convert2QImage(image)
#检测后的图片
def video_pred(self,img):
results=self.model(img)
image=results.render()[0]
return convert2QImage(image)
def open_image(self):
file_path=QFileDialog.getOpenFileNames(self,dir="./datasets/images/train",filter="*.jpg;*.png")
#设置从dir里找图片, filter分号隔离不同格式
#file_path返回的是有两个元素的数组,我们运行后,发现第一个元素是路径,第二个元素是格式。我们只输出路径。
if file_path[0]:
filePath=','.join(file_path[0])
qimage=self.image_pred(filePath)
self.input.setPixmap(QPixmap(filePath))
self.output.setPixmap(QPixmap.fromImage(qimage))
def open_video(self):#视频检测是一帧一帧地检测,利用opencv抽帧。
file_path = QFileDialog.getOpenFileNames(self, dir="./datasets", filter="*.mp4")
if file_path[0]:
filePath=','.join(file_path[0])
print(filePath)
video = cv2.VideoCapture(filePath)
while True:
ret,frame=video.read()
if not ret:
break
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
qimage=self.video_pred(frame)
self.input.setPixmap(QPixmap(convert2QImage(frame)))
self.output.setPixmap(QPixmap.fromImage(qimage))
print("点击显示视频!")
def bind_slots(self):
#绑定
self.det_image.clicked.connect(self.open_image)
self.pushButton_2.clicked.connect(self.open_video)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
不过我运行后很慢,并且只显示了视频最后一帧的检测结果和原图片。
问题:界面刷新不及时。
解决:加入计时器from PySide6.QtCore import QTimer
import sys
import cv2
import torch
from PySide6.QtWidgets import QMainWindow, QApplication,QFileDialog
#QFileDialog可以访问文件资源管理器
from PySide6.QtGui import QPixmap,QImage
#显示图片
from PySide6.QtCore import QTimer
#计时器
from main_window import Ui_MainWindow
def convert2QImage(imag):
height,width,channel=imag.shape
return QImage(imag,width,height,width*channel,QImage.Format_RGB888)
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.model=torch.hub.load("./","custom",path="runs/train/exp2/weights/best.pt",source="local")
self.timer=QTimer()
self.timer.setInterval(100) #时间间隔。默认单位毫秒ms ,可以设置的小一点儿,视频更加丝滑。
self.bind_slots() #不要忘记调用,不写这句话无法显示
def image_pred(self,filePath):
results=self.model(filePath)
image=results.render()[0]
return convert2QImage(image)
#检测后的图片
def video_pred(self):
ret, frame = self.video.read()
if not ret:
self.timer.stop()
else:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.input.setPixmap(QPixmap.fromImage(convert2QImage(frame)))#放在这儿而不是self.output上面,因为可能受检测的图像影响。在检测之前先显示出来。
results=self.model(frame)
image=results.render()[0]
self.output.setPixmap(QPixmap.fromImage(convert2QImage(image)))
def open_image(self):
file_path=QFileDialog.getOpenFileNames(self,dir="./datasets/images/train",filter="*.jpg;*.png")
#设置从dir里找图片, filter分号隔离不同格式
#file_path返回的是有两个元素的数组,我们运行后,发现第一个元素是路径,第二个元素是格式。我们只输出路径。
if file_path[0]:
filePath=','.join(file_path[0])
qimage=self.image_pred(filePath)
self.input.setPixmap(QPixmap(filePath))
self.output.setPixmap(QPixmap.fromImage(qimage))
def open_video(self):#视频检测是一帧一帧地检测,利用opencv抽帧。
file_path = QFileDialog.getOpenFileNames(self, dir="./datasets", filter="*.mp4")
if file_path[0]:
filePath=','.join(file_path[0])
print(filePath)
self.video = cv2.VideoCapture(filePath)
self.timer.start()
# while True:
# ret,frame=video.read()
#
# if not ret:
# break
# frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
# qimage=self.video_pred(frame)
# self.input.setPixmap(QPixmap(convert2QImage(frame)))
# self.output.setPixmap(QPixmap.fromImage(qimage))
print("点击显示视频!")
def bind_slots(self):
#绑定
self.det_image.clicked.connect(self.open_image)
self.pushButton_2.clicked.connect(self.open_video)
self.timer.timeout.connect(self.video_pred)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
仍然存在问题,如果再点击“图片检测”按钮,不会停下,这是因为计时器没有停。
解决方案:在打开图片时,关闭计时器,def open_image(self): self.timer.stop()
全部代码:
import sys
import cv2
import torch
from PySide6.QtWidgets import QMainWindow, QApplication,QFileDialog
#QFileDialog可以访问文件资源管理器
from PySide6.QtGui import QPixmap,QImage
#显示图片
from PySide6.QtCore import QTimer
#计时器
from main_window import Ui_MainWindow
def convert2QImage(imag):
height,width,channel=imag.shape
return QImage(imag,width,height,width*channel,QImage.Format_RGB888)
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.model=torch.hub.load("./","custom",path="runs/train/exp2/weights/best.pt",source="local")
self.timer=QTimer()
self.timer.setInterval(1) #默认单位毫秒ms
self.bind_slots() #不要忘记调用,不写这句话无法显示
def image_pred(self,filePath):
results=self.model(filePath)
image=results.render()[0]
return convert2QImage(image)
#检测后的图片
def video_pred(self):
ret, frame = self.video.read()
if not ret:
self.timer.stop()
else:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.input.setPixmap(QPixmap.fromImage(convert2QImage(frame)))
# 放在这儿而不是self.output上面,因为可能受检测的图像影响。在检测之前先显示出来。
results=self.model(frame)
image=results.render()[0]
self.output.setPixmap(QPixmap.fromImage(convert2QImage(image)))
def open_image(self):
self.timer.stop()
file_path=QFileDialog.getOpenFileNames(self,dir="./datasets/images/train",filter="*.jpg;*.png")
#设置从dir里找图片, filter分号隔离不同格式
#file_path返回的是有两个元素的数组,我们运行后,发现第一个元素是路径,第二个元素是格式。我们只输出路径。
if file_path[0]:
filePath=','.join(file_path[0])
qimage=self.video_pred(filePath)
self.input.setPixmap(QPixmap(filePath))
self.output.setPixmap(QPixmap.fromImage(qimage))
def open_video(self):#视频检测是一帧一帧地检测,利用opencv抽帧。
file_path = QFileDialog.getOpenFileNames(self, dir="./datasets", filter="*.mp4")
if file_path[0]:
filePath=','.join(file_path[0])
print(filePath)
self.video = cv2.VideoCapture(filePath)
self.timer.start()
# while True:
# ret,frame=video.read()
#
# if not ret:
# break
# frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
# qimage=self.video_pred(frame)
# self.input.setPixmap(QPixmap(convert2QImage(frame)))
# self.output.setPixmap(QPixmap.fromImage(qimage))
print("点击显示视频!")
def bind_slots(self):
#绑定
self.det_image.clicked.connect(self.open_image)
self.pushButton_2.clicked.connect(self.open_video)
self.timer.timeout.connect(self.video_pred)
if __name__=="__main__":
app=QApplication(sys.argv)
window1 = MainWindow()
window1.show()
app.exec()
九、复盘
- 首先利用Pyside6的designer设计了UI界面。两个button,两个label,一条线。导出生成ui文件。将ui文件编译生成成py文件,注意external tool选的是pyside6-uic.exe,不是uic.exe!!!
- 构建程序,设计函数:初始化;绑定按键与函数事件;打开图片;打开视频;图片预测;视频预测【预测部分利用了torch.hub,实际上是对每一帧的图片进行预测,利用了计时器】
今天最大的收获就是可以平静地对待报错了,之前从大一开始每次改报错都特别烦躁,今天竟然可以平静地坐着改几个小时的bug。我之前特别怵头以后做机器学习、python相关的研究,现在不怕了。
要相信你遇到的问题,别人也遇到过,并且他们解决了。
如果别人没有遇到过,那正好,你会是第一个发现问题并解决的人,之后的人都会借鉴你的解决方案。