【问题标题】:Memory Leak while converting Opencv Mat to .net BitMap将 Opencv Mat 转换为 .net BitMap 时出现内存泄漏
【发布时间】:2023-04-09 10:47:01
【问题描述】:

要将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 来显示图片)。以下是完整代码

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之后,它被处理掉了吗?
  • @kennyzx,感谢您的回复。你是对的!我不得不释放Bitmap,我以为.net 会自动处理它。我更新了问题,你能回答吗?
  • 这条线 this-&gt;pictureBox1-&gt;Invoke(d, bmpimg); 是不是应该是 this-&gt;pictureBox1-&gt;Invoke(spd, bmpimg);
  • 没错!现在编辑..这个问题的任何解决方法?
  • 是的,your have a good idea

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


【解决方案1】:

Bitmap 需要显式释放,因为它不仅使用托管资源,还使用 ​​非托管 资源。在这里我引用a great answer,它很好地解释了为什么如果你不调用Dispose() 方法,GC 不会帮助你。

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

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

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

在这种特殊情况下,图片在 PictureBox 中仍在使用时不能被释放。您可以使用technique 将旧图像与新图像“交换”,然后处置旧图像。没关系,因为 PictureBox 不再使用旧图像。

【讨论】:

    猜你喜欢
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多