| 
 | 1 | +from moviepy.editor import VideoFileClip, ImageSequenceClip  | 
 | 2 | +import numpy as np  | 
 | 3 | +import os  | 
 | 4 | +from datetime import timedelta, datetime  | 
 | 5 | +from glob import glob  | 
 | 6 | +from tqdm import tqdm  | 
 | 7 | +import shutil  | 
 | 8 | + | 
 | 9 | +# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total  | 
 | 10 | +SAVING_FRAMES_PER_SECOND = 30  | 
 | 11 | + | 
 | 12 | +def format_timedelta(td):  | 
 | 13 | +    """Utility function to format timedelta objects in a cool way (e.g 00:00:20.05)   | 
 | 14 | +    omitting microseconds and retaining milliseconds"""  | 
 | 15 | +    result = str(td)  | 
 | 16 | +    try:  | 
 | 17 | +        result, ms = result.split(".")  | 
 | 18 | +    except ValueError:  | 
 | 19 | +        return result + ".00".replace(":", "-")  | 
 | 20 | +    ms = int(ms)  | 
 | 21 | +    ms = round(ms / 1e4)  | 
 | 22 | +    return f"{result}.{ms:02}".replace(":", "-")  | 
 | 23 | + | 
 | 24 | + | 
 | 25 | +def extract_frames(video_file, verbose=1):  | 
 | 26 | +    # load the video clip  | 
 | 27 | +    video_clip = VideoFileClip(video_file)  | 
 | 28 | +    # make a folder by the name of the video file  | 
 | 29 | +    filename, _ = os.path.splitext(video_file)  | 
 | 30 | +    if not os.path.isdir(filename):  | 
 | 31 | +        os.mkdir(filename)  | 
 | 32 | +    # if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)  | 
 | 33 | +    saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND)  | 
 | 34 | +    # if SAVING_FRAMES_PER_SECOND is set to 0, step is 1/fps, else 1/SAVING_FRAMES_PER_SECOND  | 
 | 35 | +    step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second  | 
 | 36 | +    iteration = np.arange(0, video_clip.duration, step)  | 
 | 37 | +    if verbose:  | 
 | 38 | +        iteration = tqdm(iteration, desc="Extracting video frames")  | 
 | 39 | +    # iterate over each possible frame  | 
 | 40 | +    for current_duration in iteration:  | 
 | 41 | +        # format the file name and save it  | 
 | 42 | +        frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-")  | 
 | 43 | +        frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg")  | 
 | 44 | +        # save the frame with the current duration  | 
 | 45 | +        video_clip.save_frame(frame_filename, current_duration)  | 
 | 46 | +    return filename, video_clip.fps  | 
 | 47 | + | 
 | 48 | + | 
 | 49 | + | 
 | 50 | +def reverse_video(frames_path, video_fps, remove_extracted_frames=True):  | 
 | 51 | +    frame_files = glob(os.path.join(frames_path, "*"))  | 
 | 52 | +    # sort by duration in descending order  | 
 | 53 | +    frame_files = sorted(frame_files, key=lambda d: datetime.strptime(d.split("frame")[1], "%H-%M-%S.%f.jpg"), reverse=True)  | 
 | 54 | +    # calculate the FPS, getting the minimum between the original FPS and the parameter we set  | 
 | 55 | +    saving_frames_per_second = min(video_fps, SAVING_FRAMES_PER_SECOND)  | 
 | 56 | +    if saving_frames_per_second == 0:  | 
 | 57 | +        # if the parameter is set to 0, automatically set it to the original video fps  | 
 | 58 | +        saving_frames_per_second = video_fps  | 
 | 59 | +    print("Saving the video with FPS:", saving_frames_per_second)  | 
 | 60 | +    # load the frames into a image sequence clip (MoviePy)  | 
 | 61 | +    image_sequence_clip = ImageSequenceClip(frame_files, fps=saving_frames_per_second)  | 
 | 62 | +    # write the video file to disk  | 
 | 63 | +    output_filename = f"{frames_path}-inverted.mp4"  | 
 | 64 | +    image_sequence_clip.write_videofile(output_filename)  | 
 | 65 | +    if remove_extracted_frames:  | 
 | 66 | +        # if set to True, then remove the folder that contain the extracted frames  | 
 | 67 | +        shutil.rmtree(frames_path)  | 
 | 68 | + | 
 | 69 | + | 
 | 70 | + | 
 | 71 | +if __name__ == "__main__":  | 
 | 72 | +    import sys  | 
 | 73 | +    video_file = sys.argv[1]  | 
 | 74 | +    frames_folder_path, video_fps = extract_frames(video_file)  | 
 | 75 | +    reverse_video(frames_folder_path, video_fps=video_fps)  | 
 | 76 | +      | 
0 commit comments