浏览器端推理
放大与抠图都跑在本机显卡或 CPU 上。这里说明它是怎么选后端的、为什么要跨域隔离, 以及首次加载的代价从哪来。
推理引擎
两个工具共用同一份 ONNX Runtime Web(版本 1.30.0),模型是标准的 ONNX 格式。
运行时文件(约 25.5 MB)随站点一起部署在 /ort/ 下,不从 CDN 加载 ——
这样没有第三方依赖,也不受 CDN 可用性影响。
后端怎么选
页面加载时会探测设备能力,然后按这个顺序尝试:
| 后端 | 条件 | 速度 |
|---|---|---|
| WebGPU | 浏览器与显卡驱动都支持 | 最快。推理跑在显卡上,走 fp16 半精度权重。 |
| WASM(多线程) | 不支持 WebGPU,但页面处于跨域隔离状态 | 中等。CPU 计算,多线程并行(最多 4 线程)。 |
| WASM(单线程) | 上述都不满足 | 最慢。仍然能用,只是要等更久。 |
WebGPU 建不起来时会自动降级到 WASM,不会直接失败。 界面上会显示当前实际用的是哪个后端。
跨域隔离:为什么需要它
浏览器默认不给页面 SharedArrayBuffer,而没有它就无法让 WASM 多线程运行。
解锁的办法是让页面进入「跨域隔离」状态,通过两个响应头:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
这是一个性能开关,不是可有可无的配置。缺了它们,所有没有 WebGPU 的机器 都会掉到单线程 WASM,慢好几倍。
这两个头写在 public/_headers 里(Cloudflare Pages 格式)。
换到其他托管平台需要写对应的等价配置 —— 详见部署流程。
COEP 会拦什么、不会拦什么
require-corp 听起来很吓人,但它只约束 no-cors 子资源 ——
也就是 <img>、<script>、<link> 这类标签加载的东西。
站内目前只加载自己的资源,所以是安全的。
fetch() 是例外:它走 CORS 通道,不受 COEP 限制。
抠图从 HuggingFace 拉权重用的正是 fetch(),所以能正常工作 ——
尽管 HuggingFace 的响应并不带 Cross-Origin-Resource-Policy 头。
fetch() 取,不能塞进 <img> 或
<script> —— 换一种加载方式就会被浏览器拦下。
首次加载的代价
| 项 | 体积 | 何时加载 |
|---|---|---|
| 主程序 JS + CSS | 约 470 KB(gzip 132 KB) | 首次进站 |
| ORT 运行时 | 25.5 MB → 传输 6.3 MB | 首次用到推理时 |
| 放大模型 | 4.9 MB | 首次放大时 |
| 抠图模型(轻量) | 94 MB | 首次抠图时 |
| 抠图模型(无 WebGPU 时的版本) | 192 MB | 无 WebGPU 时首次抠图 |
模型下载后会存进浏览器的 Cache Storage,之后不再请求。
为什么 ORT 运行时是 25.5 MB 却只说传 6.3 MB
ORT 的 wasm 文件是 25.5 MB,超过了 Cloudflare 单个文件 25 MiB 的上限,
直接部署会被拒。所以构建时把它转成 gzip 存储(约 6.3 MB),
浏览器端用 DecompressionStream 解压成 Blob URL 再交给 ORT。
顺带把首次加载量砍掉了四分之三。
为什么模型权重不随仓库提交
ORT 运行时是构建时生成的,可以进 dist/。但模型权重的处理方式两边不同:
- 放大模型(4.9 MB)随仓库提交到
public/models/。 - 抠图模型(94–452 MB)不提交,运行时从 HuggingFace 拉取并缓存。
原因是体积:抠图权重远超任何静态托管能接受的范围。放大模型则小得多, 而且放在仓库里能保证部署产物完整、不依赖第三方站点的可用性。