13

I am trying to jump to a specific frame by setting the CV_CAP_PROP_POS_FRAMES property and then reading the frame like this:

cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES, current_frame );
frame = cvQueryFrame( input_video );

The problem I am facing is that, OpenCV 2.1 returns the same frame for the 12 consecutive values of current_frame whereas I want to read each individual frame, not just the key frames. Can anyone please tell me what's wrong?


I did some research and found out that the problem is caused by the decompression algorithm.

The MPEG-like algorithms (including HD, et all) do not compress each frame separately, but save a keyframe from time to time, and then only the differences between the last frame and subsequent frames.

The problem you reported is caused by the fact that, when you select a frame, the decoder (ffmpeg, likely) automatically advances to the next keyframe.

So, is there a way around this? I don't want only key frames but each individual frame.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
Random
  • 157
  • 1
  • 3
  • 13
  • I haven't been able to find any solution to this. what i am doing is avoiding OpenCv and doing the task in Matlab which gives each frame but at the expense of low speed. any solutions to the problem in OpenCv please :-/ – Random Jul 16 '12 at 11:43

5 Answers5

5

I don't know whether or not this would be precise enough for your purpose, but I've had success getting to a particular point in an MPEG video by grabbing the frame rate, converting the frame number to a time, then advancing to the time. Like so:

cv::VideoCapture sourceVideo("/some/file/name.mpg");
double frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
Trebor Rude
  • 1,904
  • 1
  • 21
  • 31
  • 1
    I have the same issue. Although this answer didn't solved the problem, I found a missing thing: frameTime is in seconds and it should be in msec: `double frameTime = 1000.0 * frameNumber / frameRate;` – cbuchart Nov 06 '13 at 12:29
  • Good point, @cbuchart. I've updated the answer. Can you tell me why this answer didn't work for you? – Trebor Rude Nov 06 '13 at 13:52
  • It seems to be missing a keyframe. Even if I make a sourceVideo.set(CV_CAP_PROP_POS_MSEC, 0.0) and start from there, it just giving me wrong frames (with "ghost" efect) – cbuchart Nov 11 '13 at 15:11
4

Due to this limitation in OpenCV, it may be wise to to use FFMPEG instead. Moviepy is a nice wrapper library.

# Get nth frame from a video
from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader
cap = FFMPEG_VideoReader("movie.mov",True)
cap.initialize()
cap.get_frame(n/FPS)

Performance is great too. Seeking to the nth frame with get_frame is O(1), and a speed-up is used if (nearly) consecutive frames are requested. I've gotten better-than-realtime results loading three 720p videos simultaneously.

Chris Merck
  • 454
  • 3
  • 12
  • 1
    This is really a lot faster for 3 simultaneous videos and jumping frames. I'm actually thinking of going around moviepy and using ffmpeg_python directly, so I will have even more flexibility. But for now it's good enough... Thanks for the suggestion. – Andrei M. Feb 03 '22 at 13:27
3

CV_CAP_PROP_POS_FRAMES jumps to a key frame. I had the same issue and worked around it using this (python-)code. It's probably not totally efficient, but get's the job done:

def seekTo(cap, position):
  positiontoset = position
  pos = -1
  cap.set(cv.CV_CAP_PROP_POS_FRAMES, position)
  while pos < position:
    ret, image = cap.read()
    pos = cap.get(cv.CV_CAP_PROP_POS_FRAMES)
    if pos == position:
      return image
    elif pos > position:
      positiontoset -= 1
      cap.set(cv.CV_CAP_PROP_POS_FRAMES, positiontoset)
      pos = -1
rumpel
  • 7,870
  • 2
  • 38
  • 39
0

I've successfully used the following on OpenCV 3 / Python 3:

 # Skip to 150 frame then read the 151th frame
 cap.set(cv2.CAP_PROP_POS_FRAMES, 150))
 ret, frame = cap.read()
ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
warchitect
  • 397
  • 1
  • 4
  • 15
0

After some years assuming this as a unsavable bug, I think I've figured out a way to use with a good balance between speed and correctness.

A previous solution suggested to use the CV_CAP_PROP_POS_MSEC property before reading the frame:

cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);

void readFrame(int frameNumber, cv::Mat& image) {
  const double frameTime = 1000.0 * frameNumber / frameRate;
  sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
  sourceVideo.read(image);
}

It does return the expected frame, but the problem is that using CV_CAP_PROP_POS_MSEC may be very slow, for example for a video conversion.

Note: using global variables for simplicity.


On the other hand, if you just want to read the video sequentially, it is enough to read frame without seeking at all.

for (int frameNumber = 0; frameNumber < nFrames; ++frameNumber) {
  sourceVideo.read(image);
}

The solution comes from combining both: using a variable to remember the last queried frame, lastFrameNumber, and only seek when requested frame is not the next one. In this way it is possible to increase the speed in a sequential reading while allowing random seek if necessary.

cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
const int lastFrameNumber = -2; // guarantee seeking the first time

void readFrame(int frameNumber, cv::Mat& image) {
  if (lastFrameNumber + 1 != frameNumber) { // not the next frame? seek
    const double frameTime = 1000.0 * frameNumber / frameRate;
    sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
  }

  sourceVideo.read(image);
  lastFrameNumber = frameNumber;
}
cbuchart
  • 10,847
  • 9
  • 53
  • 93
  • What is it about `CAP_PROP_POS_FRAMES` that makes it unusable? I'm just wondering why you favor using `CV_CAP_PROP_POS_MSEC` instead. – Mass Dot Net Jul 29 '20 at 13:48
  • @MassDotNet `CAP_PROP_POS_FRAMES` doesn't work correctly when video is encoded using keyframes (more information in [this other question](https://stackoverflow.com/q/19404245/1485885)). – cbuchart Jul 30 '20 at 13:06