图像处理循环中的内存异常(需要比 GC.collect 更好的解决方案)
Memory exception in image processing loop (Need better solution than GC.collect)
我正在制作一个小应用程序,它以 windows 形式显示实时网络摄像头,并且还存储带水印的图像以按指定的时间间隔驱动(最终目标是创建延时视频)。
我正在使用 AForge 库进行图像和视频处理。
我遇到了问题,似乎存在内存泄漏,尽管我尝试确保在每个发生图像处理的位置都使用 "using" 语句。
下面是发生图像处理的代码(NewFrame 事件)
private void Video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (ImageProcessing) // If the previous frame is not done processing, let this one go
return;
else
ImageProcessing = true;
using (Bitmap frame = (Bitmap)eventArgs.Frame)
{
// Update the GUI picturebox to show live webcam feed
Invoke((Action)(() =>
{
webcam_PictureBox.Image = (Bitmap)frame.Clone();
}));
// During tests, store images to drive at a certain interval
if (ImageStoreTimer.Elapsed.TotalSeconds > ImageStoreTime)
{
DateTime dt = DateTime.Now;
using (Graphics graphics = Graphics.FromImage(frame))
{
PointF firstLocation = new PointF(frame.Width / 2, frame.Height / 72);
PointF secondLocation = new PointF(frame.Width / 2, frame.Height / 15);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
using (Font arialFont = new Font("Arial", 15))
{
graphics.DrawString(dt.ToString(), arialFont, Brushes.Red, firstLocation, drawFormat);
graphics.DrawString(Pressure.ToString("F0") + " mbar", arialFont, Brushes.Red, secondLocation, drawFormat);
}
}
// Place images in a folder with the same name as the test
string filePath = Application.StartupPath + "\" + TestName + "\";
// Name images by number 1....N
string fileName = (Directory.GetFiles(filePath).Length + 1).ToString() + ".jpeg";
frame.Save(filePath + fileName, ImageFormat.Jpeg);
ImageStoreTimer.Restart();
}
}
//GC.Collect(); <----- I dont want this
}
catch
{
if (ProgramClosing == true){}
// Empty catch for exceptions caused by the program being closed incorrectly
else
throw;
}
finally
{
ImageProcessing = false;
}
}
现在,当 运行 运行程序时,我看到内存使用量上下波动,通常在下降前达到 900MB 左右。但是偶尔会涨到2GB。有时,我什至会在这一行遇到内存不足的异常:
Graphics graphics = Graphics.FromImage(frame)
因此,在花了一个小时左右的时间尝试重塑代码并查找内存泄漏后,我终于尝试了代码中注释掉的 GC.Collect 行(羞耻)。在那之后,我的内存使用量保持不变,低于 60MB。而且我可以 运行 这个程序 24 小时没有任何问题。
所以我读了一些关于 GC.Collect 的内容,它有多糟糕,例如,在程序中经常执行它可能需要大量的处理能力。但是当我比较我的程序使用的 CPU 功率时,无论我注释掉该行还是保留它,它都没有真正改变。但是如果我在新帧事件结束时收集,内存问题就消失了。
我想为我的问题找到一个不涉及 GC.collect 函数的解决方案,因为我知道这是糟糕的编程习惯,我应该找到潜在的问题根源。
先谢谢大家了!
我不擅长 win 表单,但我认为这一行:
webcam_PictureBox.Image = (Bitmap)frame.Clone();
将保留以前的图像未处理,这会泄漏内存(Bitmap 保留的非托管内存)。由于 Bitmap 具有终结器 - 它会在将来的某个时间(或当您调用 GC.Collect
时)被 GC 回收,但正如您已经了解的那样 - 在这种情况下依赖 GC 并不是一个好习惯。所以尝试这样做:
if (webcam_PictureBox.Image != null)
webcam_PictureBox.Image.Dispose();
webcam_PictureBox.Image = (Bitmap)frame.Clone();
Larse 的合理评论:在图像仍被分配给 PictureBox.Image
时最好不要处理图像,因为谁知道呢,也许 PictureBox
控件在您分配时会对旧图像做任何事情一个新的。那么替代方案是:
var oldImage = webcam_PictureBox.Image;
webcam_PictureBox.Image = (Bitmap)frame.Clone();
if (oldImage != null)
oldImage.Dispose();
这对我们很有效,
在此之前,我们尝试过使用语句和各种方法,但一种解决方案可能在一台机器上有效,但在另一台机器上无效,但通过引用当前在 picturebox 中设置的图像并在 afterwords 中处理它效果很好,不完全确定为什么 picture.image 在设置新图像时不会自动处理(令人沮丧!)但是嘿,至少存在这个简单的解决方法
我正在制作一个小应用程序,它以 windows 形式显示实时网络摄像头,并且还存储带水印的图像以按指定的时间间隔驱动(最终目标是创建延时视频)。
我正在使用 AForge 库进行图像和视频处理。
我遇到了问题,似乎存在内存泄漏,尽管我尝试确保在每个发生图像处理的位置都使用 "using" 语句。
下面是发生图像处理的代码(NewFrame 事件)
private void Video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (ImageProcessing) // If the previous frame is not done processing, let this one go
return;
else
ImageProcessing = true;
using (Bitmap frame = (Bitmap)eventArgs.Frame)
{
// Update the GUI picturebox to show live webcam feed
Invoke((Action)(() =>
{
webcam_PictureBox.Image = (Bitmap)frame.Clone();
}));
// During tests, store images to drive at a certain interval
if (ImageStoreTimer.Elapsed.TotalSeconds > ImageStoreTime)
{
DateTime dt = DateTime.Now;
using (Graphics graphics = Graphics.FromImage(frame))
{
PointF firstLocation = new PointF(frame.Width / 2, frame.Height / 72);
PointF secondLocation = new PointF(frame.Width / 2, frame.Height / 15);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
using (Font arialFont = new Font("Arial", 15))
{
graphics.DrawString(dt.ToString(), arialFont, Brushes.Red, firstLocation, drawFormat);
graphics.DrawString(Pressure.ToString("F0") + " mbar", arialFont, Brushes.Red, secondLocation, drawFormat);
}
}
// Place images in a folder with the same name as the test
string filePath = Application.StartupPath + "\" + TestName + "\";
// Name images by number 1....N
string fileName = (Directory.GetFiles(filePath).Length + 1).ToString() + ".jpeg";
frame.Save(filePath + fileName, ImageFormat.Jpeg);
ImageStoreTimer.Restart();
}
}
//GC.Collect(); <----- I dont want this
}
catch
{
if (ProgramClosing == true){}
// Empty catch for exceptions caused by the program being closed incorrectly
else
throw;
}
finally
{
ImageProcessing = false;
}
}
现在,当 运行 运行程序时,我看到内存使用量上下波动,通常在下降前达到 900MB 左右。但是偶尔会涨到2GB。有时,我什至会在这一行遇到内存不足的异常:
Graphics graphics = Graphics.FromImage(frame)
因此,在花了一个小时左右的时间尝试重塑代码并查找内存泄漏后,我终于尝试了代码中注释掉的 GC.Collect 行(羞耻)。在那之后,我的内存使用量保持不变,低于 60MB。而且我可以 运行 这个程序 24 小时没有任何问题。
所以我读了一些关于 GC.Collect 的内容,它有多糟糕,例如,在程序中经常执行它可能需要大量的处理能力。但是当我比较我的程序使用的 CPU 功率时,无论我注释掉该行还是保留它,它都没有真正改变。但是如果我在新帧事件结束时收集,内存问题就消失了。
我想为我的问题找到一个不涉及 GC.collect 函数的解决方案,因为我知道这是糟糕的编程习惯,我应该找到潜在的问题根源。
先谢谢大家了!
我不擅长 win 表单,但我认为这一行:
webcam_PictureBox.Image = (Bitmap)frame.Clone();
将保留以前的图像未处理,这会泄漏内存(Bitmap 保留的非托管内存)。由于 Bitmap 具有终结器 - 它会在将来的某个时间(或当您调用 GC.Collect
时)被 GC 回收,但正如您已经了解的那样 - 在这种情况下依赖 GC 并不是一个好习惯。所以尝试这样做:
if (webcam_PictureBox.Image != null)
webcam_PictureBox.Image.Dispose();
webcam_PictureBox.Image = (Bitmap)frame.Clone();
Larse 的合理评论:在图像仍被分配给 PictureBox.Image
时最好不要处理图像,因为谁知道呢,也许 PictureBox
控件在您分配时会对旧图像做任何事情一个新的。那么替代方案是:
var oldImage = webcam_PictureBox.Image;
webcam_PictureBox.Image = (Bitmap)frame.Clone();
if (oldImage != null)
oldImage.Dispose();
这对我们很有效, 在此之前,我们尝试过使用语句和各种方法,但一种解决方案可能在一台机器上有效,但在另一台机器上无效,但通过引用当前在 picturebox 中设置的图像并在 afterwords 中处理它效果很好,不完全确定为什么 picture.image 在设置新图像时不会自动处理(令人沮丧!)但是嘿,至少存在这个简单的解决方法