Model card and the original license
Browse files
README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: hustvl/Moebius
|
| 3 |
+
base_model_relation: quantized
|
| 4 |
+
license: mit
|
| 5 |
+
pipeline_tag: image-to-image
|
| 6 |
+
library_name: onnx
|
| 7 |
+
tags:
|
| 8 |
+
- onnx
|
| 9 |
+
- moonbox
|
| 10 |
+
- moebius
|
| 11 |
+
- inpainting
|
| 12 |
+
- object-removal
|
| 13 |
+
- image-to-image
|
| 14 |
+
- diffusion
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# Moebius ONNX
|
| 18 |
+
|
| 19 |
+
ONNX builds of [Moebius](https://huggingface.co/hustvl/Moebius), made by Moonbox for on-device use with ONNX Runtime (CUDA, DirectML, CPU).
|
| 20 |
+
Each precision is a folder with one graph per part of the pipeline; large weights sit next to each graph as ONNX
|
| 21 |
+
external data (`*.onnx.data`), so keep a folder's files together.
|
| 22 |
+
|
| 23 |
+
## Files
|
| 24 |
+
|
| 25 |
+
The last column is how far each graph's output is from the original PyTorch model run in fp32 on the same inputs
|
| 26 |
+
(relative RMS error). fp32 is the yardstick because the original's own bf16 run is not exact either. Image-quality
|
| 27 |
+
scores are added as each precision passes the image eval.
|
| 28 |
+
|
| 29 |
+
| Precision | File | Size | sha256 | Checked against fp32 |
|
| 30 |
+
|---|---|---|---|---|
|
| 31 |
+
| fp16 | [`fp16/unet/model.onnx`](https://huggingface.co/moonbox-dev/Moebius-ONNX/blob/main/fp16/unet/model.onnx) | 0.91 GB | `3ab9796281da` | fp32 graph (fp16 compute: 10 % off) |
|
| 32 |
+
| fp16 | [`fp16/vae_decoder/model.onnx`](https://huggingface.co/moonbox-dev/Moebius-ONNX/blob/main/fp16/vae_decoder/model.onnx) | 0.10 GB | `2818a7cbd433` | 1.9e-04 |
|
| 33 |
+
| fp16 | [`fp16/vae_encoder/model.onnx`](https://huggingface.co/moonbox-dev/Moebius-ONNX/blob/main/fp16/vae_encoder/model.onnx) | 0.07 GB | `8e37212ea885` | 6.3e-05 |
|
| 34 |
+
|
| 35 |
+
## How to run
|
| 36 |
+
|
| 37 |
+
Moebius (0.22 B) is an image-inpainting / object-removal model: give it a picture and a mask (white = remove /
|
| 38 |
+
repaint) and it fills the masked region from the surroundings. No text prompt. These folders hold its three graphs
|
| 39 |
+
at 512 px (the short side the original pipeline uses): `vae_encoder`, `unet` (with classifier-free guidance and the
|
| 40 |
+
learned prompt embeddings inside) and `vae_decoder`. The VAE is PixelHacker's (the SDXL VAE, scaling 0.13025); it
|
| 41 |
+
stays fp32 where fp16 overflows (see the table).
|
| 42 |
+
|
| 43 |
+
**Run it** (ONNX Runtime + diffusers' DDIM scheduler; the original's defaults: 20 steps at strength 0.99, CFG 2.5,
|
| 44 |
+
noise offset 0.0357, result pasted back under a blurred mask):
|
| 45 |
+
|
| 46 |
+
```python
|
| 47 |
+
import numpy as np, onnxruntime as ort, torch
|
| 48 |
+
from diffusers import DDIMScheduler
|
| 49 |
+
from PIL import Image, ImageFilter
|
| 50 |
+
|
| 51 |
+
d, prov = "int8", ["CUDAExecutionProvider", "CPUExecutionProvider"]
|
| 52 |
+
enc, unet, dec = (ort.InferenceSession(f"{d}/{p}/model.onnx", providers=prov) for p in ("vae_encoder", "unet", "vae_decoder"))
|
| 53 |
+
img = Image.open("photo.jpg").convert("RGB").resize((512, 512), Image.LANCZOS) # graphs are 512 x 512
|
| 54 |
+
mask = Image.open("mask.png").convert("L").resize((512, 512), Image.NEAREST)
|
| 55 |
+
x = np.asarray(img, np.float32).transpose(2, 0, 1)[None] / 127.5 - 1
|
| 56 |
+
m = (np.asarray(mask, np.float32)[None, None] / 255 >= 0.5).astype(np.float32)
|
| 57 |
+
lat, mlat = enc.run(None, {"image": x})[0], enc.run(None, {"image": x * (1 - m)})[0]
|
| 58 |
+
m64 = m[:, :, ::8, ::8].copy()
|
| 59 |
+
s = DDIMScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", clip_sample=False)
|
| 60 |
+
s.set_timesteps(20); ts = s.timesteps[1:] # strength 0.99: 19 steps
|
| 61 |
+
g = torch.Generator().manual_seed(0)
|
| 62 |
+
noise = torch.randn(lat.shape, generator=g) + 0.0357 * torch.randn((1, 4, 1, 1), generator=g)
|
| 63 |
+
z = s.add_noise(torch.from_numpy(lat), noise, ts[:1])
|
| 64 |
+
for t in ts:
|
| 65 |
+
eps = unet.run(None, {"latent": z.numpy(), "mask": m64, "masked_latent": mlat,
|
| 66 |
+
"timestep": np.array([float(t)], np.float32), "guidance": np.array([2.5], np.float32)})[0]
|
| 67 |
+
z = s.step(torch.from_numpy(eps), t, z).prev_sample
|
| 68 |
+
y = dec.run(None, {"latent": (z.numpy() / 0.13025).astype(np.float32)})[0][0]
|
| 69 |
+
out = Image.fromarray((np.clip((y.transpose(1, 2, 0) + 1) / 2, 0, 1) * 255).astype(np.uint8))
|
| 70 |
+
blur = np.asarray(mask.convert("RGB").filter(ImageFilter.GaussianBlur(3)), np.float32) / 255
|
| 71 |
+
Image.fromarray((np.asarray(out) * blur + np.asarray(img) * (1 - blur)).astype(np.uint8)).save("filled.png")
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
**Precisions.** fp16: every graph in fp16 where it stays within 5 % of fp32 on a test input, otherwise fp32 (the
|
| 75 |
+
table says which). int8: the UNet's weights int8 per output channel (weight-only), VAE graphs as in fp16. int4: the
|
| 76 |
+
UNet's MatMul weights 4-bit (MatMulNBits, block 32) and its other weights int8, VAE graphs as in fp16. No bf16 (no
|
| 77 |
+
bf16 convolution in ONNX Runtime's CUDA provider).
|
| 78 |
+
|
| 79 |
+
**Benchmark.** BSD100's 100 photos with seeded free-form masks (10-26 % of the picture), the pipeline above on an RTX
|
| 80 |
+
3080 Laptop GPU with ONNX Runtime 1.30: PSNR / SSIM / LPIPS against the original photo and PSNR against the fp32
|
| 81 |
+
pipeline's result (same seed). Inpainting has no single right answer, so the ground-truth numbers are for comparing
|
| 82 |
+
precisions, not a quality score.
|
| 83 |
+
|
| 84 |
+
| Pipeline | Size | vs fp32 (PSNR, same seed) | PSNR / SSIM / LPIPS vs original | Time / picture | Peak VRAM |
|
| 85 |
+
|---|---|---|---|---|---|
|
| 86 |
+
| fp32 (reference, not published) | 1.24 GB | - | 26.35 / 0.906 / 0.067 | 3.8 s | 7.8 GB |
|
| 87 |
+
| fp16 (UNet fp32) | 1.07 GB | 46.5 dB (worst 35.6) | 26.35 / 0.906 / 0.067 | 3.6 s | 5.9 GB |
|
| 88 |
+
| int8 | 0.44 GB | 40.3 dB (worst 32.5) | 26.38 / 0.906 / 0.066 | 3.9 s | 5.9 GB |
|
| 89 |
+
|
| 90 |
+
## How these were made
|
| 91 |
+
|
| 92 |
+
From [Moebius](https://huggingface.co/hustvl/Moebius) at revision `cd01f47fb6`: each part exported with `torch.onnx`, then converted or quantized
|
| 93 |
+
with ONNX Runtime's tools (fp16 / bf16 casts, int8 and int4 weight quantization). Every graph was checked against
|
| 94 |
+
the original before upload; the `sha256` column above is what the files must hash to.
|
| 95 |
+
|
| 96 |
+
## License
|
| 97 |
+
|
| 98 |
+
MIT (weights; the GitHub code is Apache-2.0). These files are a derivative of the original model and carry its license: it is copied in this
|
| 99 |
+
repository, and it governs your use of these files. The original model card follows unchanged.
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
# Original model card
|
| 104 |
+
|
| 105 |
+
《Moebius: 0.2B Lightweight Image Inpainting Framework with 10B-Level Performance》
|
| 106 |
+
Project Page: https://hustvl.github.io/Moebius
|
| 107 |
+
Paper: https://arxiv.org/abs/2606.19195
|
| 108 |
+
Code: https://github.com/hustvl/Moebius
|
| 109 |
+
weight: https://huggingface.co/hustvl/Moebius/tree/main
|