
背景需求
20251216今天打开通义万相,又改了一个版面和功能,


签到给30分


视频类:
生成=图片转成图像和音乐 10分

角色:30分

特效=图片+特效模版 5分
编辑,视频四个 10分

图像类
只有1个,生成图片,2分

数字人类
3种功能,一次30分

20251217我来测试以下视频-特效的效果样式
共有28种




用一张卡通图片。测试28种特效的效果

一张一张生成,用5分,每张2分钟,不用分,等候生成要30-60分钟

全部生成后,我希望下载这些5秒特效视频mp4,并在特效右上角写上特效名称,最后转成gif(因为没有声音,直接转动图即可)
一、按照顺序下载视频


页面放大到200%

下载视频,编号001

python
'''
20251219通义万相2.6下载通义照片 copy
Python下载通义万相的图片(存在问题,不能停止,只能默认下载300张,删除多余)
星火讯飞,阿夏
谷歌页面打开通义万相,页面放大到200%
20251003
'''
import os,time
import pyautogui
import pyperclip
import re
import win32api
import win32con
import sys
import ctypes
import time
name='20251219 28种特效'
# 先打开微信
num=1
zs=50
# 实际157D:\20251211实心福字\00原图\061
def minimize_active_window():
try:
if sys.platform == 'win32':
# 获取当前活动窗口的句柄
hwnd = ctypes.windll.user32.GetForegroundWindow()
# 最小化窗口
ctypes.windll.user32.ShowWindow(hwnd, 6) # 6 对应 SW_MINIMIZE
return True
else:
print("此功能仅支持Windows系统")
return False
except Exception as e:
print(f"最小化窗口时出错: {e}")
return False
print("程序运行中...")
time.sleep(2) # 等待2秒,让你有时间切换到VS Code窗口
# 尝试最小化活动窗口
if minimize_active_window():
print("窗口已最小化")
else:
print("无法最小化窗口")
# 读取文件名称和路径
path=fr'D:\{name}\00原图'
os.makedirs(path,exist_ok=True)
for i in range(num,num+zs):
# 下载按钮
pyautogui.moveTo(1569, 302)
pyautogui.click()
time.sleep(1)
# 点击有,无水印要包月
pyautogui.moveTo(1573, 373)
pyautogui.click()
time.sleep(2)
# 输入图片名称,复制中文内容到剪贴板
name=path+fr'\{i:03}'
pyperclip.copy(name)
# 黏贴图片地址
pyautogui.hotkey('ctrl', 'v')
time.sleep(1)
pyautogui.press('enter')
# # 图片显示需要时间
time.sleep(1)
# 模拟按键"右箭头"
pyautogui.moveTo (989, 650)
time.sleep(2)
pyautogui.moveTo (989, 641)
pyautogui.click()
time.sleep(2)
# 'left'(左箭头)
# 'up'(上箭头)
# 'down'(下箭头)
import sys
import ctypes
import time
def minimize_active_window():
try:
if sys.platform == 'win32':
# 获取当前活动窗口的句柄
hwnd = ctypes.windll.user32.GetForegroundWindow()
# 最小化窗口
ctypes.windll.user32.ShowWindow(hwnd, 6) # 6 对应 SW_MINIMIZE
return True
else:
print("此功能仅支持Windows系统")
return False
except Exception as e:
print(f"最小化窗口时出错: {e}")
return False
print("程序运行中...")
time.sleep(2) # 等待2秒,让你有时间切换到VS Code窗口
# 尝试最小化活动窗口
if minimize_active_window():
print("窗口已最小化")
else:
print("无法最小化窗口")

复制右侧的视频信息


复制到EXCEL内

因为有部分没有生成成功的视频,所以29和33数量不等

删除

确保视频数量与EXCLE文字数量相等(31条)


具体代码内容:豆包+Deepseek

1、重命名视频,改成特效的名称
2、特效名字的mp4转为gif(5秒一张,质量<5MB )
3、GIF上面写入特效文字,时长5秒
python
'''
结题报告WORD 插入 MP4转gif动画并添加字幕(降低帧率)
deepseek、阿夏
20251220
'''
import os
import shutil
from pathlib import Path
import pandas as pd
import cv2
from PIL import Image, ImageDraw, ImageFont
import imageio
from datetime import datetime
def copy_and_rename_videos(input_folder, output_folder, excel_file):
"""
第一步:复制并重命名视频文件
参数:
input_folder: 原始视频文件夹(00视频)
output_folder: 输出文件夹(01视频文字)
excel_file: 包含新名称的Excel文件
"""
print("="*60)
print("第一步:复制并重命名视频文件")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的名称
new_names = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的名称
for value in df.iloc[:, 1]:
if pd.notna(value):
new_names.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(new_names)} 个名称")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取原始视频文件并排序
video_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
video_files.append(filename)
print(f"📁 找到 {len(video_files)} 个MP4文件")
# 确保名称数量足够
if len(new_names) < len(video_files):
print(f"⚠️ 名称数量({len(new_names)})少于视频文件({len(video_files)})")
for i in range(len(new_names), len(video_files)):
new_names.append(f"视频{i+1:03d}")
# 复制并重命名文件
copied_count = 0
for i, filename in enumerate(video_files):
if i >= len(new_names):
break
# 生成新文件名(保留序号)
new_name = f"{i+1:03d}_{new_names[i]}.mp4"
src_path = os.path.join(input_folder, filename)
dst_path = os.path.join(output_folder, new_name)
try:
shutil.copy2(src_path, dst_path)
print(f"✓ {i+1:03d}: {filename} → {new_name}")
copied_count += 1
except Exception as e:
print(f"✗ 复制 {filename} 失败: {e}")
print(f"\n✅ 复制完成: {copied_count}/{len(video_files)} 个文件")
print(f" 输入: {input_folder}")
print(f" 输出: {output_folder}")
return True
def convert_mp4_to_gif(input_folder, output_folder, scale=0.5, fps=8, max_duration=10):
"""
第二步:MP4转GIF(使用OpenCV和PIL,不依赖moviepy)
参数:
input_folder: 输入MP4文件夹(01视频文字)
output_folder: 输出GIF文件夹(02视频GIF)
scale: 缩放比例
fps: 输出GIF帧率
max_duration: 最大时长(秒)
"""
print("\n" + "="*60)
print("第二步:MP4转GIF")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 获取MP4文件并排序
mp4_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
mp4_files.append(filename)
print(f"📁 找到 {len(mp4_files)} 个MP4文件")
# 处理每个文件
converted_count = 0
for i, filename in enumerate(mp4_files):
print(f"\n处理 {i+1}/{len(mp4_files)}: {filename}")
input_path = os.path.join(input_folder, filename)
output_filename = os.path.splitext(filename)[0] + '.gif'
output_path = os.path.join(output_folder, output_filename)
try:
# 使用OpenCV读取视频
cap = cv2.VideoCapture(input_path)
# 获取视频信息
original_fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 计算新的尺寸
new_width = int(width * scale)
new_height = int(height * scale)
# 计算跳帧间隔
if original_fps > 0:
frame_skip = int(original_fps / fps)
if frame_skip < 1:
frame_skip = 1
else:
frame_skip = 1
# 计算最大帧数
max_frames = int(max_duration * fps)
frames = []
frame_count = 0
frame_index = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 跳帧处理
if frame_index % frame_skip == 0:
# 转换BGR到RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 调整大小
if scale != 1.0:
frame_resized = cv2.resize(frame_rgb, (new_width, new_height))
else:
frame_resized = frame_rgb
# 转换为PIL Image
pil_img = Image.fromarray(frame_resized)
frames.append(pil_img)
frame_count += 1
# 达到最大帧数停止
if frame_count >= max_frames:
break
frame_index += 1
cap.release()
# 保存为GIF
if frames:
# 保存为GIF
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=int(1000 / fps), # 每帧持续时间(ms)
loop=0, # 无限循环
optimize=True
)
# 检查文件
if os.path.exists(output_path):
file_size = os.path.getsize(output_path) / 1024 # KB
print(f"✓ 转换成功: {output_filename} ({file_size:.1f}KB)")
converted_count += 1
else:
print(f"✗ 保存失败")
else:
print(f"✗ 未提取到任何帧")
except Exception as e:
print(f"✗ 处理 {filename} 失败: {e}")
print(f"\n✅ GIF转换完成: {converted_count}/{len(mp4_files)} 个文件")
return True
def add_text_to_gif(input_folder, output_folder, excel_file, font_size=36):
"""
第三步:给GIF添加文字(使用PIL)
参数:
input_folder: 输入GIF文件夹(02视频GIF)
output_folder: 输出GIF文件夹(03视频GIF文字)
excel_file: Excel文件(包含文字)
font_size: 字体大小
"""
print("\n" + "="*60)
print("第三步:给GIF添加文字")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的文字
text_list = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的文字
for value in df.iloc[:, 1]:
if pd.notna(value):
text_list.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(text_list)} 个文字")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取GIF文件并排序
gif_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.gif'):
gif_files.append(filename)
print(f"📁 找到 {len(gif_files)} 个GIF文件")
# 确保文字数量足够
if len(text_list) < len(gif_files):
print(f"⚠️ 文字数量({len(text_list)})少于GIF文件({len(gif_files)})")
for i in range(len(text_list), len(gif_files)):
text_list.append(f"文字{i+1:03d}")
# 加载字体(支持中文)
try:
# 尝试加载系统字体
font_paths = [
"C:/Windows/Fonts/simhei.ttf", # Windows黑体
"C:/Windows/Fonts/msyh.ttf", # Windows微软雅黑
"simhei.ttf",
None # 使用默认字体
]
font = None
for font_path in font_paths:
try:
if font_path and os.path.exists(font_path):
font = ImageFont.truetype(font_path, font_size)
print(f"✅ 使用字体: {font_path}")
break
elif font_path is None:
font = ImageFont.load_default()
print("⚠️ 使用系统默认字体(可能不支持中文)")
break
except:
continue
if font is None:
font = ImageFont.load_default()
print("⚠️ 使用默认字体(可能不支持中文)")
except Exception as e:
font = ImageFont.load_default()
print(f"⚠️ 字体加载失败,使用默认字体: {e}")
# 处理每个GIF文件
processed_count = 0
for i, filename in enumerate(gif_files):
if i >= len(text_list):
break
text = text_list[i]
input_path = os.path.join(input_folder, filename)
output_filename = filename # 保持相同文件名
output_path = os.path.join(output_folder, output_filename)
print(f"\n处理 {i+1}/{len(gif_files)}: {filename}")
print(f" 添加文字: {text}")
try:
# 读取GIF
gif = Image.open(input_path)
frames = []
# 处理每一帧
try:
while True:
# 复制当前帧
frame = gif.copy()
# 创建绘图对象
draw = ImageDraw.Draw(frame)
# 计算文字位置(右上角)
try:
# 尝试获取文字尺寸
if hasattr(font, 'getsize'):
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
else:
# 简单估算
text_width = len(text) * font_size * 0.6
text_height = font_size
except:
text_width = len(text) * font_size * 0.6
text_height = font_size
# 计算位置(右上角,带边距)
margin = 10
x = frame.width - text_width - margin
y = margin
# 添加黑色背景(提高可读性)
bg_padding = 5
draw.rectangle([
x - bg_padding,
y - bg_padding,
x + text_width + bg_padding,
y + text_height + bg_padding
], fill='black')
# 添加文字
draw.text((x, y), text, font=font, fill='white')
frames.append(frame)
# 跳转到下一帧
gif.seek(gif.tell() + 1)
except EOFError:
pass # GIF结束
# 保存新GIF
if frames:
# 保留原始GIF的调色板和持续时间
durations = []
try:
gif.seek(0)
while True:
durations.append(gif.info.get('duration', 100))
gif.seek(gif.tell() + 1)
except:
pass
# 如果无法获取持续时间,使用默认值
if len(durations) != len(frames):
durations = [100] * len(frames) # 默认100ms
# 保存
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=durations[:len(frames)],
loop=0,
optimize=True
)
# 检查文件
if os.path.exists(output_path):
file_size = os.path.getsize(output_path) / 1024 # KB
print(f"✓ 添加文字成功: {output_filename} ({file_size:.1f}KB)")
processed_count += 1
else:
print(f"✗ 保存失败")
else:
print(f"✗ 未提取到任何帧")
gif.close()
except Exception as e:
print(f"✗ 处理 {filename} 失败: {e}")
print(f"\n✅ 文字添加完成: {processed_count}/{len(gif_files)} 个文件")
return True
def check_dependencies():
"""检查依赖库"""
required_packages = ['pandas', 'opencv-python', 'Pillow', 'imageio', 'openpyxl']
missing = []
for package in required_packages:
try:
if package == 'pandas':
import pandas
elif package == 'opencv-python':
import cv2
elif package == 'Pillow':
from PIL import Image
elif package == 'imageio':
import imageio
elif package == 'openpyxl':
import openpyxl
except ImportError:
missing.append(package)
return missing
def install_dependencies(missing_packages):
"""安装缺失的依赖"""
import subprocess
import sys
print(f"缺少依赖包: {missing_packages}")
response = input("是否要自动安装?(y/n): ")
if response.lower() == 'y':
for package in missing_packages:
try:
print(f"正在安装 {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
print(f"✓ {package} 安装成功")
except:
print(f"✗ {package} 安装失败")
return True
else:
print("请手动安装: pip install pandas opencv-python Pillow imageio openpyxl")
return False
def main():
"""主函数"""
print("="*60)
print("MP4转GIF并添加文字工具(三步流程)")
print("作者: deepseek, 阿夏")
print("日期: 20251220")
print("="*60)
# 检查依赖
missing = check_dependencies()
if missing:
if not install_dependencies(missing):
return
# 设置路径
base_path = Path(r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220万相2点6特效')
# 定义文件夹路径
video_folder = base_path / '00视频' # 原始视频
renamed_folder = base_path / '01视频文字' # 重命名后的视频
gif_folder = base_path / '02视频GIF' # 转换的GIF
final_folder = base_path / '03GIF贴字' # 最终带文字的GIF
excel_file = base_path / '名称.xlsx' # Excel文件
# 检查必要文件
if not video_folder.exists():
print(f"❌ 原始视频文件夹不存在: {video_folder}")
return
if not excel_file.exists():
print(f"❌ Excel文件不存在: {excel_file}")
return
print("\n📋 处理流程:")
print(f" 1. {video_folder} → {renamed_folder} (复制重命名)")
print(f" 2. {renamed_folder} → {gif_folder} (MP4转GIF)")
print(f" 3. {gif_folder} → {final_folder} (添加文字)")
print(f" 配置: Excel={excel_file}")
# 显示参数
print("\n⚙️ 处理参数:")
print(" 缩放比例: 0.5 (50%)")
print(" GIF帧率: 8 fps")
print(" 最大时长: 10秒")
print(" 字体大小: 36px")
print("\n" + "-"*60)
confirm ='y'
# input("是否开始处理?(y/n): ")
if confirm.lower() == 'y':
start_time = datetime.now()
# 第一步:复制重命名
print("\n" + "="*60)
print("开始第一步:复制并重命名视频")
step1_success = copy_and_rename_videos(
input_folder=str(video_folder),
output_folder=str(renamed_folder),
excel_file=str(excel_file)
)
if not step1_success:
print("❌ 第一步失败,停止处理")
return
# 第二步:MP4转GIF
print("\n" + "="*60)
print("开始第二步:MP4转GIF")
step2_success = convert_mp4_to_gif(
input_folder=str(renamed_folder),
output_folder=str(gif_folder),
scale=0.5,
fps=8,
max_duration=10
)
if not step2_success:
print("❌ 第二步失败,停止处理")
return
# 第三步:添加文字
print("\n" + "="*60)
print("开始第三步:给GIF添加文字")
step3_success = add_text_to_gif(
input_folder=str(gif_folder),
output_folder=str(final_folder),
excel_file=str(excel_file),
font_size=36
)
# 统计信息
end_time = datetime.now()
duration = end_time - start_time
print("\n" + "="*60)
print("🎉 处理完成!")
print("="*60)
print(f"📊 处理结果:")
print(f" 第一步: {'成功' if step1_success else '失败'}")
print(f" 第二步: {'成功' if step2_success else '失败'}")
print(f" 第三步: {'成功' if step3_success else '失败'}")
print(f"⏱️ 总耗时: {duration}")
print(f"📁 输出文件夹:")
print(f" 重命名视频: {renamed_folder}")
print(f" 纯GIF文件: {gif_folder}")
print(f" 带文字GIF: {final_folder}")
print("="*60)
else:
print("已取消处理")
if __name__ == "__main__":
main()

00原视频

01视频文字(重命名,根据xls)

03视频MP4转动图gif

04gif右上角添加特效名称

还是有超过5的内容



测算到帧率5,才都是在5MB以内

但是这样一个个测试,很麻烦,而且统一fps,有些gif图像就很小。
我希望反复生成一个小于4.8MB的gif视频(还有0.2贴字幕)

python
'''
结题报告WORD 插入 MP4转gif动画并添加字幕(降低帧率)
deepseek、阿夏
20251220
'''
import os
import shutil
from pathlib import Path
import pandas as pd
import cv2
from PIL import Image, ImageDraw, ImageFont
import imageio
from datetime import datetime
def copy_and_rename_videos(input_folder, output_folder, excel_file):
"""
第一步:复制并重命名视频文件
参数:
input_folder: 原始视频文件夹(00视频)
output_folder: 输出文件夹(01视频文字)
excel_file: 包含新名称的Excel文件
"""
print("="*60)
print("第一步:复制并重命名视频文件")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的名称
new_names = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的名称
for value in df.iloc[:, 1]:
if pd.notna(value):
new_names.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(new_names)} 个名称")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取原始视频文件并排序
video_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
video_files.append(filename)
print(f"📁 找到 {len(video_files)} 个MP4文件")
# 确保名称数量足够
if len(new_names) < len(video_files):
print(f"⚠️ 名称数量({len(new_names)})少于视频文件({len(video_files)})")
for i in range(len(new_names), len(video_files)):
new_names.append(f"视频{i+1:03d}")
# 复制并重命名文件
copied_count = 0
for i, filename in enumerate(video_files):
if i >= len(new_names):
break
# 生成新文件名(保留序号)
new_name = f"{i+1:03d}_{new_names[i]}.mp4"
src_path = os.path.join(input_folder, filename)
dst_path = os.path.join(output_folder, new_name)
try:
shutil.copy2(src_path, dst_path)
print(f"✓ {i+1:03d}: {filename} → {new_name}")
copied_count += 1
except Exception as e:
print(f"✗ 复制 {filename} 失败: {e}")
print(f"\n✅ 复制完成: {copied_count}/{len(video_files)} 个文件")
print(f" 输入: {input_folder}")
print(f" 输出: {output_folder}")
return True
def convert_single_mp4_to_gif(input_path, output_path, scale=0.5, fps=5, max_duration=10):
"""
转换单个MP4文件为GIF
参数:
input_path: 输入MP4文件路径
output_path: 输出GIF文件路径
scale: 缩放比例
fps: 输出GIF帧率
max_duration: 最大时长(秒)
返回:
bool: 是否转换成功
int: 实际使用的fps
"""
try:
# 使用OpenCV读取视频
cap = cv2.VideoCapture(input_path)
# 获取视频信息
original_fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 计算新的尺寸
new_width = int(width * scale)
new_height = int(height * scale)
# 计算跳帧间隔
if original_fps > 0:
frame_skip = int(original_fps / fps)
if frame_skip < 1:
frame_skip = 1
else:
frame_skip = 1
# 计算最大帧数
max_frames = int(max_duration * fps)
frames = []
frame_count = 0
frame_index = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 跳帧处理
if frame_index % frame_skip == 0:
# 转换BGR到RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 调整大小
if scale != 1.0:
frame_resized = cv2.resize(frame_rgb, (new_width, new_height))
else:
frame_resized = frame_rgb
# 转换为PIL Image
pil_img = Image.fromarray(frame_resized)
frames.append(pil_img)
frame_count += 1
# 达到最大帧数停止
if frame_count >= max_frames:
break
frame_index += 1
cap.release()
# 保存为GIF
if frames:
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=int(1000 / fps), # 每帧持续时间(ms)
loop=0, # 无限循环
optimize=True
)
return True, fps
else:
return False, fps
except Exception as e:
print(f"✗ 转换失败: {e}")
return False, fps
def convert_mp4_to_gif_with_size_check(input_folder, output_folder, scale=0.5, max_fps=8, min_fps=1, max_duration=10, max_size_mb=4.8):
"""
第二步:MP4转GIF,并检查文件大小
参数:
input_folder: 输入MP4文件夹(01视频文字)
output_folder: 输出GIF文件夹(02视频GIF)
scale: 缩放比例
max_fps: 最大尝试帧率
min_fps: 最小尝试帧率
max_duration: 最大时长(秒)
max_size_mb: 最大文件大小(MB)
"""
print("\n" + "="*60)
print("第二步:MP4转GIF(带大小检查)")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 获取MP4文件并排序
mp4_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
mp4_files.append(filename)
print(f"📁 找到 {len(mp4_files)} 个MP4文件")
print(f"⚙️ 参数: 最大文件大小={max_size_mb}MB, 帧率范围={min_fps}-{max_fps}fps")
# 处理每个文件
converted_count = 0
total_files = len(mp4_files)
for i, filename in enumerate(mp4_files):
print(f"\n处理 {i+1}/{total_files}: {filename}")
input_path = os.path.join(input_folder, filename)
base_name = os.path.splitext(filename)[0]
# 从高到低尝试不同的帧率
success = False
final_fps = None
for try_fps in range(max_fps, min_fps - 1, -1):
if try_fps < 1:
try_fps = 1
output_filename = f"{base_name}_fps{try_fps}.gif"
output_path = os.path.join(output_folder, output_filename)
print(f" 尝试帧率: {try_fps}fps", end="")
# 转换文件
convert_success, used_fps = convert_single_mp4_to_gif(
input_path=input_path,
output_path=output_path,
scale=scale,
fps=try_fps,
max_duration=max_duration
)
if not convert_success:
print(" → 转换失败")
continue
# 检查文件大小
if os.path.exists(output_path):
file_size_bytes = os.path.getsize(output_path)
file_size_mb = file_size_bytes / (1024 * 1024)
if file_size_mb <= max_size_mb:
# 文件大小符合要求
success = True
final_fps = used_fps
print(f" → 成功 ({file_size_mb:.2f}MB)")
# 重命名为不带fps后缀的文件名
final_filename = f"{base_name}.gif"
final_path = os.path.join(output_folder, final_filename)
# 如果目标文件已存在,先删除
if os.path.exists(final_path):
os.remove(final_path)
# 重命名文件
os.rename(output_path, final_path)
print(f" 保存为: {final_filename}")
break
else:
# 文件太大,删除并继续尝试更低的fps
print(f" → 太大 ({file_size_mb:.2f}MB),删除重试")
os.remove(output_path)
else:
print(" → 文件未生成")
if success:
converted_count += 1
print(f"✓ 最终使用帧率: {final_fps}fps")
else:
print(f"✗ 所有帧率尝试都失败")
print(f"\n✅ GIF转换完成: {converted_count}/{total_files} 个文件")
return True
def add_text_to_gif(input_folder, output_folder, excel_file, font_size=36):
"""
第三步:给GIF添加文字(使用PIL)
参数:
input_folder: 输入GIF文件夹(02视频GIF)
output_folder: 输出GIF文件夹(03视频GIF文字)
excel_file: Excel文件(包含文字)
font_size: 字体大小
"""
print("\n" + "="*60)
print("第三步:给GIF添加文字")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的文字
text_list = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的文字
for value in df.iloc[:, 1]:
if pd.notna(value):
text_list.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(text_list)} 个文字")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取GIF文件并排序
gif_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.gif'):
# 跳过带_fps后缀的文件(中间文件)
if '_fps' not in filename:
gif_files.append(filename)
print(f"📁 找到 {len(gif_files)} 个GIF文件")
# 确保文字数量足够
if len(text_list) < len(gif_files):
print(f"⚠️ 文字数量({len(text_list)})少于GIF文件({len(gif_files)})")
for i in range(len(text_list), len(gif_files)):
text_list.append(f"文字{i+1:03d}")
# 加载字体(支持中文)
try:
# 尝试加载系统字体
font_paths = [
"C:/Windows/Fonts/simhei.ttf", # Windows黑体
"C:/Windows/Fonts/msyh.ttf", # Windows微软雅黑
"simhei.ttf",
None # 使用默认字体
]
font = None
for font_path in font_paths:
try:
if font_path and os.path.exists(font_path):
font = ImageFont.truetype(font_path, font_size)
print(f"✅ 使用字体: {font_path}")
break
elif font_path is None:
font = ImageFont.load_default()
print("⚠️ 使用系统默认字体(可能不支持中文)")
break
except:
continue
if font is None:
font = ImageFont.load_default()
print("⚠️ 使用默认字体(可能不支持中文)")
except Exception as e:
font = ImageFont.load_default()
print(f"⚠️ 字体加载失败,使用默认字体: {e}")
# 处理每个GIF文件
processed_count = 0
for i, filename in enumerate(gif_files):
if i >= len(text_list):
break
text = text_list[i]
input_path = os.path.join(input_folder, filename)
output_filename = filename # 保持相同文件名
output_path = os.path.join(output_folder, output_filename)
print(f"\n处理 {i+1}/{len(gif_files)}: {filename}")
print(f" 添加文字: {text}")
try:
# 读取GIF
gif = Image.open(input_path)
frames = []
# 处理每一帧
try:
while True:
# 复制当前帧
frame = gif.copy()
# 创建绘图对象
draw = ImageDraw.Draw(frame)
# 计算文字位置(右上角)
try:
# 尝试获取文字尺寸
if hasattr(font, 'getsize'):
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
else:
# 简单估算
text_width = len(text) * font_size * 0.6
text_height = font_size
except:
text_width = len(text) * font_size * 0.6
text_height = font_size
# 计算位置(右上角,带边距)
margin = 10
x = frame.width - text_width - margin
y = margin
# 添加黑色背景(提高可读性)
bg_padding = 5
draw.rectangle([
x - bg_padding,
y - bg_padding,
x + text_width + bg_padding,
y + text_height + bg_padding
], fill='white')
# 添加文字
draw.text((x, y), text, font=font, fill='black')
frames.append(frame)
# 跳转到下一帧
gif.seek(gif.tell() + 1)
except EOFError:
pass # GIF结束
# 保存新GIF
if frames:
# 保留原始GIF的调色板和持续时间
durations = []
try:
gif.seek(0)
while True:
durations.append(gif.info.get('duration', 100))
gif.seek(gif.tell() + 1)
except:
pass
# 如果无法获取持续时间,使用默认值
if len(durations) != len(frames):
durations = [100] * len(frames) # 默认100ms
# 保存
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=durations[:len(frames)],
loop=0,
optimize=True
)
# 检查文件
if os.path.exists(output_path):
file_size = os.path.getsize(output_path) / 1024 # KB
print(f"✓ 添加文字成功: {output_filename} ({file_size:.1f}KB)")
processed_count += 1
else:
print(f"✗ 保存失败")
else:
print(f"✗ 未提取到任何帧")
gif.close()
except Exception as e:
print(f"✗ 处理 {filename} 失败: {e}")
print(f"\n✅ 文字添加完成: {processed_count}/{len(gif_files)} 个文件")
return True
def check_dependencies():
"""检查依赖库"""
required_packages = ['pandas', 'opencv-python', 'Pillow', 'imageio', 'openpyxl']
missing = []
for package in required_packages:
try:
if package == 'pandas':
import pandas
elif package == 'opencv-python':
import cv2
elif package == 'Pillow':
from PIL import Image
elif package == 'imageio':
import imageio
elif package == 'openpyxl':
import openpyxl
except ImportError:
missing.append(package)
return missing
def install_dependencies(missing_packages):
"""安装缺失的依赖"""
import subprocess
import sys
print(f"缺少依赖包: {missing_packages}")
response = input("是否要自动安装?(y/n): ")
if response.lower() == 'y':
for package in missing_packages:
try:
print(f"正在安装 {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
print(f"✓ {package} 安装成功")
except:
print(f"✗ {package} 安装失败")
return True
else:
print("请手动安装: pip install pandas opencv-python Pillow imageio openpyxl")
return False
def main():
"""主函数"""
print("="*60)
print("MP4转GIF并添加文字工具(三步流程)")
print("作者: deepseek, 阿夏")
print("日期: 20251220")
print("="*60)
# 检查依赖
missing = check_dependencies()
if missing:
if not install_dependencies(missing):
return
# 设置路径
base_path = Path(r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220万相2点6特效')
# 定义文件夹路径
video_folder = base_path / '00视频' # 原始视频
renamed_folder = base_path / '01视频文字' # 重命名后的视频
gif_folder = base_path / '02视频GIF' # 转换的GIF
final_folder = base_path / '03GIF贴字' # 最终带文字的GIF
excel_file = base_path / '名称.xlsx' # Excel文件
# 检查必要文件
if not video_folder.exists():
print(f"❌ 原始视频文件夹不存在: {video_folder}")
return
if not excel_file.exists():
print(f"❌ Excel文件不存在: {excel_file}")
return
print("\n📋 处理流程:")
print(f" 1. {video_folder} → {renamed_folder} (复制重命名)")
print(f" 2. {renamed_folder} → {gif_folder} (MP4转GIF,自动调整帧率)")
print(f" 3. {gif_folder} → {final_folder} (添加文字)")
print(f" 配置: Excel={excel_file}")
# 显示参数
print("\n⚙️ 处理参数:")
print(" 缩放比例: 0.5 (50%)")
print(" 帧率范围: 1-8 fps (自动调整)")
print(" 最大文件大小: 4.8 MB")
print(" 最大时长: 10秒")
print(" 字体大小: 36px")
print("\n" + "-"*60)
confirm ='y'
# input("是否开始处理?(y/n): ")
if confirm.lower() == 'y':
start_time = datetime.now()
# 第一步:复制重命名
print("\n" + "="*60)
print("开始第一步:复制并重命名视频")
step1_success = copy_and_rename_videos(
input_folder=str(video_folder),
output_folder=str(renamed_folder),
excel_file=str(excel_file)
)
if not step1_success:
print("❌ 第一步失败,停止处理")
return
# 第二步:MP4转GIF(带大小检查)
print("\n" + "="*60)
print("开始第二步:MP4转GIF(自动调整帧率)")
step2_success = convert_mp4_to_gif_with_size_check(
input_folder=str(renamed_folder),
output_folder=str(gif_folder),
scale=0.5,
max_fps=8, # 从8fps开始尝试
min_fps=1, # 最低到1fps
max_duration=10,
max_size_mb=4.8 # 最大4.8MB
)
if not step2_success:
print("❌ 第二步失败,停止处理")
return
# 第三步:添加文字
print("\n" + "="*60)
print("开始第三步:给GIF添加文字")
step3_success = add_text_to_gif(
input_folder=str(gif_folder),
output_folder=str(final_folder),
excel_file=str(excel_file),
font_size=36
)
# 统计信息
end_time = datetime.now()
duration = end_time - start_time
print("\n" + "="*60)
print("🎉 处理完成!")
print("="*60)
print(f"📊 处理结果:")
print(f" 第一步: {'成功' if step1_success else '失败'}")
print(f" 第二步: {'成功' if step2_success else '失败'}")
print(f" 第三步: {'成功' if step3_success else '失败'}")
print(f"⏱️ 总耗时: {duration}")
print(f"📁 输出文件夹:")
print(f" 重命名视频: {renamed_folder}")
print(f" 纯GIF文件: {gif_folder}")
print(f" 带文字GIF: {final_folder}")
print("="*60)
else:
print("已取消处理")
if __name__ == "__main__":
main()
都是最大尺寸

因为设置的是整数,如果是浮点数,会不会更接近4.8MB
对比显示,最高帧率就是10.超过了也没有用






没用,最后还是用了0.5,FPS10.0-5.0

python
'''
结题报告WORD 插入 MP4转gif动画并添加字幕(降低帧率)
deepseek、阿夏
20251220
'''
import os
import shutil
from pathlib import Path
import pandas as pd
import cv2
from PIL import Image, ImageDraw, ImageFont
import imageio
from datetime import datetime
def copy_and_rename_videos(input_folder, output_folder, excel_file):
"""
第一步:复制并重命名视频文件
参数:
input_folder: 原始视频文件夹(00视频)
output_folder: 输出文件夹(01视频文字)
excel_file: 包含新名称的Excel文件
"""
print("="*60)
print("第一步:复制并重命名视频文件")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的名称
new_names = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的名称
for value in df.iloc[:, 1]:
if pd.notna(value):
new_names.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(new_names)} 个名称")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取原始视频文件并排序
video_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
video_files.append(filename)
print(f"📁 找到 {len(video_files)} 个MP4文件")
# 确保名称数量足够
if len(new_names) < len(video_files):
print(f"⚠️ 名称数量({len(new_names)})少于视频文件({len(video_files)})")
for i in range(len(new_names), len(video_files)):
new_names.append(f"视频{i+1:03d}")
# 复制并重命名文件
copied_count = 0
for i, filename in enumerate(video_files):
if i >= len(new_names):
break
# 生成新文件名(保留序号)
new_name = f"{i+1:03d}_{new_names[i]}.mp4"
src_path = os.path.join(input_folder, filename)
dst_path = os.path.join(output_folder, new_name)
try:
shutil.copy2(src_path, dst_path)
print(f"✓ {i+1:03d}: {filename} → {new_name}")
copied_count += 1
except Exception as e:
print(f"✗ 复制 {filename} 失败: {e}")
print(f"\n✅ 复制完成: {copied_count}/{len(video_files)} 个文件")
print(f" 输入: {input_folder}")
print(f" 输出: {output_folder}")
return True
def convert_single_mp4_to_gif(input_path, output_path, scale=0.5, fps=5.0, max_duration=10):
"""
转换单个MP4文件为GIF
参数:
input_path: 输入MP4文件路径
output_path: 输出GIF文件路径
scale: 缩放比例
fps: 输出GIF帧率(支持小数)
max_duration: 最大时长(秒)
返回:
bool: 是否转换成功
float: 实际使用的fps
"""
try:
# 使用OpenCV读取视频
cap = cv2.VideoCapture(input_path)
# 获取视频信息
original_fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 计算新的尺寸
new_width = int(width * scale)
new_height = int(height * scale)
# 计算跳帧间隔
if original_fps > 0:
frame_skip = original_fps / fps
if frame_skip < 1:
frame_skip = 1
else:
frame_skip = 1
# 计算最大帧数
max_frames = int(max_duration * fps)
frames = []
frame_count = 0
frame_index = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 跳帧处理(使用浮点数计算)
if int(frame_index) % int(frame_skip) == 0:
# 转换BGR到RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 调整大小
if scale != 1.0:
frame_resized = cv2.resize(frame_rgb, (new_width, new_height))
else:
frame_resized = frame_rgb
# 转换为PIL Image
pil_img = Image.fromarray(frame_resized)
frames.append(pil_img)
frame_count += 1
# 达到最大帧数停止
if frame_count >= max_frames:
break
frame_index += 1
cap.release()
# 保存为GIF
if frames:
# 计算每帧持续时间(毫秒),支持小数fps
duration_ms = int(1000 / fps)
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=duration_ms,
loop=0, # 无限循环
optimize=True
)
return True, fps
else:
return False, fps
except Exception as e:
print(f"✗ 转换失败: {e}")
return False, fps
def convert_mp4_to_gif_with_size_check(input_folder, output_folder, scale=0.5, max_fps=15.0, min_fps=1.0, max_duration=10, max_size_mb=4.8):
"""
第二步:MP4转GIF,并检查文件大小
参数:
input_folder: 输入MP4文件夹(01视频文字)
output_folder: 输出GIF文件夹(02视频GIF)
scale: 缩放比例
max_fps: 最大尝试帧率(支持小数)
min_fps: 最小尝试帧率(支持小数)
max_duration: 最大时长(秒)
max_size_mb: 最大文件大小(MB)
"""
print("\n" + "="*60)
print("第二步:MP4转GIF(带大小检查)")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 获取MP4文件并排序
mp4_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.mp4'):
mp4_files.append(filename)
print(f"📁 找到 {len(mp4_files)} 个MP4文件")
print(f"⚙️ 参数: 最大文件大小={max_size_mb}MB, 帧率范围={min_fps}-{max_fps}fps")
# 处理每个文件
converted_count = 0
total_files = len(mp4_files)
for i, filename in enumerate(mp4_files):
print(f"\n处理 {i+1}/{total_files}: {filename}")
input_path = os.path.join(input_folder, filename)
base_name = os.path.splitext(filename)[0]
# 首先获取视频的原始信息
try:
cap = cv2.VideoCapture(input_path)
original_fps = cap.get(cv2.CAP_PROP_FPS)
cap.release()
# 实际最大fps不能超过原始视频fps
actual_max_fps = min(max_fps, original_fps)
print(f" 原始视频帧率: {original_fps:.1f}fps, 实际尝试范围: {min_fps}-{actual_max_fps}fps")
except:
actual_max_fps = max_fps
# 生成尝试的fps列表(从高到低,支持小数)
# 使用更小的步长(0.2fps)进行更精细的调整
fps_options = []
current_fps = actual_max_fps
step = 0.2 # 减小步长为0.2fps,进行更精细的调整
while current_fps >= min_fps:
# 四舍五入保留一位小数
rounded_fps = round(current_fps, 1)
if rounded_fps >= min_fps:
fps_options.append(rounded_fps)
current_fps -= step
# 确保包含最小fps
if min_fps not in fps_options:
fps_options.append(min_fps)
# 从高到低尝试不同的帧率
success = False
final_fps = None
final_path = None
for try_fps in fps_options:
# 格式化fps字符串用于文件名(乘以10)
# 例如:8.0 -> "80", 7.5 -> "75", 4.5 -> "45"
fps_filename = f"{float(try_fps*1)}"
output_filename = f"{base_name}_fps{fps_filename}.gif"
output_path = os.path.join(output_folder, output_filename)
print(f" 尝试帧率: {try_fps:.1f}fps", end="")
# 转换文件
convert_success, used_fps = convert_single_mp4_to_gif(
input_path=input_path,
output_path=output_path,
scale=scale,
fps=try_fps,
max_duration=max_duration
)
if not convert_success:
print(" → 转换失败")
continue
# 检查文件大小
if os.path.exists(output_path):
file_size_bytes = os.path.getsize(output_path)
file_size_mb = file_size_bytes / (1024 * 1024)
if file_size_mb <= max_size_mb:
# 文件大小符合要求
success = True
final_fps = used_fps
final_path = output_path
print(f" → 成功 ({file_size_mb:.2f}MB)")
break
else:
# 文件太大,删除并继续尝试更低的fps
print(f" → 太大 ({file_size_mb:.2f}MB),删除重试")
os.remove(output_path)
else:
print(" → 文件未生成")
if success:
converted_count += 1
print(f"✓ 最终使用帧率: {final_fps:.1f}fps, 文件名: {os.path.basename(final_path)}")
else:
print(f"✗ 所有帧率尝试都失败")
print(f"\n✅ GIF转换完成: {converted_count}/{total_files} 个文件")
return True
def add_text_to_gif(input_folder, output_folder, excel_file, font_size=36):
"""
第三步:给GIF添加文字(使用PIL)
参数:
input_folder: 输入GIF文件夹(02视频GIF)
output_folder: 输出GIF文件夹(03视频GIF文字)
excel_file: Excel文件(包含文字)
font_size: 字体大小
"""
print("\n" + "="*60)
print("第三步:给GIF添加文字")
print("="*60)
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 读取Excel中的文字
text_list = []
try:
df = pd.read_excel(excel_file, header=None)
# 读取B列(索引1)的文字
for value in df.iloc[:, 1]:
if pd.notna(value):
text_list.append(str(value).strip())
print(f"✅ 从Excel读取到 {len(text_list)} 个文字")
except Exception as e:
print(f"❌ 读取Excel失败: {e}")
return False
# 获取GIF文件并排序
gif_files = []
for filename in sorted(os.listdir(input_folder)):
if filename.lower().endswith('.gif'):
# 只处理最终的文件(带_fps标注的)
if '_fps' in filename:
gif_files.append(filename)
print(f"📁 找到 {len(gif_files)} 个GIF文件")
# 确保文字数量足够
if len(text_list) < len(gif_files):
print(f"⚠️ 文字数量({len(text_list)})少于GIF文件({len(gif_files)})")
for i in range(len(text_list), len(gif_files)):
text_list.append(f"文字{i+1:03d}")
# 加载字体(支持中文)
try:
# 尝试加载系统字体
font_paths = [
"C:/Windows/Fonts/simhei.ttf", # Windows黑体
"C:/Windows/Fonts/msyh.ttf", # Windows微软雅黑
"simhei.ttf",
None # 使用默认字体
]
font = None
for font_path in font_paths:
try:
if font_path and os.path.exists(font_path):
font = ImageFont.truetype(font_path, font_size)
print(f"✅ 使用字体: {font_path}")
break
elif font_path is None:
font = ImageFont.load_default()
print("⚠️ 使用系统默认字体(可能不支持中文)")
break
except:
continue
if font is None:
font = ImageFont.load_default()
print("⚠️ 使用默认字体(可能不支持中文)")
except Exception as e:
font = ImageFont.load_default()
print(f"⚠️ 字体加载失败,使用默认字体: {e}")
# 处理每个GIF文件
processed_count = 0
for i, filename in enumerate(gif_files):
if i >= len(text_list):
break
text = text_list[i]
input_path = os.path.join(input_folder, filename)
# 保持相同的文件名(包含fps信息)
output_filename = filename
output_path = os.path.join(output_folder, output_filename)
print(f"\n处理 {i+1}/{len(gif_files)}: {filename}")
print(f" 添加文字: {text}")
try:
# 读取GIF
gif = Image.open(input_path)
frames = []
# 处理每一帧
try:
while True:
# 复制当前帧
frame = gif.copy()
# 创建绘图对象
draw = ImageDraw.Draw(frame)
# 计算文字位置(右上角)
try:
# 尝试获取文字尺寸
if hasattr(font, 'getsize'):
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
else:
# 简单估算
text_width = len(text) * font_size * 0.6
text_height = font_size
except:
text_width = len(text) * font_size * 0.6
text_height = font_size
# 计算位置(右上角,带边距)
margin = 10
x = frame.width - text_width - margin
y = margin
# 添加白色背景(提高可读性)
bg_padding = 5
draw.rectangle([
x - bg_padding,
y - bg_padding,
x + text_width + bg_padding,
y + text_height + bg_padding
], fill='white')
# 添加文字
draw.text((x, y), text, font=font, fill='black')
frames.append(frame)
# 跳转到下一帧
gif.seek(gif.tell() + 1)
except EOFError:
pass # GIF结束
# 保存新GIF
if frames:
# 保留原始GIF的调色板和持续时间
durations = []
try:
gif.seek(0)
while True:
durations.append(gif.info.get('duration', 100))
gif.seek(gif.tell() + 1)
except:
pass
# 如果无法获取持续时间,使用默认值
if len(durations) != len(frames):
durations = [100] * len(frames) # 默认100ms
# 保存
frames[0].save(
output_path,
format='GIF',
append_images=frames[1:],
save_all=True,
duration=durations[:len(frames)],
loop=0,
optimize=True
)
# 检查文件
if os.path.exists(output_path):
file_size = os.path.getsize(output_path) / 1024 # KB
print(f"✓ 添加文字成功: {output_filename} ({file_size:.1f}KB)")
processed_count += 1
else:
print(f"✗ 保存失败")
else:
print(f"✗ 未提取到任何帧")
gif.close()
except Exception as e:
print(f"✗ 处理 {filename} 失败: {e}")
print(f"\n✅ 文字添加完成: {processed_count}/{len(gif_files)} 个文件")
return True
def check_dependencies():
"""检查依赖库"""
required_packages = ['pandas', 'opencv-python', 'Pillow', 'imageio', 'openpyxl']
missing = []
for package in required_packages:
try:
if package == 'pandas':
import pandas
elif package == 'opencv-python':
import cv2
elif package == 'Pillow':
from PIL import Image
elif package == 'imageio':
import imageio
elif package == 'openpyxl':
import openpyxl
except ImportError:
missing.append(package)
return missing
def install_dependencies(missing_packages):
"""安装缺失的依赖"""
import subprocess
import sys
print(f"缺少依赖包: {missing_packages}")
response = input("是否要自动安装?(y/n): ")
if response.lower() == 'y':
for package in missing_packages:
try:
print(f"正在安装 {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
print(f"✓ {package} 安装成功")
except:
print(f"✗ {package} 安装失败")
return True
else:
print("请手动安装: pip install pandas opencv-python Pillow imageio openpyxl")
return False
def main():
"""主函数"""
print("="*60)
print("MP4转GIF并添加文字工具(三步流程)")
print("作者: deepseek, 阿夏")
print("日期: 20251220")
print("="*60)
# 检查依赖
missing = check_dependencies()
if missing:
if not install_dependencies(missing):
return
# 设置路径
base_path = Path(r'C:\Users\jg2yXRZ\OneDrive\桌面\20251220万相2点6特效')
# 定义文件夹路径
video_folder = base_path / '00视频' # 原始视频
renamed_folder = base_path / '01视频文字' # 重命名后的视频
gif_folder = base_path / '02视频GIF' # 转换的GIF
final_folder = base_path / '03GIF贴字' # 最终带文字的GIF
excel_file = base_path / '名称.xlsx' # Excel文件
# 检查必要文件
if not video_folder.exists():
print(f"❌ 原始视频文件夹不存在: {video_folder}")
return
if not excel_file.exists():
print(f"❌ Excel文件不存在: {excel_file}")
return
print("\n📋 处理流程:")
print(f" 1. {video_folder} → {renamed_folder} (复制重命名)")
print(f" 2. {renamed_folder} → {gif_folder} (MP4转GIF,自动调整帧率)")
print(f" 3. {gif_folder} → {final_folder} (添加文字)")
print(f" 配置: Excel={excel_file}")
# 显示参数
print("\n⚙️ 处理参数:")
print(" 缩放比例: 0.5 (50%)")
print(" 帧率范围: 1.0-8.0 fps (自动调整,步长0.5)")
print(" 最大文件大小: 4.8 MB")
print(" 最大时长: 10秒")
print(" 字体大小: 36px")
print("\n" + "-"*60)
confirm ='y'
# input("是否开始处理?(y/n): ")
if confirm.lower() == 'y':
start_time = datetime.now()
# 第一步:复制重命名
print("\n" + "="*60)
print("开始第一步:复制并重命名视频")
step1_success = copy_and_rename_videos(
input_folder=str(video_folder),
output_folder=str(renamed_folder),
excel_file=str(excel_file)
)
if not step1_success:
print("❌ 第一步失败,停止处理")
return
# 第二步:MP4转GIF(带大小检查)
print("\n" + "="*60)
print("开始第二步:MP4转GIF(自动调整帧率)")
step2_success = convert_mp4_to_gif_with_size_check(
input_folder=str(renamed_folder),
output_folder=str(gif_folder),
scale=0.5,
max_fps=10.0, # 从8.0fps开始尝试
min_fps=5.0, # 最低到1.0fps
max_duration=10,
max_size_mb=4.8 # 最大4.8MB
)
if not step2_success:
print("❌ 第二步失败,停止处理")
return
# 第三步:添加文字
print("\n" + "="*60)
print("开始第三步:给GIF添加文字")
step3_success = add_text_to_gif(
input_folder=str(gif_folder),
output_folder=str(final_folder),
excel_file=str(excel_file),
font_size=36
)
# 统计信息
end_time = datetime.now()
duration = end_time - start_time
print("\n" + "="*60)
print("🎉 处理完成!")
print("="*60)
print(f"📊 处理结果:")
print(f" 第一步: {'成功' if step1_success else '失败'}")
print(f" 第二步: {'成功' if step2_success else '失败'}")
print(f" 第三步: {'成功' if step3_success else '失败'}")
print(f"⏱️ 总耗时: {duration}")
print(f"📁 输出文件夹:")
print(f" 重命名视频: {renamed_folder}")
print(f" 纯GIF文件: {gif_folder}")
print(f" 带文字GIF: {final_folder}")
print("="*60)
else:
print("已取消处理")
if __name__ == "__main__":
main()






贴字多了0.1KB左右
视频特效效果
































