CREATORJD commited on
Commit
41f4e26
·
verified ·
1 Parent(s): a1fd2c6

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +65 -13
  2. requirements.txt +12 -5
app.py CHANGED
@@ -32,8 +32,29 @@ import torch
32
  from PIL import Image, ImageFilter
33
  import gradio as gr
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  SD_INPAINT_ID = "runwayml/stable-diffusion-inpainting"
36
- CONTROLNET_ID = "lllyasviel/control_v11f1p_sd15_depth" # -> hr16/ControlNet-HandRefiner-pruned for best
37
  TILE_CN_ID = "lllyasviel/control_v11f1e_sd15_tile" # detail-regeneration ControlNet
38
  SD_BASE_ID = "runwayml/stable-diffusion-v1-5" # base SD for img2img detail pass
39
  MESHGRAPHORMER_ID = "hr16/ControlNet-HandRefiner-pruned"
@@ -46,21 +67,34 @@ DETAIL_NEG = "blurry, soft, out of focus, jpeg artifacts, low quality, smudged,
46
  _PIPE = None
47
  _MESH = None
48
  _DETAIL = None
 
 
 
 
 
 
 
 
49
 
50
  def _load():
51
- """Load on CPU at import time. Models are moved to GPU inside the @spaces.GPU call,
52
- so the timed GPU window is spent on inference, not on multi-GB model loading —
53
- which is what caused first-call stalls/timeouts."""
54
- global _PIPE, _MESH
55
  if _PIPE is not None:
56
  return
57
  import time
58
  from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel, UniPCMultistepScheduler
59
- from controlnet_aux import MeshGraphormerDetector
60
  t0 = time.time()
61
  print("[load] starting model load on CPU…", flush=True)
62
- _MESH = MeshGraphormerDetector.from_pretrained(MESHGRAPHORMER_ID)
63
- print(f"[load] meshgraphormer ok ({time.time()-t0:.0f}s)", flush=True)
 
 
 
 
 
 
64
  cn = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=torch.float16)
65
  pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
66
  SD_INPAINT_ID, controlnet=cn, torch_dtype=torch.float16, safety_checker=None
@@ -122,7 +156,10 @@ def fix_hands(image, mask_layers, prompt, strength):
122
  _load() # no-op if already loaded
123
  _MESH.to("cuda")
124
  _PIPE.to("cuda")
125
- print(f"[fix] models on GPU, t={time.time()-t0:.0f}s", flush=True)
 
 
 
126
  init, (ow, oh) = _fit(image.convert("RGB"))
127
  W, H = init.size
128
  print(f"[fix] input fitted to {W}x{H}", flush=True)
@@ -136,14 +173,29 @@ def fix_hands(image, mask_layers, prompt, strength):
136
  if m.getbbox() is not None:
137
  sent_mask = m
138
 
139
- print("[fix] running MeshGraphormer…", flush=True)
140
- mg = _MESH(init)
141
- depth_img, auto_mask = (mg[0], (mg[1] if len(mg) > 1 else None)) if isinstance(mg, tuple) else (mg, None)
142
- depth_img = depth_img.convert("RGB").resize((W, H), Image.LANCZOS)
 
 
 
 
 
 
 
 
143
  mask_img = sent_mask or (auto_mask.convert("L").resize((W, H), Image.LANCZOS) if auto_mask else None)
144
  if mask_img is None:
 
 
 
145
  raise gr.Error("No hands detected. Paint a mask over the hand and try again.")
146
 
 
 
 
 
147
  mask_img = mask_img.filter(ImageFilter.GaussianBlur(2))
148
  print("[fix] running diffusion…", flush=True)
149
  out = _PIPE(
 
32
  from PIL import Image, ImageFilter
33
  import gradio as gr
34
 
35
+ # ---------------------------------------------------------------------------
36
+ # transformers compatibility shim (fixes MeshGraphormer import on new transformers)
37
+ # Newer transformers removed prune_linear_layer / Conv1D from transformers.modeling_utils,
38
+ # which is exactly what breaks the vendored MeshGraphormer (ComfyUI issue #578).
39
+ # Re-expose them so the legacy import succeeds.
40
+ # ---------------------------------------------------------------------------
41
+ def _patch_transformers():
42
+ try:
43
+ import transformers.modeling_utils as mu
44
+ need = ("prune_linear_layer", "Conv1D", "prune_layer")
45
+ if all(hasattr(mu, n) for n in need):
46
+ return
47
+ from transformers import pytorch_utils as pu
48
+ for n in need:
49
+ if not hasattr(mu, n) and hasattr(pu, n):
50
+ setattr(mu, n, getattr(pu, n))
51
+ print("[shim] transformers symbols patched", flush=True)
52
+ except Exception as e:
53
+ print("[shim] transformers patch skipped:", e, flush=True)
54
+ _patch_transformers()
55
+
56
  SD_INPAINT_ID = "runwayml/stable-diffusion-inpainting"
57
+ CONTROLNET_ID = "lllyasviel/control_v11f1p_sd15_depth"
58
  TILE_CN_ID = "lllyasviel/control_v11f1e_sd15_tile" # detail-regeneration ControlNet
59
  SD_BASE_ID = "runwayml/stable-diffusion-v1-5" # base SD for img2img detail pass
60
  MESHGRAPHORMER_ID = "hr16/ControlNet-HandRefiner-pruned"
 
67
  _PIPE = None
68
  _MESH = None
69
  _DETAIL = None
70
+ _MESH_OK = False
71
+ _MESH_ERR = None
72
+
73
+ def _make_mesh_detector():
74
+ """controlnet_aux==0.0.6 ships MeshGraphormerDetector at the top level.
75
+ (Newer versions dropped it — that's why the pin matters.)"""
76
+ from controlnet_aux import MeshGraphormerDetector as MGD
77
+ return MGD.from_pretrained(MESHGRAPHORMER_ID)
78
 
79
  def _load():
80
+ """Load SD inpaint + ControlNet (always works, diffusers-only) and attempt
81
+ MeshGraphormer (optional). If MeshGraphormer fails, the Space still runs;
82
+ hand auto-detect is then unavailable but manual-mask + detail pass work."""
83
+ global _PIPE, _MESH, _MESH_OK, _MESH_ERR
84
  if _PIPE is not None:
85
  return
86
  import time
87
  from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel, UniPCMultistepScheduler
 
88
  t0 = time.time()
89
  print("[load] starting model load on CPU…", flush=True)
90
+ # MeshGraphormer is optional — isolate it so it can't crash the container
91
+ try:
92
+ _MESH = _make_mesh_detector()
93
+ _MESH_OK = True
94
+ print(f"[load] meshgraphormer ok ({time.time()-t0:.0f}s)", flush=True)
95
+ except Exception as e:
96
+ _MESH = None; _MESH_OK = False; _MESH_ERR = str(e)
97
+ print("[load] meshgraphormer UNAVAILABLE (manual mask still works):", e, flush=True)
98
  cn = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=torch.float16)
99
  pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
100
  SD_INPAINT_ID, controlnet=cn, torch_dtype=torch.float16, safety_checker=None
 
156
  _load() # no-op if already loaded
157
  _MESH.to("cuda")
158
  _PIPE.to("cuda")
159
+ if _MESH_OK and _MESH is not None:
160
+ try: _MESH.to("cuda")
161
+ except Exception: pass
162
+ print(f"[fix] models on GPU, t={time.time()-t0:.0f}s (mesh={_MESH_OK})", flush=True)
163
  init, (ow, oh) = _fit(image.convert("RGB"))
164
  W, H = init.size
165
  print(f"[fix] input fitted to {W}x{H}", flush=True)
 
173
  if m.getbbox() is not None:
174
  sent_mask = m
175
 
176
+ depth_img = None
177
+ auto_mask = None
178
+ if _MESH_OK and _MESH is not None:
179
+ print("[fix] running MeshGraphormer…", flush=True)
180
+ try:
181
+ mg = _MESH(init)
182
+ depth_img, auto_mask = (mg[0], (mg[1] if len(mg) > 1 else None)) if isinstance(mg, tuple) else (mg, None)
183
+ if depth_img is not None:
184
+ depth_img = depth_img.convert("RGB").resize((W, H), Image.LANCZOS)
185
+ except Exception as e:
186
+ print("[fix] mesh inference failed, falling back to mask:", e, flush=True)
187
+
188
  mask_img = sent_mask or (auto_mask.convert("L").resize((W, H), Image.LANCZOS) if auto_mask else None)
189
  if mask_img is None:
190
+ if not _MESH_OK:
191
+ raise gr.Error("Auto hand-detection isn't available on this Space build. "
192
+ "Paint a mask over the bad hand (use the brush on the image) and run again.")
193
  raise gr.Error("No hands detected. Paint a mask over the hand and try again.")
194
 
195
+ # if we have no depth (no mesh), use the masked region of the image as a soft control
196
+ if depth_img is None:
197
+ depth_img = init # tile/identity-style guidance keeps structure from the source
198
+
199
  mask_img = mask_img.filter(ImageFilter.GaussianBlur(2))
200
  print("[fix] running diffusion…", flush=True)
201
  out = _PIPE(
requirements.txt CHANGED
@@ -1,10 +1,17 @@
 
1
  spaces
2
  gradio==5.49.1
3
- torch
4
- diffusers
5
- transformers
6
- accelerate
7
- controlnet_aux
 
 
 
 
 
8
  pillow
9
  numpy
10
  scipy
 
 
1
+ # ZeroGPU needs modern gradio + spaces
2
  spaces
3
  gradio==5.49.1
4
+ # --- pinned stack that ships a WORKING MeshGraphormerDetector ---
5
+ # (from hysts's official controlnet Space; these versions still export it)
6
+ controlnet_aux==0.0.6
7
+ diffusers==0.18.2
8
+ transformers==4.30.2
9
+ accelerate==0.21.0
10
+ mediapipe==0.10.1
11
+ huggingface-hub==0.16.4
12
+ safetensors==0.3.1
13
+ # torch is provided by the ZeroGPU base image; do not pin it here
14
  pillow
15
  numpy
16
  scipy
17
+ einops