[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())
相关推荐
迅猛龙办公室12 分钟前
python实现倒序输出数字
开发语言·python
全栈练习生9 小时前
AI Agent 沙箱
python·ai
SEO_juper11 小时前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站
用户83562907805112 小时前
使用 Python 对 Excel 工作表进行排序
后端·python
for_ever_love__12 小时前
MySQL 全文索引实战:FULLTEXT、ngram 中文分词与 MATCH AGAINST 到底该怎么用
java·python·mysql·全文检索·分词·索引·ngram
用户83562907805112 小时前
使用 Python 合并 PowerPoint 演示文稿
后端·python
weixin_4617694012 小时前
VS Code 中创建 Jupyter 文件(.ipynb)
ide·python·jupyter
2601_9669496513 小时前
多市场量化策略的数据接口应该如何设计:从数据层架构到策略接入
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
全栈弄潮儿13 小时前
Python实战第1期:Python环境搭建与第一个程序
python
心易行者13 小时前
Agent应用+API端点商业化进阶实战:从单体智能体到可付费调用的API全流程
运维·服务器·人工智能·python·apache