OpenCV中的内存泄漏:VideoCapture

3
我使用 Qt Creator 2.4.1(Qt 4.8.4)和 OpenCV 2.4.2 开发了一个应用程序,可以从文件夹中读取图像并显示它们。该应用程序使用了 cv::VideoCapture 和 QGraphicsScene / QGraphicsView。运行良好,但我遇到了内存泄漏的问题:每次读取新图像时,任务管理器中消耗的内存会增加,最终导致应用程序崩溃。
我的主窗口是使用 Qt Designer 创建的,它是 QMainWindow 的子类。主窗口上有一个 QGraphicsView view_src,还有一个按钮 buttonStart。
以下是代码示例:类声明:
using namespace std;
using namespace cv;

namespace Ui {
    class FenetrePrinc;
}

class FenetrePrinc : public QMainWindow {
    Q_OBJECT
public:
    explicit FenetrePrinc(QWidget *parent = 0);
    ~FenetrePrinc();

public slots:
    virtual void start();
    virtual void tick();
    virtual void stop_timer();

private:
    Ui::FenetrePrinc *ui;

    QString filename;
    QGraphicsScene *scene_src;
    QGraphicsItem *img_src;

    VideoCapture sequence;

    Mat src;
};

类定义:

FenetrePrinc::FenetrePrinc(QWidget *parent) : QMainWindow(parent), ui(new Ui::FenetrePrinc){

    ui->setupUi(this);
    scene_src = new QGraphicsScene();
    timer = new QTimer(this);

    img_src = scene_src->addPixmap(QPixmap("vide.jpg"));
    ui->view_src->setScene(scene_src);

    connect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(start()));
}

FenetrePrinc::~FenetrePrinc(){
    delete scene_src;
    delete img_src;
    delete ui;
}

void FenetrePrinc::start(){
    if(src.empty())
        sequence.open(filename.toStdString());

    connect(timer, SIGNAL(timeout()), this, SLOT(tick()));
    timer->start(1000/24);   //24 frames per second

    disconnect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(start()));
    connect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(stop_timer()));
}

void FenetrePrinc::tick(){
    sequence >> src;

    if(src.empty())
    {
        sequence.release();
        stop_timer();
        return;
    }

    scene_src->removeItem(img_src);
    img_src = scene_src->addPixmap(convert16uc1(src));

    src.release();
}

void FenetrePrinc::stop_timer(){
    timer->stop();
    disconnect(timer, SIGNAL(timeout()), this, SLOT(tick()));

    disconnect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(stop_timer()));
    connect(ui->buttonStart, SIGNAL(clicked()), this, SLOT(start()));
}

我不明白为什么每次读取图像时,内存使用量会不断上升。我确实在每次读取后释放了图像,并在完成后释放了序列。但是可能我漏掉了什么?
编辑:函数QPixmap convert16uc1(Mat img)是内存泄漏的原因。我必须使用此函数,因为我正在处理16位灰度图像,而Qt无法读取。我使用OpenCV打开图像并进行图像处理,然后使用Qt显示图像。
该函数的代码如下:
QPixmap FenetrePrinc::convert16uc1(const cv::Mat& source)
{
  quint16* pSource = (quint16*) source.data;
  int pixelCounts = source.cols * source.rows;

  QImage dest(source.cols, source.rows, QImage::Format_RGB32);

  char* pDest = (char*) dest.bits();

  for (int i = 0; i < pixelCounts; i++)
  {
    quint8 value = (quint8) ((*(pSource)) >> 8);
    *(pDest++) = value;  // B
    *(pDest++) = value;  // G
    *(pDest++) = value;  // R
    *(pDest++) = 0;      // Alpha
    pSource++;
  }
  return QPixmap::fromImage(dest);
}

1
请尝试使用更新的OpenCV版本,2.4.2版本太旧了。 - berak
1
如果您在Qt中不显示图像(只读取图像而不显示),它是否也会泄漏内存? - Micka
2
你能发布一下你的 convert16uc1 函数吗?你必须释放 Qt 图像元素吗? - Micka
2
@berak:我非常频繁地使用stoll 2.4.2和Qt,处理来自相机和硬盘的大量图像。没有内存泄漏。这不是OpenCV,当然也不是Qt的问题。 - Valentin H
显示剩余5条评论
2个回答

1

很可能是 convert16uc1

如果你不能在这里发布 convert16uc1,尝试使用 imwrite 在 opencv 中临时保存图像,并在 Qt 中加载图像。如果 memleak 消失了,请分析 convert16uc1

或者不要调用 convert16uc1(src),而是使用先前在 Qt 中加载的其他常量图像调用 addPixmap


是的,你说得对。如果我将 scene_src->addPixmap(convert16uc1(srt)) 更改为 scene_src->addPixmap(QPixmap("vide.jpg")),就不会再有内存泄漏了。我已经编辑了我的问题,添加了 convert16uc1 代码。 - zarachbaal
我按照您的建议进行了操作:不再使用QPixmap convert16uc1(Mat source)将我的OpenCV图像转换成Qt,而是使用imwrite将该图像临时保存,然后在Qt中加载该图像。使用这种技术后,我不再有内存泄漏问题。 - zarachbaal
1
@zarachbaal:这只是一个测试建议!这将会是非常缓慢的转换。我会尝试你的代码,可能会找到原因。 - Valentin H
是的,我知道这不是一个真正的解决方案,我只是尝试了一下,它可以工作,但这非常慢!我认为问题出在convert16uc1中的QPixmap :: fromImage(dest);。有一个重载版本可以在不复制的情况下进行转换,但它是在Qt 5.3中实现的,而我使用的是Qt 4.8.4。 - zarachbaal
1
@zarachbaal: fromImage 并不是一个问题。QImage 是在堆栈上创建的。当函数作用域结束时,它会被删除。 - Valentin H

0
我找到了问题的原因以及如何解决它,阅读这个thread
从Qt文档中:
void QGraphicsScene::removeItem(QGraphicsItem *item) 从场景中移除项item和其所有子项。item的所有权将转移到调用者(即,QGraphicsScene在销毁时不再删除item)。 另请参见addItem()。
一旦调用了QGraphicsScene::removeItem(QGraphicsItem *item),QGraphicsScene在销毁时不再删除该项。
修复:在函数FenetrePrinc::tick()中,在调用removeItem(img_src)后调用delete img_src:
void FenetrePrinc::tick(){
    sequence >> src;

    if(src.empty())
    {
        sequence.release();
        stop_timer();
        return;
    }

    scene_src->removeItem(img_src);
    delete img_src;
    img_src = scene_src->addPixmap(convert16uc1(src));

    src.release();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接