linoyts HF Staff commited on
Commit
5c7ad57
·
verified ·
1 Parent(s): fbc60ea

generator-based live step status (track_tqdm doesnt forward on zerogpu); fix gen_mask tqdm crash

Browse files
Files changed (1) hide show
  1. app.py +44 -17
app.py CHANGED
@@ -5,6 +5,8 @@ os.environ.setdefault("TORCHDYNAMO_DISABLE", "1")
5
 
6
  import random
7
  import tempfile
 
 
8
 
9
  import cv2
10
  import numpy as np
@@ -82,8 +84,7 @@ def _sam3_video_masks(frames_pil, text):
82
  session = sam3_processor.add_text_prompt(inference_session=session, text=text)
83
  n = len(frames_pil)
84
  masks = np.zeros((n, H, W), dtype=bool)
85
- for mo in tqdm(sam3.propagate_in_video_iterator(inference_session=session, max_frame_num_to_track=n),
86
- total=n, desc="SAM3 tracking"):
87
  proc = sam3_processor.postprocess_outputs(session, mo)
88
  m = proc.get("masks")
89
  if m is not None and len(m):
@@ -163,9 +164,9 @@ def gen_mask(video, mask_text, preset, progress=gr.Progress(track_tqdm=True)):
163
  return _write_mask_video(masks)
164
 
165
 
166
- # --- Inference --------------------------------------------------------------
167
  @spaces.GPU(duration=_duration)
168
- def inpaint(video, mask_video, prompt, preset, num_frames, seed, randomize, progress=gr.Progress(track_tqdm=True)):
169
  if video is None:
170
  raise gr.Error("Please upload a video.")
171
  if mask_video is None:
@@ -195,16 +196,41 @@ def inpaint(video, mask_video, prompt, preset, num_frames, seed, randomize, prog
195
  am[masks] = 0.0
196
  attn_mask = torch.from_numpy(am)[None, None]
197
 
198
- video_out, audio_out = pipe(
199
- prompt=prompt.strip(), negative_prompt="",
200
- reference_conditions=[LTX2ReferenceCondition(frames=ref, strength=1.0)],
201
- conditioning_attention_mask=attn_mask, reference_downscale_factor=1,
202
- width=width, height=height, num_frames=num_frames, frame_rate=FPS,
203
- num_inference_steps=NUM_STEPS, sigmas=DISTILLED_SIGMA_VALUES,
204
- guidance_scale=1.0, stg_scale=0.0, audio_guidance_scale=1.0, audio_stg_scale=0.0,
205
- generator=torch.Generator(device="cuda").manual_seed(seed),
206
- output_type="np", return_dict=False,
207
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  gen = (np.clip(video_out[0], 0, 1) * 255).astype(np.uint8)
210
  orig_arr = np.stack(orig).astype(np.float32)
@@ -216,7 +242,7 @@ def inpaint(video, mask_video, prompt, preset, num_frames, seed, randomize, prog
216
 
217
  out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
218
  _export(out, audio_out, out_path)
219
- return out_path, seed
220
 
221
 
222
  # --- UI ---------------------------------------------------------------------
@@ -249,6 +275,7 @@ with gr.Blocks(title="LTX-2.3 Video Inpainting") as demo:
249
  run = gr.Button("Inpaint", variant="primary")
250
  with gr.Column():
251
  video_out = gr.Video(label="Inpainted result")
 
252
  used_seed = gr.Number(label="Seed used", interactive=False)
253
 
254
  # a new input video invalidates any stored base mask
@@ -263,7 +290,7 @@ with gr.Blocks(title="LTX-2.3 Video Inpainting") as demo:
263
  dilate_px.release(apply_dilation, inputs=[base_mask, mask_video, dilate_px], outputs=[mask_video, base_mask])
264
 
265
  run.click(inpaint, inputs=[video_in, mask_video, prompt, preset, num_frames, seed, randomize],
266
- outputs=[video_out, used_seed])
267
 
268
  gr.Examples(
269
  examples=[
@@ -275,7 +302,7 @@ with gr.Blocks(title="LTX-2.3 Video Inpainting") as demo:
275
  "Fast (768×448)", 49, 42, False],
276
  ],
277
  inputs=[video_in, mask_video, prompt, preset, num_frames, seed, randomize],
278
- outputs=[video_out, used_seed], fn=inpaint, cache_examples=True, cache_mode="lazy",
279
  )
280
 
281
  if __name__ == "__main__":
 
5
 
6
  import random
7
  import tempfile
8
+ import threading
9
+ import time
10
 
11
  import cv2
12
  import numpy as np
 
84
  session = sam3_processor.add_text_prompt(inference_session=session, text=text)
85
  n = len(frames_pil)
86
  masks = np.zeros((n, H, W), dtype=bool)
87
+ for mo in sam3.propagate_in_video_iterator(inference_session=session, max_frame_num_to_track=n):
 
88
  proc = sam3_processor.postprocess_outputs(session, mo)
89
  m = proc.get("masks")
90
  if m is not None and len(m):
 
164
  return _write_mask_video(masks)
165
 
166
 
167
+ # --- Inference (generator: yields live step status across the ZeroGPU boundary) ---
168
  @spaces.GPU(duration=_duration)
169
+ def inpaint(video, mask_video, prompt, preset, num_frames, seed, randomize):
170
  if video is None:
171
  raise gr.Error("Please upload a video.")
172
  if mask_video is None:
 
196
  am[masks] = 0.0
197
  attn_mask = torch.from_numpy(am)[None, None]
198
 
199
+ state = {"step": 0}
200
+ holder = {}
201
+
202
+ def _cb(p, i, t, kw):
203
+ state["step"] = i + 1
204
+ return {}
205
+
206
+ def _run():
207
+ try:
208
+ holder["out"] = pipe(
209
+ prompt=prompt.strip(), negative_prompt="",
210
+ reference_conditions=[LTX2ReferenceCondition(frames=ref, strength=1.0)],
211
+ conditioning_attention_mask=attn_mask, reference_downscale_factor=1,
212
+ width=width, height=height, num_frames=num_frames, frame_rate=FPS,
213
+ num_inference_steps=NUM_STEPS, sigmas=DISTILLED_SIGMA_VALUES,
214
+ guidance_scale=1.0, stg_scale=0.0, audio_guidance_scale=1.0, audio_stg_scale=0.0,
215
+ generator=torch.Generator(device="cuda").manual_seed(seed),
216
+ output_type="np", return_dict=False, callback_on_step_end=_cb,
217
+ )
218
+ except Exception as e: # surface in main thread
219
+ holder["err"] = e
220
+
221
+ th = threading.Thread(target=_run)
222
+ th.start()
223
+ yield None, gr.update(), "### ⏳ Preparing…"
224
+ while th.is_alive():
225
+ s = state["step"]
226
+ msg = f"### 🪄 Denoising — step {s}/{NUM_STEPS}" if s else "### ⏳ Loading model / encoding…"
227
+ yield gr.update(), gr.update(), msg
228
+ time.sleep(0.4)
229
+ th.join()
230
+ if "err" in holder:
231
+ raise holder["err"]
232
+ yield gr.update(), gr.update(), "### 🎬 Decoding & encoding video…"
233
+ video_out, audio_out = holder["out"]
234
 
235
  gen = (np.clip(video_out[0], 0, 1) * 255).astype(np.uint8)
236
  orig_arr = np.stack(orig).astype(np.float32)
 
242
 
243
  out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
244
  _export(out, audio_out, out_path)
245
+ yield out_path, seed, "### ✅ Done"
246
 
247
 
248
  # --- UI ---------------------------------------------------------------------
 
275
  run = gr.Button("Inpaint", variant="primary")
276
  with gr.Column():
277
  video_out = gr.Video(label="Inpainted result")
278
+ status = gr.Markdown("")
279
  used_seed = gr.Number(label="Seed used", interactive=False)
280
 
281
  # a new input video invalidates any stored base mask
 
290
  dilate_px.release(apply_dilation, inputs=[base_mask, mask_video, dilate_px], outputs=[mask_video, base_mask])
291
 
292
  run.click(inpaint, inputs=[video_in, mask_video, prompt, preset, num_frames, seed, randomize],
293
+ outputs=[video_out, used_seed, status])
294
 
295
  gr.Examples(
296
  examples=[
 
302
  "Fast (768×448)", 49, 42, False],
303
  ],
304
  inputs=[video_in, mask_video, prompt, preset, num_frames, seed, randomize],
305
+ outputs=[video_out, used_seed, status], fn=inpaint, cache_examples=True, cache_mode="lazy",
306
  )
307
 
308
  if __name__ == "__main__":