Creating a Professional-Quality Slideshow with Python: A Guide to Building Your Own iMovie Alternative

Dmitry Romanoff - Sep 13 - - Dev Community

In today’s digital world, video content is king, and one of the most engaging formats is the slideshow. Whether you’re showcasing a collection of travel photos, celebrating an event, or simply crafting a visual story, a well-crafted slideshow can captivate audiences and convey your message effectively. While iMovie on Apple devices offers powerful tools for creating such presentations, you can achieve similar results with Python. This article demonstrates how to use Python to build a slideshow application that rivals iMovie’s functionality, using a code snippet as an example.

Project Overview

This project involves creating a Python-based slideshow application that combines images, text, and music into a cohesive video presentation. The core of this project leverages libraries like Pillow for image manipulation, moviepy for video editing, and requests for handling image downloads. Our example will be based on a hypothetical YouTube channel, The Travel Planet, known for its high-quality travel slideshows.

Creating a Professional-Quality Slideshow with Python: A Guide to Building Your Own iMovie Alternative

Key Features of the Python Slideshow Application

  • Image Handling: Load images from a local folder, URLs, or a file. Resize and format images to fit the video dimensions while maintaining their aspect ratio.
  • Title Overlay: Add a customizable title to the first image, with options for positioning, font size, and rotation.
  • Zoom-In Effect: Apply a gradual zoom-in effect to images, enhancing visual interest and engagement.
  • Crossfade Transitions: Smooth transitions between images with crossfade effects.
  • Music Integration: Incorporate background music, synchronized with the video duration.

Python Code Breakdown

Here’s a detailed explanation of the code and how it implements these features:

1. Setting Up Parameters

The code begins by defining various parameters, such as the image folder path, output video file name, image display duration, and video dimensions. It also includes settings for title text, music file, and animation effects.

image_folder = 'input/photos_collection'  # Folder containing images
output_video_file = 'output/slideshow.mp4'  # Output video file name
image_display_duration = 8  # Duration each image is shown in seconds
fps = 30  # Frames per second for the video
video_width = 1920  # Custom video width
video_height = 1080  # Custom video height
title_text = "MyTitle"  # Title text to add 
font_path = "fonts/MyFont.ttf"  # Path to the font file
font_size = 255  # Font size
title_color = (255, 255, 0)  # Yellow color
shadow_color = (0, 0, 0)  # Black color for shadow
shadow_offset = (5, 5)  # Offset for shadow
rotation_angle = 8  # Rotation angle in degrees
music_file = 'music/MyMusic.mp3'  # Music file path
crossfade_duration = 1  # Duration of the crossfade effect between images in seconds
title_mode = 1  # 1: Center, 2: Upper Left, 3: Bottom Left
use_url = 1  # 0: Use URLs, 1: Use folder, 2: Use file
is_random = 1  # 0: Use images as is, 1: Shuffle images randomly
is_animate = 1  # 0: No animation, 1: Apply zoom-in effect
Enter fullscreen mode Exit fullscreen mode

2. Loading Images

Depending on the use_url parameter, images are loaded from a folder, URLs, or a list file. They are then resized to fit the video dimensions.

if use_url == 1:
    images = load_images_from_folder(image_folder)
elif use_url == 2:
    images = load_images_from_file('input/images_list.lst')  # Load images from file
else:
    images = load_images_from_urls(image_urls)
Enter fullscreen mode Exit fullscreen mode

3. Adding Title and Effects

The first image is modified to include a rotating title. Zoom-in effects are applied to all images except the first and last if specified.

if images:
    images[0] = add_rotated_title(images[0], title_text, font_path, font_size, title_color, shadow_color, shadow_offset, rotation_angle, title_mode)

# Apply zoom-in effect
if is_animate == 1:
    for i, img in enumerate(images):
        if i != 0 and i != len(images) - 1:
            img = add_zoom_in_effect(img, zoom_ratio=0.04)
Enter fullscreen mode Exit fullscreen mode

4. Creating and Finalizing the Video

Temporary video clips are created for each image, with crossfade transitions added between them. Background music is also integrated, trimmed to fit the video duration.

# Create video clips for each image
clips = [ImageClip(img_path).set_duration(image_display_duration).set_fps(fps) for img_path in image_paths]

# Apply crossfade transitions
clips_with_transitions = add_crossfade(clips, crossfade_duration)

# Concatenate clips into a final video
final_clip = concatenate_videoclips(clips_with_transitions, method="compose")

# Add music and fadeout effect
final_clip = final_clip.set_audio(trimmed_audio).fadeout(2)
Enter fullscreen mode Exit fullscreen mode

Full Code: Python Slideshow Script

import os
import tempfile
from PIL import Image, ImageDraw, ImageFont
from moviepy.editor import ImageClip, concatenate_videoclips, AudioFileClip
import requests
from io import BytesIO
import numpy as np
import random
from moviepy.video.fx.all import resize
import math

# Parameters
image_folder = 'input/photos_collection'  # Folder containing images
output_video_file = 'output/slideshow.mp4'  # Output video file name
image_display_duration = 8  # Duration each image is shown in seconds
fps = 30  # Frames per second for the video
video_width = 1920  # Custom video width
video_height = 1080  # Custom video height
title_text = "MyTitle"  # Title text to add 
font_path = "fonts/MyFont.ttf"  # Path to the font file
font_size = 255  # Font size
title_color = (255, 255, 0)  # Yellow color
shadow_color = (0, 0, 0)  # Black color for shadow
shadow_offset = (5, 5)  # Offset for shadow
rotation_angle = 8  # Rotation angle in degrees
music_file = 'music/MyMusic.mp3'  # Music file path
crossfade_duration = 1  # Duration of the crossfade effect between images in seconds
title_mode = 1  # 1: Center, 2: Upper Left, 3: Bottom Left
use_url = 1  # 0: Use URLs, 1: Use folder, 2: Use file
is_random = 1  # 0: Use images as is, 1: Shuffle images randomly
is_animate = 1  # 0: No animation, 1: Apply zoom-in effect
image_urls = [
  "https://",
  "https://",
  "https://"
]  # List of image URLs if use_url is 0

# Custom resize function for MoviePy clips
def resize_image_custom(image, size):
    return image.resize(size, Image.Resampling.LANCZOS)  # Use LANCZOS or another resampling filter

# Function to apply resizing to an ImageClip
def resize_clip(clip, size):
    ...
    return clip.fl_image(resize_frame)

# Function to load images from a folder
def load_images_from_folder(folder):
    images = []
    ...
    return images

# Function to load images from URLs
def load_images_from_urls(urls):
    images = []
    ...
    return images

# Function to load images from the file images_list.lst
def load_images_from_file(file_path):
    images = []
    ...
    return images

# Function to resize image to fit within the specified dimensions, preserving aspect ratio
def resize_image_to_fit(image, size):
    ...
    return final_image

# Function to add rotated title to an image
def add_rotated_title(image, text, font_path, font_size, title_color, shadow_color, shadow_offset, angle, title_mode):
    ...
    return final_image


def add_zoom_in_effect(clip, zoom_ratio=0.04):
    ...
    return clip.fl(effect)

# Main processing code
if use_url == 1:
    images = load_images_from_folder(image_folder)
elif use_url == 2:
    images = load_images_from_file('input/images_list.lst')  # Load images from file
else:
    images = load_images_from_urls(image_urls)

# Shuffle images if is_random is set to 1
if is_random == 1:
    random.shuffle(images)  # Shuffle the images randomly

if images:
    images[0] = add_rotated_title(images[0], title_text, font_path, font_size, title_color, shadow_color, shadow_offset, rotation_angle, title_mode)

# Create a list to hold video clips
clips = []

# Create a temporary directory for storing temporary files
with tempfile.TemporaryDirectory() as temp_dir:
    # Create a video clip for each image
    for i, img in enumerate(images):
        img_path = os.path.join(temp_dir, f"temp_image_{i}.png")
        img.save(img_path)

        # Create an ImageClip for each image
        clip = ImageClip(img_path).set_duration(image_display_duration).set_fps(fps)
        clip = resize_clip(clip, (video_width, video_height))  # Resize the clip

        # Apply zoom-in effect to all images except the first and last one if is_animate is set to 1
        if is_animate == 1 and i != 0 and i != len(images) - 1:
            clip = add_zoom_in_effect(clip, zoom_ratio=0.04)  # Zoom ratio can be adjusted

        clips.append(clip)

    # Add crossfade transition between clips
    def add_crossfade(clips, duration):
        if is_animate == 0:
            ...
            return final_clips
        else:
            ...
            return final_clips

    # Apply crossfade transitions
    clips_with_transitions = add_crossfade(clips, crossfade_duration)

    # Concatenate all the clips into a final video
    final_clip = concatenate_videoclips(clips_with_transitions, method="compose")

    # Add fadeout effect to the final video clip
    final_clip = final_clip.fadeout(2)  # 2-second fade out

    # Load the music file and trim it to match the final video's duration
    if os.path.exists(music_file):
        audio_clip = AudioFileClip(music_file)
        audio_duration = final_clip.duration  # Duration of the final video clip
        trimmed_audio = audio_clip.subclip(0, audio_duration).audio_fadeout(2)  # Trim and fade out audio

        # Set the trimmed audio to the final video
        final_clip = final_clip.set_audio(trimmed_audio)

    # Write the final video to a file
    final_clip.write_videofile(output_video_file, codec='libx264')

# Print out the length of the slideshow video
duration_seconds = final_clip.duration
duration_minutes = int(duration_seconds // 60)
remaining_seconds = int(duration_seconds % 60)
print(f"Slideshow with music saved as {output_video_file}")
print(f"Length of the slideshow video: {duration_minutes} min {remaining_seconds} seconds")
Enter fullscreen mode Exit fullscreen mode

Conclusion

This Python script serves as a powerful alternative to traditional video editing software like iMovie, demonstrating how you can harness Python’s capabilities to create professional-quality slideshows. By customizing parameters and applying various effects, you can tailor your slideshow to match your vision, just like the high-quality content seen on The Travel Planet.

Feel free to experiment with the code and adapt it to your specific needs, creating visually engaging content that tells your story in a compelling way.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player