-1

my program has two threads, grab data from two cameras, each of thread will be used to update a PictureBox:

    private void StartGrabLoop()
    {
        m_grabThread = new BackgroundWorker();
        m_grabThread.ProgressChanged += new ProgressChangedEventHandler(UpdateUI);
        m_grabThread.DoWork += new DoWorkEventHandler(GrabLoop);
        m_grabThread.WorkerReportsProgress = true;
        m_grabThread.RunWorkerAsync();
    }


    private void StartGrabLoop1()
    {
        m_grabThread1 = new BackgroundWorker();
        m_grabThread1.ProgressChanged += new ProgressChangedEventHandler(UpdateUI1);
        m_grabThread1.DoWork += new DoWorkEventHandler(GrabLoop1);
        m_grabThread1.WorkerReportsProgress = true;
        m_grabThread1.RunWorkerAsync();
    }


    private void GrabLoop(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        while (m_grabImages)
        {
            try
            {
                m_camera.RetrieveBuffer(m_rawImage);
            }
            catch (FC2Exception ex)
            {
                Debug.WriteLine("Error: " + ex.Message);
                continue;
            }

            lock (this)
            {
                m_rawImage.Convert(PixelFormat.PixelFormatBgr, m_processedImage);
            }

            worker.ReportProgress(0);
        }

        m_grabThreadExited.Set();
    }



    private void GrabLoop1(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        while (m_grabImages1)
        {
            try
            {
                m_camera1.RetrieveBuffer(m_rawImage1);
            }
            catch (FC2Exception ex)
            {
                Debug.WriteLine("Error: " + ex.Message);
                continue;
            }

            lock (this)
            {
                m_rawImage1.Convert(PixelFormat.PixelFormatBgr, m_processedImage1);
            }

            worker.ReportProgress(0);
        }

        m_grabThreadExited1.Set();
    }

 private void UpdateUI(object sender, ProgressChangedEventArgs e)
        {
            Bitmap source = m_processedImage.bitmap;
            Bitmap resized = new Bitmap(source, source.Width / zoom, source.Height / zoom);
            pictureBox1.Image = resized;
            pictureBox1.Invalidate();
}
 private void UpdateUI1(object sender, ProgressChangedEventArgs e)
        {
            Bitmap source = m_processedImage.bitmap;
            Bitmap resized = new Bitmap(source, source.Width / zoom, source.Height / zoom);
            pictureBox2.Image = resized;
            pictureBox2.Invalidate();
}

The trick thing is it could run very well on my desktop. but have memory problem on my laptop, which either show white image, or busy to crash. I did lots of research and tried dispose(),or System.GC.Collect(); System.GC.WaitForPendingFinalizers();none of them work, anybody could help me? The reason I use "new bitmap" is because I want to simple zoom function....

  • as long as I use other command such as "source.clone" instead of "new bitmap", there is no problem for the two live video windows. I am pretty sure it is about "new bitmap", if I only use "new bitmap" in one updateUI, then live video is ok . If I want to use "new bitmap" in two threads loop, it has problem.. – user7768436 Jan 04 '18 at 22:54
  • When you assign the new bitmap to the `Image` property, what do you do with the previous bitmap? You might try `pictureBox1.Image.Dispose();` before assigning the new bitmap. I don't know if that will solve your issue, though. For a zoomable picture box see [this link](https://stackoverflow.com/questions/30489647/how-to-zoom-in-the-image-inside-a-scrollable-panel-in-vb/30510404#30510404) – Chris Dunaway Jan 04 '18 at 23:00
  • tks, but pictureBox1.Image.Dispose(); notworking, cause faster crashes...^_^ – user7768436 Jan 04 '18 at 23:10
  • Do you use the same build on the desktop and the laptop (debug/release)? Is `m_processedImage` a global variable? – Ahmed Osama Jan 04 '18 at 23:11
  • yes, exactly same for vs, release, x86, m_processedImage is a variable from SDK in each thread, The thing is it works very well on the laptop if I only use one "new bitmap()" , such as using "new bitmap()" in updateUI1, and using "source.clone()" in updateUI2. – user7768436 Jan 04 '18 at 23:21
  • 2
    You shouldn't be interacting with UI controls from threads that are not the UI thread. Read [this](https://stackoverflow.com/a/10170699/767890) for more information. – InBetween Jan 04 '18 at 23:24
  • tks,I will look, but any simple way to make the current scheme work? I use two new backgroundworkers to create each thread... – user7768436 Jan 04 '18 at 23:43
  • You didn't show what you are doing in the DoWork methods, but it looks like those two methods are competing against the same resources. Try passing the resulting image into the UserState property of the ProgressChangedEventArgs. [Dispose the pictureBox.Image](https://stackoverflow.com/q/2613286/719186) before replacing it, too. – LarsTech Jan 04 '18 at 23:54
  • tks, added on the post, – user7768436 Jan 05 '18 at 00:48
  • This is most probably a firehose problem. The background worker calling ReportProgress too often. The UI thread starts burning 100% core when it can't keep up and stops taking care of its other duties, painting and responding to input. You'll need either a faster machine or intentionally slow down the worker. – Hans Passant Jan 05 '18 at 01:23
  • please explain how to slow the worker, where to insert the Sleep()? tks – user7768436 Jan 05 '18 at 01:27
  • and if you think the worker Reportprogress too often, why using "bitmap.clone" could show two live videos? in two cases("new bitmap()" & "bitmap.clone()") the working load is same. – user7768436 Jan 05 '18 at 01:34
  • Make a new bitmap _without_ any link to the old one, and just paint it on there. – Nyerguds Jan 19 '18 at 13:43

1 Answers1

0

It's always better to make new images without any links to the old one; it helps garbage collection run better. Also, explicitly dispose the old versions when putting in new ones.

private void UpdateUI(object sender, ProgressChangedEventArgs e)
{
    Bitmap source = m_processedImage.bitmap;
    Bitmap resized = CreateWithNewSize(source, source.Width / zoom, source.Height / zoom);
    // Save reference to old image in the control
    Image oldImg = pictureBox1.Image;
    pictureBox1.Image = resized;
    // specifically dispose old image to avoid memory usage buildup
    if (oldImg != null)
    {
        try{ oldImg.Dispose(); }
        catch { /* Ignore; won't help much to process this */ }
    }
    // Request refresh.
    pictureBox1.Invalidate();
}

...and do the same for pictureBox2. As noted by InBetween though, you will probably need to set up correct communication between the worker threads and the UI thread, probably using delegates. In that case, all code under my Save reference to old image comment should probably be put in the delegate-called function to replace the image on the UI control.

The resize method is fairly simple:

public static Bitmap CreateWithNewSize(Image image, Int32 newWidth, Int32 newHeight)
{
    Bitmap bp = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
    using (Graphics gr = Graphics.FromImage(bp))
        gr.DrawImage(image, new Rectangle(0, 0, newWidth, newHeight));
    return bp;
}

The same image disposing code should probably be called on m_processedImage1 before you replace it, in the m_rawImage1.Convert call. Since this new code will make clean unlinked copies for your UI, this disposing won't cause any problems, either.

Nyerguds
  • 5,360
  • 1
  • 31
  • 63