openCV內存釋放問題

轉載請註明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7586847

前一天把系統整個重寫了一遍,脈絡清晰了很多,也終於解決了以前很多崩潰,異常退出的問題。這裏小小總結一下自己遇到的麻煩。

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()顯示的釋放

  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():

  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);  

以及人臉識別中檢測人臉的函數:

  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用了兩次。可能報錯的地方:

  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*

  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);

發佈了8 篇原創文章 · 獲贊 18 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章