from datetime import datetime
import cv2
import imageio
from PIL import Image
import subprocess
import os
def extract_best_frame(video_path):
"""使用光流法选择最具代表性的封面帧"""
cap = cv2.VideoCapture(video_path)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
target_frame = max(int(total_frames * 0.2), 1) # 优先选择视频前20%处
cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame)
success, frame = cap.read()
cap.release()
if success:
return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
else:
return None
def generate_live_photo(input_path, output_dir, platform='ios'):
"""核心转换函数"""
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
base_name = os.path.splitext(os.path.basename(input_path))[0]
# 阶段一:封面帧处理
frame = extract_best_frame(input_path)
if frame is None:
raise ValueError("无法提取有效视频帧")
img = Image.fromarray(frame)
img.save(os.path.join(output_dir, f"{base_name}_cover.jpg"),
quality=95, exif=img.info.get('exif', b''))
# 阶段二:视频转码
output_video = os.path.join(output_dir, f"{base_name}_live.mov" if platform == 'ios'
else f"{base_name}_motion.jpg")
ffmpeg_cmd = [
'ffmpeg', '-y', '-i', input_path,
'-vcodec', 'hevc', '-tag:v', 'hvc1',
'-acodec', 'aac', '-movflags', 'faststart',
'-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2', # 确保偶数分辨率
'-metadata', f'creation_time={datetime.now().isoformat()}',
output_video
]
subprocess.run(ffmpeg_cmd, check=True)
# 阶段三:安卓特殊封装
if platform == 'android':
with open(output_video, 'rb') as f:
video_data = f.read()
with Image.open(os.path.join(output_dir, f"{base_name}_cover.jpg")) as img:
img.save(output_video, 'jpeg', append_images=[Image.new('RGB', (1,1))],
save_all=True, append_images_data=[video_data])
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='MP4转动态照片工具')
parser.add_argument('input', help='输入MP4文件路径')
parser.add_argument('-o', '--output', default='./output',
help='输出目录(默认当前目录下的output文件夹)')
parser.add_argument('-p', '--platform', choices=['ios', 'android'],
default='ios', help='目标平台(默认iOS)')
args = parser.parse_args()
try:
generate_live_photo(args.input, args.output, args.platform)
print(f"转换成功!文件保存在:{os.path.abspath(args.output)}")
except Exception as e:
print(f"转换失败:{str(e)}")
执行命令示例
[Asm] 纯文本查看 复制代码# iOS设备转换
python3 video2live.py input.mp4 -p ios -o ~/Desktop/live_photos
# 安卓设备转换
python3 video2live.py input.mp4 -p android
可以加代码进行批处理
[Python] 纯文本查看 复制代码import glob
for mp4_file in glob.glob("videos/*.mp4"):
generate_live_photo(mp4_file, "output")
如何查看live图
iOS:将.mov和.jpg同时导入Photos应用
安卓:通过Google Photos查看动态效果