叠帧 · 编号图片 → 封面视频
概述
扫描文件夹,仅保留文件名主干为纯数字的文件(01.jpg、200.jpg),按数字顺序播放,并编码为H.264 mp4。图片之间可选择硬切(--transition none)或淡入淡出(--transition fade,默认)。非数字命名的图片(logo、discord等)和已有视频将被忽略。
操作步骤
- 首先检查文件夹。 列出文件,并使用ffprobe探测一张图片的尺寸。记录有多少张编号图片以及宽高比是否混合。
- 备份已有输出。 如果
<文件夹>/cover.mp4已存在,在渲染前将其重命名为cover.prev-YYYYMMDD.mp4。切勿静默覆盖用户之前的渲染结果。 - 使用内置脚本渲染:
python "<技能目录>/scripts/imgseq2mp4.py" --dir "<文件夹>" --out cover.mp4
默认值:每张图片0.8秒,0.2秒淡入淡出过渡,30 fps,1920x1080,黑边填充,CRF 18,无音频,+faststart。
- 验证,不要猜测。 探测时长/帧数,确认无黑帧,并提取最后一帧与最后一张源图片对比:
ffprobe -v error -show_entries format=duration -show_entries stream=nb_frames -of default=nw=1 cover.mp4
ffmpeg -i cover.mp4 -vf blackdetect=d=0.05:pix_th=0.10 -an -f null -
ffmpeg -y -sseof -0.05 -i cover.mp4 -frames:v 1 frame_last.png
当启用过渡时,还需在第一个过渡窗口内抓取一帧,检查是否为混合帧(更接近图片2而非硬切结果,但仍与两个源有可测量的差异)。
- 报告 图片数量、忽略的图片、总时长、输出路径和大小。
节奏
总时长 = n × 秒数 − (n−1) × 过渡秒数。每张图片的呈现场步长为 秒数 − 过渡秒数;观众感受到的是这个步长,而非 --seconds。
| 每张图片步长 | 命令 | 20张图 | 44张图 | 200张图 |
|---|---|---|---|---|
| 0.35 秒 | --seconds 0.5 --transition-seconds 0.15 | ~8 秒 | ~18 秒 | ~75 秒 |
| 0.45 秒 | --seconds 0.65 --transition-seconds 0.2 | ~9 秒 | ~22 秒 | ~95 秒 |
| 0.60 秒(默认) | --seconds 0.8 --transition-seconds 0.2 | ~12 秒 | ~27 秒 | ~120 秒 |
| 0.80 秒 | --seconds 1.0 --transition-seconds 0.2 | ~16 秒 | ~34 秒 | ~150 秒 |
硬切使用 --transition none --seconds <步长>。
当用户要求更快或更慢时,逐行调整。保持 --transition-seconds 在0.15–0.2;更长的过渡会让人感觉像幻灯片而非淡入淡出。
规则
- 仅使用淡入淡出。不要使用滑动、擦除或其他推进过渡——这些已被拒绝。
- 选择正则表达式
^\d+\.(jpe?g|png|webp)$— 文件名必须仅为数字。这排除了logo.jpg、discord.jpg、cover.jpg。 - 按整数值排序,而非字典序(
2.jpg在10.jpg之前,99.jpg在100.jpg之前)。编号不连续是可接受的;缺失编号不是错误。 - 使用
--fit pad(默认)以保持整个帧可见。仅当用户明确要求边缘到边缘裁剪时才使用--fit fill。 - 输出无声。除非用户提供音轨,否则不要添加音乐。
- 保持颜色不变。JPEG是全范围的;如果将其编码为未标记的全范围H.264,大多数播放器会解码为有限范围,导致结果看起来更亮且洗白(+10个级别)。脚本会转换为有限范围bt709并进行标记——不要“简化”该处理链。
- 脚本拒绝在未使用
--force时覆盖--out;通过备份解决,而非盲目强制。
机器说明
- ffmpeg不在PATH中。脚本优先使用真正的
ffmpeg.exe(~/.dsh/tools/ffmpeg/**/ffmpeg.exe,然后是C:/或D:/ffmpeg下的副本),再考虑任何.cmd存根,因为存根通过cmd.exe路由并限制命令行长度约8k字符。可通过FFMPEG=/path/to/ffmpeg覆盖。 - 此构建缺少
-filter_complex_script;通过-filter_complex内联传递xfade图(脚本已如此实现)。 - 过渡按
--chunk(默认6)分组渲染,然后合并,因此最多同时打开6个解码器。在16 GB机器上,单次44输入xfade传递会耗尽RAM(剩余约2.4 GB)并失败,错误为Cannot allocate memory;在此不要将--chunk提高到8以上。分组是递归的,因此200张图分三个阶段合并,不会内存不足。 - 中间阶段文件使用CRF 14 / veryfast编码并丢弃;只有最终传递使用
--crf/ medium。 - 中间帧需要可写目录。
tempfile.mkdtemp()的结果在受限沙箱下可能不可访问(在Windows + DSH上观察到:ffmpeg报Permission denied,os.listdir抛出WinError 5)。因此脚本创建一个普通目录,验证其可写性,并通过$IMGSEQ_WORKDIR→ 系统临时目录 →<输出目录>/.imgseq-work进行回退。
资源
scripts/imgseq2mp4.py— 选择、数值排序、硬切或淡入淡出渲染、便于验证的摘要。