位置: IT常識 - 正文
推薦整理分享睿智的目標(biāo)檢測——PyQt5搭建目標(biāo)檢測界面(睿智目標(biāo)檢測yolov8),希望有所幫助,僅作參考,歡迎閱讀內(nèi)容。
文章相關(guān)熱門搜索詞:睿智的目標(biāo)檢測11,目標(biāo)檢測怎么學(xué),睿智的目標(biāo)檢測61,睿智的目標(biāo)檢測環(huán)境搭建,目標(biāo)檢測是,睿智目標(biāo)檢測yolov8,睿智目標(biāo)檢測yolov8,睿智目標(biāo)檢測yolov8,內(nèi)容如對您有幫助,希望把文章鏈接給更多的朋友!
基于B導(dǎo)開源的YoloV4-Pytorch源碼開發(fā)了戴口罩人臉檢測系統(tǒng)(21年完成的本科畢設(shè),較為老舊,可自行替換為最新的目標(biāo)檢測算法)。
源碼下載https://github.com/Egrt/YOLO_PyQt5 喜歡的可以點個star噢。
支持功能支持讀取本地圖片支持讀取本地視頻支持打開攝像頭實時檢測支持多線程,防止卡頓支持檢測到人臉未佩戴口罩時記錄,并語音警告界面展示PyQt5PyQt5是Python語言中一款流行的GUI(圖形用戶界面)開發(fā)框架,基于Qt GUI應(yīng)用程序開發(fā)框架,提供了一個強(qiáng)大的工具集,用于創(chuàng)建各種桌面應(yīng)用程序。PyQt5可以用于開發(fā)桌面應(yīng)用程序、Web應(yīng)用程序和移動應(yīng)用程序,具有良好的跨平臺性和豐富的功能。
信號與槽信號和槽是PyQt5中一個重要的概念,是用于組織和管理GUI元素之間交互的機(jī)制。信號是GUI元素發(fā)出的事件或動作,槽是處理信號的函數(shù)。當(dāng)信號發(fā)生時,與之相關(guān)聯(lián)的槽將被自動調(diào)用。
下面是一個簡單的示例代碼,演示如何在PyQt5中使用信號和槽。這個示例創(chuàng)建了一個窗口,其中包含一個按鈕和一個標(biāo)簽。當(dāng)用戶單擊按鈕時,標(biāo)簽的文本將會改變:
import sysfrom PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabelclass MyWindow(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setGeometry(300, 300, 300, 200) self.setWindowTitle('Signal and Slot') self.button = QPushButton('Click', self) self.button.move(100, 100) self.button.clicked.connect(self.changeText) self.label = QLabel('Hello World', self) self.label.move(110, 60) def changeText(self): self.label.setText('Button Clicked')if __name__ == '__main__': app = QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())在這個示例代碼中,我們創(chuàng)建了一個名為MyWindow的窗口類,該類繼承自QWidget。在MyWindow的構(gòu)造函數(shù)中,我們創(chuàng)建了一個按鈕和一個標(biāo)簽,并使用clicked信號將按鈕的單擊事件連接到changeText槽函數(shù)。當(dāng)按鈕被單擊時,changeText槽函數(shù)將會被調(diào)用,該函數(shù)會改變標(biāo)簽的文本。
運行代碼后,可以看到窗口上有一個按鈕和一個標(biāo)簽,單擊按鈕后標(biāo)簽的文本會改變?yōu)椤癇utton Clicked”。這個示例演示了如何使用PyQt5中的信號和槽來實現(xiàn)交互式GUI應(yīng)用程序。
功能實現(xiàn)界面設(shè)計根據(jù)任務(wù)需求,可以將界面分為四部分:
最上方放置按鈕來實現(xiàn)選擇讀取圖片、視頻、開啟攝像頭實時檢測。左側(cè)放置目錄控件,瀏覽本地文件。中間顯示YOLO處理后的圖片。在處理視頻或?qū)崟r讀取攝像頭檢測時,如果多幀連續(xù)識別到不戴口罩人臉將其記錄并發(fā)出語音警告。因此編寫代碼如下:
class MyApp(QMainWindow): def __init__(self): super(MyApp, self).__init__() self.cap = cv2.VideoCapture() self.CAM_NUM = 0 self.thread_status = False # 判斷識別線程是否開啟 self.tool_bar = self.addToolBar('工具欄') self.action_right_rotate = QAction( QIcon("icons/右旋轉(zhuǎn).png"), "向右旋轉(zhuǎn)90", self) self.action_left_rotate = QAction( QIcon("icons/左旋轉(zhuǎn).png"), "向左旋轉(zhuǎn)90°", self) self.action_opencam = QAction(QIcon("icons/攝像頭.png"), "開啟攝像頭", self) self.action_video = QAction(QIcon("icons/video.png"), "加載視頻", self) self.action_image = QAction(QIcon("icons/圖片.png"), "加載圖片", self) self.action_right_rotate.triggered.connect(self.right_rotate) self.action_left_rotate.triggered.connect(self.left_rotate) self.action_opencam.triggered.connect(self.opencam) self.action_video.triggered.connect(self.openvideo) self.action_image.triggered.connect(self.openimage) self.tool_bar.addActions((self.action_left_rotate, self.action_right_rotate, self.action_opencam, self.action_video, self.action_image)) self.stackedWidget = StackedWidget(self) self.fileSystemTreeView = FileSystemTreeView(self) self.graphicsView = GraphicsView(self) self.dock_file = QDockWidget(self) self.dock_file.setWidget(self.fileSystemTreeView) self.dock_file.setTitleBarWidget(QLabel('目錄')) self.dock_file.setFeatures(QDockWidget.NoDockWidgetFeatures) self.dock_attr = QDockWidget(self) self.dock_attr.setWidget(self.stackedWidget) self.dock_attr.setTitleBarWidget(QLabel('上報數(shù)據(jù)')) self.dock_attr.setFeatures(QDockWidget.NoDockWidgetFeatures) self.setCentralWidget(self.graphicsView) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_file) self.addDockWidget(Qt.RightDockWidgetArea, self.dock_attr) self.setWindowTitle('口罩佩戴檢測') self.setWindowIcon(QIcon('icons/mask.png')) self.src_img = None self.cur_img = None槽函數(shù)在初始化中配置窗口的界面并使用connect連接信號與槽函數(shù),當(dāng)信號發(fā)生時,與之相關(guān)聯(lián)的槽將被自動調(diào)用。控制打開圖片、視頻與本地攝像頭的槽函數(shù)分別為:
def openvideo(self): print(self.thread_status) if self.thread_status == False: fileName, filetype = QFileDialog.getOpenFileName( self, "選擇視頻", "D:/", "*.mp4;;*.flv;;All Files(*)") flag = self.cap.open(fileName) if flag == False: msg = QtWidgets.QMessageBox.warning(self, u"警告", u"請選擇視頻文件", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.detectThread = DetectThread(fileName) self.detectThread.Send_signal.connect(self.Display) self.detectThread.start() self.action_video.setText('關(guān)閉視頻') self.thread_status = True elif self.thread_status == True: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() self.action_video.setText('打開視頻') self.thread_status = Falsedef openimage(self): if self.thread_status == False: fileName, filetype = QFileDialog.getOpenFileName( self, "選擇圖片", "D:/", "*.jpg;;*.png;;All Files(*)") if fileName != '': src_img = Image.open(fileName) r_image, predicted_class = yolo.detect_image(src_img) r_image = np.array(r_image) showImage = QtGui.QImage( r_image.data, r_image.shape[1], r_image.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.set_image(QtGui.QPixmap.fromImage(showImage))def opencam(self): if self.thread_status == False: flag = self.cap.open(self.CAM_NUM) if flag == False: msg = QtWidgets.QMessageBox.warning(self, u"警告", u"請檢測相機(jī)與電腦是否連接正確", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.detectThread = DetectThread(self.CAM_NUM) self.detectThread.Send_signal.connect(self.Display) self.detectThread.start() self.action_video.setText('關(guān)閉視頻') self.thread_status = True else: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() self.action_video.setText('打開視頻') self.thread_status = False多線程在讀取視頻文件或攝像頭時,為避免界面卡頓,使用了多線程進(jìn)行處理,并在結(jié)束處理視頻文件時需要關(guān)閉線程防止系統(tǒng)卡死,且在關(guān)閉攝像頭時還需要使用self.cap.release()對攝像頭進(jìn)行釋放。
在多線程處理連續(xù)幀時,采用了Qt自帶的多線程庫QThread:
class DetectThread(QThread): Send_signal = pyqtSignal(np.ndarray, int) def __init__(self, fileName): super(DetectThread, self).__init__() self.capture = cv2.VideoCapture(fileName) self.count = 0 self.warn = False # 是否發(fā)送警告信號 def run(self): ret, self.frame = self.capture.read() while ret: ret, self.frame = self.capture.read() self.detectCall() def detectCall(self): fps = 0.0 t1 = time.time() # 讀取某一幀 frame = self.frame # 格式轉(zhuǎn)變,BGRtoRGB frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 轉(zhuǎn)變成Image frame = Image.fromarray(np.uint8(frame)) # 進(jìn)行檢測 frame_new, predicted_class = yolo.detect_image(frame) frame = np.array(frame_new) if predicted_class == "face": self.count = self.count+1 else: self.count = 0 # RGBtoBGR滿足opencv顯示格式 frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) fps = (fps + (1./(time.time()-t1))) / 2 print("fps= %.2f" % (fps)) frame = cv2.putText(frame, "fps= %.2f" % ( fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) if self.count > 30: self.count = 0 self.warn = True else: self.warn = False # 發(fā)送pyqt信號 self.Send_signal.emit(frame, self.warn)信息記錄如果連續(xù)30幀識別到未佩戴口罩的人臉時,將發(fā)送信號在右側(cè)列表中顯示,并記錄當(dāng)前幀畫面:
def add_item(self, image): # 總Widget wight = QWidget() # 總體橫向布局 layout_main = QHBoxLayout() map_l = QLabel() # 圖片顯示 map_l.setFixedSize(60, 40) map_l.setPixmap(image.scaled(60, 40)) # 右邊的縱向布局 layout_right = QVBoxLayout() # 右下的的橫向布局 layout_right_down = QHBoxLayout() # 右下的橫向布局 layout_right_down.addWidget( QLabel(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))) # 按照從左到右, 從上到下布局添加 layout_main.addWidget(map_l) # 最左邊的圖片 layout_right.addWidget(QLabel('警告!檢測到未佩戴口罩')) # 右邊的縱向布局 layout_right.addLayout(layout_right_down) # 右下角橫向布局 layout_main.addLayout(layout_right) # 右邊的布局 wight.setLayout(layout_main) # 布局給wight item = QListWidgetItem() # 創(chuàng)建QListWidgetItem對象 item.setSizeHint(QSize(300, 80)) # 設(shè)置QListWidgetItem大小 self.stackedWidget.addItem(item) # 添加item self.stackedWidget.setItemWidget(item, wight) # 為item設(shè)置widget關(guān)閉系統(tǒng)在關(guān)閉系統(tǒng)時,需要確保關(guān)閉了多線程,且關(guān)閉了已經(jīng)打開的攝像頭,否則在退出時也將造成卡頓:
def Display(self, frame, warn): im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) showImage = QtGui.QImage( im.data, im.shape[1], im.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.set_image(QtGui.QPixmap.fromImage(showImage))def closeEvent(self, event): ok = QtWidgets.QPushButton() cacel = QtWidgets.QPushButton() msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, u"關(guān)閉", u"確定退出?") msg.addButton(ok, QtWidgets.QMessageBox.ActionRole) msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole) ok.setText(u'確定') cacel.setText(u'取消') if msg.exec_() == QtWidgets.QMessageBox.RejectRole: event.ignore() else: if self.thread_status == True: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() event.accept()最終完整的代碼如下:
import ctypesimport sysimport timeimport cv2import numpy as npimport qdarkstylefrom PIL import Imagefrom PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5.Qt import QThreadfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from custom.graphicsView import GraphicsViewfrom custom.listWidgets import *from custom.stackedWidget import *from custom.treeView import FileSystemTreeViewfrom yolo import YOLOctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")# 多線程實時檢測class DetectThread(QThread): Send_signal = pyqtSignal(np.ndarray, int) def __init__(self, fileName): super(DetectThread, self).__init__() self.capture = cv2.VideoCapture(fileName) self.count = 0 self.warn = False # 是否發(fā)送警告信號 def run(self): ret, self.frame = self.capture.read() while ret: ret, self.frame = self.capture.read() self.detectCall() def detectCall(self): fps = 0.0 t1 = time.time() # 讀取某一幀 frame = self.frame # 格式轉(zhuǎn)變,BGRtoRGB frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 轉(zhuǎn)變成Image frame = Image.fromarray(np.uint8(frame)) # 進(jìn)行檢測 frame_new, predicted_class = yolo.detect_image(frame) frame = np.array(frame_new) if predicted_class == "face": self.count = self.count+1 else: self.count = 0 # RGBtoBGR滿足opencv顯示格式 frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) fps = (fps + (1./(time.time()-t1))) / 2 print("fps= %.2f" % (fps)) frame = cv2.putText(frame, "fps= %.2f" % ( fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) if self.count > 30: self.count = 0 self.warn = True else: self.warn = False # 發(fā)送pyqt信號 self.Send_signal.emit(frame, self.warn)class MyApp(QMainWindow): def __init__(self): super(MyApp, self).__init__() self.cap = cv2.VideoCapture() self.CAM_NUM = 0 self.thread_status = False # 判斷識別線程是否開啟 self.tool_bar = self.addToolBar('工具欄') self.action_right_rotate = QAction( QIcon("icons/右旋轉(zhuǎn).png"), "向右旋轉(zhuǎn)90", self) self.action_left_rotate = QAction( QIcon("icons/左旋轉(zhuǎn).png"), "向左旋轉(zhuǎn)90°", self) self.action_opencam = QAction(QIcon("icons/攝像頭.png"), "開啟攝像頭", self) self.action_video = QAction(QIcon("icons/video.png"), "加載視頻", self) self.action_image = QAction(QIcon("icons/圖片.png"), "加載圖片", self) self.action_right_rotate.triggered.connect(self.right_rotate) self.action_left_rotate.triggered.connect(self.left_rotate) self.action_opencam.triggered.connect(self.opencam) self.action_video.triggered.connect(self.openvideo) self.action_image.triggered.connect(self.openimage) self.tool_bar.addActions((self.action_left_rotate, self.action_right_rotate, self.action_opencam, self.action_video, self.action_image)) self.stackedWidget = StackedWidget(self) self.fileSystemTreeView = FileSystemTreeView(self) self.graphicsView = GraphicsView(self) self.dock_file = QDockWidget(self) self.dock_file.setWidget(self.fileSystemTreeView) self.dock_file.setTitleBarWidget(QLabel('目錄')) self.dock_file.setFeatures(QDockWidget.NoDockWidgetFeatures) self.dock_attr = QDockWidget(self) self.dock_attr.setWidget(self.stackedWidget) self.dock_attr.setTitleBarWidget(QLabel('上報數(shù)據(jù)')) self.dock_attr.setFeatures(QDockWidget.NoDockWidgetFeatures) self.setCentralWidget(self.graphicsView) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_file) self.addDockWidget(Qt.RightDockWidgetArea, self.dock_attr) self.setWindowTitle('口罩佩戴檢測') self.setWindowIcon(QIcon('icons/mask.png')) self.src_img = None self.cur_img = None def update_image(self): if self.src_img is None: return img = self.process_image() self.cur_img = img self.graphicsView.update_image(img) def change_image(self, img): self.src_img = img img = self.process_image() self.cur_img = img self.graphicsView.change_image(img) def process_image(self): img = self.src_img.copy() for i in range(self.useListWidget.count()): img = self.useListWidget.item(i)(img) return img def right_rotate(self): self.graphicsView.rotate(90) def left_rotate(self): self.graphicsView.rotate(-90) def add_item(self, image): # 總Widget wight = QWidget() # 總體橫向布局 layout_main = QHBoxLayout() map_l = QLabel() # 圖片顯示 map_l.setFixedSize(60, 40) map_l.setPixmap(image.scaled(60, 40)) # 右邊的縱向布局 layout_right = QVBoxLayout() # 右下的的橫向布局 layout_right_down = QHBoxLayout() # 右下的橫向布局 layout_right_down.addWidget( QLabel(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))) # 按照從左到右, 從上到下布局添加 layout_main.addWidget(map_l) # 最左邊的圖片 layout_right.addWidget(QLabel('警告!檢測到未佩戴口罩')) # 右邊的縱向布局 layout_right.addLayout(layout_right_down) # 右下角橫向布局 layout_main.addLayout(layout_right) # 右邊的布局 wight.setLayout(layout_main) # 布局給wight item = QListWidgetItem() # 創(chuàng)建QListWidgetItem對象 item.setSizeHint(QSize(300, 80)) # 設(shè)置QListWidgetItem大小 self.stackedWidget.addItem(item) # 添加item self.stackedWidget.setItemWidget(item, wight) # 為item設(shè)置widget def openvideo(self): print(self.thread_status) if self.thread_status == False: fileName, filetype = QFileDialog.getOpenFileName( self, "選擇視頻", "D:/", "*.mp4;;*.flv;;All Files(*)") flag = self.cap.open(fileName) if flag == False: msg = QtWidgets.QMessageBox.warning(self, u"警告", u"請選擇視頻文件", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.detectThread = DetectThread(fileName) self.detectThread.Send_signal.connect(self.Display) self.detectThread.start() self.action_video.setText('關(guān)閉視頻') self.thread_status = True elif self.thread_status == True: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() self.action_video.setText('打開視頻') self.thread_status = False def openimage(self): if self.thread_status == False: fileName, filetype = QFileDialog.getOpenFileName( self, "選擇圖片", "D:/", "*.jpg;;*.png;;All Files(*)") if fileName != '': src_img = Image.open(fileName) r_image, predicted_class = yolo.detect_image(src_img) r_image = np.array(r_image) showImage = QtGui.QImage( r_image.data, r_image.shape[1], r_image.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.set_image(QtGui.QPixmap.fromImage(showImage)) def opencam(self): if self.thread_status == False: flag = self.cap.open(self.CAM_NUM) if flag == False: msg = QtWidgets.QMessageBox.warning(self, u"警告", u"請檢測相機(jī)與電腦是否連接正確", buttons=QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) else: self.detectThread = DetectThread(self.CAM_NUM) self.detectThread.Send_signal.connect(self.Display) self.detectThread.start() self.action_video.setText('關(guān)閉視頻') self.thread_status = True else: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() self.action_video.setText('打開視頻') self.thread_status = False def Display(self, frame, warn): im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) showImage = QtGui.QImage( im.data, im.shape[1], im.shape[0], QtGui.QImage.Format_RGB888) self.graphicsView.set_image(QtGui.QPixmap.fromImage(showImage)) def closeEvent(self, event): ok = QtWidgets.QPushButton() cacel = QtWidgets.QPushButton() msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, u"關(guān)閉", u"確定退出?") msg.addButton(ok, QtWidgets.QMessageBox.ActionRole) msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole) ok.setText(u'確定') cacel.setText(u'取消') if msg.exec_() == QtWidgets.QMessageBox.RejectRole: event.ignore() else: if self.thread_status == True: self.detectThread.terminate() if self.cap.isOpened(): self.cap.release() event.accept()if __name__ == "__main__": # 初始化yolo模型 yolo = YOLO() app = QApplication(sys.argv) app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) window = MyApp() window.show() sys.exit(app.exec_())上一篇:Java使用WebStocket實現(xiàn)前后端互發(fā)消息(java使用循環(huán)結(jié)構(gòu)輸出九九乘法表)
下一篇:vue如何定義:全局變量、全局方法(vue3定義全局變量)
網(wǎng)站地圖: 企業(yè)信息 工商信息 財稅知識 網(wǎng)絡(luò)常識 編程技術(shù)
友情鏈接: 武漢網(wǎng)站建設(shè)