aihot  2017-05-29 19:26:37  OpenCV |   查看评论   

 在做项目的过程中,使用OpenCV经常会出现一些内存泄露问题,自己编写的程序出现问题还情有可原,但若是库函数调用和使用时出现,却很令我恼火。花了好长时间和实践的经验告诉我应该客服它。下面把一些检测出的问题进行化解。(可能是水平不够,这些函数使用不当,望高手指点)

 
cvLoadImage函数:
 
可能大家还觉察不出来,但我深有体会,在程序中这个函数使用一次两次感觉不来,但在处理序列图像循环调用这个函数时,内存泄露的可能让你目瞪口呆!即使你在最后使用cvReleaseImage(&pImg);进行了释放,实验证明:视乎不能成功释放。
 
解决方法:
 
使用CvvImage类代替。并且使用CvvImage类的Load函数。
 
使用过程大概如下:
  • //变量定义:
  •  
  • CvvImage pSrcImg;
  •  
  • IplImag *pSrcImgCopy ;//使用IplImag变量做个拷贝。毕竟IplImag 类处理方便。
  •  
  • //获取图像:
  •  
  • pSrcImg.Load(str);//str为Cstring类型的图像文件名
  • pSrcImgCopy = pSrcImg.GetImage();//拷贝出pSrcImg的图像数据。
  •  
  • //释放内存
 
pSrcImg变量不需要每次释放,因为每次Load时是覆盖以前的内存区域。pSrcImgCopy 同样。
 
不过在程序结束时要释放,以免产生内存泄露或者别人以为你忘了。
  • cvReleaseImage(&pSrcImgCopy );
  • pSrcImg.Destroy();
 
不过要正确释放pSrcImgCopy 时,声明时必须create下:
  • pSrcImgCopy = cvCreateImage(cvSize(IMGWIDHT,IMGHEIGHT),IPL_DEPTH_8U, 3);
  •  
  • //IMGWIDHT,IMGHEIGHT为图像宽和高。
 
cvCloneImage函数:
 
这个函数也会出现内存泄露!虽然可以释放,但程序复杂不知道在那里释放,因为它每次拷贝是制作图像的完整拷贝包括头、ROI和数据。不会覆盖以前的内容。每次使用时编译器会分配内存空间。一个752*480大小的图像,每次泄露的内存大约为1M。
 
解决方法:
 
使用cvCopy函数代替。
 
cvCopy(pSrcImg,pImg,NULL);//代替 pImg = cvCloneImage(pSrcImg);
 
pImg初始化时必须分配空间,否则上述函数不能执行。
 
pImg = cvCreateImage(cvSize(IMGWIDHT,IMGHEIGHT),IPL_DEPTH_8U, 3);
 
 
 
 
最近一直在用opencv编写算法程序,但是cvCloneImage、cvCopyImage和cvCloneMat、cvCopyMat这几个函数让我痛苦了好一阵子,程序代码没有任何问题,但是就是得不到结果,在子函数中返回值根本不是我想要的,由于代码挺庞大的,一直没找到问题出在哪里,于是设置一个个断点,通过步步调试,终于发现问题出在了cvCloneImage、cvCopyImage和cvCloneMat、cvCopyMat这几个函数的误用,cvCloneImage与cvCloneMat是在赋值的同时会开辟一个新的空间给定义的变量,cvCopyImage与cvCopyMat只复制值,并不会分配一个空间给赋值对象,因此cvCloneImage与cvCloneMat只适合用于变量开始定义,千万不要用在算法处理中间,否则会产生一个新的地址空间,会将赋值对象的指针地址改变,这样会导致整个程序有不可预测的错误发生,最明显的就是你本来想把子函数中的新变量值送回上一层函数,但是由于指针的指向已经改变,所以返回后的值并不会改变。在程序中间进行复制时候建议使用cvCopyImage与cvCopyMat。
 
因此当使用opencv函数时候,不同函数实现同一个功能,但是一定要注意他们之间的区别,不然会让你很痛苦,寻找这种错误真的很烦人。
 
  戏剧性阶段一:问题的出现 
 
最近在使用opencv的时候,发现在图像函数部分,opencv的内存管理存在一定问题。在使用IplImage的图像cvcloneImage()后,调用cvReleaseImage()时,内存并不能全部释放。在实时视频处理程序中,伴随程序运行,很容易造成系统内存消耗殆尽。
 
举例来说,看下面的一个最简单代码:
  • #include"cv.h"
  • #include"highgui.h"
  • #pragma comment(lib,"cv.lib")
  • #pragma comment(lib,"highgui.lib")
  • #pragma comment(lib,"cxcore.lib")
  •  
  • int _tmain(int argc, _TCHAR* argv[])
  • {
  •  CvCapture* capture = cvCreateCameraCapture(0);
  •  IplImage* frame;
  •  cvNamedWindow("ExampleShow",CV_WINDOW_AUTOSIZE);
  •  while(1)
  •  {
  •   frame = cvQueryFrame(capture);
  •   if(!frame)
  •    break;
  •   cvShowImage("ExampleShow",frame);
  •   char c = cvWaitKey(33);
  •   if(c == 27)
  •     break;
  •  }
  •  cvReleaseCapture(&capture);
  •  cvDestroyWindow("ExampleShow");
  •  return 0;
  • }
 
运行程序,此时,打开资源管理器,可以看到其所占的“内存使用”一直保持稳定。而如果,简单修改下上面的程序,改变如下:
  • #include"cv.h"
  • #include"highgui.h"
  • #pragma comment(lib,"cv.lib")
  • #pragma comment(lib,"highgui.lib")
  • #pragma comment(lib,"cxcore.lib")
  •  
  • int _tmain(int argc, _TCHAR* argv[])
  • {
  • CvCapture* capture = cvCreateCameraCapture(0);
  • IplImage* frame;
  • IplImage* clImage;
  • cvNamedWindow("ExampleShow",CV_WINDOW_AUTOSIZE);
  • cvNamedWindow("Example_Clone",CV_WINDOW_AUTOSIZE);
  • while(1)
  • {
  • frame = cvQueryFrame(capture);
  • if(!frame)
  • break;
  • cvShowImage("ExampleShow",frame);
  • clImage = cvCreateImage(cvSize(frame->width,frame->height),frame->depth,frame->nChannels);
  • clImage = cvCloneImage(frame);
  • cvShowImage("Example_Clone",clImage);
  • char c = cvWaitKey(33);
  • if(c == 27)
  • break;
  • cvReleaseImage(&clImage);
  • }
  • cvReleaseCapture(&capture);
  • cvDestroyWindow("ExampleShow");
  • cvDestroyWindow("Example_Clone");
  • return 0;
  • }
 
同样,运行程序,打开资源管理器,可以看到“内存使用”中,该程序的内存使用量在不断增加。虽然,程序中对拷贝的图像进行了释放,但是,事实上,却没有看到多少效果!
 
虽然发现了这个问题,也在网络上看到相关的这个问题的讨论,试验了几种方法,发现并不work。在此提出问题,继续探索吧。
 
戏剧性阶段二:“改良方法”的出现
 
在网络上看到,除了cvCloneImage()还有cvLoadImage()也有内存泄露问题。最终的有效解决办法是使用cvCopy()来替换代码中的cvCloneImage(),这时候,不会出现内存不断递增的情况。而cvLoadImage()可以CvvImage类的图像装载函数,然后拷贝到目标图像即可。
 
戏剧性三:真正原因的捕获和分析 
 
内存无法释放的原因分析:
 
今天偶然想起,在观察上面的代码时,发现在其中存在一句:clImage = cvCreateImage(cvSize(frame->width,frame->height),frame->depth,frame->nChannels);
 
这句是向内存申请一片空间,用于存放目的图像的空间。造成内存泄露的真正原因是这句。在使用cvCloneImage()的时候,其实是对源图像指针所指向的图像头、数据、ROI等进行了一个完全的拷贝,放在一个新的内存区域,函数结果使得目标图像指向新的内存,而原来用cvCreateImage()所分配的区域没有被正确释放,成为一片“悬挂地址区域”。在后面调用cvReleaseImage()的时候,释放的是后面其所指向的区域。
 
因此,要避免这种情况的出现,一种方法是:可以在cvCloneImage()前,先调用cvReleaseImage()来释放之前分配的地址区域。然后执行克隆函数cvCloneImage()操作。也可以在前面不分配空间,直接调用克隆操作。另外一种方法,如果使用cvCopy()函数操作,由于该函数并不会对图像指针分配空间,所以需要先自己用cvCreateImage()分配一段区域,然后调用拷贝函数cvCopy(),来对图像赋值。这样最后释放的是图像指针所指的地址区域。这两种方法都不会出现内存泄露的问题了。
 

除特别注明外,本站所有文章均为 赢咖4注册 原创,转载请注明出处来自OpenCV中的内存泄露问题(cvLoadImage,cvCloneImage)

留言与评论(共有 0 条评论)
   
验证码:
[lianlun]1[/lianlun]