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.
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