2

To convert Mat to BitMap, i used below code from here

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

First i grab image from webcam(Opencv) then pass Mat to above method, then display the BitMap in winform(C++/Cli).
i call above method for every frame in a video. while this happens, i noticed that memory consumption increases exponentially (in Visual Studio's Diagnostic tool)
in few seconds i get OutOfMemoryException (when memory usage crosses 2 GB, where as only 250mb is sufficient)

how to release all resources in above method after it finishes execution
can anybody point the issue?
Thanks

Update: i had to dispose/delete Bitmap, once i release Bitmap, memory usage remains constant(around 177mb) but image will not be displayed. all methods are called from user-defined thread, so i had to to use delegate then invoke UI component (PictureBox to display pic). below is the full code

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 (...) {}
}

Some modification needed in run method, to retain current Bitmap until next one arrives?

Prakash M
  • 659
  • 1
  • 12
  • 36
  • I doubt it is in the caller of this function, after you have done with the `Bitmap` returned from this function, is it disposed of? – kennyzx May 07 '17 at 07:23
  • @kennyzx, Thanks for your reply. you were right! i had to release `Bitmap`, i thought `.net` would handle it automatically. i have updated the question, can you answer? – Prakash M May 07 '17 at 12:23
  • is this line `this->pictureBox1->Invoke(d, bmpimg);` meant to be `this->pictureBox1->Invoke(spd, bmpimg);` ? – kennyzx May 07 '17 at 14:47
  • That's right! edited now.. any workaround for this issue? – Prakash M May 07 '17 at 15:42
  • Yes, [your have a good idea](http://stackoverflow.com/a/39440021/815938). – kennyzx May 08 '17 at 02:31
  • That worked! Thanks. can you post in answer? (little explanation is appreciated - as why .net didn't replace new image with old one, instead allocated new memory block for each image) – Prakash M May 08 '17 at 04:06

1 Answers1

1

Bitmap needs to be disposed explicitly, since it uses not only managed resources, but also unmanaged resources. and here I quote from a great answer, it explains well why GC won't help you if you don't call Dispose() method.

The Bitmap class is inevitably the one where you have to stop ignoring that IDisposable exists. It is a small wrapper class around a GDI+ object. GDI+ is unmanaged code. The bitmap occupies unmanaged memory. A lot of it when the bitmap is large.

The .NET garbage collector ensures that unmanaged system resources are released with the finalizer thread. Problem is, it only kicks into action when you create sufficient amounts of managed objects to trigger a garbage collection. That won't work well for the Bitmap class, you can create many thousands of them before generation #0 of the garbage collected heap fills up. You will run out of unmanaged memory before you can get there.

Managing the lifetime of the bitmaps you use is required. Call the Dispose() method when you no longer have a use for it.

In this special case, the image can't be disposed when is still being used in a PictureBox. You can use a technique to "swap" the old image with the new one, and then dispose the old image. It is OK since the old image is no longer used by the PictureBox.

Community
  • 1
  • 1
kennyzx
  • 12,845
  • 6
  • 39
  • 83