mcpField
P
photo-slideshow

photo-slideshow

github.com/iart-ai创意与媒体11756 次安装18 周前更新
代码仓库

关于 photo-slideshow

photo-slideshow 是将照片文件夹制作成 MP4 的技能,面向需要回忆集锦或旅行蒙太奇的人。它强调按照片确定性地变化 Ken Burns 运镜、处理混合宽高比,并让切换与音乐节拍同步。

主要功能

  1. 01为每张照片生成不同的 Ken Burns 运镜
  2. 02确定性动画便于重复渲染
  3. 03用转场连接照片
  4. 04按音乐节拍安排切换
  5. 05以模糊填充处理不同宽高比
  6. 06从文件夹清单生成视频

适用场景

  1. 01制作旅行或婚礼回忆视频
  2. 02将年度照片整理成配乐集锦
  3. 03为竖图和横图混合的照片集生成视频
  4. 04用同一模板批量渲染多个照片文件夹

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iart-ai/ecommerce-video-skills --skill photo-slideshow

安装后在 Claude Code 中输入/photo-slideshow确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
references/
aspect-and-layout.md
ken-burns.md
templating-folder.md
timing-and-audio.md

详细介绍

幻灯片视频

将一组照片制作成一段精美成片,让人感觉像是实地拍摄,而非简单拼接。关键在于为每张静态照片赋予柔和的动态效果(Ken Burns),按照音乐切换幻灯片,优雅地处理不同尺寸的照片,并读取整个文件夹,让同一模板可以渲染任意一组图片。

适用场景

  • 一个照片文件夹 → 一段完整的 MP4(核心使用场景)。
  • 配乐回忆录、总结、年度回顾、婚礼/旅行蒙太奇。
  • 任何需要动态效果、转场和同步节奏的静态图片集。

让幻灯片看起来专业的关键规则

任何两张幻灯片的移动方式都不能相同。业余幻灯片最容易露馅的地方,就是每张照片都做一模一样的缓慢放大。为每张照片改变移动方向、放大或缩小,以及起始缩放比例,但要让这些变化具有确定性(由索引作为种子),这样重新渲染的结果就会一致,基于帧的渲染器也能保持稳定。

// seeded PRNG → same photo always gets the same motion, but each photo differs
const mulberry32 = (a) => () => {
  a |= 0; a = (a + 0x6d2b79f5) | 0;
  let t = Math.imul(a ^ (a >>> 15), 1 | a);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};

每张照片的 Ken Burns 效果

每张幻灯片从一组精简的运动方式中选择一种,让整组照片富有节奏感而不重复。将 scale 和 translate 一起动画处理;绝不要为 width/height 添加动画(这会触发布局重排并造成闪烁)。

参数范围原因
缩放放大 1.0→1.12 或缩小 1.12→1.0效果微妙;超过约 1.15 就会像快速变焦
平移左/右/上/下/对角线之一方向由种子决定,不能每张都相同
缓动easeInOutSine缓慢、连续——Ken Burns 效果不会突然加速
锚点将 transform-origin 朝向主体平移时让人脸/地平线留在画面内
import { useCurrentFrame, interpolate, Easing } from "remotion";
const frame = useCurrentFrame();              // the ONLY clock
const p = interpolate(frame, [0, durInFrames], [0, 1], {
  extrapolateRight: "clamp", easing: Easing.inOut(Easing.sin),
});
const scale = startScale + (endScale - startScale) * p;   // e.g. 1.0 → 1.10
const x = panX * p, y = panY * p;                          // px, sign from seed
// style: transform: `scale(${scale}) translate(${x}px, ${y}px)`

起始缩放比例始终要 ≥ 1.05,以免平移时露出空白边缘。完整的、带有主体感知锚点的种子化 <KenBurnsImage> 组件见 references/ken-burns.md。

照片之间的转场

选用一种转场并保持一致——混用擦除、旋转和立方体转场会显得像“套用模板”。0.4–0.6 秒的交叉淡化是稳妥、经典的默认选择;柔和推进适合旅行片段。让前后幻灯片重叠,避免硬切,除非是在节拍点切换。

import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";
// <TransitionSeries.Sequence durationInFrames={hold}> ...slide... </Sequence>
// <TransitionSeries.Transition timing={linearTiming({durationInFrames: 15})} presentation={fade()} />

与节拍/音乐同步切换幻灯片

幻灯片的成败取决于节奏。先确定音乐,再获取节拍时间戳,然后让每次幻灯片切换都对齐节拍。离线检测一次节拍,并将其写入 props——绝不要逐帧分析音频。

# extract beat onset times (seconds) with librosa, write to beats.json
python3 -c "import librosa,json,sys; y,sr=librosa.load(sys.argv[1]); \
_,b=librosa.beat.beat_track(y=y,sr=sr); \
json.dump([float(t) for t in librosa.frames_to_time(b,sr=sr)], open('beats.json','w'))" song.mp3

接着将节拍秒数转换为帧数,并分配幻灯片边界。每张照片保持一个音乐单位(通常每隔 2 或 4 拍切换一次——每拍都切换对照片来说太急促)。有关计数到帧数的计算、节奏到幻灯片的分配,以及无需音频分析的备用方案(固定 BPM 网格),请参阅 references/timing-and-audio.md。

不同比例的照片

真实的照片文件夹里,既有手机竖拍照片,也有相机横拍照片。绝不要拉伸——变形不可接受。以下三种策略按优先顺序排列:

策略效果适用场景
模糊填充照片适配画框,缩放并模糊后的副本填满空白区域方向混杂,默认选项
填充(cover)照片填满画框,裁掉边缘照片比例与画框大致一致
适配(letterbox)照片完整显示在纯色/渐变背景上每个像素都很重要(文档、艺术作品)

模糊填充是现代标准,因为它能填满画框,同时不裁掉主体。将同一源图渲染两次:在后方放置 cover 模糊层,前方叠加 contain 清晰照片。组件和方向检测逻辑见 references/aspect-and-layout.md。

字幕、日期、片头/片尾

  • 字幕/日期:从文件名、EXIF 或旁置 JSON 文件中读取;将其放在固定的画面下方区域,幻灯片稳定后淡入(约 0.3 秒),离场前淡出。
  • 片头卡片:标题 + 日期范围,在第一张照片出现前显示 1.5–2 秒。
  • 片尾卡片:结束语或联系信息/行动号召,显示 2–3 秒,让视频以明确的意图结束,而不是在照片播放到一半时渐隐至黑屏。

将文件夹模板化 → 视频

最终效果:将合成指向一个文件夹,根据文件列表生成时间线,然后渲染一次。照片相关内容都不是硬编码的——数量、顺序、字幕和时长都来自数据。

// data is a prop: [{ src, caption?, date? }]; the timeline is computed, not authored
export const Slideshow = ({ photos, beats, music }) => { /* maps photos → slides */ };
# build the manifest from a folder (sorted, with EXIF dates), then render
node scripts/build-manifest.mjs ./photos > photos.json
npx remotion render Slideshow out/slideshow.mp4 --props=photos.json

将颜色、字体、转场和幻灯片时长都放在一个主题对象中,这样每次渲染都保持一致,只需更换照片即可。请参阅 references/templating-folder.md,了解清单生成器(排序、EXIF 日期、去重)、完整的数据驱动 <Slideshow>,以及在无法使用 React 时仅用 FFmpeg 的方案。

输出检查清单

  • 每张照片的动效都以索引作为种子——方向和缩放各不相同,不会有两张完全相同。
  • 所有动效都是 useCurrentFrame() 的纯函数;起始缩放比例 ≥ 1.05。
  • 使用统一的转场(默认交叉淡化),采用重叠转场而非硬切。
  • 幻灯片切换与节拍对齐;照片至少停留 ≥ 1 个音乐单位,而不是每拍都切换。
  • 混合宽高比通过模糊填充处理——不拉伸任何内容。
  • 字幕/日期位于固定区域;片头和片尾卡片分别置于照片集的开头和结尾。
  • 照片、字幕和时长都来自文件夹清单——一个模板适用于任意照片集。

交付与验证(渲染静帧 → MP4)

随附的辅助脚本(scripts/):使用 scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png 将静帧拼成图集,然后使用 scripts/probe-mp4.sh out.mp4 [WxH] [fps] 验证编码。请参阅 scripts/README.md。

这是重型任务:交付物是 MP4,而不是实时页面。幻灯片会载入整个文件夹中的用户照片——验证时主要要检查的是每张照片是否都实际加载,并且经过重新构图后没有失真。

输出要求:

  • 一个注册了合成的 Remotion 项目(<Composition> + zod schema + defaultProps);Ken Burns 缩放/平移、转场和节拍时间安排都必须是 useCurrentFrame() 的纯函数(不能使用 CSS 转场、计时器、Date.now();动效以索引而非 Math.random() 作为种子)。
  • 通过 staticFile() 加载照片,并使用 delayRender/continueRender 控制渲染,确保每张图片(以及任何字幕字体)都已就绪后才渲染对应帧——否则幻灯片会空白显示。时长取决于数据 → 根据照片数量在 calculateMetadata 中计算,而不是手动指定。
  • 交付物 = 渲染后的 out/*.mp4 和项目(文件夹内容变更时重新渲染)。

验证流程——渲染静帧 → 检查 → 编码。 先生成成本较低的 PNG,确认照片效果合适后再渲染视频。

# Frame-exact stills WITH THE MANIFEST YOU'LL SHIP (real photo paths), not defaultProps
npx remotion still Slideshow out/f-intro.png  --frame=15  --props=photos.json   # intro card
npx remotion still Slideshow out/f-mid.png    --frame=200 --props=photos.json   # a mid slide mid-Ken-Burns
npx remotion still Slideshow out/f-outro.png  --frame=500 --props=photos.json   # outro card
# end frame = durationInFrames - 1 (npx remotion compositions reads it)

检查每张 PNG 的保真度(每张幻灯片上的照片是否正确;字幕/日期文本是否正确;模糊填充是否能填充混合宽高比且不拉伸),以及瑕疵(图像空白/未加载、照片拉伸/失真、平移时露出空白边缘——起始缩放比例 ≥ 1.05、字幕超出下三分之一安全区域、宽高比错误/出现黑边)。

# Only after stills are clean:
npx remotion render Slideshow out/slideshow.mp4 --props=photos.json
npx remotion render Slideshow out/slideshow.gif --props=photos.json --codec=gif   # README first-screen proof

**批量处理(一个模板,多个文件夹):**使用同一模板处理多组照片时,先通过静帧验证一个代表性文件夹的清单,再批量渲染其余文件夹——一次性发现空白图像或宽高比拉伸问题,避免重复出现 N 次。

完成前:

  1. 片头/中间/片尾的静帧都能干净渲染——没有错误,照片确实已加载(非空白)。
  2. 字幕/日期正确,且位于固定的下三分之一安全区域;混合宽高比均以模糊填充处理,没有拉伸;平移时不露出空白边缘。
  3. 所有动效都由帧驱动,并以索引作为种子——不使用 CSS 转场/计时器/Date.now()/Math.random()。
  4. 实际交付的文件夹清单能够正确渲染,而不仅是 defaultProps。
  5. 完整 MP4 已编码且能正常播放;(可选)为 README 渲染 GIF。

参考文件

  • references/ken-burns.md — 完整的种子化 <KenBurnsImage> Remotion 组件:通过 mulberry32(index) 为每张照片选择动态效果,基于主体的 transform-origin、安全的起始缩放计算,以及对应的 FFmpeg zoompan 实现。
  • references/timing-and-audio.md — 使用 librosa 检测节拍、将节拍秒数转换为帧、按音乐单位分配幻灯片、无音频分析时的 BPM 网格回退方案,以及每张照片的节奏预算。
  • references/aspect-and-layout.md — 方向检测、模糊填充/裁切铺满/完整显示布局组件,以及适用于 16:9、9:16 和 1:1 的安全区域映射,让一个主版本适配所有平台。
  • references/templating-folder.md — 文件夹→清单构建器(自然排序、EXIF 拍摄日期、从文件名生成字幕)、包含片头/片尾卡片的完整数据驱动 <Slideshow>,以及纯 FFmpeg 幻灯片工作流。

评论

支持 Markdown