如何使用Python将多组动画拼接为同画面的单份GIF或短MP4视频
Python 合并多组动画到同一画面的实现方法
下面分两种常见场景给出可直接运行的实现方案:
方案1:已有4个独立GIF,合并为2x2网格布局的单份动图
用Pillow库实现,操作最简便:
- 先安装依赖:
pip install pillow - 代码示例:
from PIL import Image, ImageSequence # 配置参数 GIF_PATHS = ["gif1.gif", "gif2.gif", "gif3.gif", "gif4.gif"] # 替换为你的4个GIF路径 BLOCK_SIZE = (300, 300) # 每个小动图的尺寸,按需调整 GRID_SIZE = (2, 2) # 2行2列布局 OUTPUT_PATH = "merged.gif" FPS = 10 # 输出帧率,和原GIF保持一致即可 # 读取所有GIF的帧列表 gif_frames = [] max_frames = 0 for path in GIF_PATHS: gif = Image.open(path) frames = [frame.copy().resize(BLOCK_SIZE) for frame in ImageSequence.Iterator(gif)] gif_frames.append(frames) max_frames = max(max_frames, len(frames)) # 对齐所有GIF的帧数,短的自动循环补全 for i in range(len(gif_frames)): while len(gif_frames[i]) < max_frames: gif_frames[i].extend(gif_frames[i]) gif_frames[i] = gif_frames[i][:max_frames] # 逐帧拼接 merged_frames = [] for frame_idx in range(max_frames): # 新建空白画布 canvas = Image.new("RGB", (BLOCK_SIZE[0]*GRID_SIZE[0], BLOCK_SIZE[1]*GRID_SIZE[1])) # 左上 canvas.paste(gif_frames[0][frame_idx], (0, 0)) # 右上 canvas.paste(gif_frames[1][frame_idx], (BLOCK_SIZE[0], 0)) # 左下 canvas.paste(gif_frames[2][frame_idx], (0, BLOCK_SIZE[1])) # 右下 canvas.paste(gif_frames[3][frame_idx], (BLOCK_SIZE[0], BLOCK_SIZE[1])) merged_frames.append(canvas) # 导出GIF merged_frames[0].save( OUTPUT_PATH, save_all=True, append_images=merged_frames[1:], duration=1000//FPS, loop=0 )
如果需要导出MP4格式,把拼接好的merged_frames转成numpy数组后用OpenCV的VideoWriter写入即可。
方案2:直接从4组原始图片序列生成合并视频(画质损失更小,推荐)
如果还保留着原始的图片序列,直接从序列拼接效率更高,用OpenCV实现:
- 先安装依赖:
pip install opencv-python numpy - 核心逻辑和GIF拼接一致,读取每组序列的对应帧→调整尺寸→拼接→写入视频文件,代码示例如下:
import cv2 import numpy as np import os # 配置参数 SEQ_DIRS = ["seq1/", "seq2/", "seq3/", "seq4/"] # 替换为你的4组图片序列文件夹路径 BLOCK_SIZE = (300, 300) FPS = 10 OUTPUT_PATH = "merged.mp4" # 读取每组序列的排序后路径 seq_paths = [] min_frames = float("inf") for dir_path in SEQ_DIRS: paths = sorted([os.path.join(dir_path, f) for f in os.listdir(dir_path) if f.endswith(('.png', '.jpg'))]) seq_paths.append(paths) min_frames = min(min_frames, len(paths)) # 初始化视频写入器 out_width = BLOCK_SIZE[0] * 2 out_height = BLOCK_SIZE[1] * 2 fourcc = cv2.VideoWriter_fourcc(*'mp4v') video_writer = cv2.VideoWriter(OUTPUT_PATH, fourcc, FPS, (out_width, out_height)) # 逐帧处理 for i in range(min_frames): # 读取4帧 frames = [] for paths in seq_paths: frame = cv2.imread(paths[i]) frame = cv2.resize(frame, BLOCK_SIZE) frames.append(frame) # 2x2拼接 top_row = np.hstack([frames[0], frames[1]]) bottom_row = np.hstack([frames[2], frames[3]]) merged_frame = np.vstack([top_row, bottom_row]) # 写入视频 video_writer.write(merged_frame) # 释放资源 video_writer.release() cv2.destroyAllWindows()
常见注意事项
- 所有动画/序列的帧率建议统一,避免合并后出现不同步问题
- 如果各组序列长度不一致,可以循环短序列或者补帧对齐,按需调整即可
- 导出GIF时如果文件太大,可以适当降低
BLOCK_SIZE或者开启optimize=True参数压缩
内容的提问来源于stack exchange,提问作者Wind Circulation
相关产品推荐
相关产品推荐

