Spaces:
Running on Zero
Running on Zero
| import spaces # must be imported before torch / any CUDA-touching import | |
| import torch | |
| import numpy as np | |
| import gradio as gr | |
| from PIL import Image, ImageChops | |
| from huggingface_hub import hf_hub_download | |
| from diffusers import DDIMScheduler | |
| from diffusers.models import AutoencoderKL | |
| from removal.v1_2.removal_model import build_removal_model, load_removal_model | |
| from removal.v1_2.pipeline import RemovalSDXLPipeline_BatchMode | |
| MODEL_CONFIG = "config/model_cfg/moebius.yaml" | |
| NUM_EMBEDDINGS = 20 | |
| DTYPE = torch.float32 | |
| VARIANTS = { | |
| "Places2 (natural scenes)": "ft_places2", | |
| "CelebA-HQ (faces)": "ft_celebahq", | |
| "FFHQ (faces)": "ft_ffhq", | |
| "Pretrained (general)": "pretrained", | |
| } | |
| # Shared VAE (PixelHacker f8d4) — load on CPU, the pipeline moves it to CUDA. | |
| vae = AutoencoderKL.from_pretrained("hustvl/PixelHacker", subfolder="vae") | |
| # Build one pipeline per fine-tuned variant; all share the same VAE. | |
| PIPELINES = {} | |
| for label, subdir in VARIANTS.items(): | |
| weight_path = hf_hub_download("hustvl/Moebius", f"{subdir}/diffusion_pytorch_model.bin") | |
| model = build_removal_model(MODEL_CONFIG, NUM_EMBEDDINGS) | |
| load_removal_model(model, weight_path, device="cpu", dtype=DTYPE) | |
| scheduler = DDIMScheduler( | |
| beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", | |
| num_train_timesteps=1000, clip_sample=False, | |
| ) | |
| PIPELINES[label] = RemovalSDXLPipeline_BatchMode( | |
| removal_model=model, vae=vae, scheduler=scheduler, device="cuda", dtype=DTYPE, | |
| ) | |
| def _extract(editor): | |
| """Pull the source image + a binary mask (white = region to inpaint) from an ImageEditor value.""" | |
| if editor is None: | |
| raise gr.Error("Please upload an image and paint over the region to fill.") | |
| image = editor["background"].convert("RGB") | |
| layers = editor.get("layers") or [] | |
| mask = Image.new("L", image.size, 0) | |
| for layer in layers: | |
| if layer.mode == "RGBA": | |
| mask = ImageChops.lighter(mask, layer.split()[-1]) | |
| else: | |
| mask = ImageChops.lighter(mask, layer.convert("L")) | |
| mask = mask.point(lambda p: 255 if p > 10 else 0) | |
| if not mask.getbbox(): | |
| raise gr.Error("The mask is empty — paint over the area you want to inpaint.") | |
| return image, mask | |
| def inpaint(editor, variant, num_steps, guidance_scale, seed, progress=gr.Progress(track_tqdm=True)): | |
| image, mask = _extract(editor) | |
| pipe = PIPELINES[variant] | |
| out = pipe( | |
| [image], [mask], | |
| image_size=512, | |
| num_steps=int(num_steps), | |
| guidance_scale=float(guidance_scale), | |
| noise_offset=0.0357, | |
| paste=True, | |
| compensate=False, | |
| retry=int(seed), | |
| mute=False, | |
| ) | |
| return out[0] | |
| with gr.Blocks(title="Moebius Inpainting") as demo: | |
| gr.Markdown( | |
| """# Moebius — 0.2B Lightweight Image Inpainting | |
| A 0.22B-parameter inpainting model (2% of FLUX.1-Fill-Dev's size) matching 10B-level quality. | |
| Upload an image, **paint over the region you want to fill**, pick a model variant, and run. | |
| [Paper](https://arxiv.org/abs/2606.19195) · [Code](https://github.com/hustvl/Moebius) · [Weights](https://huggingface.co/hustvl/Moebius) | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| editor = gr.ImageEditor( | |
| label="Image — paint the area to inpaint", | |
| type="pil", | |
| brush=gr.Brush(colors=["#ffffff"], default_size=40, color_mode="fixed"), | |
| layers=False, | |
| sources=["upload", "clipboard"], | |
| height=512, | |
| ) | |
| variant = gr.Dropdown( | |
| choices=list(VARIANTS.keys()), | |
| value="Places2 (natural scenes)", | |
| label="Model variant", | |
| ) | |
| with gr.Accordion("Advanced settings", open=False): | |
| num_steps = gr.Slider(1, 50, value=20, step=1, label="Sampling steps") | |
| guidance_scale = gr.Slider(1.0, 10.0, value=2.5, step=0.1, label="Guidance scale (CFG)") | |
| seed = gr.Slider(0, 100000, value=0, step=1, label="Seed") | |
| run = gr.Button("Inpaint", variant="primary") | |
| with gr.Column(): | |
| output = gr.Image(label="Result", type="pil", height=512) | |
| run.click( | |
| inpaint, | |
| inputs=[editor, variant, num_steps, guidance_scale, seed], | |
| outputs=output, | |
| ) | |
| demo.launch() | |