background-removal-js — ブラウザで背景除去が動く本当の仕組み
IS-Net ONNXモデル + WASM推論 + Canvas API、サーバーなしでクライアント完結するパイプライン
画像を入れると背景が消えたPNGが出る。サーバーに送らない。ブラウザ内で全て処理される。ONNX RuntimeのWASMバックエンドでディープラーニングモデルを実行する構造。
コードを開くとパイプラインが驚くほど単純だ。
5段階パイプライン
第1段階 — 画像デコード(codecs.ts)
入力がURLでもBlobでもArrayBufferでも全てNdArray<Uint8Array>(RGBA、[H, W, 4])に変換。ブラウザではcreateImageBitmap → Canvas → getImageData経路。Node.jsではsharp使用。
第2段階 — 前処理(inference.ts + utils.ts)
核心は2つ:リサイズと正規化。
画像を1024x1024にリサイズ。アスペクト比を維持せず強制的に正方形にする。bilinear interpolation使用。次にHWC uint8 RGBAをBCHW float32 RGBに変換。正規化は(pixel - 128) / 256 — 値範囲が約[-0.5, 0.5]になる。
この変換がtensorHWCtoBCHW関数で起こる:float32Data[j] = (imageBufferData[i] - 128) / 256 — R、G、Bチャネルをstrideを掛けてplanar配置。
第3段階 — モデル推論(onnx.ts)
ONNX RuntimeでIS-Net(Dichotomous Image Segmentation)モデルを実行。入力テンソル[1, 3, 1024, 1024]、出力[1024, 1024, 1](前景確率0.0〜1.0)。
実行プロバイダは2つ:wasm(デフォルト)とwebgpu(config.deviceがgpuの場合)。ort.env.wasm.numThreads = navigator.hardwareConcurrencyでマルチスレッドWASMを有効化。
モデルは3サイズ:isnet(FP32)、isnet_fp16(FP16、デフォルト)、isnet_quint8(INT8量子化)。CDNからチャンク単位でダウンロード。
第4段階 — 後処理(inference.ts)
float32マスクをuint8に変換(×255)し、元の解像度に再リサイズ。元画像のアルファチャネルにマスクを上書き:
outImageTensor.data[4 * i + 3] = alphamask.data[i] — この1行が背景除去の核心。マスク値0(背景)は透明に、255(前景)は維持。
第5段階 — エンコード(codecs.ts)
RGBAテンソルをCanvasにputImageDataで描画、convertToBlobでPNG/JPEG/WebP Blobを返却。
正直に言うと
パイプラインが単純なのはいいが、代価がある。
1024x1024強制リサイズが致命的。元が4000x3000でも200x200でも全て1024x1024に押しつぶす。アスペクト比無視。縦長の人物写真は横に引き伸ばされ、横長の風景は縦に圧縮される。モデル入力前に歪みが発生するのでマスク品質が落ちざるを得ない。
trimapもalpha mattingもない。他の背景除去システム(MODNet、RVM等)はtrimap生成→alpha mattingで髪の毛のような半透明境界を処理する。このライブラリはIS-Net一発に依存。結果品質がモデル性能に100%依存。髪の毛境界がぼやけるのは構造的限界。
セッションメモ化がlodash.memoize。設定JSONをキーにメモ化、同じ設定なら数百MBモデルを再ロードしない。合理的。だがメモリ解放タイミングがない。
AGPL-3.0ライセンス。商用利用にはソース公開義務。MIT/Apacheではない。
69個のオープンissue、多くが未回答。メンテナ2名、最終コミット2025年7月。8ヶ月間mainに何も入っていない。
それでも、ブラウザでONNX Runtime WASMによるディープラーニング推論を実行するパターン自体に価値がある。Web Workerでこのライブラリを実行すればUIをブロックせずに背景除去が可能 — web-workers-internalsの記事で扱ったまさにそのパターンだ。
リポジトリ現況(2026-03基準)
Star 6,973、Fork 453。JS背景除去ライブラリで最も有名。メンテナ2名。main最終コミット2025-07-18。オープンissue 69個。AGPL-3.0。TypeScript 59%。npm最新版1.5.7(2024-11-27)。
パイプラインコード探索
各カードをクリックすると実際のソースコードが展開されます
IS-Netモデル3種比較
| 設定値 | 精度 | サイズ | 速度 |
|---|---|---|---|
large | FP32 | ~176MB | 遅い |
medium(デフォルト) | FP16 | ~88MB | 普通 |
small | INT8 | ~44MB | 速い |
キーポイント
画像入力(URL/Blob/Buffer)→ createImageBitmap + CanvasでRGBA NdArrayに変換
1024x1024リサイズ(比率無視)+ HWC→BCHW変換 + (pixel-128)/256正規化
ONNX Runtime(WASM/WebGPU)でIS-Net推論 → [1024,1024,1]前景確率マスク
マスクをuint8変換 → 元解像度リサイズ → アルファチャネルに上書き
Canvas putImageData → convertToBlobでPNG/JPEG/WebP出力