将Opencv Mat转换为.net BitMap时内存泄漏

时间:2017-05-07 03:19:34

标签: .net winforms visual-c++ memory-management memory-leaks

要将Mat转换为BitMap,我使用了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;
}

首先我从网络摄像头(Opencv)抓取图像,然后将Mat传递给上面的方法,然后在winform(C ++ / Cli)中显示BitMap
我为视频中的每一帧调用上述方法。当发生这种情况时,我注意到内存消耗呈指数级增长(在Visual Studio的诊断工具中),在几秒钟内我得到OutOfMemoryException(当内存使用量超过2 GB时,只有250mb就足够了) )

如何在完成执行后释放上述方法中的所有资源
任何人都可以指出这个问题吗?
感谢

更新:我必须处置/删除Bitmap,一旦我发布Bitmap,内存使用率保持不变(约为177mb),但图片将不会显示。所有方法都是从用户定义的线程调用的,所以我不得不使用委托然后调用UI组件(PictureBox来显示pic)。以下是完整的代码

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方法需要进行一些修改,以保留当前的Bitmap直到下一个到达?

1 个答案:

答案 0 :(得分:1)

Bitmap需要明确处理,因为它不仅使用托管资源,还使用 非托管 资源。在这里我引用a great answer,它解释了为什么如果你不打电话给Dispose()方法,GC不会帮助你。

  

Bitmap类不可避免地是您必须停止忽略IDisposable存在的类。它是围绕GDI +对象的小包装类。 GDI +是非托管代码。位图占用非托管内存。当位图很大时很多。

     

.NET垃圾收集器确保使用终结器线程释放非托管系统资源。问题是,当您创建足够数量的托管对象以触发垃圾回收时,它才会生效。这对于Bitmap类来说效果不错,你可以在垃圾收集堆的第0代填充之前创建数千个它们。在你到达那里之前,你将耗尽非托管内存。

     

需要管理您使用的位图的生命周期。当你不再使用它时,调用Dispose()方法。

在这种特殊情况下,当图像框中仍然使用图像时,图像无法处理。您可以使用technique到&#34;交换&#34;旧图像与新图像,然后处置旧图像。没关系,因为PictureBox不再使用旧图像。