opencv 內存泄露

OpenCV中的內存泄露問題(cvLoadImage,cvCloneImage) 

轉自:http://apps.hi.baidu.com/share/detail/30893646

 

在做項目的過程中,使用OpenCV經常會出現一些內存泄露問題,自己編寫的程序出現問題還情有可原,但若是庫函數調用和使用時出現,卻很令我惱火。花了好長時間和實踐的經驗告訴我應該客服它。下面把一些檢測出的問題進行化解。(可能是水平不夠,這些函數使用不當,望高手指點)

cvLoadImage函數:

可能大家還覺察不出來,但我深有體會,在程序中這個函數使用一次兩次感覺不來,但在處理序列圖像循環調用這個函數時,內存泄露的可能讓你目瞪口呆!i_f23.gif即使你在最後使用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(),來對圖像賦值。這樣最後釋放的是圖像指針所指的地址區域。這兩種方法都不會出現內存泄露的問題了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章