c# – 图像处理循环中的内存异常(需要比GC.collect更好的解决方案)

我正在制作一个小型应用程序,以窗体形式显示实时网络摄像头,并且还存储水印图像以指定间隔驱动(创建间隔拍摄视频是最终目标).

我正在使用AForge库进行图像和视频处理.

我有问题,似乎有内存泄漏,即使我试图确保在图像处理发生的每个位置使用“使用”语句.

下面是进行图像处理的代码(The 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行(Shame).之后,我的内存使用率保持不变,低于60MB.我可以毫不费力地运行该程序24小时.

所以我读了一下关于GC.Collect的内容,以及它有多糟糕,例如它可能需要很多处理能力才能在程序中经常使用它.但是,当我比较我的程序使用的CPU功率时,无论我是否评论该线路或离开它,它都不会真正改变.但是如果我在新的帧事件结束时收集内存问题就消失了.

我想找到一个不涉及GC.collect函数的问题的解决方案,因为我知道它是错误的编程实践,我应该找到潜在的问题源.

谢谢大家!

解决方法:

我对胜利表格不满意,但我认为这一行:

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();
上一篇:c# – NuGet:AForge.Video.FFMPEG在哪里


下一篇:讨人喜欢的 MySQL replace into 用法(insert into 的增强版)