Home > database >  How to display images in pictureBox1 one by one?
How to display images in pictureBox1 one by one?

Time:10-22

maybe i need a timer ?

i want that before the image is saved or after saved but to display the images one by one. now it's just doing the loop so it's not showing the designer at all until the loop will end.

using Accord.Video.FFMPEG;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;

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

            backgroundWorker1.RunWorkerAsync();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
        
        private void button1_Click(object sender, EventArgs e)
        {
            
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            using (var vFReader = new VideoFileReader())
            {
                vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
                for (int i = 0; i < vFReader.FrameCount; i  )
                {
                    Bitmap bmpBaseOriginal = vFReader.ReadVideoFrame();
                    //bmpBaseOriginal.Save(@"d:\frames\frame"   i   ".bmp");
                    pictureBox1.Image  = bmpBaseOriginal;
                    //bmpBaseOriginal.Dispose();
                }
                vFReader.Close();
            }
        }
    }
}

It's working for while but after some images it's throwing exception on the line :

pictureBox1.Image  = bmpBaseOriginal;

the exception say the object is in use.

System.InvalidOperationException: 'Object is currently in use

CodePudding user response:

Your code should probably be something like this:

private readonly Queue<Image> images = new Queue<Image>();

private void Form1_Load(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    using (var vFReader = new VideoFileReader())
    {
        vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");

        for (var i = 0; i < vFReader.FrameCount; i  )
        {
            images.Enqueue(vFReader.ReadVideoFrame());
        }

        // Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
        vFReader.Close();
    }
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    pictureBox1.Image = images.Dequeue();
    timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
    pictureBox1.Image.Dispose();

    var image = images.Dequeue();

    pictureBox1.Image = image;

    if (images.Count == 0)
    {
        timer1.Stop();
    }
}

All the Images are added to a queue and then dequeued one by one. The first one is displayed as soon as the loading has completed and the rest will be displayed at regular intervals. This code displayed each Image once and then destroys and discards it. If you want to display them all again when you reach the last then you can use a different type of collection and wrap back to the beginning when you get to the end.

EDIT:

I may have misunderstood a little what you were asking for. This code should display the Images as they're generated:

private void Form1_Load(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    using (var vFReader = new VideoFileReader())
    {
        vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");

        for (var i = 0; i < vFReader.FrameCount; i  )
        {
            backgroundWorker1.ReportProgress(0, vFReader.ReadVideoFrame());
        }

        // Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
        vFReader.Close();
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pictureBox1.Image?.Dispose();
    pictureBox1.Image = (Image)e.UserState;
}

You can ditch the Timer and the Queue. My only concern is that this may lead to an OutOfMemoryException. If it does, you can call explicitly call GC.Collect intermittently, e.g. every 100 frames.

  • Related