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

如何在QML/PySide6应用中显示OpenCV相机画面?

问题:在PySide6/QML应用中显示OpenCV相机画面

我需要在QML/PySide6应用中显示OpenCV相机画面,应用包含开始/停止流两个按钮和一个用于显示视频的Image元素。目前已能设置静态图片,但不知道如何将每一帧动态图像传递给requestImage;同时不清楚QML文件中onImageChanged函数的image属性如何与source: "image://MyImageProvider/img"关联。找不到其他OpenCV与QML/PySide6集成的示例,希望得到帮助。我更熟悉QtWidgets,刚学习QML!

初始代码

main.py

import sys
from pathlib import Path
import os
import cv2

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtGui import QIcon, QPixmap, QImage
from PySide6.QtWidgets import QFileDialog, QApplication
from PySide6.QtCore import Qt, QThread, Signal, Slot, QObject, QSize
from PySide6.QtQuick import QQuickPaintedItem, QQuickView, QQuickImageProvider


class ThreadCamera(QThread):
    updateFrame = Signal(QImage)

    def __init__(self, parent=None):
        QThread.__init__(self, parent)
    
    def run(self):
        self.cap = cv2.VideoCapture(0)
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            if not ret:
                img = QImage("./images/network.png")
                self.updateFrame.emit(img)
            color_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QImage(color_frame.data, color_frame.shape[1], color_frame.shape[0], QImage.Format_RGB888)
            self.updateFrame.emit(img)


class ImageProvider(QQuickImageProvider):
    imageChanged = Signal(QImage)

    def __init__(self):
        super(ImageProvider, self).__init__(QQuickImageProvider.Image)

        self.cam = ThreadCamera() 
        self.cam.updateFrame.connect(self.update_image)

    def requestImage(self, id, size, requestedSize):
        img = QImage(600, 500, QImage.Format_RGBA8888)
        img.fill(Qt.black)
        return img
      
    @Slot()
    def update_image(self, img):
        self.imageChanged.emit(img)
    
    @Slot()
    def start(self):
        print("Starting...")
        self.cam.start()
    
    @Slot()
    def killThread(self):
        print("Finishing...")
        try:
            self.cam.cap.release()
            cv2.destroyAllWindows()
        except:
            pass


class MainWindow(QObject):
    def __init__(self):
        QObject.__init__(self)
    

if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("./images/network.png"));
    engine = QQmlApplicationEngine()

    #Get Context
    main = MainWindow()
    myImageProvider = ImageProvider()
    engine.rootContext().setContextProperty("backend", main)
    engine.rootContext().setContextProperty("myImageProvider", myImageProvider)
    
    engine.addImageProvider("MyImageProvider", myImageProvider)

    #Load QML File
    engine.load(os.fspath(Path(__file__).resolve().parent /  "test2.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec())

main.qml

import QtQuick 2.15
import QtQuick.Window 
import QtMultimedia
import QtQuick.Controls 6.3
import QtQuick.Layouts 6.3
import QtQuick.Dialogs


Window {
    visible: true
    width: 600
    height: 500
    title: "WebCam"

    Image {
        id: feedImage
        width: parent.width
        height: parent.height - 50
        fillMode: Image.PreserveAspectFit
        cache: false
        source: "image://MyImageProvider/img"
        property bool counter: false

        function reloadImage() {
            counter = !counter
            source = "image://MyImageProvider/img?id=" + counter
        }
    }

    RowLayout {
        anchors.top: feedImage.bottom
        anchors.horizontalCenter: feedImage.horizontalCenter

        Button {
            id: btnStartCamera
            text: "Start Camera"
            onClicked: {
                myImageProvider.start()
            }
        }
        
        Button {
            id: btnStopCamera
            text: "Stop Camera"
            onClicked: {
                myImageProvider.killThread()
             }
        }
    }

    Connections{
        target: myImageProvider

        function onImageChanged(image) {
            console.log("emit")
            feedImage.reloadImage()
        }
    }
}

更新后的实现(已成功显示视频流)

修改了ImageProvider类后,视频流可以正常显示,代码如下:

class ImageProvider(QQuickImageProvider):
    imageChanged = Signal(QImage)

    def __init__(self):
        super(ImageProvider, self).__init__(QQuickImageProvider.Image)

        self.cam = ThreadCamera() 
        self.cam.updateFrame.connect(self.update_image)
        self.image = None

    def requestImage(self, id, size, requestedSize):
        if self.image:
            img = self.image
        else:
            img = QImage(600, 500, QImage.Format_RGBA8888)
            img.fill(Qt.black)

        return img
          
    @Slot()
    def update_image(self, img):
        self.imageChanged.emit(img)
        self.image = img
    
    @Slot()
    def start(self):
        print("Starting...")
        self.cam.start()
    
    @Slot()
    def killThread(self):
        print("Finishing...")
        try:
            self.cam.cap.release()
            cv2.destroyAllWindows()
        except:
            pass

但我不确定这种实现方式是否正确。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 17:20:36