You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6中QImage转QVideoFrame问题及图像流显示方案咨询

PySide6中QImage转QVideoFrame的解决方案及图像流显示替代方案

一、QImage转QVideoFrame的正确实现

PySide6里QVideoFrame的构造逻辑和PySide2完全不同,不能直接传入QImage,必须先通过QVideoFrameFormat指定图像的尺寸、像素格式,再初始化帧并手动复制QImage的数据。

转换函数实现

from PySide6.QtMultimedia import QVideoFrame, QVideoFrameFormat
from PySide6.QtGui import QImage

def qimage_to_videoframe(image: QImage) -> QVideoFrame:
    # 统一转换为支持的像素格式(RGBA8888兼容性最好)
    if image.format() != QImage.Format_RGBA8888:
        image = image.convertToFormat(QImage.Format_RGBA8888)
    
    # 创建匹配图像尺寸和格式的视频帧格式
    frame_format = QVideoFrameFormat(image.size(), QVideoFrameFormat.PixelFormat.Format_RGBA8888)
    # 初始化视频帧
    video_frame = QVideoFrame(frame_format)
    
    # 映射帧内存,写入QImage数据
    if video_frame.map(QVideoFrame.MapMode.WriteOnly):
        video_frame.bits().copy(image.bits(), image.sizeInBytes())
        video_frame.unmap()
    
    return video_frame

修改你的VideoSink类

替换原有的QVideoFrame构造代码,改用上面的转换函数:

import os
from PySide6.QtCore import QObject, Signal, Property
from PySide6.QtGui import QImage

class VideoSink(QObject):
    sinkChanged = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.sink = None

    def get_sink(self):
        return self.sink

    def set_sink(self, sink):
        self.sink = sink
        # 加载测试图片并转换
        img_path = os.path.join(os.path.dirname(__file__), "./res/initialization.jpg")
        qimage = QImage(img_path)
        frame = qimage_to_videoframe(qimage)
        
        if self.sink and frame.isValid():
            self.sink.setVideoFrame(frame)
        self.sinkChanged.emit()

    videoSink = Property(QObject, get_sink, set_sink, notify=sinkChanged)

二、图像流显示的替代方案:QQuickImageProvider

如果只是实现相机实时取景,用QQuickImageProvider配合QML的Image元素会更简洁,不需要处理VideoSink的复杂逻辑,适合逐帧刷新的场景。

1. 自定义ImageProvider(兼容gphoto2+PIL场景)

from PySide6.QtQuick import QQuickImageProvider
from PySide6.QtGui import QImage, QPixmap
import gphoto2 as gp
from PIL import Image
import io

class CameraPreviewProvider(QQuickImageProvider):
    def __init__(self):
        super().__init__(QQuickImageProvider.ImageType.Image)
        # 初始化gphoto2相机(根据你的实际逻辑调整)
        self.camera = gp.Camera()
        self.camera.init()

    def requestImage(self, id, size, requestedSize):
        # 从相机获取预览帧并转为PIL Image
        preview_data = self.camera.capture_preview()
        pil_img = Image.open(io.BytesIO(preview_data))
        # PIL转QImage
        qimage = self.pil_to_qimage(pil_img)
        # 返回QPixmap供QML显示
        return QPixmap.fromImage(qimage), qimage.size()

    def pil_to_qimage(self, pil_img):
        # 适配不同PIL图像模式
        if pil_img.mode == "RGB":
            q_format = QImage.Format_RGB888
        elif pil_img.mode == "RGBA":
            q_format = QImage.Format_RGBA8888
        else:
            pil_img = pil_img.convert("RGB")
            q_format = QImage.Format_RGB888
        return QImage(pil_img.tobytes(), pil_img.width, pil_img.height, q_format)

2. QML端调用

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 800
    height: 600
    visible: true

    Image {
        anchors.fill: parent
        fillMode: Image.Stretch
        // 引用自定义ImageProvider,前缀"image://camera"对应注册名
        source: "image://camera/preview"
        
        // 定时刷新图像(模拟30fps)
        Timer {
            interval: 1000/30
            running: true
            repeat: true
            onTriggered: parent.source = "image://camera/preview?" + Math.random()
        }
    }
}

3. 注册ImageProvider到QML引擎

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

if __name__ == "__main__":
    app = QGuiApplication([])
    engine = QQmlApplicationEngine()
    # 注册ImageProvider,名称为"camera"
    engine.addImageProvider("camera", CameraPreviewProvider())
    engine.load("main.qml")
    app.exec()

内容的提问来源于stack exchange,提问作者christianPro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 13:32:37