qml_puthon_user6 декабря 2019 г. 7:10

подружить qml и opencv

OpenCV, qml, Python 3, QML

Доброго времени суток.
Пытаюсь сделать отображение с веб-камеры в графическом интерфейсе написанном на qml.
Код программы:

#системные библиотеки
import cv2
import numpy as np
import sys
import threading

#PyQt библиотеки
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtQml import *
from PyQt5.QtQuick import *


class MyImageProvider(QQuickImageProvider):
    def __init__(self):
        super(MyImageProvider, self).__init__(QQuickImageProvider.Image)

    def requestImage(self, p_str, size):
        cap = cv2.VideoCapture(0)
        while True:
            ret, frame = cap.read()
            #image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            height, width, channel = frame.shape
            #step = channel * width
            img = QImage(frame, width, height, QImage.Format_RGB888)
            #img.fill(Qt.green)
            return img, img.size()
            cv2.waitKey(1)

if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    MyImageProvider()
    engine = QQmlApplicationEngine()
    engine.addImageProvider("myprovider", MyImageProvider())
    engine.load(QUrl.fromLocalFile("tst.qml"))
    if len(engine.rootObjects()) == -1:
        sys.exit(-1)
    sys.exit(app.exec_())

Вот код формы:

import QtQuick 2.12
import QtQuick.Window 2.12

Window{
    visible: true
    width: 800
    height: 600
    title: ("QML OPENCV")
    color: "green"
    Image
    {
         //anchors.fill : parent
         source: "image://myprovider/test.png"
    }
}

Читал статью про слоты и сигналы в python , но пока не могу сообразить, как это всё складывается воедино.
Представленный код отображает окно с веб-камерой и окно qml интерфейста.

Есть на C++ образец... Я в нём не силён, подсобите перевести на python3. :)
Утечка памяти (OpenCV + QML)

Нашёл пример с изображением:
QQuickImageProvider PyQt5
С помощью этого примера почти получилось добиться желаемого результата. Получается, что отображается только последний снятый кадр веб-камерой, а не поток. Есть идеи, как сделать поток из кадров в форму qml?

Спасибо!

Вот такой набросок пока что получился.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
21

Добрый день.
Да... OpenCV, конечно, интересная тема. Но вот я от неё далёк.
QQuickImageProvider использутеся для предоставления изображений, но точно не для потока этих самых изображений. В каком-то смысле все те примеры выглядят как костыли, поскольку QQuickImageProvider используется не по своему прямому назначению. Но тем не менее это должно работать.

Обратите своё внимание на то, что в примере с утечкой памяти имеется сигнал, который сообщает об изменении изображения на каждый новый кадр. Вам нужно сделать тоже самое. Вам нуэно использовать сигналы и слоты для этого. Не помню, вы уже кажется смотрели ту статью по сигналам и слотам PyQt5 - Урок 007. Работаем с QML QtQuick (Сигналы и слоты)

Добрый.
Да, я пытался связаться через сигналы и слоты, но у меня не выходит перевести программу с плюсов на питон. Если не затруднит помочь в переводе, то есть вероятность, что получится написать конечный результат. :)

Думаю, что в QQuickImageProvider следует добавить сигнал, что-то вроде такого

class MyImageProvider(QQuickImageProvider):
    frameChanged = pyqtSignal()

А потом как минимум подключить к этому сигналу обработчик в QML и посмотреть, сыпется ли что-то нибудь в консоль QML

Window
{
    visible: true
    width: 640
    height: 480
    title: ("QML OPENCV")
    color: "green"
    id: root

    // ...

    Connections
    {
        target: myprovider
        property int frameCounter: 0

        onFrameChanged:
        {
            frameCounter ^= 1;
            console.log(frameCounter); // проверяем здесь изменение счётчика
        }
    }
}

Тогда уже можно будет пытаться сообразить что-нибудь дальше

А через "console.log" Вы куда выводите сообщения? В PyCharm? Я работаю в основном в стандартной IDLE Python. Такое не срабатывает в стандартной IDLE. PyCharm у меня стоит.

вообще - это тоже должно сыпаться в консоль и без разницы, какую IDE использовать. К IDE это не должно иметь никакого отношения. Если вывода нет, то скорее всего не работает.

А да, ещё этот сигнал нужно вызывать

class MyImageProvider(QQuickImageProvider):
    frameChanged = pyqtSignal()

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

    def requestImage(self, p_str, size):
        cap = cv2.VideoCapture(0)
        while True:
            ret, frame = cap.read()
            #image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            height, width, channel = frame.shape
            #step = channel * width
            img = QImage(frame, width, height, QImage.Format_RGB888)
            #img.fill(Qt.green)
            self.frameChanged.emit() # вызвать сигнал
            return img, img.size() # а вот из-за этого у вас выполняется выход из функции, 
            cv2.waitKey(1) # до выполнения этого кода метод никогда не доходит

И кстати сам запрос изображения у вас как-то странно написан

Вы сейчас разбираете некий костыль, посоветовали с сигналами и слотами поработать, вот и хочу с ними разобраться, если так имеет место быть более правильное решение.) Я могу по-новому написать, чтобы было понятнее работать.

что предоставлено, с тем и работаю. не писать же мне с нуля ваш проект ))

Это понятное дело.)
Я сделал вот так, чтобы проверить, работает ли счётчик.
Код программы:

# системные библиотеки
import cv2
import numpy as np
import sys
import threading

# PyQt библиотеки
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtQml import *
from PyQt5.QtQuick import *


class MyImageProvider(QQuickImageProvider):
    def __init__(self):
        super(MyImageProvider, self).__init__(QQuickImageProvider.Image)

    frameChanged = pyqtSignal()

    def reauestSignal(self):

        self.frameChanged.emit()    #вызываю сигнал

if __name__ == '__main__':

    # создаём экземпляр приложения
    app = QGuiApplication(sys.argv)
    # создаём QML движок
    engine = QQmlApplicationEngine()
    # создаём объект vision
    videoProvider = MyImageProvider()
    # и регистрируем его в контексте QML
    engine.rootContext().setContextProperty("myprovider", videoProvider)
    #engine.addImageProvider("myprovider", videoProvider)
    # загружаем файл qml в движок
    engine.load("test.qml")

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

Код формы:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

Window
{
    visible: true
    width: 640
    height: 480
    title: ("QML OPENCV")
    color: "green"
    id: root

    // ...

    TextArea 
    {
        id: debug_area
        x: 183
        y: 361
        width: 275
        height: 145
        text: qsTr("Debug\n")
        selectByMouse: true
    }

    Connections
    {
        target: myprovider
        property int frameCounter: 0

        onFrameChanged:
        {
            frameCounter ^= 1;
            debug_area.text += frameCounter; // проверяем здесь изменение счётчика
        }
    }
}

Не хочет работать.

Я сделал простой вывод текста по испусканию сигнала...
Чего не хватает программе?)
Python:

# системные библиотеки
import cv2
import numpy as np
import sys
import threading

# PyQt библиотеки
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtQml import *
from PyQt5.QtQuick import *


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

    frameChanged = pyqtSignal()


if __name__ == '__main__':

    # создаём экземпляр приложения
    app = QGuiApplication(sys.argv)
    # создаём QML движок
    engine = QQmlApplicationEngine()
    # создаём объект vision
    videoProvider = MyImageProvider()
    # и регистрируем его в контексте QML
    engine.rootContext().setContextProperty("myprovider", videoProvider)
    #engine.addImageProvider("myprovider", videoProvider)
    # загружаем файл qml в движок
    engine.load("test.qml")

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

QML:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

Window
{
    visible: true
    width: 640
    height: 480
    title: ("QML OPENCV")
    color: "green"
    id: root

    //здесь увидим информацию
    TextArea 
    {
        id: debug_area
        x: 183
        y: 10
        width: 200
        height: 400
        text: qsTr("Debug\n")
        selectByMouse: true
    }
    Button
    {
        x: 10
        y: 10
        width: 100
        height: 50
        text: qsTr("Debug Btn")
        onClicked:
        {
            debug_area.text += "BTN is WORK\n"
        }
    }
    Connections
    {
        target: myprovider
        property int frameCounter: 0

        onFrameChanged:
        {
            debug_area.text = "Signal is work"
            //frameCounter += 1;
            //debug_area.text = frameCounter; // проверяем здесь изменение счётчика
        }
    }
}

Есть на qml что-то типа window.onload? Чтобы при загрузке формы срабатывал сигнал. По нажатию кнопки всё прекрасно работает... А вот при загрузке формы нет...

Да, с console.log() я разобрался, счётчик ничего не показывает

У всех компонентов есть встраиваемый обработчик Component.onCompleted, который выполняется, когда объект полностью создан

Window {
    Component.onCompleted: {
        console.log("Window Completed Running!")
    }
}

Наверное сигнал всё-таки не вызывается правильно

А не подскажете, как вызвать правильно? :)

Сделал вот такой вариант и он работает, только мне непонятно, почему в консоли пишется undefined...
Форма:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

Window
{
    visible: true
    width: 640
    height: 480
    title: ("QML OPENCV")
    color: "green"
    id: root

    Component.onCompleted:
    {
        console.log("onload")
        console.log(myprovider.say())
    }

    Connections
    {
        target: myprovider

        /*onFrameChanged:
        {   
        }*/
    }
}

Скрипт:

# системные библиотеки
import cv2
import numpy as np
import sys
import threading

# PyQt библиотеки
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtQml import *
from PyQt5.QtQuick import *


class MyImageProvider(QObject):

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

    #frameChanged = pyqtSignal()

    @pyqtSlot()
    def say(self):
        print("blablabla")

if __name__ == '__main__':

    # создаём экземпляр приложения
    app = QGuiApplication(sys.argv)
    # создаём QML движок
    engine = QQmlApplicationEngine()
    # создаём объект vision
    videoProvider = MyImageProvider()
    # и регистрируем его в контексте QML
    engine.rootContext().setContextProperty("myprovider", videoProvider)
    #engine.addImageProvider("myprovider", videoProvider)
    # загружаем файл qml в движок
    engine.load("test.qml")

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

undefined пишет потому, что метод сам по себе ничего не возвращает, то есть void, напишите так

Component.onCompleted:
{
    console.log("onload")
    console.log(myprovider)
}

Вернул код инструкции, если я правильно понял.

Вернулся указатель на ваш объект. Ну слот срабатывает. Вам теперь нужно дальше развить вашу задачу. У меня к сожалению нет времени разбираться с opencv, поэтому не могу ответить на некоторые вопросы.

С opencv я разберусь. :) У меня пока только вопросы по сигналам и слотам.)

На сколько мне известно в opencv с камеры подпются картинки через определенное время и так выходит видео. По идее можно по получении этой картинки передавать ее в qml. Прикрепить сигнал к получению картинки или таймеру и так подовать сигнал в qml об необходимости обновления.

Если смотреть глубже, то там подаётся массив байтов(bytes в питоне). Ну, а по простому, то да. Я пока не разобрался как переменную передавать в qml форму. В этой самой переменной, пускай будет frame, как раз хранится поток с веб-камеры. Как только я передам её на форму, то по идее, элемент в котором будет "отображаться" данная переменная, нужно же перерисовывать, чтобы кадры обновлялись.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
VD

C++ - Тест 001. Первая программа и типы данных

  • Результат:73баллов,
  • Очки рейтинга1
Ds

C++ - Тест 003. Условия и циклы

  • Результат:64баллов,
  • Очки рейтинга-1
o

C++ - Тест 001. Первая программа и типы данных

  • Результат:86баллов,
  • Очки рейтинга6
Последние комментарии
D:

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Добрый день, пытаюсь разобраться и подргнать пример под себя. Есть бд с огромным количеством полей. В приложении на виджетах при использовании QTableView все работает и путем простого sql запрос…

Django - Урок 039. Добавление личных сообщений и чатов на сайте - Часть 2 (Счётчик диалогов и чатов с непрочитанными сообщениями)

Добавляйте поле файла в модель сообщения. И в форме сообщения указывайте, что поле с файлом.
s

Django - Урок 023. Like Dislike система с помощью GenericForeignKey

все, я со всем разобрался!) Извините!)
s

Django - Урок 023. Like Dislike система с помощью GenericForeignKey

Доброго времени суток!) Я случайно набрел на вашу статью, и она помогла мне решить некоторые мои трудности, я прошел за вами по шагам, в попытках адаптировать это под себя, и возник вопрос. У ва…
Сейчас обсуждают на форуме
  • BlinCT
  • 7 августа 2020 г. 9:05

Динамическое заполнение StackLayout в qml

Всем привет. Пытаюсь решить такую задачку, есть TabBar и его кнопки. StackLayout{ currentIndex: tabBar.currentIndex A {id: tabA} B {id: tabB} C {id: tabC} D {id: ta…

Наследование QWidget

Добрый день В addWidget нужно ещё указывать номер строки и колонки, куда добаляется виджет. И в вашем случае лучше Диалоговое окно не наследовать сразу от QDialog и W, а наследовать …
М

QML: изменение стиля при наведении и при нажатии на кнопку

enabled = false перестанет быть активной и не будет ни на что реагировать) Хм.. по-моему пробовал такое. Проверю ещё раз после работы. Ура, спасибо большо…
U

Динамическое наполнение StackView QML

Во затупил))) Спасибо за все))) StackView.push("ModuleTip1.qml") ну или в сложной иерархии StackView.push("qrc:/folder/ModuleTip1.qml") и всего делов... Не пойму, почему сра…

QEventLoop тормозит при удалении экземпляра

Думаю, что нет. Лучше вообще без исключений, но не всегда возможно.
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB