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 imagesoutput_video_file = 'output/slideshow.mp4' # Output video file nameimage_display_duration = 8 # Duration each image is shown in secondsfps = 30 # Frames per second for the videovideo_width = 1920 # Custom video widthvideo_height = 1080 # Custom video heighttitle_text = "MyTitle" # Title text to addfont_path = "fonts/MyFont.ttf" # Path to the font filefont_size = 255 # Font sizetitle_color = (255, 255, 0) # Yellow colorshadow_color = (0, 0, 0) # Black color for shadowshadow_offset = (5, 5) # Offset for shadowrotation_angle = 8 # Rotation angle in degreesmusic_file = 'music/MyMusic.mp3' # Music file pathcrossfade_duration = 1 # Duration of the crossfade effect between images in secondstitle_mode = 1 # 1: Center, 2: Upper Left, 3: Bottom Leftuse_url = 1 # 0: Use URLs, 1: Use folder, 2: Use fileis_random = 1 # 0: Use images as is, 1: Shuffle images randomlyis_animate = 1 # 0: No animation, 1: Apply zoom-in effectimage_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 effectimage_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 fileelse: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 effectif 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 imageclips = [ImageClip(img_path).set_duration(image_display_duration).set_fps(fps) for img_path in image_paths]# Apply crossfade transitionsclips_with_transitions = add_crossfade(clips, crossfade_duration)# Concatenate clips into a final videofinal_clip = concatenate_videoclips(clips_with_transitions, method="compose")# Add music and fadeout effectfinal_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 osimport tempfilefrom PIL import Image, ImageDraw, ImageFontfrom moviepy.editor import ImageClip, concatenate_videoclips, AudioFileClipimport requestsfrom io import BytesIOimport numpy as npimport randomfrom moviepy.video.fx.all import resizeimport math# Parametersimage_folder = 'input/photos_collection' # Folder containing imagesoutput_video_file = 'output/slideshow.mp4' # Output video file nameimage_display_duration = 8 # Duration each image is shown in secondsfps = 30 # Frames per second for the videovideo_width = 1920 # Custom video widthvideo_height = 1080 # Custom video heighttitle_text = "MyTitle" # Title text to addfont_path = "fonts/MyFont.ttf" # Path to the font filefont_size = 255 # Font sizetitle_color = (255, 255, 0) # Yellow colorshadow_color = (0, 0, 0) # Black color for shadowshadow_offset = (5, 5) # Offset for shadowrotation_angle = 8 # Rotation angle in degreesmusic_file = 'music/MyMusic.mp3' # Music file pathcrossfade_duration = 1 # Duration of the crossfade effect between images in secondstitle_mode = 1 # 1: Center, 2: Upper Left, 3: Bottom Leftuse_url = 1 # 0: Use URLs, 1: Use folder, 2: Use fileis_random = 1 # 0: Use images as is, 1: Shuffle images randomlyis_animate = 1 # 0: No animation, 1: Apply zoom-in effectimage_urls = ["https://","https://","https://"] # List of image URLs if use_url is 0# Custom resize function for MoviePy clipsdef resize_image_custom(image, size):return image.resize(size, Image.Resampling.LANCZOS) # Use LANCZOS or another resampling filter# Function to apply resizing to an ImageClipdef resize_clip(clip, size):...return clip.fl_image(resize_frame)# Function to load images from a folderdef load_images_from_folder(folder):images = []...return images# Function to load images from URLsdef load_images_from_urls(urls):images = []...return images# Function to load images from the file images_list.lstdef load_images_from_file(file_path):images = []...return images# Function to resize image to fit within the specified dimensions, preserving aspect ratiodef resize_image_to_fit(image, size):...return final_image# Function to add rotated title to an imagedef add_rotated_title(image, text, font_path, font_size, title_color, shadow_color, shadow_offset, angle, title_mode):...return final_imagedef add_zoom_in_effect(clip, zoom_ratio=0.04):...return clip.fl(effect)# Main processing codeif 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 fileelse:images = load_images_from_urls(image_urls)# Shuffle images if is_random is set to 1if is_random == 1:random.shuffle(images) # Shuffle the images randomlyif 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 clipsclips = []# Create a temporary directory for storing temporary fileswith tempfile.TemporaryDirectory() as temp_dir:# Create a video clip for each imagefor 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 imageclip = 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 1if 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 adjustedclips.append(clip)# Add crossfade transition between clipsdef add_crossfade(clips, duration):if is_animate == 0:...return final_clipselse:...return final_clips# Apply crossfade transitionsclips_with_transitions = add_crossfade(clips, crossfade_duration)# Concatenate all the clips into a final videofinal_clip = concatenate_videoclips(clips_with_transitions, method="compose")# Add fadeout effect to the final video clipfinal_clip = final_clip.fadeout(2) # 2-second fade out# Load the music file and trim it to match the final video's durationif os.path.exists(music_file):audio_clip = AudioFileClip(music_file)audio_duration = final_clip.duration # Duration of the final video cliptrimmed_audio = audio_clip.subclip(0, audio_duration).audio_fadeout(2) # Trim and fade out audio# Set the trimmed audio to the final videofinal_clip = final_clip.set_audio(trimmed_audio)# Write the final video to a filefinal_clip.write_videofile(output_video_file, codec='libx264')# Print out the length of the slideshow videoduration_seconds = final_clip.durationduration_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.
暂无评论内容