要将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直到下一个到达?
答案 0 :(得分:1)
Bitmap
需要明确处理,因为它不仅使用托管资源,还使用 非托管 资源。在这里我引用a great answer,它解释了为什么如果你不打电话给Dispose()
方法,GC不会帮助你。
Bitmap类不可避免地是您必须停止忽略IDisposable存在的类。它是围绕GDI +对象的小包装类。 GDI +是非托管代码。位图占用非托管内存。当位图很大时很多。
.NET垃圾收集器确保使用终结器线程释放非托管系统资源。问题是,当您创建足够数量的托管对象以触发垃圾回收时,它才会生效。这对于Bitmap类来说效果不错,你可以在垃圾收集堆的第0代填充之前创建数千个它们。在你到达那里之前,你将耗尽非托管内存。
需要管理您使用的位图的生命周期。当你不再使用它时,调用Dispose()方法。
在这种特殊情况下,当图像框中仍然使用图像时,图像无法处理。您可以使用technique到&#34;交换&#34;旧图像与新图像,然后处置旧图像。没关系,因为PictureBox不再使用旧图像。