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

Time:09-25

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

CodePudding user response:

You need to know how "video" works.

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:

  • ffmpeg -i INPUT -r 3 OUTPUT would read the entire video, and duplicate/drop frames as needed to achieve 3 frames per second, while maintaining the "speed" of what you see in the video. It would also have to re-encode the result. That's only a sensible option if you need to read the same video repeatedly in that frame rate.

  • Involving GNU Parallel with ffmpeg would be pointless because ffmpeg itself runs its decoding and encoding in parallel (for most codecs), using all available CPU.

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`

  • Related