post: running AI video locally on a 12 GB GPU (EN+ZH) with art, OG cards and banner
Deploy / build (push) Successful in 20s

This commit is contained in:
hoelee committed 2026-10-20 05:45:49 +08:00
1 parent a96cfb73db
commit 3efeafc567
13 files changed
+455 -1

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+6 -1
View File
@@ -1,6 +1,11 @@
{
"_about": "Inputs for scripts/og-gen/render_from_art.mjs ('one art, four targets'). One entry per post: `art` = the zero-text base frame (default <art-dir>/<slug>.png), `evidence` = the short factual line shown in the card's evidence bar (REQUIRED, written by hand — never derived), optional `ogTitle_en` / `ogTitle_zh` = shortened display titles for the card only (consultant question 1, option C: the article keeps its real title), optional `tags` = override the three badge tags. Cards render from the post's own frontmatter (title/description/category) unless an ogTitle override is present.",
"_status": "72 of 72 posts have a hand-written evidence line; base art is generated from C:/AI/ideogram4/captions/<slug>.json",
"_status": "73 of 73 posts have a hand-written evidence line; base art is generated from C:/AI/ideogram4/captions/<slug>.json",
"running-ai-video-locally-on-a-12gb-gpu": {
"evidence": "1280x736 -> 1080p - 32 min / 5 s - $0",
"ogTitle_en": "AI Video on a 12 GB GPU",
"ogTitle_zh": "12GB 显卡跑 1080p 视频"
},
"adding-a-domain-to-someone-elses-mail-server": {
"evidence": "4 DNS records · 554 antispam reject"
},
+24
View File
@@ -940,6 +940,30 @@
768
]
},
"running-ai-video-locally-on-a-12gb-gpu": {
"sw": 2158,
"sh": 1218,
"left": 745,
"top": 83,
"fill": [
8,
13,
20
],
"zoom": 1.586,
"silhouette": 0.47,
"cut": 0.0,
"band": [
772,
168,
1246,
504
],
"source_size": [
1360,
768
]
},
"running-production-infrastructure-solo": {
"sw": 2001,
"sh": 1130,
@@ -0,0 +1 @@
{"high_level_description":"A dark-mode 3D cyber-bento product render of a single piece of industrial hardware, photographed on a seamless dark backdrop. The main high-detail 3D focal subject is positioned on the right side of the canvas, leaving clean dark negative space across the left side.","style_description":{"aesthetics":"modern dark cyber-bento, industrial precision, sleek machined surfaces, raytraced ambient occlusion","lighting":"volumetric studio rim lighting in royal cobalt blue (#4d82ff) with neon cyan (#22d3ee) and electric violet-magenta (#a855f7) synaptic glow and subtle cyan glow","medium":"3d_render","art_style":"octane render, matte obsidian hardware, frosted acrylic glass, precise machined surface detail","color_palette":["#0B0F19","#111827","#1E293B","#22D3EE","#A855F7","#4D82FF"]},"compositional_deconstruction":{"background":"Deep midnight obsidian slate background (#0b0f19 to #111827) fading into pure clean negative space across the entire left 60% of the horizontal canvas, with a soft rim vignette and faint volumetric haze.","elements":[{"type":"obj","bbox":[150,550,850,945],"desc":"A compact dark alloy pressure vessel clamped inside a narrow slot, its single outlet feeding a translucent ribbon of thin frames between two polished steel rollers that widen the ribbon as it passes. Clean unbranded matte surfaces, blank panels, no printed markings. Razor-sharp detail, no text, no letters, no watermark."}]}}
+1
View File
@@ -38,6 +38,7 @@
"read-only-nocodb-dashboard-for-a-remote-database": "A glass observation window mounted over a distant obsidian data silo, the viewing frame open while every latch on the data hatch itself stays closed",
"reading-a-containers-own-api-docs": "A scanning beam unfolding a lathe-cut obsidian shell to expose its inner circuit routing, four small inspection tools laid out on the dark bench beside it",
"replacing-rdpguard-with-ipban": "An old shield slab lifted out of a dark doorway as a tighter woven black mesh gate is lowered into the same frame, grey marker pins fenced off outside it",
"running-ai-video-locally-on-a-12gb-gpu": "A compact dark alloy pressure vessel clamped inside a narrow slot, its single outlet feeding a translucent ribbon of thin frames between two polished steel rollers that widen the ribbon as it passes",
"running-production-infrastructure-solo": "Three interconnected matte-black server monoliths in isometric perspective surrounded by a dense floating matrix of tiny glowing glass container cubes with emerald heartbeat lights, routed by blue optical fiber conduits into one master control node",
"running-tts-as-a-service-with-token-sidecars": "A central obsidian voice resonance horn flanked by two small paired fuel canisters that replenish its expiring energy keys through thin silver tubes",
"scraping-bot-walled-marketplace-warm-browser-session": "A dexterous glass gripper carrying a warm amber-lit browser window pane through a walled dark tunnel gate, holding the pane at a preserved angle",
+7
View File
@@ -582,6 +582,13 @@
"prompt": "An old shield slab lifted out of a dark doorway as a tighter woven black mesh gate is lowered into the same frame, grey marker pins fenced off outside it",
"caption": "captions/replacing-rdpguard-with-ipban.json"
},
{
"slug": "running-ai-video-locally-on-a-12gb-gpu",
"unique_elements": "A compact dark alloy pressure vessel clamped inside a narrow slot, its single outlet feeding a translucent ribbon of thin frames between two polished steel rollers that widen the ribbon as it passes",
"prompt": "A compact dark alloy pressure vessel clamped inside a narrow slot, its single outlet feeding a translucent ribbon of thin frames between two polished steel rollers that widen the ribbon as it passes",
"evidence_summary": "1280x736 -> 1080p - 32 min / 5 s - $0",
"caption": "captions/running-ai-video-locally-on-a-12gb-gpu.json"
},
{
"slug": "running-production-infrastructure-solo",
"title": "Running Production Infrastructure Solo: 162 Containers Across Three Hosts",
@@ -0,0 +1,241 @@
---
title: "Running AI Video Locally on a 12 GB GPU: Real Numbers for Wan 2.2 and SeedVR2"
description: "Measured on an RTX 3060: Wan 2.2 TI2V-5B generation, SeedVR2 upscaling, the 1080p resolution trap, and the one command that fixed five failed runs."
pubDate: 2026-10-20
category: ai
tags: [ai-video, wan, comfyui, seedvr2, local-llm, gpu, cost]
ogImage: /og/running-ai-video-locally-on-a-12gb-gpu.jpg
banner: /banners/running-ai-video-locally-on-a-12gb-gpu.jpg
---
Every guide to local AI video says the same thing about a 12 GB card: it can
run the small models, sort of. None of them say what that means in minutes, or
whether the output is actually 1080p, or what breaks first.
I spent a night finding out on an RTX 3060 12 GB (i9-13900K, 48 GB RAM, ComfyUI
0.41.2 running inside a Windows VM with the GPU passed through). The end result
is a pipeline that turns one prompt and one start frame into a real 1920×1080
shot without paying anyone per second. It is also a pipeline I only got working
after five consecutive runs failed in four different places.
This is the record of that.
## What I wanted, and what it competes with
The target: short shots, 5 seconds each, stitched into something longer, at
1080p. The commercial alternative is not exotic — Kling runs about \$0.112 per
second for reference-to-video, so a 5-second shot is roughly \$0.56. That is
cheap if a shot lands first try.
The problem is that shots do not land first try. Generating video is an
iterative act: you change the prompt, change the start frame, drop a seed, run
it again. The cost that hurts is not the final render, it is the thirty renders
that taught you what the final one should be. That is the work a local GPU is
actually good at.
## The models that fit in 12 GB
| File | Role | Size |
|---|---|---|
| `wan2.2_ti2v_5B_fp16.safetensors` | video model | 9.99 GB |
| `umt5_xxl_fp8_e4m3fn_scaled.safetensors` | text encoder | 6.74 GB |
| `wan2.2_vae.safetensors` | video VAE | 1.41 GB |
| `seedvr2_3b_int8_convrot.safetensors` | upscaler | 3.46 GB |
| `seedvr2_ema_vae_fp16.safetensors` | upscaler VAE | 0.50 GB |
Wan 2.2 TI2V-5B is the one that fits. It is trained around 1280×704, it does
text-to-video and image-to-video with the same weights, and Apache-2.0 means the
output is mine to use. The 14B variant is better and does not fit; LTX-2.3 is
22B and runs, but at a speed that turns iteration into a chore.
## What it costs in time (all measured, not estimated)
5-second clips, 121 frames at 24 fps, 20 sampling steps:
| Resolution | Total | Sampling | Decode | VRAM peak |
|---|---|---|---|---|
| 832×480 | 6.1 min | 4.8 min | 1.3 min | 10.3 GB |
| 1280×704 | 17.9 min | 15.1 min | 2.9 min | 12.1 GB |
Halving the pixel count does not halve the time — it cuts it to about a third.
That is the usual superlinear scaling of a diffusion transformer, and it is the
reason 480p exists as a draft tier at all.
## Failure one: the decode that runs out of host memory
The first 720p attempt sampled fine and then died in the VAE decode:
```
RuntimeError: DefaultCPUAllocator: not enough memory:
you tried to allocate 1308426240 bytes
```
That is not a VRAM error. The Wan VAE decode for 121 frames wants about 1.3 GB
of CPU-side buffers in one go, and the VM had only about 12 GB free out of 48.
Switching to a tiled decode — tile 272, overlap 32, temporal 32, temporal
overlap 8 — dropped decode VRAM from 11.9 GB to 3.9 GB and it completed.
The lesson is not "use tiled decode everywhere". At 25 frames the non-tiled
decode needs about 0.27 GB and is faster and simpler. Match the decoder to the
frame count instead of always reaching for the tiled one.
## Failure two: the upscaler takes the whole server down
SeedVR2 3B produces genuinely good 1.5× output, and at 121 frames it reliably
killed the process:
```
Windows fatal exception: access violation
comfy/ldm/seedvr/vae.py ... decode_tiled
```
Not an exception I could catch — a native crash that takes the HTTP server with
it, leaving the port dead. Installing `triton-windows` removed an earlier
`PatchTritonVAE requires triton` import error but changed nothing about the
crash. It reproduced three times at 121 frames and never once at 41.
So the workaround is a cut: split the clip into segments of at most 41 frames,
upscale each, concatenate. Ugly, reliable, and the seams are invisible if every
cut lands on an exact frame boundary.
## Failure three, and the one command that fixed it
Two hours after a clean 5-second render, the same script started failing. Then
it failed again, differently. Over five runs:
```
aimdo: xfer_file_read_at: GetOverlappedResult failed error=1450
HostBuffer.read_file_slice failed ← during sampling
hostbuf_file_reader_read failed ← during decode
Windows fatal exception: access violation ← during model load
```
I did what anyone would do and blamed the thing I had just changed — the
resolution. Ran a 704p control at the same frame count. It failed too. The
resolution was never the problem.
The actual cause: ComfyUI keeps the 10 GB model resident after a job finishes.
Idle VRAM was sitting at 9.7 GB. The next submission could not fit it, so the
loader fell back to streaming weight slices from disk, and that path is broken
on this machine — error 1450 is `ERROR_NO_SYSTEM_RESOURCES`, a host-side
resource failure, not a sampling failure.
One request fixes it, and it belongs before every run:
```bash
curl -X POST http://127.0.0.1:8188/free \
-H "Content-Type: application/json" \
-d '{"unload_models": true, "free_memory": true}'
```
VRAM went 9.7 GB → 1.1 GB, and the exact job that had failed three times passed
on the next attempt.
It is also worth saying which fix made things worse, because it is the one most
guides suggest. Launching with `--disable-dynamic-vram` moved the crash
earlier: the 9.99 GB model no longer fit the 8.45 GB left after the text
encoder, so the loader sliced the memory map and died in `torch.storage` while
loading. Same wall, worse side of it.
## The 1080p trap: 720 is not a multiple of 32
`Wan22ImageToVideoLatent` requires width and height to be multiples of 32. 720
is not — 720 ÷ 32 = 22.5 — so the nearest legal height under 720 is 704, and
1280×704 is 16:8.8, not 16:9. Upscale that by 1.5 and the honest output is
**1920×1056**, which is not 1080p and will letterbox or stretch somewhere later.
Three ways out:
| Route | Result | Cost |
|---|---|---|
| Generate 1280×**736** (23×32), upscale 1.5× → 1920×1104, crop 24 px | exact 1920×1080 | no bars, no distortion |
| Keep 704, pad | 1920×1080 | 12 px black bars top and bottom |
| Keep 704, scale 1.534 then crop | 1920×1080 | loses ~2 % of the width |
I took the first. 736 is a legal multiple of 32, it is much closer to 16:9 than
704, and cropping 24 rows off the top and bottom removes only the padding the
model never used.
Do not stretch 1056 into 1080. Faces get 2.3 % wider and it reads as wrong
instantly.
## The pipeline, end to end
Draft at 480p to judge motion and composition — sharpness at 480p tells you
nothing, so do not evaluate it there. Then the accepted shots go through:
```bash
# clear VRAM first, every time
curl -X POST http://127.0.0.1:8188/free -d '{"unload_models":true,"free_memory":true}'
# 1. generate at 1280x736, 25 frames per second of runtime
python bench_wan.py --w 1280 --h 736 --frames 25 --steps 20 --tag final_1s --cold
# 2. upscale each <=41-frame segment to 1920x1104
python bench_upscale.py --file segment.mp4 --scale 1.5 --tsize 21 --chunk 21
# 3. crop to exact 1080p
ffmpeg -i upscaled.mp4 -vf "crop=1920:1080:0:12" -c:v libx264 -crf 16 final.mp4
```
This is the actual output, generated locally and hosted on my own CDN: one
second, 1920×1080, 24 fps, from a single start frame.
<video controls playsinline preload="metadata" poster="https://content.hoelee.com/file/hoelee/video/Wan22-1080p-poster.jpg">
<source src="https://content.hoelee.com/file/hoelee/video/Wan22-TI2V-5B-1080p-test.mp4" type="video/mp4" />
Your browser does not support HTML5 video.
</video>
*Wan 2.2 TI2V-5B, 1280×736 → SeedVR2 → cropped to 1920×1080. 2 minutes 13 seconds
of sampling, 2 minutes 49 seconds of upscaling, nothing rendered by anyone else.*
## What the upscale actually buys
Measured with Laplacian variance, the upscale moves detail energy from 673 to
766 — about 14 %. Edges, hair and neon signage get visibly crisper at 1:1.
Skin does not. A 3B upscaler sharpens information that is already in the frame;
it does not invent pores or fabric grain. Treating SeedVR2 as "restoration"
leads to disappointment. Treating it as "make the 720p-class render hold up at
1080p" is accurate.
## The result
| Shot length | Generate | Upscale | Total | Cost |
|---|---|---|---|---|
| 1 second (25 frames) | 3.4 min | 2.8 min | ~6 min | \$0 |
| 5 seconds (121 frames) | 17.9 min | 14.1 min | ~32 min | \$0 |
An 8-shot, 60-second sequence is roughly half a day of local compute, or about
\$5 through a commercial API — and 40 minutes instead of half a day. Both are
true, and they answer different questions. The local machine is for the thirty
takes nobody pays for. The API is for the one take that ships.
## What I would do differently
Three things, in order of how much time they would have saved.
**Instrument the VRAM before blaming the code.** The five-failure detour
existed because I trusted "this configuration worked two hours ago" and started
changing parameters. One `nvidia-smi` between runs would have shown 9.7 GB
resident and ended the hunt immediately.
**Read the layout constraint before designing the pipeline.** The 1920×1056
surprise came from not checking that the latent node wants multiples of 32.
That is a five-second check that cost a full re-render.
**Cut the upscale into segments from the start.** A 41-frame ceiling sounds
arbitrary until the first crash. Now it is just the unit of work.
The honest summary: 12 GB is enough to make 1080p AI video locally, at about
6 minutes per finished second. It is not enough to be careless about memory.
---
*If this is the kind of thing you want running in your own business — local
video generation, self-hosted AI pipelines, or the plumbing that keeps a GPU
busy without an engineer babysitting it — I build these:*
- 📱 **WhatsApp:** [+60 12-797 2969](https://wa.me/60127972969)
- 📧 **Email:** [[email protected]](mailto:[email protected]?subject=Local%20AI%20video%20pipeline)
- 🌐 **Website:** [hoelee.com](https://hoelee.com)
@@ -0,0 +1,175 @@
---
title: "12GB 显卡跑本地 AI 视频:Wan 2.2 加 SeedVR2 的真实数据"
description: "在 RTX 3060 上实测:Wan 2.2 TI2V-5B 生成、SeedVR2 放大、1080p 分辨率陷阱,以及那条让连续五次失败全部通过的命令。"
pubDate: 2026-10-20
category: ai
tags: [ai-video, wan, comfyui, seedvr2, local-llm, gpu, cost]
ogImage: /og/zh-running-ai-video-locally-on-a-12gb-gpu.jpg
banner: /banners/running-ai-video-locally-on-a-12gb-gpu.jpg
---
所有讲本地 AI 视频的文章对 12GB 显卡的说法都差不多:小模型能跑,勉强。没有一篇说清楚"勉强"折算成多少分钟,也没人说产出到底是不是真的 1080p,更没人说最先崩的是哪一环。
我用一个晚上把这些问题问清楚了。硬件是 RTX 3060 12GB(i9-13900K、48GB 内存,ComfyUI 0.41.2 跑在 Windows 虚拟机里,显卡直通)。最后拿到一条流水线:一个提示词加一张起始图,就能产出真正的 1920×1080 镜头,不用按秒付钱给任何人。但它是在连续五次失败、而且崩在四个不同位置之后才跑通的。
这篇就是那晚的记录。
## 我要的东西,以及它和什么在竞争
目标很具体:每个镜头 5 秒,拼成更长的片子,分辨率 1080p。商业方案并不稀奇——Kling 的参考图生视频大约 \$0.112/秒,5 秒镜头约 \$0.56。如果一次就过,这个价格很便宜。
问题在于镜头不会一次就过。生成视频是个反复试的过程:改提示词、换起始帧、换种子、再跑一次。真正花钱的不是最后那一条,是教会我"最后一条该长什么样"的那三十条。这正好是本地显卡擅长的事。
## 12GB 能装下的模型
| 文件 | 作用 | 大小 |
|---|---|---|
| `wan2.2_ti2v_5B_fp16.safetensors` | 视频模型 | 9.99 GB |
| `umt5_xxl_fp8_e4m3fn_scaled.safetensors` | 文本编码器 | 6.74 GB |
| `wan2.2_vae.safetensors` | 视频 VAE | 1.41 GB |
| `seedvr2_3b_int8_convrot.safetensors` | 放大器 | 3.46 GB |
| `seedvr2_ema_vae_fp16.safetensors` | 放大器 VAE | 0.50 GB |
能用的是 Wan 2.2 TI2V-5B。它围绕 1280×704 训练,同一套权重同时做文生视频和图生视频,Apache-2.0 意味着产出归我。14B 版本更好,但装不下;LTX-2.3 是 22B,能跑,但那个速度会让"反复试"变成一种折磨。
## 时间成本(全部实测,不是估算)
5 秒片段,121 帧、24fps、20 步采样:
| 分辨率 | 总计 | 采样 | 解码 | 显存峰值 |
|---|---|---|---|---|
| 832×480 | 6.1 分钟 | 4.8 分钟 | 1.3 分钟 | 10.3 GB |
| 1280×704 | 17.9 分钟 | 15.1 分钟 | 2.9 分钟 | 12.1 GB |
像素数减半,时间不是减半,而是降到约三分之一。这是扩散 Transformer 典型的超线性关系,也正是 480p 这档草稿存在的理由。
## 第一次失败:解码把主机内存吃光
第一次 720p 采样很顺利,然后死在 VAE 解码:
```
RuntimeError: DefaultCPUAllocator: not enough memory:
you tried to allocate 1308426240 bytes
```
这不是显存错误。121 帧的 Wan VAE 解码需要一次性开出约 1.3GB 的 CPU 侧缓冲,而虚拟机 48GB 里当时只剩约 12GB 空闲。改用分块解码——tile 272、overlap 32、temporal 32、temporal overlap 8——解码显存从 11.9GB 降到 3.9GB,跑通了。
教训不是"一律用分块解码"。25 帧时普通解码只要约 0.27GB,更快也更简单。解码方式应该跟着帧数走,而不是条件反射地选分块。
## 第二次失败:放大器把整个服务打死
SeedVR2 3B 的 1.5× 输出确实不错,但在 121 帧上它稳定地把进程干掉:
```
Windows fatal exception: access violation
comfy/ldm/seedvr/vae.py ... decode_tiled
```
这不是可以 catch 的异常,是原生崩溃,顺手把 HTTP 服务一起带走,端口直接死掉。装上 `triton-windows` 消掉了更早的那个 `PatchTritonVAE requires triton` 导入错误,但对崩溃毫无帮助。121 帧复现三次,41 帧一次都没崩。
所以绕过方式是切:把片段切成不超过 41 帧的段,分别放大,再拼接。难看,但可靠;只要每刀都落在精确的帧边界上,接缝看不出来。
## 第三次失败,以及那条解决问题的命令
在一条干净的 5 秒渲染之后两小时,同一个脚本开始失败。然后又失败,而且报的错不一样。五次下来:
```
aimdo: xfer_file_read_at: GetOverlappedResult failed error=1450
HostBuffer.read_file_slice failed ← 采样阶段
hostbuf_file_reader_read failed ← 解码阶段
Windows fatal exception: access violation ← 模型加载阶段
```
我做了任何人都会做的事:怪到刚改过的东西头上——分辨率。于是跑了一次同帧数的 704p 对照。它也失败。分辨率从来就不是问题。
真正的原因:ComfyUI 在作业结束后把 10GB 模型留在显存里常驻。空闲时显存占着 9.7GB。下一次提交装不下,加载器就退回"从磁盘流式读权重切片"的路径,而那条路径在这台机器上是坏的——error 1450 是 `ERROR_NO_SYSTEM_RESOURCES`,属于主机侧资源失败,不是采样失败。
一个请求就解决,而且应该放在每次跑之前:
```bash
curl -X POST http://127.0.0.1:8188/free \
-H "Content-Type: application/json" \
-d '{"unload_models": true, "free_memory": true}'
```
显存从 9.7GB 掉到 1.1GB,那个已经失败三次的作业下一次就过了。
还有一个"修复"必须点名,因为多数教程会推荐它:加 `--disable-dynamic-vram` 启动。它把崩溃提前了——9.99GB 的模型塞不进文本编码器剩下的 8.45GB,加载器转去切内存映射,在 `torch.storage` 里直接崩。同一堵墙,更糟的一面。
## 1080p 的陷阱:720 不是 32 的倍数
`Wan22ImageToVideoLatent` 要求宽高都是 32 的倍数。720 不是——720 ÷ 32 = 22.5——所以 720 以下最近的合法高度是 704,而 1280×704 是 16:8.8,不是 16:9。乘 1.5 之后,诚实的产出是 **1920×1056**,这不是 1080p,后面某处一定会出现黑边或被拉伸。
三条路:
| 做法 | 结果 | 代价 |
|---|---|---|
| 生成 1280×**736**(23×32),放大 1.5× → 1920×1104,裁 24px | 精确 1920×1080 | 无黑边、无变形 |
| 保持 704,补边 | 1920×1080 | 上下各 12px 黑边 |
| 保持 704,放大 1.534 后裁宽 | 1920×1080 | 损失约 2% 画面宽度 |
我选第一条。736 是合法的 32 倍数,比 704 更接近 16:9,而上下裁掉 24 行只是去掉模型本来就没用上的边缘。
不要把 1056 拉伸成 1080。人脸会横向宽 2.3%,一眼就看得出不对。
## 完整的流水线
先用 480p 出草稿,判断动作和构图——480p 的判断不了清晰度,所以别在那档评估画质。通过的镜头再走:
```bash
# 每次跑之前先清显存
curl -X POST http://127.0.0.1:8188/free -d '{"unload_models":true,"free_memory":true}'
# 1. 按每秒 25 帧在 1280x736 生成
python bench_wan.py --w 1280 --h 736 --frames 25 --steps 20 --tag final_1s --cold
# 2. 每段不超过 41 帧,分别放大到 1920x1104
python bench_upscale.py --file segment.mp4 --scale 1.5 --tsize 21 --chunk 21
# 3. 裁成精确 1080p
ffmpeg -i upscaled.mp4 -vf "crop=1920:1080:0:12" -c:v libx264 -crf 16 final.mp4
```
下面这条就是在本地生成的输出,放在我自己的 CDN 上:1 秒、1920×1080、24fps,只给了一张起始图。
<video controls playsinline preload="metadata" poster="https://content.hoelee.com/file/hoelee/video/Wan22-1080p-poster.jpg">
<source src="https://content.hoelee.com/file/hoelee/video/Wan22-TI2V-5B-1080p-test.mp4" type="video/mp4" />
你的浏览器不支持 HTML5 视频。
</video>
*Wan 2.2 TI2V-5B,1280×736 → SeedVR2 → 裁到 1920×1080。采样 2 分 13 秒,放大 2 分 49 秒,没有一帧是别人渲染的。*
## 放大到底买到了什么
用拉普拉斯方差量,放大把细节能量从 673 提到 766,大约 14%。边缘、头发、霓虹招牌在 1:1 下确实更锐。
皮肤没有。3B 的放大器只是把画面里已有的信息锐化,它不会凭空造出毛孔或布料颗粒。把 SeedVR2 当成"修复"会失望;把它当成"让 720p 级的渲染在 1080p 下站得住",才准确。
## 结果
| 镜头长度 | 生成 | 放大 | 合计 | 成本 |
|---|---|---|---|---|
| 1 秒(25 帧) | 3.4 分钟 | 2.8 分钟 | 约 6 分钟 | \$0 |
| 5 秒(121 帧) | 17.9 分钟 | 14.1 分钟 | 约 32 分钟 | \$0 |
8 个镜头、60 秒的片子,本地大约半天算力;走商业 API 大约 \$5,40 分钟。两个数字都是真的,回答的是不同问题。本地机器负责那三十条没人付钱的试错,API 负责最后那一条要交付的。
## 如果重来一次
三件事,按能省的时间排序。
**先量显存,再怀疑代码。** 那五次失败的弯路,是因为我相信"这套配置两小时前还能跑",于是开始改参数。中间跑一次 `nvidia-smi` 就会看见 9.7GB 常驻,当场结束排查。
**设计流水线之前先读尺寸约束。** 1920×1056 那个意外,来自没先确认 latent 节点要求 32 的倍数。这是五秒钟的检查,代价是一整轮重渲。
**一开始就把放大切成段。** 41 帧这个上限听起来很随意,直到第一次崩溃为止。现在它就是工作单位。
诚实的总结:12GB 足够在本地做 1080p AI 视频,大约每完成 1 秒成片要 6 分钟;但它不足以让人对内存掉以轻心。
---
*如果你想把这类东西搬进自己的生意——本地视频生成、自托管的 AI 流水线,或者让一张显卡持续干活又不用工程师盯着的那套管道——这些我都做:*
- 📱 **WhatsApp:** [+60 12-797 2969](https://wa.me/60127972969)
- 📧 **邮件:** [[email protected]](mailto:[email protected]?subject=%E6%9C%AC%E5%9C%B0%20AI%20%E8%A7%86%E9%A2%91%E6%B5%81%E6%B0%B4%E7%BA%BF)
- 🌐 **网站:** [hoelee.com](https://hoelee.com)