从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

火山方舟 Agent Plan 与 Coding Plan 活动,价格 ¥9.9 起

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

查看套餐优惠
Seedance 2.5 视频创作活动:30 秒视频、多模态参考、局部编辑

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

探索视频创作

照片生成延时视频

查看 Markdown

这个应用使用 Grok Imagine 构建 AI 延时视频生成器,将一张照片变成展示其随时间变化的视频。它展示了如何通过针对每个阶段编辑同一张照片来保持场景一致,以及如何把图像固定在视频中的指定时刻。

从单张照片出发,生成一段十秒的变化过程,例如一条街道经历四季,或一幅素描逐渐变成绘画。Grok 分析照片并规划各阶段;每个阶段都是对原照片的一次编辑;最终视频会在指定时刻依次经过这些编辑结果。一个小型 Web 应用会实时展示每一步,并在视频播放到对应位置时点亮相应的固定点。

你将学到什么

  • 将照片发给 Grok,并逐阶段以结构化输出流式接收计划

  • 通过编辑同一张照片而不是重新生成图像,让场景在各张图中保持一致

  • 将图像固定在同一段视频的指定时刻:首帧、按三分之一秒网格排列的关键帧,以及末帧

  • 在展示进度的同时等待耗时较长的视频任务,并在视频 URL 过期前保存视频

  • 使用服务器发送事件,将 Node 服务器的进度流式传输到网页

运行方式

需要 Node.js 22.13 或更新版本。将 API 密钥放在仓库根目录的 .env 中,或导出 XAI_API_KEY。

Bash

cd examples/photo-to-time-lapse/typescript
npm install
npm run web

打开 http://localhost:3000。页面已加载街角示例照片;点击 Make time-lapse,即可让它经历四季。你也可以先选择其他变化:空地变成建筑、白天变成夜晚,或素描变成绘画。还可以拖入自己的照片,或描述自定义的变化。页面的操作方式类似关键帧编辑器:左侧面板显示 Grok 的推理和计划;每个阶段会落在时间轴的标记位置,编辑完成后立即显示对应的静态图像。视频渲染期间,监视器会显示进度。随后视频开始播放,播放头到达各固定点时,相应标记会亮起。点击标记可跳转到该位置,点击 Stop 可取消本次运行。

如果要从终端生成延时视频,请传入一张照片,并指定预设(seasons、day-to-night、sketch-to-painting 或 construction)或用自己的话描述变化:

Bash

npm start -- ./your-photo.jpg day-to-night
npm start -- ./your-photo.jpg "A field of tulips, from closed buds to full bloom"

不传参数时,程序会使用 sample-street.jpg,这张图由 grok-imagine-image-2.0 制作,并生成四季变化。两个版本都会将计划、静态图像和视频保存到 output/<title>/;终端版本还会在该目录写入 index.html,在固定的静态图像上方播放视频。一次运行通常生成四张图和一段十秒视频,耗时两到四分钟。我们运行时的费用为 $1.72:每次图像编辑 $0.07,视频 $1.44。结束时,网页和终端都会显示图像与视频各自的费用;这两项占了几乎全部费用。

工作原理

共享代码位于 src/timelapse.ts。makeTimeLapse() 执行以下步骤,将各文件保存至 output/<title>/,并在每一步开始和结果就绪时报告进度:

  1. planStages() 将照片和变化描述发送给 grok-4.7,以结构化输出流式返回标题、各阶段以及描述阶段间场景如何变化的一句话。每个阶段包含标签、给图像编辑器的指令,以及视频应到达该阶段的秒数。Grok 的推理一到达就会报告,每个阶段写出后也立即报告。由于计划较短,即使用默认推理强度,通常也能在半分钟内完成。

  2. placeStages() 将各阶段放到片段的时间网格上。第一阶段成为首帧,最后阶段成为末帧;中间阶段成为关键帧。API 最多接受四个关键帧,它们必须严格位于片段内部、落在三分之一秒的网格上,且不能占用同一位置。因此每个关键帧会吸附到网格;必要时移动到下一个空位。

  3. editPhoto() 使用 client.images.edit() 为每个阶段制作静态图像,每次都以原照片为输入。编辑方式让每张图中的相机视角、构图和建筑保持一致,视频只需呈现变化过程。这些编辑会并行执行。

  4. renderVideo() 启动一个十秒的 grok-imagine-video-1.5 任务,调用 client.videos.generate():第一张静态图作为 image,中间的图作为对应秒数的 keyframes,最后一张作为 last_frame。SDK 0.2.2 已支持 keyframes,但尚未支持 last_frame;因此代码通过类型断言加入 last_frame,并将其图像作为数据 URL 传入,因为 SDK 只会转换它认识的字段中的 Blob。也可以将最后阶段固定为结束前三分之一秒的关键帧,但视频在该位置后还会继续变化,结尾会与静态图略有差异。

  5. waitForVideo() 每五秒轮询一次 client.videos.get(),并报告 API 在每次响应中提供的进度;client.videos.wait() 不会转发这一进度。视频 URL 是临时的,因此 renderVideo() 在完成后立即下载视频。

在我们的运行中,首帧和末帧均与对应的静态图像一致;每个关键帧的图像也都在其标记时间的三分之一秒内出现,通常略早一些。

src/server.ts 是一个小型 node:http 服务器。由于 EventSource 只能发送 GET 请求,页面会先把照片上传到 /api/photos,然后使用返回的 ID 打开 /api/time-lapse。服务器运行 makeTimeLapse(),通过服务器发送事件向页面推送进度,并以字节范围形式提供已保存的静态图像和视频,以便浏览器跳转播放。makeTimeLapse() 会向每次 API 调用传入 AbortSignal;关闭页面或点击 Stop 时,服务器会中止这些调用。已经启动的视频仍会继续渲染并计费,因为 API 无法取消该任务。

public/index.html 使用原生 HTML 和 JavaScript,在时间轴上展示这些事件并播放视频。src/index.ts 在终端中执行相同的工作并打印每一步。