【OpenCV】有关内存释放的一些问题

原文出处:http://blog.sina.com.cn/s/blog_67a7426a0101czyr.html

 

本文是对原文的转载、整理和总结补充(结尾)

 

1、内存泄露

 

内存泄露是说没有释放已经不能使用的内存,这里一般指堆的内存才需要显示的释放。比如用malloc,calloc,realloc,new分配的内存是在堆上的,需要用free,delete显示的回收。内存泄露最明显的一是程序很慢,在运行程序时你可以启动任务管理器,会看到程序占用的内存一直“砰砰砰”的往上涨:

最后直接崩溃,或者你关闭程序的时候也会异常退出,出现

Debug Assertion Failed!
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

之类的问题。

除了new的对象我们知道要delete。OpenCV中使用cvCreateImage()新建一个IplImage*,以及使用cvCreateMat()新建一个CvMat*,都需要cvReleaseImage()  cvReleaseMat()显示的释放

[cpp] view plaincopy

  1. IplImage* subImg=cvCreateImage( cvSize((img->width)*scale,(img->height)*scale), 8, 3 );  
  2. CvMat *tempMat=cvCreateMat((img->width)*scale,(maxFace->height)*scale,CV_MAKETYPE(image->depth,image->nChannels));  
  3. cvReleaseImage(&subImg);  
  4. cvReleaseMat(&tempMat);  

另外一些函数要用到 CvSeq*来存放结果(通常这些都要用cvCreateMemStorage()事先分配一块内存CvMemStorage*),都要是释放掉相应的内存,这是很难找的。

比如从二值图像中寻找轮廓的函数cvFindContours():

[cpp] view plaincopy

  1. CvMemStorage* m_storage=cvCreateMemStorage(0);  
  2. CvSeq * m_contour=0;  
  3. cvFindContours( img, m_storage, &m_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));  
  4. //释放内存  
  5. cvReleaseMemStorage(&m_storage);  

以及人脸识别中检测人脸的函数:

[cpp] view plaincopy

  1. CvMemStorage* m_storage=cvCreateMemStorage(0);  
  2. CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );  
  3. CvSeq* faces = cvHaarDetectObjects( img, cascade, m_storage,1.1, 2, 0,cvSize(30, 30) );  
  4. //释放内存  
  5. cvReleaseMemStorage( &faces->storage);  
  6. cvReleaseHaarClassifierCascade( &cascade );  

注意这里我们可以使用
cvReleaseMemStorage( &faces->storage);
来释放m_storate,也可以使用:
cvReleaseMemStorage(&m_storage);
释放内存,这是等效的,但一定不要用两次!!

 

2、一块内存多次释放

对应没有释放内存,对应就是一个内存释放多次,如同上面的 cvReleaseMemStorage用了两次。可能报错的地方:

[cpp] view plaincopy

  1. __declspec(noinline)  
  2. void __cdecl _CRT_DEBUGGER_HOOK(int _Reserved)  
  3. {  
  4.       
  5.     (_Reserved);  
  6.     _debugger_hook_dummy = 0;  
  7. }  


或者: Unhandled exception at XXXXXXXXXX in XXX.exe: XXXXXXXXXXX: 堆已损坏。 

 


除了上述的MemStorge问题,使用cvQueryFrame()取出CvCapture*每帧图像,只需在最后释放CvCapture*,不需要释放IplImage*

[cpp] view plaincopy

  1. CvCapture* pCapture = cvCreateCameraCapture(-1);  
  2. IplImage* pFrame=cvQueryFrame( pCapture );  
  3. cvReleaseCapture(&pCapture);  

*这篇是以前写的,其实还是建议大家用C++接口的OpenCV,内存问题很少了~

=================================================================================================================

OpenCV中的内存泄露问题(cvLoadImage,cvCloneImage)

在做项目的过程中,使用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函数时候,不同函数实现同一个功能,但是一定要注意他们之间的区别,不然会让你很痛苦,寻找这种错误真的很烦人。

http://benson.is-programmer.com/posts/21042.html

  戏剧性阶段一:问题的出现 

最近在使用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(),来对图像赋值。这样最后释放的是图像指针所指的地址区域。这两种方法都不会出现内存泄露的问题了。

 

 1. cvCloneImage()

        ......
        IplImag *img = cvCreateImage( cvSize(frame->width,frame->hight), frame->depth, frame->nChannels );
        img = cvCloneImage(frame);

    这里出现内存泄露,因为调用cvCloneImage()之前已经用cvCreateImage()为图像分配了内存空间,而cvCloneImage()函数是对源图像的所有数据的拷贝,包括图像头、数据、ROI等,这就导致原来分配的内存空间变成了内存碎片,造成内存泄露。.解决方法如下:

        IplImag *img = cvCloneImage(frame);

    或 

        IplImag *img = cvCreateImage( cvSize(frame->width,frame->hight), frame->depth, frame->nChannels );

        cvReleaseImage(&img);
        img = cvCloneImage(frame);

    或

        IplImag *img = cvCreateImage( cvSize(frame->width,frame->hight), frame->depth, frame->nChannels );
        img = cvCloneImage(frame);

        cvCopy(frame,img,NULL);

 

    2. cvGetCols()、cvGetRows()

    ......

    CvMat *srcMat = cvCreateMat(width, height, CV_8UC3);  // 创建一个三通道无符号整数类型的矩阵
    cvGetCols(frame,srcMat,0,width);

    这里出现内存泄露,因为cvGetCols()、cvGetRows() 是为目标矩阵分配一块新的数据内存区域,如果目标矩阵的数据区域之前已经分配了内存,则会原始数据内存区域将变成内存碎片,造成内存泄露。解决方法如下:

        CvMat *srcMat = cvCreateMat(width, height, CV_8UC3);  // 创建一个三通道无符号整数类型的矩阵

        cvReleaseData(srcMat);    // 先释放目标矩阵的数据区

        cvGetCols(frame,srcMat,0,width);

    或者

         cvCreateMatHeader(width, height, CV_8UC3);

         cvGetCols(frame,srcMat,0,width);

 

    3. IplImag*、CvMat*、CvHistogram* 等结构体指针在使用后要释放。

         IplImage *frame= cvCreateImage(cvSize(width,height),8,1);

         CvMat *srcMat = cvCreateMat(width, height, CV_8U);

         ......

         CvHistogram *hist = cvCreateHist(1,&histSize,CV_HIST_ARRAY,ranges,1);

         ......

         cvReleaseMat(&srcMat);
         cvReleaseImage(&frame);  
         cvReleaseHist(&hist);

 

 

 

总结:

1.new的对象要delete。

2.Mat类型是自动内存管理,不需要显式释放(当然也可以手动调用release()方法强制Mat矩阵数据释放);

OpenCV中使用cvCreateImage()新建一个IplImage*,使用cvCreateMat()新建一个CvMat*,都需要cvReleaseImage(&cvmat) cvReleaseMat(&iplimage)显示的释放;

 

3.一些函数要用到 CvSeq*来存放结果(通常这些都要用cvCreateMemStorage()事先分配一块内存CvMemStorage*),都要是释放掉相应的内存                                           cvReleaseMemStorage(&m_storage); 
4.使用cvQueryFrame()取出CvCapture*每帧图像,只需在最后释放CvCapture*,不需要释放IplImage*。                                                                            cvReleaseCapture(&pCapture); 
5.cvLoadImage函数,使用CvvImage类代替。并且使用CvvImage类的Load函数。
6.cvCloneImage函数,使用cvCopy函数代替。       cvReleaseImage(&img);
7.cvCloneMat、cvCopyMat
8.与CvMat *用的cvGetCols()、cvGetRows()。 CvMat *srcMat = cvCreateMat(width, height, CV_8UC3);                                                                     cvReleaseData(srcMat); 
9. IplImag*、CvMat*、CvHistogram* 等结构体指针在使用后要释放。

 

                       cvReleaseImage(&);   cvReleaseMat(&);   cvReleaseHist(&);

 

    

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值