使用pyqt5搭建yolo3目標識別界面的方法
由於這是我第一次寫這種博客,其目的也不是為瞭賺取積分,主要是為瞭記錄我的學習過程中的一些方法,以便以後我再次需要用的時候可以知道我當時是怎麼做的。所以文中會有很多地方並不會解釋其原理(主要是我自己壓根也沒搞明白,當時隻想知道怎麼用就行瞭,遇到需要用其他的再百度),主要著重於怎麼運用。如有不當之處,請指出我當改正。
搭建pyqt5環境
我用的IDE是PyCharm,深度學習環境搭建可以參考其他博主的教程。
pyqt5的環境搭建流程參考的是b站up主@劉金玉編程。
- 安裝Anaconda3,搭建好虛擬環境,在虛擬環境裡配置好yolo3所需的庫,並在這個虛擬環境裡安裝pyqt5。
- 用win + R打開快速運行,搜索cmd打開命令行窗口。
打開命令提示符窗口,輸入’activate‘+空格+所在虛擬環境的名稱。然後輸入’pip install pyqt5‘+回車,就會開始安裝pyqt5。
在PyCharm中配置好pyqt5。首先打開設置,在’External Tool‘中添加ptqt5組件。點擊 ‘+‘號,
點擊 ‘+‘號,創建工具,工具名字是自己設定的,第一個可以命名為’QTDesinger‘,Program路徑選擇Anaconda安裝路徑下的’Ananconda3\Library\bin\designer.exe‘,因為我是直接裝在D盤的,所以完整路徑為’D:\Ananconda3\Library\bin\designer.exe‘。點擊Program這一行末尾的’+‘號即可選擇路徑。選擇好之後,Argument不用填,Working directory會自己填好。
然後再添加一個工具,名字可以命名為’PyUIC‘,Program選擇路徑為虛擬環境的路徑,一般都安裝在Ananconda3下的evns文件夾裡,下一級文件夾就是虛擬環境的名稱,找到你創建的虛擬環境文件夾,在裡面找到 python.exe文件,完整路徑為’D:\Ananconda3\envs\python3-6\python.exe‘,Argument填入
-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
Working directory填入
$FileDir$
完成以上步驟,即可配置好所需工具,開始設計界面瞭。
可以右鍵項目名稱,選擇External Tool裡的QTDesinger可以直接開始設計界面,最後需要保存在項目根目錄裡,會生成一個後綴為’ui’的文件,然後右鍵點擊這個文件,選擇External Tool裡的PyUIC即可將其轉換為’py’文件,就可以從代碼中對界面進行調用和編輯。
我沒有從直接設計界面開始,而是從代碼中設計界面。
程序流程
主界面
子界面
設計界面
1.主函數
需要導入以下常用的庫:
from PyQt5.QtWidgets import * #這兩個是pyqt5常用的庫 from PyQt5.QtGui import QIcon, QPixmap #可以滿足小白大多數功能 import os #這兩個是其他的庫 import sys #可以完成一些打開文件保存文件的功能 from yolov3 import train, predict #這是我自己將yolov3模型整體放在我項目的子文件夾中,從中調用我的訓練和預測函數
下面是主程序:
# 創立一個主界面,並保持它,從各種按鈕或者組件中接受信號完成界面功能,相當於無限循環 # 隻有選擇退出後才會關掉程序退出循環 if __name__ == '__main__': app = QApplication(sys.argv) mc = MyClass() #這裡相當於實例化一個主界面,myclass是自己定義的主界面類 sys.exit(app.exec_()) #監聽退出,如果選擇退出,界面就會關掉
2.定義主界面的類
(1)主界面
首先是初始化,需要繼承父類。
class MyClass(QWidget): def __init__(self): super().__init__() #繼承父類 self.initUI() #自己定義的函數,初始化類界面,裡面放著自己各種定義的按鈕組件及佈局 self.child_window = ChildClass() #子界面的調用,本質和主界面一樣是一個類,在這裡將其聲明為主界面的成員
(2)主界面佈局
功能實現在代碼註釋都有詳細說明,關於self.TModelSelectSignal = [0, 0]
和self.TModel= [1, 0]
,可以理解為一種主界面和子界面的信號傳遞,由於我對深層次的參數傳遞不清楚,所以隻能采用一種的最笨的方法,在主界面的類裡活得已訓練好模型的序號,放在數組self.TModel
裡,數組的第一位為1表示第一個模型已訓練,為0表示未訓練。同理第二位則表示第二個模型,如果有更多模型,可以設置更多位。數組的設置實在模型訓練之後,因此將賦值數組的代碼放在訓練函數裡。
def initUI(self): self.setWindowTitle("COC缺陷檢測") #設置界面名稱 # self.setWindowIcon(QIcon("iconimg/zhou.png")) #設計界面的圖標,圖片放在項目文件夾的子文件夾裡就不會出錯,名字也要對應 self.resize(350, 200) #設置界面大小 self.TModelSelectSignal = [0, 0] #選擇按鈕對應的模型 self.TModel= [0, 0] #表示已經訓練好的模型編號 myframe = QFrame(self) #實例化一個QFrame可以定義一下風格樣式,相當於一個框架,可以移動,其內部組件也可以移動 btn2 = QPushButton("開始訓練模型", self) #定義一個按鈕,括號裡需要一個self,如果需要在類內傳遞,則應該定義為self.btn2 btn2.clicked.connect(self.TestModel) #將點擊事件與一個函數相連,clicked表示按鈕的點擊事件,還有其他的功能函數,後面連接的是一個類內函數,調用時無需加括號 btn3 = QPushButton("上傳數據集", self) btn3.clicked.connect(self.DataExplorerSelect) #連接一個選擇文件夾的函數 btn5 = QPushButton("退出程序", self) btn5.clicked.connect(self.close) #將按鈕與關閉事件相連,這個關閉事件是重寫的,它自帶一個關閉函數,這裡重寫為點擊關閉之後會彈窗提示是否需要關閉 btn6 = QPushButton("檢測", self) btn6.clicked.connect(self.show_child) #這裡將聯系彈出子界面函數,具體彈出方式在函數裡說明 combol1 = QComboBox(myframe) #定義為一個下拉框,括號裡為這個下拉框從屬的骨架(框架) combol1.addItem(" 選擇模型") #添加下拉選項的文本表示,這裡因為沒有找到文字對齊方式,所以采用直接打空格,網上說文字對齊需要重寫展示函數 combol1.addItem(" YOLOv3") combol1.addItem(" YOLOv4") combol1.activated[str].connect(self.TModelSelect) #|--將選擇好的模型序號存到模型選擇數組裡 #|--後面的訓練函數會根據這個數組判斷需要訓練哪個模型 #|--[str]表示會將下拉框裡的文字隨著選擇信號傳過去 #|--activated表示該選項可以被選中並傳遞信號 vlo = QVBoxLayout() #創建一個垂直佈局,需要將需要垂直佈局的組件添加進去 vlo.addWidget(combol1) #添加相關組件到垂直佈局裡 vlo.addWidget(btn3) vlo.addWidget(btn2) vlo.addWidget(btn6) vlo.addWidget(btn5) vlo.addStretch(1) #一個伸縮函數,可以一定程度上防止界面放大之後排版不協調 hlo = QVBoxLayout(self) #創建整體框架佈局,即主界面的佈局 hlo.addLayout(vlo) #將按鈕佈局添加到主界面的佈局之中 hlo.addWidget(myframe) #將框架也加入到總體佈局中,當然也可以不需要這框架,直接按照整體框架佈局來排版 #之所以這裡有這個myframe,是因為嘗試過很多種佈局,其中一個佈局就是將其他組件都放到這個myframe中,移動這個myframe #其裡面的組件佈局相對位置不會改變,後面又嘗試瞭多種佈局,所以這個myframe最後裡面其實就剩下一個下拉框 self.show() #顯示主界面
(3)主界面的功能函數(槽函數)
選擇數據上傳,其本質是打開一個文件夾,然後將相關照片按照規定排列好。這裡采用的是絕對路徑,按理來說相對路徑較好,但是沒有找到具體實現方法,一般的相對路徑方法打不開對應的文件夾,所以暫時選擇用這個。
def DataExplorerSelect(self): path = r'D:\pycharm\QTYOLOV3\yolov3\VOCdevkit\VOC2007' os.system("explorer.exe %s" % path)
打開子界面函數
def show_child(self): TModel1 = self.TModel #|--這是子界面的類內函數 self.child_window.GetTModel(TModel1) #|--將訓練好的模型序號傳到子界面的類內參數裡面 self.child_window.show() #|--子界面相當於主界面的一個類內成員 #|--但是本質還是一個界面類,也有show函數將其展示
選擇需要訓練的模型序號
如果這裡報錯,有可能是下拉框中文本信息與這裡的判斷文本信息不同。
def TModelSelect(self, s): #s是形參,表示傳回來的選中的選項的文字 if s == ' YOLOv3': self.TModelSelectSignal[0] = 1 #如果選中的是YOLOv3-COC就將第一位置1 # print(self.TModelSelectSignal[0]) elif s == ' YOLOv4': self.TModelSelectSignal[1] = 1 #如果選中的是YOLO-Efficientnet就將第二位置1 # print(self.TModelSelectSignal[1])
訓練函數。因為這裡隻導入一個訓練函數,所以隻有一個判別選項,訓練完之後會將self.TModelSelectSignal
的對應位置零以便下一次可以繼續訓練。
def TestModel(self): if self.TModelSelectSignal[0] == 1: train.run() self.TModelSelectSignal[0] = 0 else: print("沒有該模型")
關閉函數。這裡是將其重寫,多瞭一個關閉時會有彈窗出現的功能。前一個文本參數時彈出框的名字,後一個文本參數是顯示在窗口的文本。
def closeEvent(self, event): result = QMessageBox.question(self, "提示:", "您真的要退出程序嗎", QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes) if result == QMessageBox.Yes: event.accept() else: event.ignore()
3.定義子界面
(1)子函數
同樣需要初始化並繼承父類。
class ChildClass(QWidget): def __init__(self): super().__init__() self.initUI() self.TModel = [] #用來接收主界面的訓練好的模型的序號 self.openfile_name_image = '' #存儲原始圖像的地址 self.result_name_image = '' #存儲檢測好的圖像的地址
(2)子界面佈局
def initUI(self): self.resize(1100, 450) #縮放界面大小 self.setWindowTitle("目標檢測") #設置界面標題 # self.setWindowIcon(QIcon("iconimg/zhou.png")) #設置界面圖標 self.PModelSelectSignal = [0, 0] #設置需要預測模型的序號,在下拉框裡選擇 myframe = QFrame(self) self.label1 = QLabel("檢測模型", self) combol1 = QComboBox(myframe) combol1.addItem("選擇檢測模型") combol1.addItem("YOLOV3") combol1.addItem("YOLOV4") combol1.activated[str].connect(self.PModelSelect) #鏈接預測模型序號選擇函數 btn1 = QPushButton("選擇檢測圖片", self) btn1.clicked.connect(self.select_image) #鏈接檢測圖片選擇函數,本質是打開一個文件夾 btn2 = QPushButton("開始檢測", self) btn2.clicked.connect(self.PredictModel) #鏈接預測模型函數 self.label2 = QLabel("", self) #創建一個label,可以存放文字或者圖片,在這裡是用來存放圖片,文本參數為空就會顯示為空,留出空白區域,選擇好圖片時會有函數展示圖片 self.label2.resize(400, 400) self.label3 = QLabel("", self) self.label3.resize(400, 400) label4 = QLabel(" 原始圖片", self) #用來放在圖片底部表示這是哪一種圖片 label5 = QLabel(" 檢測圖片", self) vlo2 = QHBoxLayout() #創建一個子佈局,將圖片水平排放 vlo2.addWidget(label4) vlo2.addWidget(label5) vlo = QHBoxLayout() #創建一個子佈局,將按鈕水平排放 vlo.addStretch() vlo.addWidget(self.label1) vlo.addWidget(combol1) vlo.addWidget(btn1) vlo.addWidget(btn2) vlo.addStretch(1) vlo1 = QHBoxLayout() #創建一個水平佈局,將兩個提示標簽豎直排放 vlo1.addWidget(self.label2) vlo1.addWidget(self.label3) hlo = QVBoxLayout(self) #創建一個總的垂直佈局,將三個子佈局垂直排放 hlo.addLayout(vlo) hlo.addLayout(vlo1) hlo.addStretch(1) hlo.addLayout(vlo2) hlo.addStretch(0) hlo.addWidget(myframe)
(3)子界面功能函數(槽函數) 一個賦值函數,在外部調用給類內成員賦值
def GetTModel(self, a): self.TModel = a
關閉事件,和主界面一樣是重寫之後的。
def closeEvent(self, event): result = QMessageBox.question(self, "提示:", "您真的要退出程序嗎", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if result == QMessageBox.Yes: event.accept() else: event.ignore()
def select_image(self): self.openfile_name_image, _ = QFileDialog.getOpenFileName(self, "選擇照片文件", r"./yolov3/imgtest/") #彈出一個對話窗,是一個文件夾,可以選擇一個文件然後返回地址到 self.openfile_name_image中 print('加載照片文件地址為:' + str(self.openfile_name_image)) self.label2.setPixmap(QPixmap(str(self.openfile_name_image))) #將選中的文件名字傳入QPixmap()中,括號內為文件地址,就會讀取這個圖片 self.label2.resize(300, 400) self.label2.setScaledContents(True) #表示這個label可以可以自適應窗口大小,可以讓圖片隨窗口大小而變化
選擇需要預測的模型,s是形參,傳回的是下拉框中的文本信息。
def PModelSelect(self, s): if s == 'YOLOV3': if self.TModel[0] == 1: self.PModelSelectSignal[0] = 1 self.PModelSelectSignal[1] = 0 print(self.PModelSelectSignal[0]) else: print("模型YOLOV3未訓練") ##如果已經訓練好的模型數組裡對應的位置為0,則表示該模型未訓練 self.PModelSelectSignal[1] = 0 #同時也要講模型選擇信號清零,以便下次可以繼續選擇賦值 elif s == 'YOLOV4': if self.TModel[1] == 1: self.PModelSelectSignal[1] = 1 self.PModelSelectSignal[0] = 0 print(self.PModelSelectSignal[1]) else: print("模型YOLOV4未訓練") self.PModelSelectSignal[0] = 0
圖像預測。由於隻導入瞭一個模型,所以隻有一個判別程序,我寫的預測函數是可以讀取文件路徑的圖片,所以我隻需將需要預測的圖片的路徑傳入預測函數,就會將預測好的圖片保存在指定文件夾,然後後面用程序將其讀出展示在界面裡。
def PredictModel(self): if self.PModelSelectSignal[0] == 1: predict.predict(self.openfile_name_image) #將需要預測的圖片傳入導入的預測函數 elif self.PModelSelectSignal[1] == 1: print('YOLOV4正在檢測') #這裡應該放入另外一個模型 else: print('沒有該模型') a = self.openfile_name_image a = a.split('/') #將預測圖片裡的編號分離出來 a = './yolov3/imgtestresult/' + a[-1] #將指定路徑與圖片編號組合,即可得到預測好的圖片的路徑 self.label3.setPixmap(QPixmap(a)) #直接讀取預測好的圖片 self.label3.resize(300, 400) self.label3.setScaledContents(True) print(a)
界面展示
一些尚未解決的問題
由於保存圖片路徑變量覆蓋的問題,會導致選擇第二張檢測圖片之後,檢測圖片的結果仍然展示的是第一張,可以在選擇檢測圖片的函數裡加上每當選擇一張新的圖片,即可清除上一張圖片。
另外一個問題就是無法連續兩次檢測,具體原因還沒有查明,可能是用的同一個保存圖片文件的變量,最後並沒有傳入預測函數。
另外一個還沒有實現的功能就是實時展示訓練進程,在原始訓練函數裡,是會實時打印出訓練進程,所以應該可以做到讀取訓練函數裡的打印的文本,然後傳遞到界面類裡的一個函數,然後展示在界面裡。
到此這篇關於使用pyqt5搭建yolo3目標識別界面的方法的文章就介紹到這瞭,更多相關pyqt5搭建yolo3識別界面內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!