I use cv2
for creating videos from different frames that I have. When I create the video, I cannot change the fps (frame per second). I want the video be slow at the beginning but fast towards the end, meaning small fps at the beginning but large ones towards the end. However, when I instantiate cv2.VideoWriter
I cannot change the fps anymore. What should I do?
Replicable code
import numpy as np
import cv2, os
import matplotlib
image_size = 200
def create_image_array(image_size):
image_array = np.random.randn(image_size, image_size)
row = np.random.randint(0, image_size)
image_array[row, :] = 100
return image_array
frame_numbers = 200
for i in range(frame_numbers):
image_array = create_image_array(image_size)
matplotlib.image.imsave(f'./shots/frame_{i:03d}.png', image_array)
def make_a_video(shots_folder, video_path):
shots_folder = 'shots'
fps = 25
images = [img for img in os.listdir(shots_folder) if img.endswith(".png")]
images = sorted(images)[:]
frame = cv2.imread(os.path.join(shots_folder, images[0]))
height, width, layers = frame.shape
video = cv2.VideoWriter(video_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (width, height))
for image in images:
video.write(cv2.imread(os.path.join(shots_folder, image)))
cv2.destroyAllWindows()
video.release()
shots_folder = 'shots'
video_path = 'video.mp4'
make_a_video(shots_folder, video_path)
Think of it as if you are copying one video to another. While doing this you actually don't want to change the output frame rate but the input frame rate. Here is a very basic example of how you can continuesly change the read frame rate while you are writing a stream of constant frame rate.
The example generates a sample video of 200 frames and writes a video of 320 frames. The first 100 frames are read with 5 fps, the next 100 frames are read with an increasing rate from 5 to 25 fps and the last 120 frames are read with 25 fps.
import math
import numpy as np
import cv2
image_size = 200
src_fps = 25
dst_fps = 25
# create sample frame with four rotating balls
def create_sample_frame(size, frame_no, fps):
img = np.zeros((size, size, 3), dtype=np.uint8)
ctr = size // 2
for i in range(1, 5):
phi = -frame_no / fps * i
r = size * (0.5 - 0.1 * i)
cv2.circle(img, (round(math.sin(phi) * r + ctr), round(math.cos(phi) * r + ctr)), size // 30, (0, 255, 0), -1)
return img
frames = [create_sample_frame(image_size, i, src_fps) for i in range(200)]
height, width, layers = frames[0].shape
video = cv2.VideoWriter('video.mp4', cv2.VideoWriter_fourcc(*'mp4v'), dst_fps, (width, height))
# ramp array with number of destination frames, start fps of source, end fps of source
fps_ramp = [[100, 5, 5], [100, 5, 25], [120, 25, 25]]
src_pos = 0
dst_pos = 0
for n, start_src_fps, end_src_fps in fps_ramp:
for i in range(n):
print(f"writing source frame {int(src_pos)} to destination frame {dst_pos}")
video.write(frames[round(src_pos)])
dst_pos += 1
cur_fps = (end_src_fps - start_src_fps) * (i / n) + start_src_fps
src_pos += cur_fps / src_fps
video.release()
Result: