ZH ·
🌏 English

使用 PySide6 在 Python 中实现 OpenCV 视频流显示的最简指南

深度学习算法(尤其是图像处理领域)的应用,通常需要直观的可视化界面。虽然 OpenCV 自带的 imshow 方法简单易用,但在扩展性和可维护性方面存在明显不足。

Qt 是一个成熟的 UI 框架,起源于 C/C++,现已完美支持 Python。在 Python 生态中,PyQt 是社区维护的版本,而 PySide 则是 Qt 官方提供的版本。本文将采用官方的 PySide6 来构建界面。

通常,图像数据由 OpenCV 的 Mat 格式封装。我们的工作流程如下:通过摄像头或视频文件捕获原始帧,将其解码为 OpenCV Mat,传给算法进行处理,最后由 Qt 负责在界面上显示结果。

导入必要的库

import sys # PySide6所需

from PySide6 import QtWidgets
from PySide6 import QtGui, QtCore
from PySide6.QtCore import * 
from PySide6.QtGui import *
from PySide6.QtWidgets import QFileDialog, QMainWindow, QMessageBox

from generated_files.uic.mainwindow import Ui_MainWindow

import cv2
import numpy as np # cv2 mat由numpy包装

创建主窗口类

Qt QMainWindow 继承并创建主窗口类:

class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

编写主程序入口

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

运行上述代码,即可看到一个空白窗口。

使用 Qt Designer 设计界面

  1. 使用 Designer 创建 UI 文件: image
  2. 在窗口中添加 Label 控件: image
  3. 清除 Label 中的默认文本,并将控件的对象名称修改为 image_labelimage

添加数据捕获逻辑

我们使用 OpenCV 打开摄像头,并通过定时器(QTimer)周期性地获取图像。定义 display_video_stream 方法读取相机画面,并将其连接到定时器,每 30 毫秒触发一次。

class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.timer = QTimer()
        self.timer.timeout.connect(self.display_video_stream)
        self.cap = cv2.VideoCapture(0) # 调用默认相机设备
        self.timer.start(30) # 每30ms触发一次

    def display_video_stream(self):
        '''从USB相机显示视频流'''
        ret, frame = self.cap.read() # 获取读取状态和帧数据
        if ret:
            self.display(frame) 

图像格式转换

OpenCV 获取的图像数据需要转换为 Qt 可识别的 QImage 格式,才能在 QLabel 上正确显示。

    def display(self, frame):
        '''将OpenCV帧显示在Qt控件上'''
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        
        image = QImage(frame, frame.shape[1], frame.shape[0], 
                        frame.strides[0], QImage.Format_RGB888)
        
        self.ui.image_label.setPixmap(QPixmap.fromImage(image))

完整代码

import sys
from PySide6 import QtWidgets
from PySide6.QtCore import * 
from PySide6.QtGui import *
from PySide6.QtWidgets import QMainWindow
from generated_files.uic.mainwindow import Ui_MainWindow
import cv2
import numpy as np

class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.timer = QTimer()
        self.timer.timeout.connect(self.display_video_stream)
        self.cap = cv2.VideoCapture(0) 
        self.timer.start(30)
    
    def display(self, frame):
        '''将OpenCV帧显示在Qt控件上'''
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        image = QImage(frame, frame.shape[1], frame.shape[0], 
                        frame.strides[0], QImage.Format_RGB888)
        self.ui.image_label.setPixmap(QPixmap.fromImage(image))

    def display_video_stream(self):
        '''从USB相机显示视频流'''
        ret, frame = self.cap.read()
        if ret:
            self.display(frame)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

实现过程非常简单。😊

完整项目代码请参考 GitHub 仓库:https://github.com/BigBookPlus/PythonQtWithOpenCV.git