File size: 4,946 Bytes
41f94a2
b1d8561
7997806
 
41f94a2
 
 
7997806
41f94a2
 
 
 
 
 
 
 
6db36cd
41f94a2
 
9f46175
 
 
 
 
 
 
 
 
 
 
41f94a2
 
 
 
 
 
 
 
 
 
9f46175
41f94a2
 
b1d8561
 
 
41f94a2
9f46175
 
 
 
 
 
 
 
41f94a2
6db36cd
41f94a2
 
 
 
b1d8561
9f46175
aa3787a
 
 
 
b1d8561
 
9f46175
b1d8561
 
 
 
 
9f46175
 
b1d8561
 
 
 
41f94a2
9f46175
 
 
 
 
 
 
b1d8561
 
 
 
41f94a2
 
b1d8561
 
 
 
 
 
 
 
9f46175
 
 
 
 
b1d8561
9f46175
 
 
b1d8561
 
 
 
9f46175
 
 
b1d8561
9f46175
 
 
 
b1d8561
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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()