pythonopencv

How to change frame per second (FPS) while using `cv2` when converting video to frames?


Currently I am using 2017_08_31_0121.mp4 as my video which is a 21-second long video and once I break it into frames, I get 504 frames. This means that frame per second is set to 24. I want to change the number of frames but I do not know which part of the following code is responsible for setting frame per second.

Questions:

  1. I thought for a long time that the default FPS is 25 but now I have 24, can you please let me know where the default FPS is set and what it is?

  2. If I want to use a custom FPS let's say 10, how can I modify the following code to do it?

import cv2
vidcap = cv2.VideoCapture('/content/2017_08_31_0121.mp4')
success, image = vidcap.read()
count = 0
while success:
  if count<10:
    id = f'00{count}'
  elif count < 100:
    id = f'0{count}'
  else:
    id = count
  cv2.imwrite(f"./new_frames/frame{id}.jpg", image)    # save frame as JPEG file      
  success, image = vidcap.read()
  count += 1

Solution

  • You need to know how "video" works. I'll simplify somewhat, relative to some codecs that complicate things.

    Video consists of keyframes and P/B-frames. Keyframes are complete images on their own. To decode a P/B-frame, preceding frames need to be decoded first. Some video files consist only of keyframes ("intra"). Some video files consist of a keyframe every ~0.1-10 seconds, and only P/B-frames in between.

    You can skip around in a video, but you can only directly skip to keyframes. If you wanted to skip to a non-keyframe, you'd have to first skip to the preceding keyframe, and then decode each following frame, until you're at the destination.

    Ideas I would recommend that you not follow:

    Here is what you can do:

    use the methods grab and retrieve of VideoCapture. Call grab repeatedly. This does the minimal work to decode a frame and advance in the video. Call retrieve for the frame you actually want. This does the rest of the work and it will give you that frame as an array.

    You would have to check the video's fps value using vidcap.get(cv.CAP_PROP_FPS) and then count along and decide if you only need to grab, or both grab and retrieve.

    import numpy as np
    import cv2 as cv
    
    vidcap = cv2.VideoCapture('/content/2017_08_31_0121.mp4')
    assert vidcap.isOpened()
    
    fps_in = vidcap.get(cv.CAP_PROP_FPS)
    fps_out = 3
    
    index_in = -1
    index_out = -1
    
    while True:
        success = vidcap.grab()
        if not success: break
        index_in += 1
    
        out_due = int(index_in / fps_in * fps_out)
        if out_due > index_out:
            success, frame = vidcap.retrieve()
            if not success: break
            index_out += 1
    
            # do something with `frame`