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

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.

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
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_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)
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)
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)
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)
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)
# 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)
# 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")
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")
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.

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

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
The best things in life are free.
生活中最美好的事都是免费的
评论 抢沙发

请登录后发表评论

    暂无评论内容