0

私はメモリの問題に苦しんでいます。何かを見逃したと思います。誰かが私が理解している/間違っていることを指摘してくれれば、非常に感謝しています.

私がしたいこと

私のGUIはメインスレッドで実行されます。別のスレッド T で計算を開始しています。この計算の結果は、一連の opencv イメージです。計算中にGUIに表示したい。

どうすればいいのかわからない

  • 計算スレッドを起動します。
  • 新しい画像が計算されたら、それを QImage に変換し、カスタム QEvent でラップして、GUI に投稿します。
  • ヒープ メモリのみを使用します。

実装方法

私の計算スレッドでは、新しい画像の準備ができたら:

    std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
    cv::Mat cvimBGR;
    cv::Mat cvim = MyNewComputedImage;
    cvim.convertTo(cvimBGR,CV_8UC3);
    cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);
    std::shared_ptr<QImage> qim = std::shared_ptr<QImage>(
        new QImage((uint8_t*) cvimRGB->data,cvimRGB->cols,cvimRGB->rows,cvimRGB->step,QImage::Format_RGB888));
    ImageAddedEvent* iae =  new ImageAddedEvent(qim,i);
    QCoreApplication::postEvent(gui, iae);

私のイベントハンドラーで:

bool mosaicage::event(QEvent * e){
    if (e->type() == ImageAdded) {
            ImageAddedEvent* ie = dynamic_cast<ImageAddedEvent*>(e); 
            QImage qim(*(ie->newImage));
            QPixmap pm(QPixmap::fromImage(qim));
            auto p = scene.addPixmap(pm);
            images_on_display.push_back(p);
            return true;
    } else {
            return QWidget::event(e);
    }
}

私のカスタムイベントは次のように定義されています:

    class ImageAddedEvent: public QEvent {
public:
    ImageAddedEvent();
    ~ImageAddedEvent();
    ImageAddedEvent(std::shared_ptr<QImage> im, int i);
    std::shared_ptr<QImage> newImage;
    int index;
};

何が起こるのですか

デバッグモードでは、ディスプレイにがらくたが表示されます。リリース モードで、アクセス違反エラーが発生します。cv::Mat を qimage に変換する部分については、変更していないのでかなり自信があります。以前は計算スレッドから表示を更新していましたが、よりよく学びました。ただし、機能しました(クラッシュしなかった場合)。

どのように修正したか

問題は、私がそれを構築したcv::Matによって担当されたQImageが指すメモリにありました。他の誰かが管理するデータを使用して QImage を構築するこの方法を維持したい場合は、データを有効に保つ必要があります。したがって、cv::Mat をカスタム イベントに移動しました。

class ImageAddedEvent: public QEvent {
public:
    ImageAddedEvent();
    ~ImageAddedEvent();
    ImageAddedEvent(cv::Mat im, int i);
    QImage newImage;
    cv::Mat cvim;
    int index;
};

イベントのコンストラクターを変更して、QImage を cv::Mat データで初期化しました。

ImageAddedEvent::ImageAddedEvent(cv::Mat cvimRGB, int i) : QEvent(ImageAdded),
    index(i),
    cvim(cvimRGB)
{
    newImage = QImage((uint8_t*) cvim.data,cvim.cols,cvim.rows,cvim.step,QImage::Format_RGB888);
}

そして今、cv::Mat をイベント コンストラクターに渡すだけです。

cv::Mat cvimBGR,cvimRGB;
cv::Mat cvim = MyNewImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,cvimRGB,cv::COLOR_BGR2RGB);
ImageAddedEvent* iae =  new ImageAddedEvent(cvimRGB,i);
QCoreApplication::postEvent(gui, iae);

et voilà、再び、助けてくれてありがとう!

4

1 に答える 1

3

間違ったコンストラクタを使用しています

ドキュメントから(emph mine):

バッファーは、QImageと、変更されていない、または元のバッファーから切り離されていないすべてのコピーの有効期間を通じて有効である必要があります。イメージは破棄時にバッファを削除しません。最後のコピーが破棄されたときに呼び出される追加のポインター cleanupInfo とともに、関数ポインター cleanupFunction を提供できます。

cvimRGBそして、イベントが処理される前にデストラクタのバッファをクリーンアップするデータポインタに割り当てられたスタックを使用しているため、「がらくた」データにアクセスすることになります

そのため、新しいQimageを作成してからデータをコピーする必要があります

std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
cv::Mat cvimBGR;
cv::Mat cvim = MyNewComputedImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);

QImage qim =  QImage(cvimRGB->cols,cvimRGB->rows,QImage::Format_RGB888));
//copy from cvimRGB->data to qim.bits()

ImageAddedEvent* iae =  new ImageAddedEvent(qim,i);
QCoreApplication::postEvent(gui, iae);

または切り離しcvimRGB->dataて cleanupFunction にバッファを削除させます

別の注意として、必要な場合を除き、基になるデータをコピーしないstd::shared_ptr<QImage>asを使用する必要はありません。これは、Qt では暗黙的なデータ共有として知られています。QImage

GUI を呼び出すには、Q_INVOKABLE メソッド (または単にスロット) を提供しguiて使用できますQMetaObject::invokeMethod(gui, "imageUpdated", Q_ARG(QImage, qim));

于 2013-09-18T10:15:37.030 に答える