2017-05-07 41 views
2

要轉換到MatBitMap,我用下面的代碼從here內存泄漏而轉換opencv的墊到.NET BitMap的

System::Drawing::Bitmap^ MatToBitmap(const cv::Mat& img) 
{ 
    PixelFormat fmt(PixelFormat::Format24bppRgb); 
    Bitmap ^bmpimg = gcnew Bitmap(img.cols, img.rows, fmt); //unfortunately making this variable global didn't help 
    BitmapData ^data = bmpimg->LockBits(System::Drawing::Rectangle(0, 0, img.cols, img.rows), ImageLockMode::WriteOnly, fmt); 
    byte *dstData = reinterpret_cast<byte*>(data->Scan0.ToPointer()); 
    unsigned char *srcData = img.data; 
    for (int row = 0; row < data->Height; ++row) 
     memcpy(reinterpret_cast<void*>(&dstData[row*data->Stride]), reinterpret_cast<void*>(&srcData[row*img.step]), img.cols*img.channels()); 
    bmpimg->UnlockBits(data); 
    return bmpimg; 
} 

首先,我抓住從網絡攝像頭(opencv的)圖像然後傳遞到Mat上述方法,則顯示winform中的BitMap(C++/Cli)。
我爲視頻中的每一幀調用上面的方法。當發生這種情況時,我注意到內存消耗以指數形式增加(在Visual Studio的診斷工具中)
在幾秒鐘內我得到OutOfMemoryException(當內存使用量超過2 GB時,只有250mb就足夠了)

如何釋放上面的所有資源方法完成後執行
任何人都可以指出問題嗎?
感謝

更新:我不得不出售/刪除Bitmap,一旦我釋放Bitmap,內存使用量保持不變(約177MB),但圖像也不會被顯示出來。所有的方法都是從用戶定義的線程調用的,所以我不得不使用委託,然後調用UI組件(PictureBox來顯示圖片)。下面是完整的代碼

private: delegate Void SetPicDelegate(System::Drawing::Image ^pic); 
     SetPicDelegate^ spd; 
     System::Threading::Thread ^user_Thread; 

private: Void Main() 
{ 
    user_Thread= gcnew System::Threading::Thread(gcnew ThreadStart(this, &MainClass::run)); 
    user_Thread->Start(); 
} 

private: void run() 
{ 
    while(true) 
    { 
    cv::Mat img; //opencv variable to hold image 
    ///code to grab image from webcam using opencv 
    Bitmap ^bmpimg; 
    spd = gcnew SetPicDelegate(this, &MainClass::DisplayPic);  
    bmpimg = MatToBitmap(img); 
    this->pictureBox1->Invoke(spd, bmpimg); 
    delete bmpimg; 
    //above line helps control of memory usage, but image will not be displayed 
    //perhaps it will be displayed and immediately removed! 
    } 
} 

private: Void DisplayPic(System::Drawing::Image ^pic) 
{ 
    try { this->pictureBox1->Image = pic; }catch (...) {} 
} 

run方法需要時,直到下一個到達保留當前位圖一些修改?

+0

我懷疑它是在這個函數的調用者中,當你完成從這個函數返回的'Bitmap'之後,它是否被處理? – kennyzx

+0

@kennyzx,謝謝你的回覆。你是對的!我不得不發佈'Bitmap',我以爲'.net'會自動處理它。我已經更新了這個問題,你能回答嗎? –

+0

is this line'this-> pictureBox1-> Invoke(d,bmpimg);'本意是this-> pictureBox1-> Invoke(spd,bmpimg);'? – kennyzx

回答

1

Bitmap需要顯式地配置,因爲它不僅使用管理資源,也非託管資源。在這裏我引用a great answer,這很好的解釋了爲什麼GC不會幫助你,如果你不打電話給Dispose()的方法。

Bitmap類不可避免地要停止忽略IDisposable的存在。這是一個圍繞GDI +對象的小包裝類。 GDI +是非託管代碼。位圖佔用非託管內存。當位圖很大時,它很多。

.NET垃圾收集器確保使用終結器線程釋放非託管系統資源。問題是,當你創建足夠數量的託管對象來觸發垃圾收集時,它只會觸發行動。這對Bitmap類來說不太好,你可以在垃圾收集堆的第0代填滿之前創建數千個。你可以在你到達那裏之前用完非託管內存。

管理您使用的位圖的生命週期是必需的。當你不再使用它時調用Dispose()方法。

在這種特殊情況下,在PictureBox中仍然使用圖像時不能放置圖像。您可以使用technique將舊圖像與新圖像「交換」,然後丟棄舊圖像。這是可以的,因爲舊圖像不再被PictureBox使用。