4

I am experiencing a strange memory stack-up in my c# windows form program that occurs all the time on slow PCs, and when the windows form loses focus or is otherwise interrupted on faster PCs.

The program I have written uses Aforge to get images from my webcam, which I then display in an Aforge picturebox control (CurrImagePic in code) in the windows form. The images are switched into the picture box and then disposed at the camera's native framerate, so it appears as video to the user, not still images. The picture box is 1080x1920, but the space for it in the form is smaller and so I allow the user to scroll around the picture.

After about ~30 seconds of memory-stable operation on slower PCs, the problem begins. On faster PCs, the problem only occurs when holding down scroll bar arrows or clicking and dragging around either scroll bar, and if I lock the PC or bring up the Ctrl+Alt+Delete menu.

The problem itself is that memory used by the program starts to increase in very large chunks, leading to an out of memory crash. This is unstoppable on slower PCs, but on the faster PCs if you stop scrolling or return from the lock/Ctrl+alt+delete menu, the program stabilizes at the higher memory usage level. The memory that was accrued during scrolling or while in the lock menu is never collected by the garbage collector. I've even tried to put in a button that forces a GC.collect() when pressed, and it does not reduce this memory usage.

I've run perfmon and found that the memory increase is on the unmanaged heap, but I don't know if it's coming from bitmaps which are not being disposed or what it could be from. It's been impossible to track down since it does not occur except in the above conditions. I've tried various solutions (like moving my image processing out of the event handler and even using both global flags and a "lock" statement to try and ensure that only one thread or frame can access the image processing and displaying method at at time, but I have seen no change. In fact, I am now seeing some unexplained small jumps in memory usage that I wasn't seeing before I put in the lock and moved the processing out of the handler.

Has anyone run into situations like this? I am at a loss for what I need to fix, and I am not getting much help from the Aforge forums. I think the problem is based around my Aforge event handler and image processing method if it is in my code at all - but I also have a suspicion that this is something deeper in the windows form code that I am either misusing or that can't keep up with the demands of my code. Code below:

//Applicable Globals to this code snippet
private bool ALLOWFRAME = true;
private Object FRAMEKEY = new Object();
private VideoCaptureDevice COMPVID;
private Bitmap TMPLTCAP;
private System.Drawing.Image OLDIMAGE;
private bool RCRDPIC = false;

private void COMPVID_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            //Only process a frame when another is done processing
            if (ALLOWFRAME == true)
            {
                ALLOWFRAME = false;
                Bitmap PassFrame = AForge.Imaging.Image.Clone(eventArgs.Frame);
                ProcessFrame(PassFrame);
                PassFrame.Dispose();
            }
        }

private void ProcessFrame(Bitmap frameIn) 
        {
            lock (FRAMEKEY) 
            { 
                if (OLDIMAGE != null) { OLDIMAGE.Dispose(); }
                //Call comparison method if flag is set.
                if (COMPON == true)
                {
                    Difference TmpltFilter = new Difference(TMPLTCAP);
                    TmpltFilter.ApplyInPlace(frameIn);

                    OLDIMAGE = CurrImagePic.Image;
                    CurrImagePic.Image = AForge.Imaging.Image.Clone(frameIn);
                    OLDIMAGE.Dispose();
                }
                else
                {
                    OLDIMAGE = CurrImagePic.Image;
                    CurrImagePic.Image = AForge.Imaging.Image.Clone(frameIn);
                    OLDIMAGE.Dispose();
                    //Toggle the flag back to false to show it's safe (i.e., comparisons have stopped)
                    //for the result-recording method to copy from the picture box if it is attempting to copy
                    if (RCRDPIC == true)
                    {
                        RCRDPIC = false;
                    }
                }
                ALLOWFRAME = true;
            }
        }
GrayBoard
  • 41
  • 1
  • Is COMPON true or false? Could it happen due to frame skip? Aforge creates a frame and calls the event, however that specific frame is not displayed and therefore never disposed of? I can't remember if Aforge cleans up after itself. – Jens Mar 04 '15 at 17:45
  • COMPON is true if someone has pressed a 'compare images' button in the windows form. So far, I have been observing the problem with COMPON == false, but I am certain it would behave the same with COMPON == true. The only difference in the branches is the running of the difference filter. If it were a frame skip, wouldn't my global lockout on the processing method just make the frame get ignored? Also, why would a frame skip only occur when grabbing the scroll bar? – GrayBoard Mar 04 '15 at 17:50
  • `frameIn` is already created by aforge before you lock. If you just assign `frameIn` to your picturebox without clone, does it still work? If yes, then Aforge does not dispose of the bitmap itself, so they would build up, even if you don't use one of the frames. – Jens Mar 04 '15 at 22:59
  • @Jens I was under the impression that `frameIn`, being a method input, was disposed of when the method finished running. In any case, I don't think that answers the problem. If `frameIn` sticking around was a problem, then wouldn't the problem occur all the time regardless of PC speed or the dragging of the scroll bar? Also, `ALLOWFRAME` should prevent `ProcessFrame(bitmap)` from being called if a frame needs to be skipped (i.e., if any instance of `ProcessFrame(bitmap)` already exists. I only put in `lock` to try and ensure other threads doing other things did not interrupt. – GrayBoard Mar 05 '15 at 13:23
  • I'm guessing here as well, don't get me wrong. See it as pointers on what may be worth checking out. Have you also tried stripping it down to the bare minimum (like `if (Picturebox1.Image != null) Picturebox1.Image.Dispose(); PictureBox1.Image = frameIn;` and nothing else) to narrow in on the problem? I did use the Aforge Webcam functionality a few years back, I'm can't really remember the details. – Jens Mar 05 '15 at 13:32
  • Thanks for taking a guess at it. It's helping draw more info out for other people who might be interested in answering or commenting. Now, if you do what you are suggesting, it actually turns the picturebox into a big white box with a red X, and does not recover (no matter how many times the camera is started and stopped). That code is the precise reason why I had to implement pointing to old image (with `=`), cloning the new image into the picture box, and then disposing of the old image (so the picturebox is never 'null' at any time). – GrayBoard Mar 05 '15 at 17:10
  • That at least confirms, that Aforge does clean up after itself. You could maybe just stop the stream while the user is scrolling/resizing the form using the various mouse events of the picturebox/form. That may at least be a workaround. – Jens Mar 05 '15 at 18:58

1 Answers1

1

One approach that has often improved performance is to queue up he images in memory and use a timer control to dequeue/display them in a picture box. This way, you get control over proper disposal and allow the NewFrame event to return faster rather than being tied up in image processing.

Also, in the Timer_Tick event, try and do the following:

this.Timer.Stop();
Bitmap image = null;
var temp = this.PictureBox.Image;

lock (FRAMEKEY)
{
    if (this.ImageQueue.Any())
    {
        image = this.ImageQueue.Dequeue();
        if (temp != null) { temp.Dispose(); }
    }
}

this.PictureBox.Image = image;
if (temp != null) { temp.Dispose(); }

this.Timer.Start();
Raheel Khan
  • 14,205
  • 13
  • 80
  • 168