Home > Software design >  OpenCV/Python - get specific frame ranges, CAP_PROP_POS_FRAMES not working
OpenCV/Python - get specific frame ranges, CAP_PROP_POS_FRAMES not working

Time:09-29

I need to get some groups of consecutive frames from one video. The groups are all formed by the same amount of frames, and they are consecutive, so something like:

[10, 11, 12, 13, 14, 15],
[32, 33, 34, 35, 36, 37],
[123, 124, 125, 126, 127, 128]

this is just an example, in my case I don't know the ranges of indices in advance, they are provided during the code execution!

I tried to use (e.g., as suggested here) this code every time I have to extract the frames in a given range

video_capture = cv2.VideoCapture(video_path)
for frame_id in frames_range:
    video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_ind)
    success, image = video_capture.read()
    if not success:
        logging.warning(f"Error reading frame {frame_id}", frame_ind)
    # do something with frames

But this is not working, I'm generally always getting the first N frames of the video (N is the length of the ranges, 6 in my example).
I read that video_capture.set could fail in some cases due to the video compression and, as a matter of fact, I am getting some errors when reading certain video frames with this approach.

Another simple approach is to go every time through the video frame by frame, and select the ones I need:

def get_required_frames(frames_range: list, video_cap) -> list:
    frames = []
    frame_counter = 0
    while True:
        ret, frame = video_cap.read()
        if not ret:
            return []
        # collect frames in selected range
        if frame_counter in frames_range:
            frames.append(frame)
        # after selected range, stop loop
        if frame_counter > frames_range[-1]:
            break
        frame_counter  = 1
    return frames 

# every time I get the required range, I call this function
frames = get_required_frames(frames_range, cv2.VideoCapture(video_path))
# do something with frames

This second method is working ok, but it's kinda slow, since I have to go through the whole video from frame 0 until I reach the current frame range every time (and some of the videos can be quite long). Considering the frame ranges are always consecutive, is there any way to leave the video..."hanging", so that at least I can start again from the last position used in the previous range of frames?

CodePudding user response:

I'm not sure if this is a typo created while simplifying the code for this post, but this seems wrong:

for frame_id in frames_range:
    video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_ind)

You are getting 'frame_id' from the 'frames_range' but then setting the index of video_capture to 'frame_ind'.

If that's not the issue then yes you're right - setting frame index via 'CAP_PROP_POS_FRAMES' is not necessarily reliable. In answer to your question if you can leave the video_capture 'hanging' the answer is yes. You should be able to do the following:

cap = cv2.VideoCapture(video_path)
frames = get_required_frames(frames_range, cap)

When you read from 'cap' again, it should be where you left it.

  • Related