
OpenCV能夠處理圖像、視頻、深度圖像等各種類型的視覺數據,在某些情況下,盡管OpenCV可以顯示窗口,但PyQt5可能更適合用于創建復雜的交互式應用程序,而自帶GPU的H618就成為了這些圖像顯示的最佳載體。
本文將實現的功能是使用圖像處理庫OpenCV從攝像頭獲取數據,縮放后從PyQt5的窗口中顯示出來。


創建pyqt5窗口
這里在電腦上使用designer軟件,創建一個Main Window類型窗體。從左邊組件欄中拖出一個label放到窗口中間。
點一下放在窗口中的label,在軟件右下角的屬性編輯器里可以設置很多東西,這里就不細介紹了。這里我是設置了QFrame啟用了邊框,QLabel中的texte屬性控制顯示的文本,QLabel中的alignment屬性控制文本對齊方式。
然后保存為.ui結尾的文件:

隨后將designer繪制的ui文件轉化為py文件
python3 -m PyQt5.uic.pyuic ui_main.ui -o ui_main.py接下來編寫main.py程序,調用剛剛畫的窗口進行顯示,先把剛剛的ui_main.py以及一些qt庫給import進來:
from ?ui_main import Ui_MainWindowimport PyQt5from PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5.QtCore import *from PyQt5.QtGui import *# 修正qt的plugin路徑,因為某些程序(cv2)會將其改到其他路徑import osos.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.dirname(PyQt5.__file__)
放入一點輔助代碼,一個是為了實現從遠程命令行運行qt程序顯示到桌面上,一個是為了在命令行下可以按ctrl+c快捷鍵來強制退出qt程序
#【可選代碼】允許遠程運行import osos.environ["DISPLAY"] = ":0.0"#【建議代碼】允許終端通過ctrl+c中斷窗口,方便調試import signalsignal.signal(signal.SIGINT, signal.SIG_DFL)timer = QtCore.QTimer()timer.start(100) ?# You may change this if you wish.timer.timeout.connect(lambda: None) ?# Let the interpreter run each 100 ms
加上調用函數進行顯示的部分,這個顯示pyqt5窗口的基本程序就完成了
# 初始化窗口import sysapp = QtWidgets.QApplication(sys.argv)window = WINDOW()ui = Ui_MainWindow()ui.setupUi(window)window.showFullScreen() #全屏顯示sys.exit(app.exec_())

在核桃派lcd屏上的效果展示

OpenCV圖像讀取及顯示
調用頭文件,opencv的頭文件只需要這一個
import cv2打開攝像頭,其中傳入的參數1是攝像頭編號,一般是從0開始往后排
cap = cv2.VideoCapture(1)從攝像頭讀取一幀圖像,ret是讀取狀態,frame是圖像數據
ret, frame = cap.read()cap.read函數讀到的是bgr格式的,為了把opencv的圖像數據顯示到qt的label,需要先轉為rgb格式,并將圖像轉為Qt中用來表示圖像的QImage。
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)h, w, ch = rgbImage.shapeqtImage = QImage(rgbImage.data, h, w, ch*w, QtGui.QImage.Format_RGB888)
label的setPixmap方法可以圖像數據覆蓋label
label.setPixmap(QPixmap.fromImage(qtImage))
顯示效果

線程,信號與槽
原代碼為了簡單,沒有使用信號槽機制,不夠線程安全,我們這里使用qt自帶的多線程功能,他的使用很簡單,只需要創建一個類并繼承自QThread, 然后將要運行的東西寫到類里的run方法下面。實例化一個對象后,調用start方法即可創建新線程
class Work(QThread):? ?def run(self):? ? ? ?passwork = Work()work.start()
直接在線程內調用函數去修改qt窗口的內容,不能滿足線程安全。
我們需要創建一個信號,把修改qt窗口的語句寫到一個槽內,連接他們,在想修改窗口時發出信號,讓qt內部去調度,防止跟其他qt內部的線程發生沖突。

因為我們這個線程類繼承自QThread,所以可以在類內定義信號。只需要實例化一個pyqtSignal對象即可,調用時括號內的參數決定了槽函數必須有什么類型的參數,以及發送信號時需要傳入什么參數。
?signal_update_label = pyqtSignal( QPixmap)槽函數就是隨便定義一個函數,只要函數參數跟信號一樣就行。
label:QLabel? ?def sloat_update_label( self, pixmap:QPixmap):? ? ? ?self.label.setPixmap(pixmap)
使用emit方法即可發送信號,qt內部會進行調度,將所有連接到本信號的函數都調出來運行,并將參數傳給他們。這是qt實現線程安全的重要機制。
self.signal_update_label.emit(QPixmap.fromImage(qtImage))-End-
本文轉載自:https://forum.walnutpi.com/t/topic/84
