[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())
相关推荐
多打代码13 分钟前
2025.09.05 用队列实现栈 & 有效的括号 & 删除字符串中的所有相邻重复项
python·算法
@CLoudbays_Martin1120 分钟前
为什么动态视频业务内容不可以被CDN静态缓存?
java·运维·服务器·javascript·网络·python·php
程序猿炎义43 分钟前
【NVIDIA AIQ】自定义函数实践
人工智能·python·学习
THMAIL1 小时前
深度学习从入门到精通 - BERT与预训练模型:NLP领域的核弹级技术详解
人工智能·python·深度学习·自然语言处理·性能优化·bert
nuclear20112 小时前
Python 实现 Markdown 与 Word 高保真互转(含批量转换)
python·word转markdown·markdown转word·word转md·md转word
山烛2 小时前
深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
人工智能·pytorch·python·深度学习·cnn·调整学习率
THMAIL2 小时前
深度学习从入门到精通 - 神经网络核心原理:从生物神经元到数学模型蜕变
人工智能·python·深度学习·神经网络·算法·机器学习·逻辑回归
dbdr09013 小时前
Linux 入门到精通,真的不用背命令!零基础小白靠「场景化学习法」,3 个月拿下运维 offer,第二十六天
linux·运维·服务器·网络·python·学习
花花无缺3 小时前
python自动化-pytest-用例发现规则和要求
后端·python