如何使用Python动态水平拼接多张GIF生成单张GIF文件
多GIF水平拼接Python实现方案
方案1:基于ffmpeg的封装实现(推荐,还原hstack原生效果)
该方案直接调用ffmpeg的hstack滤镜,自动处理不同尺寸GIF的高度对齐、帧率统一,无手动帧处理的误差问题。
依赖安装
- 系统环境需提前安装ffmpeg并添加到环境变量
- 安装Python依赖包:
pip install ffmpeg-python
实现代码
import ffmpeg from typing import List def hstack_gifs(input_paths: List[str], output_path: str, fill_color: str = "white"): # 读取所有输入GIF的流 input_streams = [ffmpeg.input(p) for p in input_paths] # 统一所有GIF的帧率为最小输入帧率,避免丢帧 min_fps = min(int(ffmpeg.probe(p)['streams'][0]['r_frame_rate'].split('/')[0]) for p in input_paths) # 对齐高度:统一为最大输入高度,不足的部分用指定颜色填充,内容居中 max_height = max(ffmpeg.probe(p)['streams'][0]['height'] for p in input_paths) processed_streams = [] for stream in input_streams: processed = stream.filter('fps', fps=min_fps)\ .filter('pad', height=max_height, y='(oh - ih)/2', color=fill_color) processed_streams.append(processed) # 水平拼接 joined = ffmpeg.filter(processed_streams, 'hstack', inputs=len(processed_streams)) # 输出GIF,设置合理的像素格式 joined.output(output_path, vcodec='gif', pix_fmt='rgb24').run(overwrite_output=True) # 调用示例 if __name__ == "__main__": # 可传入任意数量的GIF路径 hstack_gifs(["1.gif", "2.gif", "3.gif"], "output.gif")
方案2:基于Pillow的纯Python实现(无外部依赖)
不需要安装ffmpeg,纯Python处理帧序列,适合轻量使用场景。
依赖安装
pip install pillow
实现代码
from PIL import Image, ImageSequence from typing import List def hstack_gifs_pillow(input_paths: List[str], output_path: str, fill_color: tuple = (255,255,255)): # 读取所有GIF的帧序列和每帧时长 gifs_data = [] max_height = 0 min_duration = float('inf') for path in input_paths: gif = Image.open(path) frames = [frame.copy() for frame in ImageSequence.Iterator(gif)] duration = gif.info.get('duration', 100) gifs_data.append({"frames": frames, "duration": duration}) if gif.height > max_height: max_height = gif.height if duration < min_duration: min_duration = duration # 统一所有GIF的帧数为最大帧数,不足的循环补帧 max_frames = max(len(d["frames"]) for d in gifs_data) for data in gifs_data: frame_count = len(data["frames"]) if frame_count < max_frames: # 循环补帧到最大帧数 data["frames"] += data["frames"] * ((max_frames - frame_count) // frame_count) + data["frames"][:(max_frames % frame_count)] # 逐帧拼接 output_frames = [] for frame_idx in range(max_frames): # 计算当前拼接后总宽度 total_width = sum(data["frames"][frame_idx].width for data in gifs_data) # 创建新帧 new_frame = Image.new('RGB', (total_width, max_height), fill_color) current_x = 0 for data in gifs_data: frame = data["frames"][frame_idx].convert('RGB') # 垂直居中对齐 y_offset = (max_height - frame.height) // 2 new_frame.paste(frame, (current_x, y_offset)) current_x += frame.width output_frames.append(new_frame) # 保存GIF output_frames[0].save( output_path, save_all=True, append_images=output_frames[1:], duration=min_duration, loop=0 ) # 调用示例 if __name__ == "__main__": hstack_gifs_pillow(["1.gif", "2.gif", "3.gif"], "output_pillow.gif")
注意事项
- 如果需要保留GIF透明背景,将
Image.new的模式改为RGBA,fill_color改为带alpha通道的元组即可 - 帧率可根据需求自行调整duration参数
内容的提问来源于stack exchange,提问作者Ratintosh1
相关产品推荐
相关产品推荐

