opencv3.0 Mat之内存管理应用实例

一、案例
最近使用一款工业相机循环取图,使用相机自带SDK,结果出现了内存泄漏现象,原因是SDK提供的接口没有对其开辟的内存进行释放,而程序里一直在调用该接口,直到后来看到接口文档里的一段注释才恍然大悟,泄漏代码如下:

void grabImg(cv::Mat &grab_img)
{
 if (pDevice == NULL)
 {
  return;
 }
 else
 {
  try
  {
    Arena::IImage* pImage = pDevice->GetImage(2000);//获取图像
    /*问题就是出在下面这个接口上,这个接口是对图像进行格式转换,
    转换后的图像是存在一块新开辟的内存上,需要程序手动释放,
    由于一直在调用该接口,导致内存不断泄漏*/
    auto pConverted = Arena::ImageFactory::Convert(pImage,PIXEL_FORMAT);//转换为RGB
    grab_img = cv::Mat((int)pConverted->GetHeight(), (int)pConverted->GetWidth(), CV_8UC3,  (uint8_t*)pConverted->GetData());
    pDevice->RequeueBuffer(pImage);
}
  catch (const std::exception&)
  {
  }
 }
}

文档注释:

  • Convert converts an image (Arena::IImage) to a select pixel
  • format. In doing so, it creates a completely new image, similar to a
  • deep copy but with a different pixel format. Images created with the
  • image factory must be destroyed (Arena::ImageFactory::Destroy) when no
  • longer needed; otherwise, memory will leak.

大意就每次都会创建一个新的图片,类似“深拷贝”,不用时一定要调用destroyed来释放!

更改后代码如下:

grabImg(cv::Mat &grab_img)
{
 if (pDevice == NULL)
 {
  return;
 }
 else
 {
  try
  {
   Arena::IImage* pImage = pDevice->GetImage(2000);//获取图像
   auto pConverted = Arena::ImageFactory::Convert(
    pImage,
    PIXEL_FORMAT);//转换为RGB
   grab_img = cv::Mat((int)pConverted->GetHeight(), (int)pConverted->GetWidth(), CV_8UC3, (uint8_t*)pConverted->GetData()).clone();//将图片复制到自己定义的内存(可由opencv自动管理)
   pDevice->RequeueBuffer(pImage);
   Arena::ImageFactory::Destroy(pConverted);//手动释放原来图片内存
 }
  catch (const std::exception&)
  {
  }
 }
}

二、总结
从上面的案例中我总结出了2个知识点
1.内存谁开辟谁释放,比如上面案例中内存是SDK里的接口开辟的,就必须要调用它提供的接口来释放,否则会泄漏
2.内存管理机制
对于上面的案例,我每次都会调用下面这行代码:
grab_img = cv::Mat((int)pConverted->GetHeight(), (int)pConverted->GetWidth(), CV_8UC3, (uint8_t*)pConverted->GetData()).clone();
起初我比较疑惑,clone()也是“深拷贝”,为何它就不会造成内存泄漏呢?答案是opencv 3.0以后的版本加入了内存管理机制,这个机制在当你不再使用某块内存时,会自动帮你释放掉它,当然前提是该内存也是由opencv帮你开辟的。
这个机制工作大概如下:
1)Mat大概可以分为2个部分,指针域和数据域,指针域指向的是实际的图片内存,而数据域存储了该图片的一些相关信息,比如长宽信息。
2)当创建一块内存时,还是看下面这行代码
grab_img = cv::Mat((int)pConverted->GetHeight(), (int)pConverted->GetWidth(), CV_8UC3, (uint8_t*)pConverted->GetData()).clone();
grab_img 的指针便指向了一块内存,这块内存由clone()函数来开辟,存储了实际的照片数据,并且伴随而生的还有一个计数器counter,counter = 1,表示有一个指针指向了该内存,当后面的程序继续有别的指针指向该内存时counter ++,反之有一个指针不再指向它时,counter --,在counter 为0时,内存被释放。在上面这个案例中,虽然每次都会调用clone开辟一块新内存,但是grab_img每次都会指向最新的内存,而旧的内存由于没有指针再指向它,便被opencv的管理单元自动回收,所以避免了内存泄漏问题。所以以后如果开辟内存,一定要搞清楚有没有自动管理机制,何时被开辟,何时应该被释放。

你可能感兴趣的:(图像处理)