Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emgu CV get all frames from video file

Tags:

c#

video

emgucv

I would like to ask you to help me with getting all the frames from video file using Emgu CV. I know I can use Capture class and its QueryFrame() method, but this only returns one frame. What is the easiest way to get all the frames? (and save it for example to Image<Bgr, Byte>[]) I need all the frames to do some more processing (to be more specific: key frame extraction for video summarization).

Thanks a lot for your help.

like image 234
tom Avatar asked Dec 12 '11 10:12

tom


1 Answers

See my answer here for reference Emgu Capture plays video super fast

But this should do as you ask I've used a list to store the images you can use an array but you will need to know how big your avi file is.

Timer My_Time = new Timer();
int FPS = 30;
List<Image<Bgr,Byte>> image_array = new List<Image<Bgr,Byte>>();
Capture _capture;    

public Form1()
{
    InitializeComponent();

    //Frame Rate
    My_Timer.Interval = 1000 / FPS;
    My_Timer.Tick += new EventHandler(My_Timer_Tick);
    My_Timer.Start()
    _capture = new Capture("test.avi");   
}

private void My_Timer_Tick(object sender, EventArgs e)
{
    Image<Bgr, Byte> frame = _capture.QueryFrame();
    if (frame != null)
    {
        imageBox.Image = _capture.QueryFrame();
        image_array.Add(_capture.QueryFrame().Copy());
    }
    else
    {
         My_Timer.Stop();
    {
}

This was designed to allow playback of the video file at a responsible rate but as your simply converting you could use the Application.Idle method just as easily like this...

List<Image<Bgr,Byte>> image_array = new List<Image<Bgr,Byte>>();
Capture _capture;
    
public Form1()
{
    InitializeComponent();

    //Frame Rate
    _capture = new Capture("test.avi");   
    Application.Idle += ProcessFrame;
}

private void ProcessFrame(object sender, EventArgs arg)
{
    Image<Bgr, Byte> frame = _capture.QueryFrame();
    if (frame != null)
    {
        image_array.Add(frame.Copy());
    }
    else
    {
        Application.Idle -= ProcessFrame;// treat as end of file
    }
    
}

You will have to be careful of the end of the file error you will receive an error. You could always use a try catch statement to catch the specific error it will give instead of simply terminating conversion.

If you use to use an image array you will have to loop through the file incrementing a variable and counting the frames and then create the image array before converting the video file to an array.


[EDIT]

As requested this is a method version of retrieving all frames from a video file I have not tested this on a large video file as I expect that the program would crash since it will require a lot of memory.

    private List<Image<Bgr, Byte>> GetVideoFrames(String Filename)
    {
        List<Image<Bgr,Byte>> image_array = new List<Image<Bgr,Byte>>();
        Capture _capture = new Capture(Filename);

        bool Reading = true;

        while (Reading)
        {
            Image<Bgr, Byte> frame = _capture.QueryFrame();
            if (frame != null)
            {
                image_array.Add(frame.Copy());
            }
            else
            {
                Reading = false;
            }
        }

        return image_array;
    }

Alternatively I realise you may wish to record say 10 seconds of video from a webcam so this method will do that, I used a Stopwatch as the the while loop prohibits the use of a timer unless your multi threading the application

    private List<Image<Bgr, Byte>> GetVideoFrames(int Time_millisecounds)
    {
        List<Image<Bgr,Byte>> image_array = new List<Image<Bgr,Byte>>();
        System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch();
        
        bool Reading = true;
        Capture _capture = new Capture();
        SW.Start();

        while (Reading)
        {
            Image<Bgr, Byte> frame = _capture.QueryFrame();
            if (frame != null)
            {
                image_array.Add(frame.Copy());
                if (SW.ElapsedMilliseconds >= Time_millisecounds) Reading = false;
            }
            else
            {
                Reading = false;
            }
        }

        return image_array;
    }

and this would be called like this:

List<Image<Bgr, Byte>> Image_Array = GetVideoFrames(10000); //10 Secounds

Hope this helps,

Cheers,

Chris

like image 93
Chris Avatar answered Oct 23 '22 00:10

Chris