[Python] pyqt6+opencv实现摄像头图像的实时读取并显示(完整源代码)

本文将会介绍如何通过opencv来实时捕获摄像头的图像,并通过pyqt6进行图像视频呈现。

实现思路

  1. 通过opencv的VideoCapture类读取摄像头的每一帧图像,通过pyqt6的QLabel来显示图像

  2. 根据获取的图像的宽和高大小以及QLabel的大小来动态调整最后输出的图像的宽和高

  3. 调整窗体大小,动态调整显示摄像头的标签图像大小

  4. 提供一个按钮来控制摄像头打开或者关闭

  5. 通过QTimer实现每20毫秒刷新获取的摄像头图像,也就是产生每秒50帧的视频效果。

安装依赖

复制代码
pip install opencv-python pyqt6

涉及知识点

opencv的VideoCapture(摄像头视频捕获)使用

[Python] opencv - 如何使用VideoCapture类进行摄像头视频捕获并显示

QTimer(定时器)使用

[Python] pyqt6 - Timer定时器介绍和使用场景(案例)

QGridLayout(网格布局)使用

[Python] pyqt6 - QGridLayout(网格布局)介绍和使用案例

QImage类使用

图像格式(Format)

QImage --- PyQt Documentation v6.6.0 (riverbankcomputing.com)

rgbSwap()和rgbSwapped()方法的区别

rgbSwap()进行内部转换,直接修改QImage对象; rdbSwapped() 返回交换完之后的对象,原图像不受影响。

scaled()方法

实现对图像进行按照新的高度和宽度进行缩放,可以选择是否保持原始摄像头图像的比例。

QImage --- PyQt Documentation v6.6.0 (riverbankcomputing.com)

完整源代码(带注释)

复制代码
import sys

from PyQt6 import QtCore
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QGridLayout, QPushButton, QWidget
from PyQt6.QtGui import QPixmap, QImage, QGuiApplication
import cv2


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('pyqt6显示opencv获取的摄像头图像')
        self.btn_camera = QPushButton('打开摄像头')  # 控制摄像头的状态
        self.lbl_img = QLabel('显示摄像头图像')  # 创建标签控件来显示摄像头的图像, 标签的大小由QGridLayout的布局来决定
        self.lbl_img.setStyleSheet('border: 1px solid black;')  # 给标签设置黑色边框
        self.lbl_img.setAlignment(Qt.AlignmentFlag.AlignCenter)  # 让标签要显示的内容居中
        self.lbl_img.setMinimumSize(640, 480)  # 宽和高保持和摄像头获取的默认大小一致
        self.btn_camera.clicked.connect(self.btn_camera_click)
        top_widget = QWidget()
        grid = QGridLayout()
        grid.addWidget(self.lbl_img, 0, 0, Qt.AlignmentFlag.AlignTop)  # 放置顶部
        grid.addWidget(self.btn_camera, 1, 0, Qt.AlignmentFlag.AlignBottom)  # 放置底部
        top_widget.setLayout(grid)
        self.setCentralWidget(top_widget)

        self.center_win()  # 居中显示主窗口

        self.is_open_camera = False  # 是否打开了摄像头标志位
        self.video_cap = None
        self.camera_timer = QtCore.QTimer(self)  # 创建读取摄像头图像的定时器
        self.camera_timer.timeout.connect(self.play_camera_video)

    def center_win(self):
        qr = self.frameGeometry()
        cp = QGuiApplication.primaryScreen().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def btn_camera_click(self):
        if not self.is_open_camera: # 按下 打开摄像头 按钮
            self.video_cap = cv2.VideoCapture(0)  # 打开默认摄像头(索引为0)
            print('camera fps:', self.video_cap.get(cv2.CAP_PROP_FPS))
            # 每个20毫秒获取一次摄像头的图像进行刷新, 具体设置多少合适, 可以参考你的摄像头帧率cv2.CAP_PROP_FPS,
            # 刷新频率设置一个小于 1000 / cv2.CAP_PROP_FPS 的值即可
            self.camera_timer.start(20)  
            self.is_open_camera = True
            self.btn_camera.setText('关闭摄像头')
        else:  # 按下 关闭摄像头 按钮
            self.camera_timer.stop()
            self.video_cap.release()
            self.video_cap = None
            self.lbl_img.clear()
            self.btn_camera.setText('打开摄像头')
            self.is_open_camera = False

    def play_camera_video(self):
        if self.is_open_camera:
            # ret, frame = self.video_cap.read()  # 读取视频流的每一帧
            self.video_cap.grab()
            ret, frame = self.video_cap.retrieve()  # 读取视频流的每一帧
            if ret:
                height, width, channel = frame.shape  # 获取图像高度、宽度和通道数, 通常为为640x480x3
                # opencv获取的图像默认BGR格式
                # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 转换BRG 到 RGB
                # 或者
                # 将OpenCV格式转换成QImage格式, 需要进行颜色通道交换(.rgbSwapped())
                img = QImage(frame.data, width, height, QImage.Format.Format_RGB888)
                img = img.rgbSwapped()
                # 或者
                # img = QImage(frame.data, width, height, QImage.Format.Format_RGB888)
                # img.rgbSwap()
                # 或者
                # img = QImage(frame.data, width, height, QImage.Format.Format_BGR888)
                pixmap = QPixmap.fromImage(img)  # 从QImage生成QPixmap对象
                #
                lbl_width = self.lbl_img.size().width()  # 通过size()获取图像标签的真实的宽度
                lbl_height = self.lbl_img.size().height()  # 通过size()获取图像标签的真实的高度
                # 按照图像标签的真实的宽和高进行缩放,但保持摄像头的宽和高的比例
                pixmap = QPixmap(pixmap).scaled(
                    self.lbl_img.width(), self.lbl_img.height(),
                    aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio)
                self.lbl_img.setPixmap(pixmap)  # 在标签上显示图片


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
相关推荐
choke2335 小时前
[特殊字符] Python异常处理
开发语言·python
玄同7656 小时前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
小瑞瑞acd6 小时前
【小瑞瑞精讲】卷积神经网络(CNN):从入门到精通,计算机如何“看”懂世界?
人工智能·python·深度学习·神经网络·机器学习
火车叼位7 小时前
也许你不需要创建.venv, 此规范使python脚本自备依赖
python
火车叼位7 小时前
脚本伪装:让 Python 与 Node.js 像原生 Shell 命令一样运行
运维·javascript·python
孤狼warrior7 小时前
YOLO目标检测 一千字解析yolo最初的摸样 模型下载,数据集构建及模型训练代码
人工智能·python·深度学习·算法·yolo·目标检测·目标跟踪
Katecat996637 小时前
YOLO11分割算法实现甲状腺超声病灶自动检测与定位_DWR方法应用
python
nLsUCWFJR7 小时前
(Matlab)基于贝叶斯优化卷积双向长短期记忆网络(CNN-BiLSTM)回归预测
opencv
玩大数据的龙威7 小时前
农经权二轮延包—各种地块示意图
python·arcgis
ZH15455891317 小时前
Flutter for OpenHarmony Python学习助手实战:数据库操作与管理的实现
python·学习·flutter