How to show Opencv camera feed in a Qml application?

Question:

I’m trying to show opencv processed camera feed inside and Image object in Qml application.

The feed page is a separate page loaded with a Loader object to the main page when a button is pressed, the feed page is inactive by default.

Here is the code I’m using:

main.py

from PyQt5.QtGui import QGuiApplication, QImage
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QTimer, QUrl, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickImageProvider, QQuickView


class CamFeedWorker(QThread):
    image_update = pyqtSignal(QImage)

    def __init__(self):
        super(CamFeedWorker, self).__init__()
        self.processor = FieldProcessor()  # Just gets the camera frame and does some processing
        self.thread_active = True


    def run(self):
        self.thread_active = True
        self.processor.init_cap()

        while self.thread_active:
            img = self.processor.frame_capture()
            qt_img = QImage(img.data,
                            img.shape[1],
                            img.shape[0],
                            QImage.Format_RGB888
                            )
            self.image_update.emit(qt_img)

    def stop(self):
        self.thread_active = False
        sleep(0.2)
        self.processor.cap.release()
        self.quit()


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

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

        self.cam = CamFeedWorker() 
        self.cam.image_update.connect(self.update_image)

    def requestImage(self, id, requestedSize):
        print("id: ", id)
        print("requested size: ", requestedSize)
        img = QImage(300, 300, QImage.Format_RGBA8888)
        img.fill(Qt.black)
        img = QImage("qml/pages/test.png")
        return img, img.size()

    def update_image(self, img):
        self.imageChanged.emit(img)

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


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = MainWindow()
    engine.rootContext().setContextProperty("backend", main)

    # Image provider setup for camera feed:
    engine.addImageProvider("MyImageProvider", ImageProvider())
    engine.load(QUrl.fromLocalFile("qml/pages/FieldProcessingPage.qml"))

    # Loading qml file
    engine.load(os.fspath(Path(__file__).resolve().parent / "./qml/main.qml"))

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

main.qml

A normal page with Loader for every subpage and buttons for activating and viewing a page and deactivating the rest.

FieldProcessingPage.qml

import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
    id: fieldProcessingPage

    Rectangle {
        id: pageRectangle
        anchors.fill: parent
        implicitWidth: 800
        implicitHeight: 600

        Rectangle {
            id: contentRectangle
            anchors.fill: parent

            Image {
                id: feedImage
                anchors.fill: parent
                fillMode: Image.PreserveAspectFit
                cache: false
                source: "image://MyImageProvider/img"
                property bool counter: false

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

    Connections {
        target: myImageProvider

        function onImageChanged(image) {
            feedImage.reloadImage()
        }
    }
}

I get These error messages when running the code:

file:qml/pages/FieldProcessingPage.qml:43:5: QML Connections: Detected function "onImageChanged" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name.
file:qml/pages/FieldProcessingPage.qml:26:13: QML Image: Failed to get image from provider: image://myimageprovider/img
file:FieldProcessingPage.qml:44: ReferenceError: myImageProvider is not defined

I actually didn’t find an example in python on running a camera feed from opencv on qml so I need help getting this running, thanks.

Asked By: Arsany Samuel

||

Answers:

First of all, thanks I found your code useful for my use case since all similar solutions were C++.

I think the issue is that target connection in FieldProcessingPage.qml is targeting the class rather than the class object instantiated by default at rendering time.

A workaround is to create an object of class ImageProvider and reference it for property context as well as the default QQuickImageProvider.

main.py

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = MainWindow()
    myImageProvider = ImageProvider()
    engine.rootContext().setContextProperty("backend", main)
    engine.rootContext().setContextProperty("myImageProvider", myImageProvider)

    # Image provider setup for camera feed:
    engine.addImageProvider("MyImageProvider", myImageProvider)
    engine.load(QUrl.fromLocalFile("qml/pages/FieldProcessingPage.qml"))

    # Loading qml file
    engine.load(os.fspath(Path(__file__).resolve().parent / "./qml/main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())
Answered By: Jesper

i saw you did manage to solve your problem, i’m having same issue with my code, is it possible to share your entire code somehow? a github rep or here
im trying to get 6 camera output into qml using python and as you may know there is not much documentation around this one

Answered By: rezash
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.