2017-11-02 30 views
1

OpenCV MatをQMLイメージに表示しようとしています。メモリリーク(OpenCV + QML)

OpenCVでカメラからフレームを取得すると、フレームはQMLで正常に表示されますが、メモリ使用量は時間とともに増加します。どうすれば修正できますか?

main.cppに

#include <QGuiApplication> 
#include "videoprovider.h" 

int main(int argc, char *argv[]) 
{ 
    QGuiApplication app(argc, argv); 
    VideoProvider videoProvider; 
    return app.exec(); 
} 

VideoProvider.h

#ifndef VIDEOPROVIDER_H 
#define VIDEOPROVIDER_H 

#include <QObject> 
#include <QFuture> 
#include <QImage> 
#include <QQmlApplicationEngine> 
#include <QQuickImageProvider> 
#include <opencv2/opencv.hpp> 

class VideoProvider : public QObject, public QQuickImageProvider 
{ 
    Q_OBJECT 
public: 
    explicit VideoProvider(); 
    QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); 

signals: 
    void frameChanged(); 

public slots: 
    void framePainted(); 

private: 
    QQmlApplicationEngine engine; 
    bool readyfor; 
    cv::Mat mat; 
    QImage outputImage; 
    void process(); 
}; 

#endif // VIDEOPROVIDER_H 

VideoProvider.cpp

#include <QQmlContext> 
#include <QtConcurrent/QtConcurrent> 
#include <QDebug> 
#include <QThread> 
#include "videoprovider.h" 
#include <QQuickImageProvider> 

VideoProvider::VideoProvider() : QQuickImageProvider (QQuickImageProvider :: Pixmap) 
{ 
    engine.rootContext()->setContextProperty("videoProvider", this); 
    engine.addImageProvider(QLatin1String ("videoCapture"), this); 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 
    readyfor = true; 
    QtConcurrent::run(this, VideoProvider::process); 
} 

void VideoProvider::framePainted() 
{ 
    readyfor = true; 
} 

void VideoProvider::process() 
{ 
    cv::VideoCapture capture(0); 

    while(true){ 

     QThread::currentThread()->msleep(80); 

     if(!readyfor) continue; 

     mat.release(); 
     capture >> mat; 

     if(mat.empty()) 
     { 
      qDebug()<<"disconnect"; 
     } 
     else 
     { 
      readyfor = false; 
      cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB); 
      outputImage = QImage((uchar*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); 
      emit frameChanged(); 
     } 
    } 

    capture.release(); 
} 

QPixmap VideoProvider::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) 
{ 
    return QPixmap::fromImage(outputImage); 
} 

main.qml

import QtQuick 2.6 
import QtQuick.Window 2.2 

Window 
{ 
    visible: true 
    width: 640 
    height: 480 
    title: qsTr("Hello World") 
    id: root 

    Image{ 
     id: videoLayer 
     anchors.fill: parent 
     cache: false 

     onSourceChanged:{ 
      videoProvider.framePainted(); 
     } 
    } 

    Connections 
    { 
     target: videoProvider 
     property int frameCounter: 0 

     onFrameChanged: 
     { 
      videoLayer.source = "image://videoCapture/hoge" + frameCounter; 
      frameCounter ^= 1; 
     } 
    } 
} 
0123:ここに私のコードです

QMLにシグナル(frameChanged(); emit)を送信すると起こったことがわかりました。

UPD:

Valgrindのログ:

==18038== Memcheck, a memory error detector 
==18038== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. 
==18038== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==18038== Command: ./prog 
==18038== 
QML debugging is enabled. Only use this in a safe environment. 
==18038== Warning: noted but unhandled ioctl 0x30000001 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x27 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x7ff with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x25 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x17 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: set address range perms: large range [0x200000000, 0x500000000) (noaccess) 
==18038== Warning: set address range perms: large range [0x500000000, 0x700000000) (noaccess) 
==18038== Warning: noted but unhandled ioctl 0x19 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x21 with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Warning: noted but unhandled ioctl 0x1b with no size/direction hints. 
==18038== This could cause spurious value errors to appear. 
==18038== See README_MISSING_SYSCALL_OR_IOCTL for guidance on writing a proper wrapper. 
==18038== Thread 8 Thread (pooled): 
==18038== Invalid read of size 4 
==18038== at 0xAF682D0: QImage::~QImage() (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Gui.so.5.9.1) 
==18038== by 0x40570F: VideoProvider::process() (videoprovider.cpp:50) 
==18038== by 0x406AEB: QtConcurrent::VoidStoredMemberFunctionPointerCall0<void, VideoProvider>::runFunctor() (qtconcurrentstoredfunctioncall.h:205) 
==18038== by 0x405FC6: QtConcurrent::RunFunctionTask<void>::run() (qtconcurrentrunbase.h:136) 
==18038== by 0xBC44BA2: ??? (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Core.so.5.9.1) 
==18038== by 0xBC48849: ??? (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Core.so.5.9.1) 
==18038== by 0xCE456B9: start_thread (pthread_create.c:333) 
==18038== by 0xC9773DC: clone (clone.S:109) 
==18038== Address 0x23d1b260 is 0 bytes inside a block of size 128 free'd 
==18038== at 0x4C2F24B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==18038== by 0xAF682F3: QImage::~QImage() (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Gui.so.5.9.1) 
==18038== by 0x4047CB: VideoProvider::~VideoProvider() (videoprovider.h:11) 
==18038== by 0x403A34: main (main.cpp:8) 
==18038== Block was alloc'd at 
==18038== at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==18038== by 0xAF656BE: QImageData::create(unsigned char*, int, int, int, QImage::Format, bool, void (*)(void*), void*) (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Gui.so.5.9.1) 
==18038== by 0xAF65971: QImage::QImage(unsigned char*, int, int, int, QImage::Format, void (*)(void*), void*) (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Gui.so.5.9.1) 
==18038== by 0x4056E2: VideoProvider::process() (videoprovider.cpp:50) 
==18038== by 0x406AEB: QtConcurrent::VoidStoredMemberFunctionPointerCall0<void, VideoProvider>::runFunctor() (qtconcurrentstoredfunctioncall.h:205) 
==18038== by 0x405FC6: QtConcurrent::RunFunctionTask<void>::run() (qtconcurrentrunbase.h:136) 
==18038== by 0xBC44BA2: ??? (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Core.so.5.9.1) 
==18038== by 0xBC48849: ??? (in /home/dmytro/Qt/5.9.1/gcc_64/lib/libQt5Core.so.5.9.1) 
==18038== by 0xCE456B9: start_thread (pthread_create.c:333) 
==18038== by 0xC9773DC: clone (clone.S:109) 
+2

プロジェクトでmem checkを実行しようとしましたか? 'valgrind --leak-check = full your-app-path'を実行しようとしました – Simon

+0

OpenCVが独自のスレッドを持ち、UIが完全に非同期である限り、キューに入れられた接続を経由してUIに画像を送信します。それ以外の場合は、そのカメラ画像QImageへのアクセスをブロックする必要があります。もしあなたがそれをしなければ、あなたはまたトラブルを探します。 QImage :: bitsは画像を送信する前にディープコピーを呼び出します。 – AlexanderVX

+0

はい! QImageを別のスレッドから同時に使用しようとしたため、私のプログラムがクラッシュしました。次に、この状況を防ぐために、bool変数 'readyfor 'を追加しました。今度は、QMLから、Imageが新しいフレームを取得する準備ができてから、新しいフレームを取得する準備ができたという信号を送ります。 – DYY

答えて

1

私はOpenCVのとカメラからフレームをつかむ、フレームはQMLで 正常表示が、時間とメモリ使用量が増加しています。どうすれば を修正できますか?標準カメラ速度は25の範囲内にある間

以下の前提条件

ビデオキャプチャ1000ミリ秒/フレーム当たり80ミリ秒=(処理時間に実際より少なくによる)毎秒12.5フレームよりも何より速く画像を処理しないように画像フレームがOpenCVなどの内部バッファに張り付いているのはこのためです.OSがまだタイムスライスを放棄できる限り、ここで1〜5ミリ秒待つだけで十分です他のスレッド。私は個人的に C++ 11または Qtのいずれかの条件変数を使用し、 "スリープ"しません。条件変数を使用すると、より優雅に待機を中断できます。

while(true){ 

    QThread::currentThread()->msleep(80); // too much wait 
    // also just do QThread::msleep instead 

    if(!readyfor) continue; 

    mat.release(); 
    capture >> mat 
+0

ありがとうございました!本当に80ミリ秒待っています。しかし、問題はOpenCVのバッファにないようです。私は自分のプロジェクトと関数からOpenCVを削除しました。QPixmap VideoProvider :: requestPixmap(const QString&id、QSize * size、const QSize&requestedSize) { return QPixmap :: fromImage(outputImage); } 'return return:' return QPixmap(500、500); 'QMLでは黒い矩形のように見えますが、このポインターでもメモリ使用量が増えます。これらのQPixmapはキャッシュされ、メモリ使用量が増加するようです。そして私は記憶をきれいにする方法を知らない。 – DYY

+0

また、 'QQuickImageProvider :: requestImage'を試してみることもできます。なぜイメージプロバイダを使用するのかもわかりません。私はhttp://doc.qtを使います。いくつかのアプリでio/qt-5/qml-qtmultimedia-videooutput.htmlを使用して、ソースサーフェスオブジェクトを使用します。http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#source-prop from C++コード。 – AlexanderVX

+0

ありがとうございました!私はVideoOutputも使用しようとします。私はこの方法を使うことさえ考えなかった!たぶん、それを正しく行うための小さな例がありますか? – DYY