MoebiusDemo / app.py
jonatei
Fix quality: paste=True, compensate=True, add variant selector
9f46175
Raw
History Blame Contribute Delete
4.95 kB
import subprocess
import sys
import os
# ── 1. Clone Moebius repo ────────────────────────────────────────────────────
MOEBIUS_DIR = "/app/Moebius"
if not os.path.exists(MOEBIUS_DIR):
subprocess.run(
["git", "clone", "https://github.com/hustvl/Moebius.git", MOEBIUS_DIR],
check=True,
)
# Moebius config uses relative paths β€” must run from its root
os.chdir(MOEBIUS_DIR)
sys.path.insert(0, MOEBIUS_DIR)
# ── 2. Download weights ──────────────────────────────────────────────────────
from huggingface_hub import hf_hub_download, snapshot_download
VARIANTS = ["pretrained", "ft_places2", "ft_celebahq", "ft_ffhq"]
for variant in VARIANTS:
weight_path = f"weight/Moebius/{variant}/diffusion_pytorch_model.bin"
os.makedirs(f"weight/Moebius/{variant}", exist_ok=True)
if not os.path.exists(weight_path):
hf_hub_download(
repo_id="hustvl/Moebius",
filename=f"{variant}/diffusion_pytorch_model.bin",
local_dir="weight/Moebius",
)
VAE_DIR = "weight/vae"
os.makedirs(VAE_DIR, exist_ok=True)
if not os.path.exists(os.path.join(VAE_DIR, "config.json")):
snapshot_download(
repo_id="stabilityai/sd-vae-ft-mse",
local_dir=VAE_DIR,
ignore_patterns=["*.msgpack", "*.h5", "flax_model*"],
)
# ── 3. Build pipelines (one per variant) ────────────────────────────────────
import torch
from types import SimpleNamespace
from infer.utils import build_pipeline
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
pipelines = {}
for variant in VARIANTS:
args = SimpleNamespace(
model_config="config/model_cfg/moebius.yaml",
model_weight=f"weight/Moebius/{variant}/diffusion_pytorch_model.bin",
device=DEVICE,
)
pipelines[variant] = build_pipeline(args)
# ── 4. Gradio UI ─────────────────────────────────────────────────────────────
import gradio as gr
from PIL import Image
import numpy as np
def remove_object(image_dict, variant, image_size, num_steps, guidance_scale):
# Force square β€” the lambda attention assumes h == w
size = (image_size, image_size)
image = image_dict["background"].convert("RGB").resize(size, Image.LANCZOS)
mask_layer = image_dict["layers"][0].convert("RGBA").resize(size, Image.NEAREST)
mask = Image.fromarray(np.array(mask_layer)[:, :, 3]).convert("L")
results = pipelines[variant](
input_image_list=[image],
input_mask_list=[mask],
image_size=image_size,
num_steps=num_steps,
guidance_scale=guidance_scale,
paste=True, # paste result back onto original β€” preserves detail outside mask
compensate=True, # blend edges
mute=True,
)
return results[0]
VARIANT_LABELS = {
"pretrained": "Pretrained (general)",
"ft_places2": "Places2 (scenes & backgrounds)",
"ft_celebahq": "CelebA-HQ (faces)",
"ft_ffhq": "FFHQ (portraits)",
}
with gr.Blocks(title="Moebius Object Removal") as demo:
gr.Markdown(
"## Moebius β€” Lightweight Object Removal\n"
"Paint over the object you want removed, then click **Remove**.\n"
"([Paper](https://arxiv.org/abs/2606.19195) Β· "
"[GitHub](https://github.com/hustvl/Moebius))"
)
with gr.Row():
with gr.Column():
canvas = gr.ImageEditor(
label="Upload image & paint mask",
type="pil",
brush=gr.Brush(colors=["#FFFFFF"], color_mode="fixed"),
)
variant = gr.Radio(
choices=list(VARIANT_LABELS.values()),
value=VARIANT_LABELS["ft_places2"],
label="Model variant",
)
with gr.Accordion("Advanced", open=False):
image_size = gr.Slider(256, 1024, value=512, step=64, label="Image size")
num_steps = gr.Slider(5, 50, value=20, step=1, label="Diffusion steps")
guidance_scale = gr.Slider(1.0, 10.0, value=4.5, step=0.5, label="Guidance scale")
btn = gr.Button("Remove", variant="primary")
with gr.Column():
output = gr.Image(label="Result")
# Map display label back to key
label_to_key = {v: k for k, v in VARIANT_LABELS.items()}
btn.click(
fn=lambda img, var, sz, steps, cfg: remove_object(
img, label_to_key[var], sz, steps, cfg
),
inputs=[canvas, variant, image_size, num_steps, guidance_scale],
outputs=output,
)
demo.launch()