BiRefNet の ONNX モデルで人物画像の背景を透過する
こんにちは、皆さん。
画像の背景を消す処理はよく見かけますが、髪のような細い部分まで手元の CPU で切り抜けるのか気になります。
さて、今日は BiRefNet の公式 ONNX モデルを ONNX Runtime から実行し、人物画像の背景を透過しました。
先に結果を書くと、人物と髪の主要部分を残した透過 PNG を生成できました。Apple M1 Max の CPU では、1024x1024入力の推論に平均約4.32秒かかりました。
今回検証する内容
BiRefNet は、高解像度画像を前景と背景の2領域に分けるモデルです。この処理は dichotomous image segmentation(DIS、画像を2種類の領域へ分割する処理)と呼ばれます。
今回は、公式 GitHub Release の汎用 Swin-Tiny 版 ONNX モデルを使い、次の点を確認します。
- Python と ONNX Runtime の CPU backend だけで実行できるか
- 人物、白い服、風で広がった細い髪を背景から分離できるか
- 前処理、推論、後処理、PNG保存にどの程度の時間がかかるか
- 出力した alpha channel がどのような画素分布になるか
検証に使用した元画像です。
対象の lab: kiarina/labs/2026/07/13/birefnet-onnx
検証環境の再現
mise と uv、初回のモデル・共有画像取得にインターネット接続が必要です。
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/kiarina/labs.git
cd labs
git sparse-checkout set .gitignore .mise/tasks Makefile mise.toml 2026/07/13/birefnet-onnx
mise -C 2026/07/13/birefnet-onnx run
初回はモデルの SHA-256 を検証してダウンロードします。実行後、背景を alpha channel で透過した output_removed_bg.png が生成されます。
使用したモデルとライセンス
使用したモデルは1つです。
| モデル | 役割 | 入出力 |
|---|---|---|
BiRefNet-general-bb_swin_v1_tiny-epoch_232.onnx |
画像の各画素が前景か背景かを推定する | 1x3x1024x1024画像 → 1x1x1024x1024 logits |
Swin-Tiny は、画像の特徴を読み取る backbone(モデルの土台部分)です。公式モデル一覧では、大きな Swin-Large より速度とモデルサイズを抑えた構成として掲載されています。今回の ONNX ファイルは224,005,088 bytesでした。
論文で説明されている BiRefNet の内部は、画像全体を見て対象の場所を捉える localization module と、画像の部分的な特徴や輪郭を参照して細部を戻す reconstruction module で構成されています。記事では推論済みの ONNX モデルを使うため、これらを個別には実行しません。
データの流れを短く表すと次のようになります。
1536x1024 JPEG
-> 1024x1024へリサイズ、RGB変換、正規化
-> BiRefNet ONNXで前景のlogitsを推定
-> sigmoidで0〜1のmaskへ変換
-> maskを元の1536x1024へ戻す
-> alpha channelとして元画像へ追加
-> 透過PNG
logits はモデルの生の出力値です。sigmoid は、それを0〜1の範囲へ変換します。最後に0を透明、1を不透明として扱います。
- BiRefNet のコード: MIT License
- 公式 Hugging Face モデル: MIT と表示
- 使用した ONNX weight: 公式 GitHub Release v1
モデルの再配布や製品への組み込みでは、利用時点のライセンス本文、著作権表示、依存物の条件も確認してください。
検証方法
入力は、海辺に立つ人物を写した固定画像1枚です。白い服と、風で右側へ細く広がった髪を含みます。
file: tests/assets/jpg/removebg_1536x1024_141kb.jpg
resolution: 1536x1024
SHA-256: d3d362b876936c57cfaf61eedd0ada05fd4950483ab79502aa5a67ded4a6b910
OpenCV で画像を1024x1024へリサイズし、ImageNet の平均・標準偏差で正規化します。ONNX Runtime で推論した後、mask を元の解像度へ戻し、8-bit の alpha channel として保存します。
速度は、モデルのダウンロード、SHA-256検証、InferenceSession の初期化、画像読み込みを除いて測定しました。推論 benchmark は3回の warm-up 後に10回実行しています。warm-up は、初回だけ発生する準備処理の影響を減らすための事前実行です。
検証結果
生成した透過 PNG です。表示環境によって、透明部分は白や格子模様で表示されます。
MacBook Pro(Apple M1 Max)の CPU で記録した結果です。
--- One-shot processing time ---
Preprocessing: 23.86 ms
Inference: 4699.83 ms
Postprocessing: 6.80 ms
PNG save: 41.07 ms
Total: 4771.56 ms
--- Inference benchmark (Warmup: 3, Iterations: 10) ---
Average time: 4319.62 ms
Min time: 4206.41 ms
Max time: 4480.35 ms
Std dev: 88.62 ms
記事作成時に同じ環境で再実行すると、推論平均は4302.47 ms、最小4179.94 ms、最大4477.45 msでした。2回とも約4.3秒ですが、速度は実行ごとに変動する参考値です。
出力画像は1536x1024の8-bit RGBA PNGです。alpha channel の分布は2回の実行で同じでした。
Transparent (alpha=0): 65.92%
Transition (1-254): 9.37%
Opaque (alpha=255): 24.71%
Transition は完全な透明でも不透明でもない画素です。髪や服の境界を滑らかに見せるために使われます。ただし、この割合だけで切り抜きの正しさは判断できません。
検証環境は次のとおりです。
machine: MacBook Pro (Apple M1 Max, arm64)
OS: macOS 26.5.1
Python: 3.12.10
ONNX Runtime: 1.27.0
OpenCV: 5.0.0
NumPy: 2.5.1
provider: CPUExecutionProvider
結果の考察
専門的な数値は上に載せたとおりですが、簡単に読むと次の3点です。
- 人物と髪の主要部分は切り抜けた
顔、白い服、髪の大部分を背景から分離できました。右側へ伸びた細い髪も多く残り、半透明の画素によって境界が急に途切れない結果になっています。
- 細い飛び毛と色のにじみは残った
頭頂部や右側の特に細い飛び毛には消えた箇所があります。また、髪と服の輪郭には元背景の青色がわずかに残りました。背景を別の色へ置き換える用途では、輪郭の色を補正する追加処理が必要になりそうです。
- CPUだけで動くが、リアルタイム向けの速度ではない
1枚の推論は平均約4.3秒でした。ローカルで少数の画像を順番に処理する用途には使えますが、動画や大量画像には GPU、より小さいモデル、量子化などの追加検証が必要です。量子化は、計算に使う数値の精度を下げてモデルを軽くする方法です。
今回は明るい屋外の人物画像1枚だけを使っています。正解 mask がないため、品質評価は目視に限られます。他の被写体、複雑な背景、低照度、低解像度、複数人物、Swin-Large版との品質・速度比較は未検証です。また、入力を正方形へ変形してから推論する実装なので、別のリサイズ方法による差も確認していません。
検証後の感想
CPUだけの簡単な ONNX Runtime 実装でも、髪をかなり残した透過画像を作れたのは予想より良い結果でした。背景除去を別サービスへ送らず、手元で完結させたい用途には使いやすそうです。LLM エージェントが収集した人物画像を、表示や画像合成に使う透過素材へ自動変換する用途なら、十分実用になりそうです。

