Unlock the secrets of your code with our AI-powered Code Explainer. Take a look!
Digital media such as videos created a lot of creative options where we can process video and audio files and create awesome effects with them. A lot of media processing programs and tools offer the reversing feature.
In this tutorial, you will learn how you can reverse a video in Python using the MoviePy library.
The basic idea behind the code you'll see in this tutorial, is we extract all the frames from the video using a configurable fps parameter, then we load these frames back into a video in the reverse order.
To get started, let's install the required libraries:
$ pip install tqdm moviepy numpy
Let's start now by importing our modules:
from moviepy.editor import VideoFileClip, ImageSequenceClip
import numpy as np
import os
from datetime import timedelta, datetime
from glob import glob
from tqdm import tqdm
import shutil
Next, to avoid redundancy, I'm bringing the below code from this tutorial:
# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total
SAVING_FRAMES_PER_SECOND = 30
def format_timedelta(td):
"""Utility function to format timedelta objects in a cool way (e.g 00:00:20.05)
omitting microseconds and retaining milliseconds"""
result = str(td)
try:
result, ms = result.split(".")
except ValueError:
return result + ".00".replace(":", "-")
ms = int(ms)
ms = round(ms / 1e4)
return f"{result}.{ms:02}".replace(":", "-")
def extract_frames(video_file, verbose=1):
# load the video clip
video_clip = VideoFileClip(video_file)
# make a folder by the name of the video file
filename, _ = os.path.splitext(video_file)
if not os.path.isdir(filename):
os.mkdir(filename)
# if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)
saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND)
# if SAVING_FRAMES_PER_SECOND is set to 0, step is 1/fps, else 1/SAVING_FRAMES_PER_SECOND
step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second
iteration = np.arange(0, video_clip.duration, step)
if verbose:
iteration = tqdm(iteration, desc="Extracting video frames")
# iterate over each possible frame
for current_duration in iteration:
# format the file name and save it
frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-")
frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg")
# save the frame with the current duration
video_clip.save_frame(frame_filename, current_duration)
return filename, video_clip.fps
You'll get a lot of details in the extracting frames tutorial. However, in brief, the extract_frames()
function accepts the video file path as a parameter and extracts frames with the corresponding duration into a folder named after the original video file name. Finally, it returns that folder name.
Now, let's make a function that reads these extract frames in the reverse order and save them as an inverted video:
def reverse_video(frames_path, video_fps, remove_extracted_frames=True):
frame_files = glob(os.path.join(frames_path, "*"))
# sort by duration in descending order
frame_files = sorted(frame_files, key=lambda d: datetime.strptime(d.split("frame")[1], "%H-%M-%S.%f.jpg"), reverse=True)
# calculate the FPS, getting the minimum between the original FPS and the parameter we set
saving_frames_per_second = min(video_fps, SAVING_FRAMES_PER_SECOND)
if saving_frames_per_second == 0:
# if the parameter is set to 0, automatically set it to the original video fps
saving_frames_per_second = video_fps
print("Saving the video with FPS:", saving_frames_per_second)
# load the frames into a image sequence clip (MoviePy)
image_sequence_clip = ImageSequenceClip(frame_files, fps=saving_frames_per_second)
# write the video file to disk
output_filename = f"{frames_path}-inverted.mp4"
image_sequence_clip.write_videofile(output_filename)
if remove_extracted_frames:
# if set to True, then remove the folder that contain the extracted frames
shutil.rmtree(frames_path)
The reverse_video()
function expects the folder name that contains the video frames extracted by the previous function as an argument. We use the glob()
function from the glob module to get all the file names of the frames.
Next, we sort those frame files by the duration in descending order. After that, we pass these frames in the reversed order to the ImageSequenceClip()
object from MoviePy, and set the FPS to the minimum of SAVING_FRAMES_PER_SECOND
we used in the frame extraction process and the original video FPS, the reason is when we set a higher FPS than the original video FPS, the resulting video will be speeded.
We then use the write_videofile()
method to save the reversed video to a video file on the disk.
If you set the remove_extracted_frames
to True
(as the default), the folder where the extracted frames are located will be deleted along with its contents.
Finally, let's use these functions to accomplish our task:
if __name__ == "__main__":
import sys
video_file = sys.argv[1]
frames_folder_path, video_fps = extract_frames(video_file)
reverse_video(frames_folder_path, video_fps=video_fps)
We're done! Let's try it out with a YouTube video of a scene from TENET movie:
$ python reverse_video.py Tenet-the-breach-scene-in-forward.mp4
This is the output of the code:
Extracting video frames: 100%|██████████████████████████████████████████████████████████████████████████████████████████| 485/485 [00:10<00:00, 47.71it/s]
Moviepy - Building video Tenet-the-breach-scene-in-forward-inverted.mp4.
Moviepy - Writing video Tenet-the-breach-scene-in-forward-inverted.mp4
Moviepy - Done !
Moviepy - video ready Tenet-the-breach-scene-in-forward-inverted.mp4
And the reversed video appeared in the current directory!
If your video is quite long, then make sure to reduce the FPS (the SAVING_FRAMES_PER_SECOND
parameter), I have set it to as low as 10, you can increase it if you feel the lag in the output video, this will increase the size of the video, as well as the execution time of the program to extract frames and load them in the reverse order.
Obviously, the output video will have no sound, you can use AudioClip()
loaded from audio (maybe the audio extracted from the original video), and simply set image_sequence_clip.audio
to this newly created AudioClip()
object, and then continue the same process of saving the video.
You can get the demo video here, and the full code here.
Learn also: How to Combine a Static Image with Audio in Python
Happy coding ♥
Save time and energy with our Python Code Generator. Why start from scratch when you can generate? Give it a try!
View Full Code Explain My Code
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!