浏览器端推理

放大与抠图都跑在本机显卡或 CPU 上。这里说明它是怎么选后端的、为什么要跨域隔离, 以及首次加载的代价从哪来。

推理引擎

两个工具共用同一份 ONNX Runtime Web(版本 1.30.0),模型是标准的 ONNX 格式。 运行时文件(约 25.5 MB)随站点一起部署在 /ort/ 下,不从 CDN 加载 —— 这样没有第三方依赖,也不受 CDN 可用性影响。

后端怎么选

页面加载时会探测设备能力,然后按这个顺序尝试:

后端条件速度
WebGPU 浏览器与显卡驱动都支持 最快。推理跑在显卡上,走 fp16 半精度权重。
WASM(多线程) 不支持 WebGPU,但页面处于跨域隔离状态 中等。CPU 计算,多线程并行(最多 4 线程)。
WASM(单线程) 上述都不满足 最慢。仍然能用,只是要等更久。

WebGPU 建不起来时会自动降级到 WASM,不会直接失败。 界面上会显示当前实际用的是哪个后端。

降级会触发第二次下载。抠图的 fp16(WebGPU 用)与 fp32(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/。但模型权重的处理方式两边不同

原因是体积:抠图权重远超任何静态托管能接受的范围。放大模型则小得多, 而且放在仓库里能保证部署产物完整、不依赖第三方站点的可用性。