2

I have written a code to allow the user to start and stop a feed from their webcam. I have used AForge.NET's NewFrameEventArgs to update a PictureBox with the new frame each time it changes. Everything works perfectly however whenever I start the feed, the RAM usage on my computer slowly goes up until an OutOfMemoryException occurs.

Please could you help me find out how to clear or flush this somehow. When I get the exception, it occurs at the bottom of the code in ScaleImage:

System.Drawing.Graphics.FromImage(newScaledImage).DrawImage(image, 0, 0, newWidth, newHeight);

My code so far:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using AForge.Video;
using AForge.Video.DirectShow;

namespace WebCameraCapture
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private VideoCaptureDevice FinalFrame;
        System.Drawing.Bitmap fullResClone;

        void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            pictureBox1.Image = ScaleImage((Bitmap)eventArgs.Frame.Clone(), 640, 480);
        }

        private void btn_startCapture_Click(object sender, EventArgs e)
        {
            FinalFrame = new VideoCaptureDevice(CaptureDevice[comboBox1.SelectedIndex].MonikerString);//Specified web cam and its filter moniker string.
            FinalFrame.NewFrame += new NewFrameEventHandler(FinalFrame_NewFrame);
            FinalFrame.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            CaptureDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            foreach (FilterInfo Device in CaptureDevice) { comboBox1.Items.Add(Device.Name); }

            comboBox1.SelectedIndex = 0; //Default index.
            FinalFrame = new VideoCaptureDevice();
        }

        //This ScaleImage is where the OutOfMemoryException occurs.
        public static System.Drawing.Image ScaleImage(System.Drawing.Image image, int maxWidth, int maxHeight) //Changes the height and width of the image to make sure it fits the PictureBox.
        {
            var ratioX = (double)maxWidth / image.Width;
            var ratioY = (double)maxHeight / image.Height;
            var ratio = Math.Min(ratioX, ratioY);

            var newWidth = (int)(image.Width * ratio);
            var newHeight = (int)(image.Height * ratio);

            var newScaledImage = new Bitmap(newWidth, newHeight);
            System.Drawing.Graphics.FromImage(newScaledImage).DrawImage(image, 0, 0, newWidth, newHeight); // <<<< RIGHT HERE
            return newScaledImage;
        }
    }
}
Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
  • Everytime there is a new graphics object created into memory, perhaps you can try to dispose the previous one after assigning a new one. – Sievajet Jan 03 '15 at 19:38
  • 1
    Yes, you should be disposing the graphics objects, probably both the one you get from AForge and the one you create yourself in your ScaleImage method. – RenniePet Jan 03 '15 at 20:57
  • I am new to disposing objects, could you give me an example of how to implement this, where to put it and what to put etc. – Callum Watkins Jan 04 '15 at 20:50

1 Answers1

2

You need to dispose the previous image (as long as it is not null) before you add the new scaled image.

void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    if (pictureBox1.Image != null) { pictureBox1.Image.Dispose(); }

    Bitmap tempBitmap = (Bitmap)eventArgs.Frame.Clone();
    pictureBox1.Image = ScaleImage(tempBitmap, 640, 480);
    tempBitmap.Dispose();
}

Question: Is it really necessary to create a clone of the input image? What exactly does eventArgs.Frame reference? (I don't have AForge.)

Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
RenniePet
  • 11,420
  • 7
  • 80
  • 106
  • 1
    Thanks so much, this seems to have fixed the issue. I am new to AForge and tbh I don't really know why cloning the frame is necessary but it has something to do with the AForge dll requiring it... – Callum Watkins Jan 05 '15 at 19:24
  • 1
    Cloning the image is necessary because its memory is owned by AForge. AForge will `Dispose` it at its own discretion when the event handler(s) returns and you would end up with an invalid Bitmap. – Joan Charmant Jan 08 '15 at 11:21
  • This still doesn't `Dispose()` the `Graphics` object returned in `System.Drawing.Graphics.FromImage(newScaledImage)` within the `ScaleImage` method (just the bitmap). This will run into problems eventually aswell. – Jcl Jul 20 '15 at 17:09