huozhiyan
全部作品
已上线 / 2026.08 / ID Photo

证件照制作

技术栈

Vue 3 · Vite 6 · Transformers.js · ONNX Runtime · RMBG-1.4

分类

证件照 · AI 抠图 · 隐私友好

运行方式

浏览器本地

huozhiyan.github.io/ID-Photo/
证件照制作 界面
拖一张照片进去,剩下的都是选项。

它解决什么

报名、考试、签证、入职,每一次都会把你推到一个要求奇怪的页面前:白底、35×49mm、不超过 50KB、不能是中文文件名。去照相馆花几十块,或者在网上找个工具把照片传上去——后者的代价是把正脸照片留在了一台不知道是谁的服务器上。

这个工具的前提是:你的照片不需要经过任何人。抠图模型直接下载到你本机,推理在浏览器里跑,从头到尾没有一个字节上传。

现在能做什么

  • AI 抠图:基于 RMBG-1.4,浏览器端 ONNX 推理,头发丝和衣领这种边缘也能留住
  • 尺寸:一寸、大一寸、小一寸、二寸、小二寸、大二寸、护照 / 签证,也支持自定义毫米
  • 底色:白、红、蓝、深蓝、浅蓝、灰,外加自定义颜色和透明底
  • 排版打印:5 / 6 / 7 寸与 A4 相纸,横竖可选,自动算能排几张,带裁切线
  • 文件大小:JPG 导出可以直接限定 KB 上限,自动降画质压像素,并告诉你最后用了什么参数
  • DPI:150 / 300 / 600 可选
  • 零外网:模型和 ONNX 运行时都自托管,打开页面之后即使断网也能用

怎么做的

抠图这件事本身不难,难的是抠完之后照片还能看。

模型选了 RMBG-1.4,不是因为它最强,是因为它够小。 更强的分割模型动辄几百 MB,放在浏览器里下载就是灾难。RMBG-1.4 量化之后 44MB,一次性下载、长期缓存,对证件照这种场景(人像、背景简单)准确率完全够用。整个模型和 ONNX 运行时都放进了仓库,代价是仓库大了几十 MB,换来的是运行时零外网请求。

一个刻意的取舍:单线程推理。 多线程的 ONNX Runtime 需要 SharedArrayBuffer,而它要求服务器返回 COOP/COEP 两个响应头。GitHub Pages 不给设响应头。为了不引入任何服务端,最后选了单线程 WASM——慢一些,但部署是纯静态的,任何一个静态托管都能跑。

抠图之后的”防抠洞”才是真正花时间的部分。 模型输出的 mask 往往在几个地方翻车:头发缝里出现零星噪点、手臂和身体之间的缝隙被误判成背景、衣服上的深色区域被整块挖掉。三步修:中值滤波去杂点、把被包住的内部空洞回填、再清一遍背景上的浮斑。没有这三步,输出就是一张破洞的照片。

# 一条照片的处理链路
原图 ──▶ RMBG-1.4 (ONNX/WASM) ──▶ mask
   ──▶ 中值滤波 ──▶ 洞回填 ──▶ 浮斑清除
   ──▶ 合成新底色 ──▶ 裁切到 mm 尺寸 ──▶ 排版 ──▶ JPG (≤ 指定 KB)

“不超过 50KB”是一个很硬的约束。 证件照网报系统经常卡这条线,而一张 300 DPI 的证件照随便就有 200KB。做法是二分搜索质量参数,同时允许降采样,跑几轮找到能满足上限、又尽量好看的那组参数,最后把实际用的 DPI 和质量报给用户看。

踩过的坑

中文文件名会被网报系统吞掉。 导出时文件名自动转成纯 ASCII,这是个很小的改动,但它决定了很多次报名会不会卡在最后一步。

模型的首次加载是体验的分水岭。 44MB 在快网络上几秒,在慢网络上足够让人以为页面坏了。所以下载进度必须给出来,并且在模型就绪之前不能让用户点”开始处理”。

mask 的边缘不能硬切。 一开始直接按 0.5 阈值二值化,头发边缘像被剪刀剪过。改成保留 alpha 渐变、再做一点点羽化,贴在纯色底上才不违和。

下一步

  • 服装替换(换正装)
  • 更多地区的证件照规格预设
  • 批量处理:一次报名可能要好几个尺寸