【问题标题】:Avoiding memory leaks while using vector<Mat>使用 vector<Mat> 时避免内存泄漏
【发布时间】:2016-03-29 19:30:53
【问题描述】:

我正在尝试编写一个使用 opencv Mat 对象的代码,它是这样的

Mat img;
vector<Mat> images;
for (i = 1; i < 5; i++)
{
  img.create(h,w,type) // h,w and type are given correctly
  // input an image from somewhere to img correctly. 
  images.push_back(img);
  img.release()
}
for (i = 1; i < 5; i++)
  images[i].release();

但是我似乎仍然有内存泄漏,谁能告诉我为什么会这样? 我认为如果 mat 对象的 refcount = 0 那么内存应该被自动释放

【问题讨论】:

  • @Miki 我放入 img 的图像类型和尺寸相同,但图像不同。想象一下在每次迭代中执行类似 imread(FileName) 的操作。
  • 啊等等..你的代码看起来坏了,你为什么在循环中调用img.release()
  • 是的,在循环中。因为我也在循环中创建它。
  • 如何测量内存泄漏?
  • 我基本上为 Mathematica 创建了一个数据包。并且有一个代码可以测量运行代码之前和之后的内存使用情况。评估后差异应为 0。

标签: opencv memory-management memory-leaks destructor mat


【解决方案1】:

您很少需要显式调用release,因为OpenCV Mat 对象会自动处理内部内存。

还要注意Mat copy 只会创建一个指向相同数据的新标头。如果原始 Mat 超出范围,您将留下无效矩阵。因此,当您将图像推入矢量时,请使用深拷贝(clone())以避免图像进入矢量变得无效。

既然你提到了:

我有一个大的 3D 图像存储在一个 Mat 对象中。我正在使用 for 循环运行它。创建一个称为“图像”的 2D 垫子,将切片放入图像中,将图像推回矢量图像。释放图像。然后在图像向量上执行一个 for 循环,一个一个地释放所有矩阵。

您可以使用以下代码将所有切片存储到向量中。要释放向量中的图像,只需clear 向量。

#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;

int main() 
{
    // Init the multidimensional image
    int sizes[] = { 10, 7, 5 };
    Mat data(3, sizes, CV_32F);
    randu(data, Scalar(0, 0, 0), Scalar(1,1,1));

    // Put slices into images
    vector<Mat> images;
    for (int z = 0; z < data.size[2]; ++z)
    {
        // Create the slice
        Range ranges[] = { Range::all(), Range::all(), Range(z, z + 1) };
        Mat slice(data(ranges).clone()); // with clone slice is continuous, but still 3d
        Mat slice2d(2, &data.size[0], data.type(), slice.data); // make the slice a 2d image

        // Clone the slice into the vector, or it becomes invalid when slice goes of of scope.
        images.push_back(slice2d.clone());
    }

    // You can deallocate the multidimensional matrix now, if needed
    data.release();

    // Work with slices....

    // Release the vector of slices
    images.clear();

    return 0;
}

【讨论】:

  • 使用深拷贝的时间成本很高,我知道它会创建一个指向相同数据的新标头。因此,要真正释放所有数据,我正在考虑显式释放所有标题。此处的文档说,如果矩阵头指向外部数据集(请参阅 Mat::Mat() ),则引用计数器为 NULL,并且该方法在这种情况下无效。我不确定这意味着什么。 docs.opencv.org/2.4/modules/core/doc/…
  • 如果你在外部分配数据并且只是在它上面创建一个矩阵头,释放矩阵将保持原始数据不变。您还需要显式释放原始数据。
  • 如果您准确地展示您想要做什么以及您的数据来自哪里,那么正确回答会容易得多。
  • 不幸的是,我无法发布实际代码,但从概念上讲,我正在做的事情如下:我有一个大型 3D 图像存储在一个 Mat 对象中。我正在使用 for 循环运行它。创建一个称为“图像”的 2D 垫子,将切片放入图像中,将图像推回矢量图像。释放图像。然后在图像向量上执行一个 for 循环,一个一个地释放所有矩阵。
  • 感谢您的帮助。事实证明,我一直在很好地管理矩阵及其标题,但由于一些强制转换,我遇到了内存泄漏。如果 img 本身是无符号 8 位矩阵,则使用 ((raw_t_real32*)img.data)[i] 转换访问 mat 对象的数据是一种非常糟糕的做法。从现在开始,我将使用 cv::Mat::at。
【解决方案2】:

请试试这段代码,这基本上就是你所做的:

void testFunction()
{
    // image width/height => 80MB images
    int size = 5000;

    cv::Mat img = cv::Mat(size, size, CV_8UC3);

    std::vector<cv::Mat> images;
    for (int i = 0; i < 5; i++)
    {
      // since image size is the same for i==0 as the initial image, no new data will be allocated in the first iteration.
      img.create(size+i,size+i,img.type()); // h,w and type are given correctly

      // input an image from somewhere to img correctly. 
      images.push_back(img);
      // release the created image.
      img.release();
    }

    // instead of manual releasing, a images.clear() would have been enough here.
    for(int i = 0; i < images.size(); i++)
      images[i].release();

    images.clear();
}

int main()
{
    cv::namedWindow("bla");
    cv::waitKey(0);

    for(unsigned int i=0; i<100; ++i)
    {
        testFunction();
        std::cout << "another iteration finished" << std::endl;
        cv::waitKey(0);
    }

    std::cout << "end of main" << std::endl;
    cv::waitKey(0);
    return 0;
}

在第一次调用 testFunction 后,内存将“泄漏”,因此应用程序在我的设备上多消耗 4 KB 内存。但是在额外呼叫我之后没有更多的“泄漏”......

所以这看起来你的代码没问题,“内存泄漏”与该矩阵的创建和释放无关,但可能与 openCV 库或 C++ 中发生的一些“全局”事情有关,以优化未来的函数调用或内存分配。

【讨论】:

  • 感谢您的帮助。事实证明,我一直在很好地管理矩阵及其标题,但由于一些强制转换,我遇到了内存泄漏。如果 img 本身是无符号 8 位矩阵,则使用 ((raw_t_real32*)img.data)[i] 转换访问 mat 对象的数据是一种非常糟糕的做法。从现在开始,我将使用 cv::Mat::at。
【解决方案3】:

我在迭代 openCV mat 时遇到了同样的问题。内存消耗可以是1.1G,然后警告没有内存就停止了。在我的程序中,有宏#define new new(FILE, LINE),与一些标准库崩溃。所以我删除了所有关于新/删除的重载运算符。调试时,它没有错误。但是当它运行时,我得到了“Debug Assertion Failed! Expression: _pFirstBlock == pHead”。按照指示 Debug Assertion Error in OpenCV 我将设置从 MT (Release)/MTd (Debug) 更改为

Project Properties &gt;&gt; Configuration Properties &gt;&gt; C/C++ &gt;&gt; 代码生成并将运行时库更改为:

多线程调试 DLL (/MDd),如果您正在构建代码的调试版本。 多线程 DLL(/MD),如果您正在构建代码的发布版本。

内存泄漏消失了。内存消耗为38M

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-22
    • 2018-04-08
    • 2013-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多