全志科技
直播中

corkia

6年用户 657经验值
擅长:嵌入式技术
私信 关注
[经验]

可视可交互!H618上用OpenCV读取图像显示到PyQt5窗口上

这里分享一个代码,功能是使用图像处理库opencv从摄像头获取数据,缩放后从pyqt5的窗口中显示出来。
安装opencvsudo pip3 install opencv-python创建一个pyqt5窗口
1. 用Qt Designer画个窗口
这里我在电脑上使用designer软件,创建一个Main Window类型窗体。从左边组件栏中拖出一个label放到窗口中间。
点一下放在窗口中的label,在软件右下角的属性编辑器里可以设置很多东西,这里就不细介绍了。这里我是设置了QFrame启用了边框,QLabel中的texte属性控制显示的文本,QLabel中的alignment属性控制文本对齐方式。
然后保存为.ui结尾的文件
2. 将designer绘制的ui文件转化为py文件
python3 -m PyQt5.uic.pyuic ui_main.ui -o ui_main.py
3. 编写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
定义窗口类,重写窗口的一些触发事件。这里我修改了鼠标点击后会被自动调用的mousePressEvent和窗口绘制时会被调用的paintEvent
class WINDOW(QtWidgets.QMainWindow):    def mousePressEvent(self, event):        # 被左键点击后退出本程序        if event.button() == Qt.LeftButton:            self.close()            exit(-1)    def paintEvent(self,event):        # 修改label的大小和位置        new_width = int(window.width()/10*8)        new_height = int(window.height()/10*8)        lab_x = int((window.width() - new_width) / 2)        lab_y = int((window.height() - new_height) / 2)        ui.label.setGeometry( lab_x, lab_y, new_width, new_height)
加上调用函数进行显示的部分,这个显示pyqt5窗口的基本程序就完成了
# 初始化窗口import sysapp = QtWidgets.QApplication(sys.argv)window = WINDOW()ui = Ui_MainWindow()ui.setupUi(window)window.showFullScreen() #全屏显示# window.show() #按绘制时的尺寸显示sys.exit(app.exec_())
在核桃派lcd屏上的效果展示
opencv怎么读取摄像头
调用头文件,opencv的头文件只需要这一个
import cv2
打开摄像头,其中传入的参数1是摄像头编号,一般是从0开始往后排
cap = cv2.VideoCapture(1)
从摄像头读取一帧图像,ret是读取状态,frame是图像数据
ret, frame = cap.read()
怎么把opencv的图像数据显示到qt的label
cap.read函数读到的是bgr格式的,需要先转为rgb格式
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
将图像转为Qt中用来表示图像的QImage
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)
连接信号与槽,使用connect方法即可
self.signal_update_label.connect(self.sloat_update_label)
使用emit方法即可发送信号,qt内部会进行调度,将所有连接到本信号的函数都调出来运行,并将参数传给他们。这是qt实现线程安全的重要机制。
self.signal_update_label.emit(QPixmap.fromImage(qtImage))
最终代码
import cv2from  ui_main import Ui_MainWindowimport PyQt5from PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *# 修正qt的plugin路径,因为某些程序(cv2)会将其改到其他路径import osos.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.dirname(PyQt5.__file__)#【可选代码】允许Thonny远程运行import osos.environ["DISPLAY"] = ":0.0"#【建议代码】允许终端通过ctrl+c中断窗口,方便调试import signalsignal.signal(signal.SIGINT, signal.SIG_DFL)timer = QTimer()timer.start(100)  # You may change this if you wish.timer.timeout.connect(lambda: None)  # Let the interpreter run each 100 ms# 线程类class Work(QThread):    signal_update_label = pyqtSignal(QPixmap)    label:QLabel    def sloat_update_label( self, pixmap:QPixmap):        self.label.setPixmap(pixmap)    def run(self):        print("label.width()=", self.label.width())        print("label.height()=", self.label.height())        self.signal_update_label.connect(self.sloat_update_label)        cap = cv2.VideoCapture(1)        while True:            ret, frame = cap.read()            if ret:                # 颜色转换                rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)                # 按比例缩放                h, w, ch = rgbImage.shape                aspect_ratio = w / h                new_width = self.label.width()                new_height = int(new_width / aspect_ratio)                if new_height > self.label.height():                    new_height = self.label.height()                    new_width = int(new_height * aspect_ratio)                rgbImage = cv2.resize(rgbImage, (new_width, new_height))                                # 显示到label                bytesPerLine = ch * new_width                self.signal_update_label.emit(QPixmap.fromImage(QImage(rgbImage.data, new_width, new_height, bytesPerLine, QImage.Format_RGB888)))            else :                print("cap read error")                returnclass WINDOW(QMainWindow):    def mousePressEvent(self, event):        if event.button() == Qt.LeftButton:            self.close()    def paintEvent(self,event):        new_width = int(window.width()/10*8)        new_height = int(window.height()/10*8)        lab_x = int((window.width() - new_width) / 2)        lab_y = int((window.height() - new_height) / 2)        ui.label.setGeometry( lab_x, lab_y, new_width, new_height)import sysapp = QApplication(sys.argv)window = WINDOW()ui = Ui_MainWindow()ui.setupUi(window)window.showFullScreen() #全屏显示# window.show() #按绘制时的尺寸显示# 创建读取摄像头并显示的线程work = Work()work.label = ui.labelwork.start()sys.exit(app.exec_())

更多回帖

发帖
×
20
完善资料,
赚取积分