作者使用SAM 2/3.1分割模型在Modal无服务器GPU上实现GIF物体跟踪字幕和背景替换,详解Browser→Lambda API→Modal的架构设计与成本控制策略。
我构建了 GifGadgets,一套运行在浏览器端的 GIF 工具集。其中大部分是枯燥的客户端工作,但有两个功能不是:跟随 GIF 中物体移动的字幕,以及背景移除或替换。这两个功能都使用了无服务器 GPU(Modal)上的分割模型(SAM 2 和 3.1)。这篇文章讲述它们的实现方式,以及如何保持低成本运行和低维护。作为一个有日常工作的独立开发者,我希望把它搭好后能基本"撒手不管"(虽然要做到真正撒手不管还是很难的)。
跟随物体:点击 GIF 中的某个物体,字幕会逐帧追踪它。
移除或替换背景:点击主体,获取每一帧的遮罩,然后可以换一个新背景或留成透明。
其他所有功能(调整大小、裁剪、反转、格式转换、添加文字)都在浏览器中运行,文件永远不会离开设备。
Browser → Lambda API → Modal。
服务器返回每一帧的 gzip 压缩、位打包遮罩。所有合成操作(新背景、文字放置、编码)都在浏览器的 web worker 中进行。更换替换背景不需要再次调用 GPU,这才是最大程度节省成本的地方。
GPU 任务将进度写入以 call ID 为键的 Modal Dict,这样 UI 可以显示"正在启动 GPU…"然后是"第 12 帧,共 80 帧"。如果写入失败,任务本身不会失败,UI 会回退到普通的"运行中"提示。
追踪功能使用 L4 上的 SAM 2.1。背景移除使用 H100 上的 SAM 3.1。两者各自独立镜像、锁定依赖,升级一个不会影响另一个。模型权重在构建时 baked 到容器镜像中,所以冷启动从不等待数 GB 的下载。
无服务器 GPU 会 scale 到零,这很好——直到某个功能突然爆红。按重要性排序的保护措施如下:
预付信用额度。硬性上限是 Modal 上的信用余额。如果用完,功能会降级,但不会继续计费。
DynamoDB 中的每 IP 配额,外加 WAF 速率规则。
Kill Switch。一个环境变量即可禁用分割路由,无需重新部署。
AWS 端的预算告警,设置成提前通知我。
预计算演示。示例 GIF 使用存储的结果,所以用户可以体验功能而无需消耗 GPU 算力。
以上都不是真正的全局上限,我在自己的文档中如实说明了这一点。我接受的失败模式是:AI 功能可能在繁忙的一天耗尽信用,所以 UI 会告知用户。
一开始我尝试在 Lambda 上运行 SAM 推理,但速度极慢,用户体验很差(我自己测试和使用时就能感受到)。处理追踪帧需要超过一分钟。为了缩短时间,我迁移到 Modal 无服务器函数来利用 GPU,速度确实提升了。然而帧数更多的 GIF 仍然很慢,所以我对目标追踪的 GIF 按每秒 10 帧采样,效果仍然可以接受。
目前项目有一个 src 文件夹存放前端构建产物,使用 jinja 构建 HTML 文件,然后放到名为 frontend 的文件夹中,同步到 S3 并通过 CloudFront 分发。
我觉得 src 和 build 可以更"DRY",但目前这东西能正常跑,我就不想动它了。
演示地址是 gifgadgets.com。演示中的背景功能有限制:[具体限制说明]。如果服务繁忙,示例 GIF 仍然可用。希望得到反馈,尤其是追踪漂移或遮罩错误的 GIF。