Create Stunning Slideshows with the Ken Burns Effect Using Python

Discover how to create captivating video slideshows from your images and music using Python and the Ken Burns effect. Enhance your storytelling and bring your memories to life!

Create Stunning Slideshows with the Ken Burns Effect Using Python

Introduction:

Do you have a bunch of cool photos and want to turn them into an awesome video slideshow? In this blog post, we'll learn how to create a video slideshow using Python with the Ken Burns effect, which makes your photos zoom and pan to create a dynamic and engaging video. We'll be using the MoviePy library to put together our video and add background music. Don't worry, we'll explain everything step-by-step, so you'll be able to create your own video slideshow in no time!


Example Video:

I created this nice little video of robots studying with the script below.


First, let's import the necessary libraries:

import os
import glob
from moviepy.editor import ImageClip, CompositeVideoClip, AudioFileClip, concatenate_videoclips, vfx
import random

Now, let's create a function to get all the image files in a folder:

def get_images_in_folder(folder_path, extensions=("*.jpg", "*.jpeg", "*.png")):
    images = []
    for ext in extensions:
        images.extend(glob.glob(os.path.join(folder_path, ext)))
    return images

The get_images_in_folder function will search for image files with the specified extensions (JPG, JPEG, and PNG) in the provided folder and return a list of image file paths.

Next, let's create the main function that will generate the video with the Ken Burns effect:

def create_video_with_ken_burns_effect(image_files, music_file, output_file, duration_per_image=3, zoom_factor=1.2, pan_factor=0.3, fps=24):
    # ... (rest of the function)

The create_video_with_ken_burns_effect function takes several arguments:

  • image_files: A list of image file paths.
  • music_file: The path to the background music file.
  • output_file: The path to save the output video.
  • duration_per_image: How long each image will be shown in seconds (default is 3 seconds).
  • zoom_factor: How much the image will be zoomed in (default is 1.2, or 20% larger).
  • pan_factor: The maximum percentage of the image that the pan effect will cover (default is 0.3, or 30%).
  • fps: The frames per second of the output video (default is 24).

Inside the function, we create a list of clips and loop through each image file:

clips = []
for image_file in image_files:
    # ... (rest of the loop)

For each image file, we create an ImageClip and apply the Ken Burns effect by zooming and panning the image:

clip = ImageClip(image_file, duration=duration_per_image)
clip = clip.fx(vfx.resize, width=clip.w * zoom_factor)  # Zoom the image

To create a random pan effect, we calculate random start and end positions for both the x and y axes:

x_start = random.uniform(0, clip.w * pan_factor)
y_start = random.uniform(0, clip.h * pan_factor)
x_end = random.uniform(clip.w - (clip.w // zoom_factor) - x_start, clip.w * pan_factor)
y_end = random.uniform(clip.h - (clip.h // zoom_factor) - y_start, clip.h * pan_factor)

Then, we crop the image and resize it, making sure it smoothly zooms and pans during the duration of the clip:

width = int(clip.w // zoom_factor)
height = int(clip.h // zoom_factor)
clip = clip.fx(vfx.crop, x1=x_start, y1=y_start, x2=x_start + width, y2=y_start + height)
clip = clip.fx(vfx.resize, width=width, height=height).resize(lambda t: 1 + t * (zoom_factor - 1) / duration_per_image)
clip = clip.set_position(lambda t: (x_start - t * (x_end - x_start) / duration_per_image, y_start - t * (y_end - y_start) / duration_per_image))

We also add a crossfade transition to make the video look smoother:

clip = clip.crossfadein(1).crossfadeout(1)
clips.append(clip)

After processing all the images, we concatenate the clips, add the background music, and save the video to the output file:

video = concatenate_videoclips(clips, method="compose")
audio = AudioFileClip(music_file)
audio = audio.subclip(0, video.duration)
video = video.set_audio(audio)
video.write_videofile(output_file, codec='libx264', audio_codec='aac', fps=fps)

Finally, we'll call our functions to create the video slideshow:

if __name__ == "__main__":
    folder_path = "./imgs"
    image_files = get_images_in_folder(folder_path)
    music_file = "summer.mp3"
    output_file = "RobotsStudying.mp4"
    create_video_with_ken_burns_effect(image_files, music_file, output_file)

Wrap-up:

Congratulations! You've learned how to create a video slideshow with the Ken Burns effect using Python and the MoviePy library. Now you can turn your favorite photos into engaging video slideshows with background music to share with friends and family. The best part is that you can customize the code to create different effects and styles to make your slideshows unique.

Next steps:

Why not give it a try? Experiment with different zoom factors, pan effects, and transition times to create a one-of-a-kind video slideshow. You can also explore other MoviePy features to add more effects and make your video even more impressive.

Don't forget to share your creations and inspire others!

Full Script:


import os
import glob
from moviepy.editor import ImageClip, CompositeVideoClip, AudioFileClip, concatenate_videoclips, vfx
import random


def get_images_in_folder(folder_path, extensions=("*.jpg", "*.jpeg", "*.png")):
    images = []
    for ext in extensions:
        images.extend(glob.glob(os.path.join(folder_path, ext)))
    return images


def create_video_with_ken_burns_effect(image_files, music_file, output_file, duration_per_image=3, zoom_factor=1.2, pan_factor=0.3, fps=24):
    clips = []
    for image_file in image_files:
        # Create an ImageClip and apply the Ken Burns effect
        clip = ImageClip(image_file, duration=duration_per_image)
        clip = clip.fx(vfx.resize, width=clip.w * zoom_factor)  # Zoom the image

        # Apply the random pan effect
        x_start = random.uniform(0, clip.w * pan_factor)
        y_start = random.uniform(0, clip.h * pan_factor)
        x_end = random.uniform(clip.w - (clip.w // zoom_factor) - x_start, clip.w * pan_factor)
        y_end = random.uniform(clip.h - (clip.h // zoom_factor) - y_start, clip.h * pan_factor)
        width = int(clip.w // zoom_factor)
        height = int(clip.h // zoom_factor)
        clip = clip.fx(vfx.crop, x1=x_start, y1=y_start, x2=x_start + width, y2=y_start + height)
        clip = clip.fx(vfx.resize, width=width, height=height).resize(lambda t: 1 + t * (zoom_factor - 1) / duration_per_image)
        clip = clip.set_position(lambda t: (x_start - t * (x_end - x_start) / duration_per_image, y_start - t * (y_end - y_start) / duration_per_image))

        clip = clip.crossfadein(1).crossfadeout(1)  # Add crossfade transition
        clips.append(clip)

    # Concatenate the clips and add the background music
    video = concatenate_videoclips(clips, method="compose")
    audio = AudioFileClip(music_file)
    audio = audio.subclip(0, video.duration)  # Cut the audio to match the video duration
    video = video.set_audio(audio)

    # Save the video to the output file
    video.write_videofile(output_file, codec='libx264', audio_codec='aac', fps=fps)


if __name__ == "__main__":
    folder_path = "./imgs"
    # Get a list of image files in the folder
    image_files = get_images_in_folder(folder_path)

    # Background music file
    music_file = "summer.mp3"

    # Output video file
    output_file = "RobotsStudying.mp4"

    # Create the video
    create_video_with_ken_burns_effect(image_files, music_file, output_file)