Spaces:
Build error
Build error
Upload app.py
Browse files
app.py
CHANGED
|
@@ -43,56 +43,88 @@ _PIPE = None
|
|
| 43 |
_MESH = None
|
| 44 |
|
| 45 |
def _load():
|
|
|
|
|
|
|
|
|
|
| 46 |
global _PIPE, _MESH
|
| 47 |
if _PIPE is not None:
|
| 48 |
return
|
|
|
|
| 49 |
from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel, UniPCMultistepScheduler
|
| 50 |
from controlnet_aux import MeshGraphormerDetector
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
| 52 |
cn = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=torch.float16)
|
| 53 |
pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
|
| 54 |
SD_INPAINT_ID, controlnet=cn, torch_dtype=torch.float16, safety_checker=None
|
| 55 |
)
|
| 56 |
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
def _fit(img):
|
| 60 |
w, h = img.size
|
| 61 |
s = min(1.0, MAX_SIDE / max(w, h))
|
| 62 |
return img.resize((max(8, int(round(w*s/8))*8), max(8, int(round(h*s/8))*8)), Image.LANCZOS), (w, h)
|
| 63 |
|
| 64 |
-
@spaces.GPU(duration=
|
| 65 |
def fix_hands(image, mask_layers, prompt, strength):
|
| 66 |
-
"""ZeroGPU-allocated worker
|
|
|
|
|
|
|
| 67 |
if image is None:
|
| 68 |
raise gr.Error("Upload an image first.")
|
| 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 |
with gr.Blocks(title="DARKROOM HandRefiner", theme=gr.themes.Base()) as demo:
|
| 98 |
gr.Markdown("## 🖐️ DARKROOM HandRefiner\nUpload AI art with bad hands. It auto-detects hands "
|
|
|
|
| 43 |
_MESH = None
|
| 44 |
|
| 45 |
def _load():
|
| 46 |
+
"""Load on CPU at import time. Models are moved to GPU inside the @spaces.GPU call,
|
| 47 |
+
so the timed GPU window is spent on inference, not on multi-GB model loading —
|
| 48 |
+
which is what caused first-call stalls/timeouts."""
|
| 49 |
global _PIPE, _MESH
|
| 50 |
if _PIPE is not None:
|
| 51 |
return
|
| 52 |
+
import time
|
| 53 |
from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel, UniPCMultistepScheduler
|
| 54 |
from controlnet_aux import MeshGraphormerDetector
|
| 55 |
+
t0 = time.time()
|
| 56 |
+
print("[load] starting model load on CPU…", flush=True)
|
| 57 |
+
_MESH = MeshGraphormerDetector.from_pretrained(MESHGRAPHORMER_ID)
|
| 58 |
+
print(f"[load] meshgraphormer ok ({time.time()-t0:.0f}s)", flush=True)
|
| 59 |
cn = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=torch.float16)
|
| 60 |
pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
|
| 61 |
SD_INPAINT_ID, controlnet=cn, torch_dtype=torch.float16, safety_checker=None
|
| 62 |
)
|
| 63 |
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
|
| 64 |
+
try: pipe.enable_attention_slicing()
|
| 65 |
+
except Exception as e: print("[load] attn-slicing skip:", e, flush=True)
|
| 66 |
+
try: pipe.enable_vae_tiling()
|
| 67 |
+
except Exception as e: print("[load] vae-tiling skip:", e, flush=True)
|
| 68 |
+
_PIPE = pipe
|
| 69 |
+
print(f"[load] pipeline ready on CPU ({time.time()-t0:.0f}s total)", flush=True)
|
| 70 |
+
|
| 71 |
+
# preload at import — runs once when the container boots, OUTSIDE any GPU-timed window
|
| 72 |
+
try:
|
| 73 |
+
_load()
|
| 74 |
+
except Exception as _e:
|
| 75 |
+
print("[load] preload deferred:", _e, flush=True)
|
| 76 |
|
| 77 |
def _fit(img):
|
| 78 |
w, h = img.size
|
| 79 |
s = min(1.0, MAX_SIDE / max(w, h))
|
| 80 |
return img.resize((max(8, int(round(w*s/8))*8), max(8, int(round(h*s/8))*8)), Image.LANCZOS), (w, h)
|
| 81 |
|
| 82 |
+
@spaces.GPU(duration=120)
|
| 83 |
def fix_hands(image, mask_layers, prompt, strength):
|
| 84 |
+
"""ZeroGPU-allocated worker. Models are already loaded (CPU) at import;
|
| 85 |
+
here we move them onto the GPU that ZeroGPU just attached, then infer."""
|
| 86 |
+
import time, traceback
|
| 87 |
if image is None:
|
| 88 |
raise gr.Error("Upload an image first.")
|
| 89 |
+
try:
|
| 90 |
+
t0 = time.time()
|
| 91 |
+
_load() # no-op if already loaded
|
| 92 |
+
_MESH.to("cuda")
|
| 93 |
+
_PIPE.to("cuda")
|
| 94 |
+
print(f"[fix] models on GPU, t={time.time()-t0:.0f}s", flush=True)
|
| 95 |
+
init, (ow, oh) = _fit(image.convert("RGB"))
|
| 96 |
+
W, H = init.size
|
| 97 |
+
print(f"[fix] input fitted to {W}x{H}", flush=True)
|
| 98 |
|
| 99 |
+
# optional hand-drawn mask from the ImageMask component
|
| 100 |
+
sent_mask = None
|
| 101 |
+
if isinstance(mask_layers, dict):
|
| 102 |
+
layers = mask_layers.get("layers") or []
|
| 103 |
+
if layers:
|
| 104 |
+
m = layers[0].convert("L").resize((W, H), Image.LANCZOS)
|
| 105 |
+
if m.getbbox() is not None:
|
| 106 |
+
sent_mask = m
|
| 107 |
|
| 108 |
+
print("[fix] running MeshGraphormer…", flush=True)
|
| 109 |
+
mg = _MESH(init)
|
| 110 |
+
depth_img, auto_mask = (mg[0], (mg[1] if len(mg) > 1 else None)) if isinstance(mg, tuple) else (mg, None)
|
| 111 |
+
depth_img = depth_img.convert("RGB").resize((W, H), Image.LANCZOS)
|
| 112 |
+
mask_img = sent_mask or (auto_mask.convert("L").resize((W, H), Image.LANCZOS) if auto_mask else None)
|
| 113 |
+
if mask_img is None:
|
| 114 |
+
raise gr.Error("No hands detected. Paint a mask over the hand and try again.")
|
| 115 |
|
| 116 |
+
mask_img = mask_img.filter(ImageFilter.GaussianBlur(2))
|
| 117 |
+
print("[fix] running diffusion…", flush=True)
|
| 118 |
+
out = _PIPE(
|
| 119 |
+
prompt=prompt or DEFAULT_PROMPT, negative_prompt=NEG, image=init, mask_image=mask_img,
|
| 120 |
+
control_image=depth_img, num_inference_steps=25, strength=float(strength),
|
| 121 |
+
guidance_scale=7.5, controlnet_conditioning_scale=0.7,
|
| 122 |
+
).images[0]
|
| 123 |
+
print(f"[fix] done, total {time.time()-t0:.0f}s", flush=True)
|
| 124 |
+
return out.resize((ow, oh), Image.LANCZOS)
|
| 125 |
+
except Exception as e:
|
| 126 |
+
print("[fix] ERROR:\n" + traceback.format_exc(), flush=True)
|
| 127 |
+
raise gr.Error(f"Fix failed: {e}")
|
| 128 |
|
| 129 |
with gr.Blocks(title="DARKROOM HandRefiner", theme=gr.themes.Base()) as demo:
|
| 130 |
gr.Markdown("## 🖐️ DARKROOM HandRefiner\nUpload AI art with bad hands. It auto-detects hands "
|