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
相关产品推荐
相关产品推荐

