Reducing Video Frames and Frame Rates (FPS) in Python

usmanmalik57 2 Tallied Votes 617 Views Share

A video is a series of images, or frames, shown in rapid succession. Its frame rate, measured in frames per second (FPS), dictates the display speed. For instance, a 30 FPS video shows 30 frames each second. The frame count and frame rate determine a video's detail, smoothness, file size, and the processing power needed for playback or editing.

Higher frame rates and more frames result in finer detail and smoother motion but at the cost of larger file sizes and greater processing requirements. Conversely, lower frame rates and fewer frames reduce detail and smoothness but save on storage and processing needs.

In this article, you will see how to reduce the frame rate per second for a video, and total number of frames in a video using the Python programming language.

But before that, let's see why you would want to reduce the number of frames and frame rate of a video.

Why Reduce the Number of Frames and the Frame Rate of a Video?

Reducing the number of frames and frame rate of a video can be beneficial for several reasons:

Storage Efficiency: Videos with fewer frames and lower frame rates take up less disk space, which is helpful when storage capacity is limited or for easier online sharing.

Bandwidth Conservation: Such videos use less network bandwidth, making them suitable for streaming over slow or unstable internet connections.

Performance Optimization: They require fewer computational resources, ideal for low-end devices or resource-intensive processes like deep learning algorithms.

Let's now see how to reduce video frame rates in Python.

Reducing Frame Rates Without Video Processing

One of the simplest ways to reduce the frame rate of a video without applying any preprocessing to the video is to use the moviepy library in Python, which is a powerful and easy-to-use library for video editing and manipulation.

The following script installs the moviepy and deepface libraries. We will use the deepface library in a later section of the article.


! pip install deepface
! pip install moviepy

Next, we import the libraries required to run scripts in this article.

from moviepy.editor import VideoFileClip
import cv2
import glob
import os
from deepface import DeepFace
import numpy as np
from moviepy.editor import *
import math

To reduce the frame rate of a video, we will define a function that takes a video path, an output directory, and a new frame rate as inputs and reduces the frame rate of the video accordingly. The function performs the following steps:

  1. It extracts the file name from the video path using the os.path.basename function.
  2. Loads the video using the VideoFileClip class and prints the original frame rate using the fps attribute.
  3. Reduces the frame rate of the video using the .set_fps method and passes the new frame rate as an argument.
  4. Finally, it writes the modified video to the output directory using the write_videofile method.

The function looks like this:


def reduce_fps(video_path, output_directory, new_fps):

    # Extract file name
    file_name = os.path.basename(video_path)

    # Load the video
    video = VideoFileClip(video_path)

    # print the original FPS
    print(f"Orignal FPS: {video.fps}")

    # Reduce the FPS
    video = video.set_fps(new_fps)

    # Write the modified video
    output_path = os.path.join(output_directory, file_name)
    video.write_videofile(output_path, fps=new_fps)

To use the function, specify the input video path, the output directory, and the new frame rate.

For example, if you want to reduce the frame rate of a video named input_video.mp4 to 10 FPS and save the modified video to a directory named D:\Datasets\Reduced FPS Videos, you can do this:


input_video = r"D:\Datasets\input_video.mp4"
output_directory = r"D:\Datasets\Reduced FPS Videos"
new_fps = 10

reduce_fps(input_video,
           output_directory,
           new_fps)

The function will print the original and the new frame rate and write the modified video to the output directory. You can see the output in the image below:

Output:

image_1.png

Reducing FPS of Videos with Special Characters in Names

Sometimes, you might encounter videos that have special characters in their names, such as ---#input_video.mp4. These characters can cause problems when writing the modified video using the moviepy library because they can be interpreted as commands or arguments by the underlying ffmpeg tool that moviepy uses.

For example, if you try to reduce the frame rate of a video named ---#input_video.mp4 using the same function as before, you will get an error like this:


input_video = r"D:\Datasets\---#input_video.mp4"
output_directory = r"D:\Datasets\Reduced FPS Videos"
new_fps = 10

reduce_fps(input_video,
           output_directory,
           new_fps)

Output:

image_2.png

To avoid this error, you need to modify the function slightly. Instead of writing the modified video directly to the output path with the same file name, you need to write the video using a temporary output path with a different file name, such as temp.mp4.

You can then rename the temporary file to the actual file name using the shutil.move function

Here is a modified reduce_fps function that writes video files with special characters in their names.


def reduce_fps(video_path, output_directory, new_fps):
    # Extract file name
    file_name = os.path.basename(video_path)

    # Load the video
    video = VideoFileClip(video_path)

    # Reduce the FPS
    video = video.set_fps(new_fps)

    # Prepare the output path with a temporary file name
    temp_output_path = os.path.join(output_directory, "temp.mp4")
    print(f"Temporary output path is: {temp_output_path}")

    # Write the modified video to the temporary file
    video.write_videofile(temp_output_path, fps=new_fps)

    # Prepare the final output path
    final_output_path = os.path.join(output_directory, file_name)
    print(f"Final output path is: {final_output_path}")

    # Rename the temporary file to the final file name
    shutil.move(temp_output_path, final_output_path)

The function will print the temporary and the final output paths and write the modified video to the output directory. You can see the output in the image below:

Output:

image_3.png

Reducing Number of Frames with Video Processing

You have already seen how to reduce the frame rate of a video without video processing in Python. Sometimes, you might need to reduce the total number of frames in a video without losing much information from the video.

A possible solution for that is to extract frames at regular intervals. This way, the total number of frames can be reduced and also the maximum information from the video can be retained. This can be particularly handy when you want to do some processing on video frames, but processing each frame can be costly.

For example, in the following script, we define the reduce_total_frames function that reduces the total number of frames in a video and extracts faces from it. The function takes three arguments: the video path, the output directory, and the number of frames to capture.

Next, the function reads the video file and gets the total number of frames, the frame rate, and the file name. The function calculates the interval for capturing frames by dividing the total number of frames by the number of frames to capture.

The function loops over the video frames and checks if the current frame index is divisible by the interval. If it is, the function tries to extract the face from the frame using the DeepFace.extract_faces function. The function uses the retinaface backend, a state-of-the-art face detector.

If the function detects a face with a high confidence score, it converts the face image to the format expected by the moviepy library and appends it to a list. The function increments the captured frame count by 1.

The function creates a video clip from the list of face images using the ImageSequenceClip class from the moviepy library and writes it to the output path.

The following script defines the reduce_total_frames function that performs the above steps.



def reduce_total_frames(video_path, directory, frames_to_capture):

    faces = []
    cap = cv2.VideoCapture(video_path)  # Read video file
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    print("==================================")
    print(f"Original total number of frames: {total_frames}")
    print("===================================")

    fps = cap.get(cv2.CAP_PROP_FPS)
    fps = math.ceil(fps)

    path = os.path.basename(video_path)

    # Calculate interval for capturing frames

    interval = max(1, float(total_frames) / float(frames_to_capture))
    interval = np.round(interval)

    print("===============================")
    print(f"Interval to process frame : {interval}")
    print("===============================")

    frame_index = 0
    captured_frame_count = 0

    while cap.isOpened() and captured_frame_count < frames_to_capture:
        ret, frame = cap.read()

        if not ret:
            break

        # Capture frames at calculated intervals
        if frame_index % interval == 0:
            print(f"processing frame number {frame_index + 1}")
            try:
                face_props = DeepFace.extract_faces(img_path=frame,
                                                    target_size=(224, 224),
                                                    detector_backend="retinaface",
                                                    enforce_detection=False)

                if face_props:
                    frame = cv2.cvtColor(face_props[0]['face'], cv2.COLOR_BGR2RGB)
                    frame  = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    confidence = face_props[0]['confidence']
                    if confidence > 0.990:
                        features_dict = {"frames": frame, "confidence": confidence}
                        faces.append(features_dict)
                        captured_frame_count += 1
            except Exception as e:
                print(f"Error processing frame at index {frame_index}: {e}")


            if captured_frame_count % 10 == 0:
                print("============================")
                print(f"Total frames processed: {captured_frame_count}")
                print("============================")


        frame_index += 1

    image_frames = []

    for video_frame in faces:
        a = video_frame['frames']
        image_frame = np.interp(a, (a.min(), a.max()), (0, 255)).astype(np.uint8)
        image_frames.append(image_frame)

     # Create a video clip from the frames
    clip = ImageSequenceClip(image_frames, fps = fps)

    final_output_path = os.path.join(directory, path)

    clip.write_videofile(final_output_path)


The function will print the original and the reduced number of frames, the interval, the frame index, the captured frame count, and the output path. You can see the output in the image below:

Output:

image_4.png

Conclusion

In this article, you learned to reduce video frame rate and frame count using Python with moviepy and DeepFace libraries. You also discovered how to handle videos with special characters in their names. These techniques save storage space, reduce bandwidth usage, and enhance performance, especially for demanding video processing tasks like deep learning.

I hope you found this article helpful. If you have any questions or feedback, please share them below.

Reverend Jim 4,968 Hi, I'm Jim, one of DaniWeb's moderators. Moderator Featured Poster

Since the underlying tool is ffmpeg, why bother with all the code and overhead? You can just use ffmpeg directly with the -r option.

Aside from this I enjoyed the article (and the others you have posted).

usmanmalik57 16 Junior Poster in Training

Yes, that's an option, but while you are developing Python applications where you have to process multiple videos, I don't think ffmpeg is scalable enough. Thanks for your feedback r though :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.