OpenCV VideoCapture: How to get a specific frame correctly?

I am trying to get a specific frame from a video file using OpenCV 2.4.11. I tried to follow the documentation and online tutorials on how to do this correctly and have now tested two approaches:

1) The first method is brute force reading each frame with video.grab () until I get to the specific frame (timestamp) I want. This method is slow if a certain frame is late in the video sequence!

string videoFile(videoFilename);
VideoCapture video(videoFile);
double videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC);
int videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES));
while (videoTimestamp < targetTimestamp)
{
    videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC);
    videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES));

    // Grabe frame (but don't decode the frame as we are only "Fast forwarding")
    video.grab();
}
// Get and save frame
if (video.retrieve(frame))
{
    char txtBuffer[100];
    sprintf(txtBuffer, "Video1Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber);
    string imgName = txtBuffer;
    imwrite(imgName, frame);
}

      

2) In the second method, I am using video.set (...). This method is faster and does not seem to be slower if a certain frame is late in the video sequence.

string videoFile(videoFilename);
VideoCapture video2(videoFile);
videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC);
videoFrameNumber = static_cast<int>(video2.get(CV_CAP_PROP_POS_FRAMES));
video2.set(CV_CAP_PROP_POS_MSEC, targetTimestamp);
while (videoTimestamp < targetTimestamp)
{
    videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC);
    videoFrameNumber = (int)video2.get(CV_CAP_PROP_POS_FRAMES);

    // Grabe frame (but don't decode the frame as we are only "Fast forwarding")
    video2.grab();
}
// Get and save frame
if (video2.retrieve(frame))
{
    char txtBuffer[100];
    sprintf(txtBuffer, "Video2Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber);
    string imgName = txtBuffer;
    imwrite(imgName, frame);
}

      

Problem). The problem is that using these two methods ends up with the same frame number as the content of the target image frame does not equal?!?

I am tempted to conclude that method 1 is correct and that there is something wrong with the OpenCV video.set (...) method. But if I use a VLC player that finds the approximate position of the target frame, is it actually method 2 which is closest to the "correct" result?

As additional information: I tested the same video sequence, but in two different video files encoded with WMV AVv1 MPG4 and WMV codecs respectively.

Using a WMV file, are the two found frames disabled?

Using an MPG4 file, are the two found frames cut off slightly?

Does anyone have any experience with this, can they explain my findings and tell me the correct way to get a certain frame from a video file?

+3


source to share


2 answers


I think OpenCV uses FFmpeg for video decoding.

We had a similar problem, but we used FFmpeg directly. By default, random (but accurate) frame access is not guaranteed. The WMV decoder was particularly fuzzy. Newer versions of FFmpeg allow you to access lower level routines that can be used to create a search function for frames. This solution was a little related and I can't remember anything now. I will try to find more details later.



As a quick job, I would suggest decoding your videos offline and then working on sequences from images. While this increases the amount of storage required, it guarantees accurate random access to frames. You can use FFmpeg to convert your video file to a sequence of images as follows:

ffmpeg -i "input.mov" -an -f image2 "output_%05d.png"

      

0


source


Obviously there is still a bug in opencv / ffmpeg. ffmpeg doesn't deliver the frames it needs and / or opencv doesn't handle this. See here and here .



[Edit: Until this bug is fixed (either in ffmpeg or (as a workaround in opencv)), the only way to get an exact frame by number is to "fast-forward" as you did. (As for the VLC player: I suspect it uses this set () -interface buggy. As for the player, it's usually not too important to find the exact frame, but for the editor it is.)

0


source







All Articles