Image Segmentation
BiRefNet
swift-object-cutout
background-removal
image-matting
dichotomous-image-segmentation
core-ai
apple-silicon
macos
Instructions to use arraypress/cutout-birefnet with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- BiRefNet
How to use arraypress/cutout-birefnet with BiRefNet:
# Option 1: use with transformers from transformers import AutoModelForImageSegmentation birefnet = AutoModelForImageSegmentation.from_pretrained("arraypress/cutout-birefnet", trust_remote_code=True)# Option 2: use with BiRefNet # Install from https://github.com/ZhengPeng7/BiRefNet from models.birefnet import BiRefNet model = BiRefNet.from_pretrained("arraypress/cutout-birefnet") - Notebooks
- Google Colab
- Kaggle
BiRefNet general as a Core AI .aimodel (float16), with README and licence
Browse files- .gitattributes +1 -0
- LICENSE +21 -0
- NOTICE +11 -0
- README.md +107 -0
- cutout-birefnet-float16.aimodel/main.hash +1 -0
- cutout-birefnet-float16.aimodel/main.mlirb +3 -0
- cutout-birefnet-float16.aimodel/metadata.json +8 -0
- export_birefnet.py +140 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
cutout-birefnet-float16.aimodel/main.mlirb filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2024 ZhengPeng
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
NOTICE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cutout-birefnet: BiRefNet general (ZhengPeng7/BiRefNet at revision
|
| 2 |
+
e2bf8e4460fc8fa32bba5ea4d94b3233d367b0e4), MIT, Copyright (c) 2024 ZhengPeng.
|
| 3 |
+
See LICENSE.
|
| 4 |
+
|
| 5 |
+
Changed for Core AI, in export_birefnet.py:
|
| 6 |
+
- torchvision's deform_conv2d is computed from gathers and matrix products
|
| 7 |
+
(identical result: max |alpha difference| 4.6e-6 in float32 on the CPU).
|
| 8 |
+
- The ImageNet normalisation and the final sigmoid are inside the graph; only
|
| 9 |
+
the last, full-size prediction is returned.
|
| 10 |
+
- Weights and arithmetic are float16; the input and output stay float32.
|
| 11 |
+
The trained weights are otherwise the authors', unmodified.
|
README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
base_model: ZhengPeng7/BiRefNet
|
| 4 |
+
tags:
|
| 5 |
+
- background-removal
|
| 6 |
+
- image-matting
|
| 7 |
+
- dichotomous-image-segmentation
|
| 8 |
+
- birefnet
|
| 9 |
+
- core-ai
|
| 10 |
+
- apple-silicon
|
| 11 |
+
- macos
|
| 12 |
+
library_name: swift-object-cutout
|
| 13 |
+
pipeline_tag: image-segmentation
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# cutout-birefnet: BiRefNet general for Core AI (macOS 27)
|
| 17 |
+
|
| 18 |
+
[BiRefNet](https://github.com/ZhengPeng7/BiRefNet) general — Zheng, Gao, Fan, Liu, Laaksonen,
|
| 19 |
+
Ouyang and Sebe, *Bilateral Reference for High-Resolution Dichotomous Image Segmentation*, CAAI
|
| 20 |
+
Artificial Intelligence Research, 2024; code and weights MIT — exported to an Apple Core AI
|
| 21 |
+
`.aimodel`. Give it a photo and it returns a soft alpha matte of the main subject: a product, a
|
| 22 |
+
plant, a glass, an animal, a person — the background-removal job, on device.
|
| 23 |
+
|
| 24 |
+
It is what [`cutout`](https://github.com/arraypress/swift-cutout-cli) uses for `--subject` and
|
| 25 |
+
`--person`, through [swift-object-cutout](https://github.com/arraypress/swift-object-cutout), and
|
| 26 |
+
what `design` uses to lift a subject off its photo. Both download this repository by themselves the
|
| 27 |
+
first time it is needed, pinned to one commit, every file checked against its published hash.
|
| 28 |
+
Apple's built-in Vision matte stays as the no-download fallback.
|
| 29 |
+
|
| 30 |
+
## Files
|
| 31 |
+
|
| 32 |
+
| Path | What |
|
| 33 |
+
|---|---|
|
| 34 |
+
| `cutout-birefnet-float16.aimodel/` | One entry point, `main`: `image` `[1,3,1024,1024]` float32, RGB in 0…1 → `alpha` `[1,1,1024,1024]` float32 in 0…1. Float16 weights and arithmetic, 463 MB. |
|
| 35 |
+
| `export_birefnet.py` | The export script (`uv run export_birefnet.py --dtype float16`). |
|
| 36 |
+
| `LICENSE`, `NOTICE` | The authors' MIT licence, and what the export changed. |
|
| 37 |
+
|
| 38 |
+
The host resizes the picture to 1024×1024 without keeping its aspect ratio (as the network was
|
| 39 |
+
trained) and stretches the alpha back to the picture's size with bilinear sampling. ImageNet
|
| 40 |
+
normalisation and the sigmoid are inside the asset.
|
| 41 |
+
|
| 42 |
+
## What changed
|
| 43 |
+
|
| 44 |
+
Core AI has no deformable convolution, which BiRefNet's decoder uses. The export computes it from
|
| 45 |
+
what it is: for each kernel tap, a bilinear sample of the feature map (four gathers from a
|
| 46 |
+
zero-padded copy), scaled by the modulator, then a matrix product with that tap's weights, summed
|
| 47 |
+
over the taps. Sample positions stay float32 in the float16 export. Against the authors' code in
|
| 48 |
+
float32 on the CPU, the rewrite differs by at most 4.6 × 10⁻⁶ in alpha.
|
| 49 |
+
|
| 50 |
+
## Fidelity
|
| 51 |
+
|
| 52 |
+
Run through Core AI on the GPU, against the authors' PyTorch model (float32, CPU) on the same 30
|
| 53 |
+
inputs at 1024²:
|
| 54 |
+
|
| 55 |
+
| Export | Mean abs. alpha diff. | 99.9th percentile | Worst pixel | Mask IoU (>0.5), worst image |
|
| 56 |
+
|---|---|---|---|---|
|
| 57 |
+
| float32 | 1.9 × 10⁻⁷ | 2.4 × 10⁻⁵ | 1.3 × 10⁻⁴ | 0.99996 |
|
| 58 |
+
| float16 (this repo) | 1.9 × 10⁻⁵ | 1.5 × 10⁻³ | 0.068 | 0.9985 |
|
| 59 |
+
|
| 60 |
+
End to end (Swift, Core Graphics resizing) the float16 asset scores the same as PyTorch on the
|
| 61 |
+
true-alpha sets below: SAD 23.40 against 23.39 on objects and 6.12 against 6.10 on animals.
|
| 62 |
+
|
| 63 |
+
## Quality
|
| 64 |
+
|
| 65 |
+
Measured with the standard matting metrics against true alpha on AIM-500 (Li et al., 2021), which
|
| 66 |
+
this checkpoint was not trained on (its training sets are DIS5K, DUTS, HRSOD, UHRSD, HRS10K, P3M-10k
|
| 67 |
+
and a humans set). 100 objects (plants, furniture, toys, fruit, glass), 100 animals and the 100
|
| 68 |
+
portraits, at their own size (at most 1920 px). Lower is better except IoU; the edge band is the
|
| 69 |
+
uncertain strip round the subject where hair and fur live.
|
| 70 |
+
|
| 71 |
+
| Set | Matte | SAD | MSE ×10³ | Grad | Conn | IoU | Edge-band MSE ×10³ |
|
| 72 |
+
|---|---|---|---|---|---|---|---|
|
| 73 |
+
| Objects | Apple Vision subject mask, cutout's crisp edge | 81.39 | 33.09 | 0.690 | 82.82 | 0.802 | 92.5 |
|
| 74 |
+
| Objects | **this model** | **23.40** | **3.29** | **0.211** | **22.87** | **0.919** | **12.6** |
|
| 75 |
+
| Animals | Apple Vision subject mask, crisp edge | 17.17 | 5.79 | 0.309 | 17.15 | 0.976 | 47.7 |
|
| 76 |
+
| Animals | **this model** | **6.12** | **0.99** | **0.074** | **5.50** | **0.991** | **9.4** |
|
| 77 |
+
| Portraits | Apple Vision person segmentation, crisp edge | 24.91 | 10.63 | 0.224 | 24.45 | 0.962 | 36.0 |
|
| 78 |
+
| Portraits | **this model** | **4.86** | **0.62** | **0.058** | **4.19** | **0.992** | **6.7** |
|
| 79 |
+
|
| 80 |
+
It beat Vision by SAD on 99 of the 100 objects, all 100 animals and 99 of the 100 portraits. Vision found no subject at all in 4 of the objects (counted
|
| 81 |
+
as empty mattes); glass is the hardest category for both (SAD 211 for Vision, 51 here). Re-solving this
|
| 82 |
+
model's alpha from colour (the crisp edge that helps Vision's softer mask) made every set worse, so
|
| 83 |
+
cutout passes this model's alpha through.
|
| 84 |
+
|
| 85 |
+
## Speed and memory
|
| 86 |
+
|
| 87 |
+
Apple M3 Max, macOS 27, GPU: the first load compiles the model for the Mac in about 15 s and keeps a
|
| 88 |
+
553 MB compiled copy in `~/Library/Caches/coreai-cache`; later loads are quick. About 0.9 s per
|
| 89 |
+
1024² inference from Python, and 0.9–1.3 s per photo through `cutout` end to end (decode, model, edge,
|
| 90 |
+
PNG). A folder run of 100 photos peaked at 3.2–3.8 GB of memory, against 5.6 GB for the float32 export
|
| 91 |
+
and up to 10 GB for PyTorch on MPS.
|
| 92 |
+
|
| 93 |
+
## Use
|
| 94 |
+
|
| 95 |
+
```sh
|
| 96 |
+
cutout photo.jpg # fetches this model on first use
|
| 97 |
+
cutout ./shots --out ./cut # a folder, the model loaded once
|
| 98 |
+
cutout model remove subject # the model and its compiled copy
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
Or by hand: `hf download arraypress/cutout-birefnet --local-dir models`, then point
|
| 102 |
+
`$CUTOUT_SUBJECT_MODEL` at the `.aimodel`. Requires macOS 27 (Core AI) on Apple silicon. Converted
|
| 103 |
+
with coreai-torch 0.4.3, coreai-core 1.0.0b3, torch 2.9.0 and transformers 4.57.1.
|
| 104 |
+
|
| 105 |
+
## Licence
|
| 106 |
+
|
| 107 |
+
MIT, as the original: see `LICENSE` (the authors' copyright) and `NOTICE` (what the export changed).
|
cutout-birefnet-float16.aimodel/main.hash
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
j�@��پō��4s`]!��tU��S/�w��fw
|
cutout-birefnet-float16.aimodel/main.mlirb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6aad408d80d9bec58df1e93473605d21169dfe7455bda3532f987708add66677
|
| 3 |
+
size 463261242
|
cutout-birefnet-float16.aimodel/metadata.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"producer" : "coreai-core 1.0.0b3",
|
| 3 |
+
"creationDate" : "20261016T175354Z",
|
| 4 |
+
"description" : "BiRefNet general (ZhengPeng7\/BiRefNet@e2bf8e4), float16. Entry 'main': image [1,3,1024,1024] float32 RGB 0…1 → alpha [1,1,1024,1024] float32 0…1. Resize to 1024² and back in the host.",
|
| 5 |
+
"assetVersion" : "2.0",
|
| 6 |
+
"license" : "MIT",
|
| 7 |
+
"author" : "Zheng, Gao, Fan, Liu, Laaksonen, Ouyang, Sebe — BiRefNet (CAAI AIR 2024), general; Core AI export by ObjectCutout"
|
| 8 |
+
}
|
export_birefnet.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.11,<3.13"
|
| 3 |
+
# dependencies = ["torch==2.9.0", "torchvision==0.24.0", "transformers==4.57.1", "timm", "kornia", "einops",
|
| 4 |
+
# "numpy", "pillow", "coreai-torch==0.4.3", "coreai-core==1.0.0b3"]
|
| 5 |
+
# ///
|
| 6 |
+
"""BiRefNet general (Zheng et al., CAAI AIR 2024; MIT) → cutout-birefnet-<dtype>.aimodel.
|
| 7 |
+
|
| 8 |
+
uv run Tools/export_birefnet.py [--dtype float16|float32] [--out Tools/exports] [--check image.jpg …]
|
| 9 |
+
|
| 10 |
+
The network is the authors' own (ZhengPeng7/BiRefNet at a pinned revision, loaded with its remote code); one
|
| 11 |
+
change, made for the runtime and checked against the original: torchvision's `deform_conv2d`, which Core AI does
|
| 12 |
+
not have, is rewritten as what it computes — each kernel tap's bilinear sample (four gathers from the zero-padded
|
| 13 |
+
feature map), scaled by the modulator, then a matrix product with that tap's weights, summed over the taps.
|
| 14 |
+
Sample positions are computed in float32 even in a float16 export: at 256 px a half float steps by 0.125, which
|
| 15 |
+
would move every bilinear weight.
|
| 16 |
+
|
| 17 |
+
One entry point, `main`: `image` [1,3,1024,1024] float32 RGB in 0…1 (resized by the host) → `alpha`
|
| 18 |
+
[1,1,1024,1024] float32 in 0…1. The ImageNet normalisation and the final sigmoid are inside. In a float16 export
|
| 19 |
+
the weights and arithmetic are half; input and output stay float32, so the host is the same for both.
|
| 20 |
+
|
| 21 |
+
--check runs the original network (torchvision's deform_conv2d) and the rewritten one on each image in float32 on
|
| 22 |
+
the CPU and prints the alpha difference, before anything is exported."""
|
| 23 |
+
import argparse, os, sys, time, shutil
|
| 24 |
+
from pathlib import Path
|
| 25 |
+
import numpy as np, torch, torch.nn as nn, torch.nn.functional as F
|
| 26 |
+
|
| 27 |
+
HERE = Path(__file__).resolve().parent
|
| 28 |
+
REPOSITORY = "ZhengPeng7/BiRefNet"
|
| 29 |
+
REVISION = "e2bf8e4460fc8fa32bba5ea4d94b3233d367b0e4"
|
| 30 |
+
SIZE = 1024
|
| 31 |
+
|
| 32 |
+
ap = argparse.ArgumentParser()
|
| 33 |
+
ap.add_argument("--dtype", default="float16", choices=["float16", "float32"])
|
| 34 |
+
ap.add_argument("--out", default=str(HERE / "exports"))
|
| 35 |
+
ap.add_argument("--check", nargs="*", default=[], help="images to compare original vs rewritten on (float32, CPU)")
|
| 36 |
+
ap.add_argument("--no-export", action="store_true")
|
| 37 |
+
args = ap.parse_args()
|
| 38 |
+
|
| 39 |
+
from transformers import AutoModelForImageSegmentation
|
| 40 |
+
torch.set_num_threads(4)
|
| 41 |
+
model = AutoModelForImageSegmentation.from_pretrained(REPOSITORY, trust_remote_code=True, revision=REVISION).float().eval()
|
| 42 |
+
remote = sys.modules[type(model).__module__]
|
| 43 |
+
print(f"BiRefNet general @ {REVISION[:7]}: {sum(p.numel() for p in model.parameters())/1e6:.1f} M parameters", flush=True)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def deform_conv2d(x, offset, weight, bias, padding, mask):
|
| 47 |
+
"""torchvision.ops.deform_conv2d (stride 1, dilation 1, one offset group), from gathers and matmuls."""
|
| 48 |
+
_, C, H, W = x.shape
|
| 49 |
+
O, _, kh, kw = weight.shape
|
| 50 |
+
pad = padding if isinstance(padding, int) else padding[0]
|
| 51 |
+
Ho, Wo = H + 2 * pad - kh + 1, W + 2 * pad - kw + 1
|
| 52 |
+
Hp, Wp = H + 2, W + 2
|
| 53 |
+
# One row of zeros round the map: every corner that falls outside reads 0, as torchvision's does.
|
| 54 |
+
table = F.pad(x, (1, 1, 1, 1)).reshape(C, Hp * Wp).t() # [Hp*Wp, C]
|
| 55 |
+
off = offset.float().reshape(kh * kw, 2, Ho, Wo) # (dy, dx) per tap
|
| 56 |
+
mod = mask.float().reshape(kh * kw, Ho * Wo, 1)
|
| 57 |
+
ys = torch.arange(Ho, dtype=torch.float32).reshape(Ho, 1) - pad
|
| 58 |
+
xs = torch.arange(Wo, dtype=torch.float32).reshape(1, Wo) - pad
|
| 59 |
+
out = None
|
| 60 |
+
for k in range(kh * kw):
|
| 61 |
+
ky, kx = divmod(k, kw)
|
| 62 |
+
py = ys + ky + off[k, 0]
|
| 63 |
+
px = xs + kx + off[k, 1]
|
| 64 |
+
y0, x0 = torch.floor(py), torch.floor(px)
|
| 65 |
+
ly, lx = py - y0, px - x0
|
| 66 |
+
y0c, y1c = y0.clamp(-1, H) + 1, (y0 + 1).clamp(-1, H) + 1
|
| 67 |
+
x0c, x1c = x0.clamp(-1, W) + 1, (x0 + 1).clamp(-1, W) + 1
|
| 68 |
+
sample = None
|
| 69 |
+
for yc, xc, w in ((y0c, x0c, (1 - ly) * (1 - lx)), (y0c, x1c, (1 - ly) * lx),
|
| 70 |
+
(y1c, x0c, ly * (1 - lx)), (y1c, x1c, ly * lx)):
|
| 71 |
+
index = (yc * Wp + xc).reshape(-1).to(torch.int64)
|
| 72 |
+
term = F.embedding(index, table) * (w.reshape(-1, 1) * mod[k]).to(x.dtype)
|
| 73 |
+
sample = term if sample is None else sample + term
|
| 74 |
+
part = sample @ weight[:, :, ky, kx].t() # [Ho*Wo, O]
|
| 75 |
+
out = part if out is None else out + part
|
| 76 |
+
out = out.t().reshape(1, O, Ho, Wo)
|
| 77 |
+
return out if bias is None else out + bias.reshape(1, O, 1, 1)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def rewritten_forward(self, x):
|
| 81 |
+
offset = self.offset_conv(x)
|
| 82 |
+
modulator = 2. * torch.sigmoid(self.modulator_conv(x))
|
| 83 |
+
return deform_conv2d(x, offset, self.regular_conv.weight, self.regular_conv.bias, self.padding, modulator)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
original_forward = remote.DeformableConv2d.forward
|
| 87 |
+
MEAN = torch.tensor([0.485, 0.456, 0.406]).reshape(1, 3, 1, 1)
|
| 88 |
+
STD = torch.tensor([0.229, 0.224, 0.225]).reshape(1, 3, 1, 1)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class Net(nn.Module):
|
| 92 |
+
"""0…1 RGB in, alpha out: the normalisation and the sigmoid inside, the last (full-size) prediction only."""
|
| 93 |
+
def __init__(self, m, dtype):
|
| 94 |
+
super().__init__(); self.m = m.to(dtype); self.dtype = dtype
|
| 95 |
+
self.register_buffer("mean", MEAN.to(dtype)); self.register_buffer("std", STD.to(dtype))
|
| 96 |
+
def forward(self, image):
|
| 97 |
+
x = (image.to(self.dtype) - self.mean) / self.std
|
| 98 |
+
return self.m(x)[-1].sigmoid().to(torch.float32)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
if args.check:
|
| 102 |
+
from PIL import Image
|
| 103 |
+
with torch.no_grad():
|
| 104 |
+
for path in args.check:
|
| 105 |
+
im = Image.open(path).convert("RGB").resize((SIZE, SIZE), Image.BILINEAR)
|
| 106 |
+
x = torch.from_numpy(np.asarray(im, np.float32) / 255).permute(2, 0, 1)[None]
|
| 107 |
+
remote.DeformableConv2d.forward = original_forward
|
| 108 |
+
a = Net(model, torch.float32)(x)
|
| 109 |
+
remote.DeformableConv2d.forward = rewritten_forward
|
| 110 |
+
b = Net(model, torch.float32)(x)
|
| 111 |
+
d = (a - b).abs()
|
| 112 |
+
print(f"{Path(path).name}: |original − rewritten| max {d.max():.2e} mean {d.mean():.2e}", flush=True)
|
| 113 |
+
|
| 114 |
+
if args.no_export:
|
| 115 |
+
sys.exit(0)
|
| 116 |
+
|
| 117 |
+
import coreai_torch
|
| 118 |
+
from coreai.runtime import AIModelAssetMetadata
|
| 119 |
+
remote.DeformableConv2d.forward = rewritten_forward
|
| 120 |
+
dtype = torch.float16 if args.dtype == "float16" else torch.float32
|
| 121 |
+
net = Net(model, dtype).eval()
|
| 122 |
+
t0 = time.time()
|
| 123 |
+
converter = coreai_torch.TorchConverter(mode=coreai_torch.TorchConverter.Mode.RELEASE)
|
| 124 |
+
with torch.no_grad():
|
| 125 |
+
ex = torch.export.export(net, (torch.rand(1, 3, SIZE, SIZE),)).run_decompositions(coreai_torch.get_decomp_table())
|
| 126 |
+
print(f"traced in {time.time()-t0:.0f} s", flush=True)
|
| 127 |
+
converter.add_exported_program(ex, input_names=["image"], output_names=["alpha"], entrypoint_name="main")
|
| 128 |
+
program = converter.to_coreai()
|
| 129 |
+
print(f"converted in {time.time()-t0:.0f} s", flush=True)
|
| 130 |
+
meta = AIModelAssetMetadata()
|
| 131 |
+
meta.author = "Zheng, Gao, Fan, Liu, Laaksonen, Ouyang, Sebe — BiRefNet (CAAI AIR 2024), general; Core AI export by ObjectCutout"
|
| 132 |
+
meta.license = "MIT"
|
| 133 |
+
meta.model_description = (f"BiRefNet general ({REPOSITORY}@{REVISION[:7]}), {args.dtype}. Entry 'main': image [1,3,{SIZE},{SIZE}] "
|
| 134 |
+
f"float32 RGB 0…1 → alpha [1,1,{SIZE},{SIZE}] float32 0…1. Resize to {SIZE}² and back in the host.")
|
| 135 |
+
meta.creation_date = int(time.time())
|
| 136 |
+
out = Path(args.out); out.mkdir(parents=True, exist_ok=True)
|
| 137 |
+
asset = out / f"cutout-birefnet-{args.dtype}.aimodel"
|
| 138 |
+
if asset.exists(): shutil.rmtree(asset)
|
| 139 |
+
program.save_asset(asset, meta)
|
| 140 |
+
print(f"saved {asset} ({sum(f.stat().st_size for f in asset.rglob('*'))/1e6:.0f} MB) in {time.time()-t0:.0f} s", flush=True)
|