【问题标题】:How to force an opencv image (cv::Mat) to release its memory如何强制opencv图像(cv :: Mat)释放其内存
【发布时间】:2016-03-31 08:54:58
【问题描述】:

我有这样的课程:

class MyClass
{
   cv::Mat image;
public:
   init()
   {
        image=imread("test.jpg");
   }
   cv::Mat getImage()
   {
        return image;
    }
   void reset()
   {
      // what should I write here?
    }
}
cv::Mat myImage;

void main()
{
    MyClass myclass;
    myclass.init();
    myImage=myclass.getImage();
    // do something with image;
    myclass.reset();
    // at this point the memory allocated by myclass.image should be released and also myImage should be invalidated (be an empty image).
}

注意:我知道我可以将 myImage 设为局部变量并解决问题,但我想知道如何释放由 cv::Mat 分配的内存,即使引用计数不为零。

image.release() 不会释放内存,因为存在图像副本 (myImage),我需要确保即使存在图像副本也释放内存。

对于那些可能抱怨使 myImage 无效的人,类规范说,当调用 reset 时,由类创建的所有图像都变得无效,如果用户需要图像,他们需要在类上调用 reset 之前克隆它。

编辑1

deallocate 不起作用,它不会释放内存。

【问题讨论】:

  • 您必须保留对仍在使用图像的所有位置的引用并重置这些位置。你为什么打扰?你为什么要强行使某些东西无效?
  • @UlrichEckhardt 释放内存!我需要内存,我不能强迫我的班级用户释放内存。但是如果我释放内存,它会使图像的副本失效。
  • 您是否考虑过类似于shared_ptr 和weak_ptr 的方法?
  • 你的类规范是什么,你为什么不只存储一个原始指针,例如,cv::Mat * MyClass::getImage()。尽其所能。

标签: c++ opencv memory-management


【解决方案1】:

这段代码呢?

  1. 分配内存来保存您的图像
  2. 使用该预分配内存创建 Mat 标头
  3. 将加载的图像复制到该内存中

这应该会停用引用计数。

我没有尝试过,但我想它应该可以工作(我希望这个想法很清楚)。但是,如果还有其他 Mat 引用仍在使用,您显然会遇到访问错误!

class MyClass
{
   cv::Mat image;
   unsigned char * imageData; // TODO: set to zero in constructor!
public:
   init()
   {
        cv::Mat tmp = imread("test.jpg");
        // TODO: test if non-zero and release before allocating new memory
        imageData = (unsigned char*) malloc(tmp.cols*tmp.rows*3*sizeof(unsigned char)); // allocate memory for your image
        // TODO: maybe it is possible to get the size of "tmp"'s data directly, which would be much better because of widthStep things, etc. The formula "tmp.cols*tmp.rows*3*sizeof(unsigned char)" might not be correct for all kind of input images!

        // create Mat header that uses present memory
        image=cv::Mat(tmp.rows, tmp.cols, 3, imageData  );

        // copy loaded image to allocated memory:
        tmp.copyTo(image); // here you must be sure that "image" is big enough to hold "tmp"'s data. Otherwise a new Mat will be created.

        // tmp will be cleared automatically
   }
   cv::Mat getImage()
   {
        return image;
   }
   void reset()
   {
      image = cv::Mat();
      free(imageData);
   }
}

【讨论】:

  • 谢谢,这样就可以了,但是我有几张图片,以这种方式管理所有图片非常麻烦。有没有更好的方法来做到这一点?有什么办法可以强制 cv::Mat 释放内存?
  • 据我所知不是这样,因为那会破坏引用计数的意图(但我没有测试它)。只需将路径传递给初始化函数,或将 cv::Mat 传递给初始化函数,而不是在初始化函数中硬编码路径。还是我不明白这个问题?也许您可以尝试使用 cv::Mat 指针和/或多次调用 ::release 函数等等......并且您始终可以修改 openCV 源代码以直接在 Mat 对象中添加一些“killswitch”。
  • 如果我返回对图像的引用呢?引用图像的副本并添加引用计数器吗?
  • 你应该试试。但请注意,用户可以通过将该 Mat 引用传递给不同的变量或函数来自己增加引用计数器。如果您返回一个指针并且用户取消引用该指针,则相同。
  • 你可以做的是在你的类中创建一个 Mat,但返回一个新的 Mat 参考 Mat 数据。这与我的代码基本相同,但没有 malloc 和 free。在 getImage 中,您只需返回 cv::Mat(image.rows, image.cols, image.channels(), image.type(), image.widthstep(), image.data())
【解决方案2】:

也许真正的问题是这种设计是否有意义。类的用户在调用reset 时保留先前收到的Mat 对象的副本的唯一原因是他们是否仍想处理它。没有其他理由保留该对象。因此,在调用reset 时使该副本无效必然会导致崩溃。因此,这种行为毫无意义。此外,它引入了一个用户必须知道的隐式约束,并且不通过类的接口进行通信。这不是好的设计。

相反,内存使用的责任在类用户一方。他们知道,如果在保留所有图像的同时调用getImage 和reset 一百次,他们会消耗大量内存。这是他们的决定,而不是你班级的决定。

相反,您应该确保在您的类中创建新的Mat 对象时处理内存不足的情况,以便在无法创建新图像的情况下正确处理和通信。毕竟你永远不知道你的图像是否有足够的内存,即使你每次都释放旧图像。

【讨论】:

    【解决方案3】:

    getImage 必须返回一个参考 Mat& 和 image.release() 在 reset 函数中将完成这项工作。

    【讨论】:

      猜你喜欢
      • 2023-03-18
      • 1970-01-01
      • 2014-01-07
      • 2016-05-08
      • 2017-01-12
      • 1970-01-01
      • 2013-06-26
      • 2015-10-25
      • 2018-03-24
      相关资源
      最近更新 更多