MogensR commited on
Commit
1073095
·
verified ·
1 Parent(s): ad468b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -16
app.py CHANGED
@@ -4,12 +4,15 @@
4
  GPU-optimized with real verification & self-tests (no mocks).
5
 
6
  Key guarantees:
7
- - MatAnyone init uses InferenceCore("PeiqingYang/MatAnyone") and runs a real clip test.
8
  - SAM2 marked Verified only after a successful micro-inference.
9
  - rembg verified by actually removing BG on a tiny image.
10
  - FFmpeg/MoviePy tested by encoding/decoding a tiny clip.
11
  - CUDA verified by real CUDA ops; GPU stats shown from PyTorch (NVML if present).
12
  - "Run Self-Test" button & "--self-test" CLI flag for CI / manual checks.
 
 
 
13
  """
14
 
15
  import os
@@ -55,9 +58,9 @@
55
  os.environ["GRADIO_SERVER_PORT"] = "7860"
56
 
57
  # Feature flags
58
- os.environ["USE_MATANYONE"] = "true"
59
- os.environ["USE_SAM2"] = "true"
60
- os.environ["SELF_CHECK_MODE"] = "false"
61
 
62
  # =========================
63
  # Imports
@@ -315,18 +318,20 @@ def build_professional_bg(w, h, preset: str) -> np.ndarray:
315
  return make_solid(w, h, (240, 240, 240))
316
 
317
  # =========================
318
- # MatAnyone wrapper (fixed)
319
  # =========================
320
  class OptimizedMatAnyoneProcessor:
321
  """
322
- Thin wrapper around MatAnyone's InferenceCore.
323
- Uses HF repo-id constructor InferenceCore("PeiqingYang/MatAnyone").
 
324
  """
325
  def __init__(self):
326
  self.processor = None
327
  self.device = "cuda" if (TORCH_AVAILABLE and CUDA_AVAILABLE) else "cpu"
328
  self.initialized = False
329
  self.verified = False
 
330
 
331
  def initialize(self) -> bool:
332
  if not MATANYONE_IMPORTED:
@@ -334,18 +339,72 @@ def initialize(self) -> bool:
334
  return False
335
  if self.initialized and self.processor is not None:
336
  return True
 
 
 
 
337
  try:
338
- print(f"Initializing MatAnyone on {self.device}…")
339
- self.processor = MatAnyInferenceCore("PeiqingYang/MatAnyone") # auto-downloads
340
  self.verified = hasattr(self.processor, "process_video")
341
  if self.device == "cuda":
342
  torch.cuda.empty_cache()
343
- _ = torch.rand(1, device="cuda") * 0.0 # warm CUDA context
344
  self.initialized = True
345
- print("✅ MatAnyone initialized (HF repo-id).")
346
  return True
 
 
 
 
 
 
 
347
  except Exception as e:
348
- print(f"MatAnyone initialization failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  import traceback; traceback.print_exc()
350
  return False
351
 
@@ -374,7 +433,7 @@ def create_mask_optimized(self, video_path: str, output_path: str) -> str:
374
  except Exception as e:
375
  print(f"SAM2 mask creation failed; fallback rectangle. Error: {e}")
376
 
377
- # Fallback: centered box (ensures pipeline continuity)
378
  h, w = frame.shape[:2]
379
  mask = np.zeros((h, w), dtype=np.uint8)
380
  mx, my = int(w * 0.15), int(h * 0.10)
@@ -575,8 +634,9 @@ def self_test_sam2() -> (bool, str):
575
 
576
  def self_test_matanyone() -> (bool, str):
577
  try:
578
- if not matanyone_processor.initialize():
579
- return False, "MatAnyone init failed"
 
580
  if not matanyone_processor.verified:
581
  return False, "MatAnyone missing process_video API"
582
  # Create a tiny real video + mask, then run process_video
@@ -588,8 +648,15 @@ def self_test_matanyone() -> (bool, str):
588
  x = 8 + t*4
589
  cv2.rectangle(frame, (x, 20), (x+12, 44), (200, 200, 200), -1)
590
  frames.append(frame)
 
 
591
  vid_path = os.path.join(td, "tiny_input.mp4")
592
- ImageSequenceClip(frames, fps=8).write_videofile(vid_path, audio=False, logger=None)
 
 
 
 
 
593
 
594
  # Simple central mask (seed)
595
  mask = np.zeros((64, 64), dtype=np.uint8)
@@ -703,6 +770,10 @@ def gradio_interface_optimized(video_file, bg_image, use_matanyone=True, bg_pres
703
  def gradio_run_self_test():
704
  return run_self_test()
705
 
 
 
 
 
706
  # =========================
707
  # UI
708
  # =========================
@@ -756,6 +827,10 @@ def gradio_run_self_test():
756
  selftest_btn = gr.Button("Run Self-Test")
757
  selftest_out = gr.Textbox(label="Self-Test Report", lines=16)
758
 
 
 
 
 
759
  with gr.Column():
760
  output_video = gr.Video(label="✨ Result")
761
  download_file = gr.File(label="💾 Download")
@@ -773,11 +848,18 @@ def gradio_run_self_test():
773
  outputs=[selftest_out],
774
  )
775
 
 
 
 
 
 
 
776
  gr.Markdown("---")
777
  gr.Markdown("""
778
  **Notes**
779
  - SAM2 shows ✅ only after a real micro-inference passes.
780
  - MatAnyone shows ✅ only if initialization succeeded and `process_video` exists; the self-test also runs a tiny real video through it.
 
781
  - FFmpeg/MoviePy, CUDA, and rembg are validated by actually running them.
782
  """)
783
 
 
4
  GPU-optimized with real verification & self-tests (no mocks).
5
 
6
  Key guarantees:
7
+ - MatAnyone init tries HF repo-id first, then local checkpoint fallback; errors are surfaced.
8
  - SAM2 marked Verified only after a successful micro-inference.
9
  - rembg verified by actually removing BG on a tiny image.
10
  - FFmpeg/MoviePy tested by encoding/decoding a tiny clip.
11
  - CUDA verified by real CUDA ops; GPU stats shown from PyTorch (NVML if present).
12
  - "Run Self-Test" button & "--self-test" CLI flag for CI / manual checks.
13
+
14
+ NOTE: Ensure requirements include at least:
15
+ huggingface-hub, requests, hydra-core, omegaconf, einops, timm, opencv-python-headless, moviepy, rembg
16
  """
17
 
18
  import os
 
58
  os.environ["GRADIO_SERVER_PORT"] = "7860"
59
 
60
  # Feature flags
61
+ os.environ["USE_MATANYONE"] = os.getenv("USE_MATANYONE", "true")
62
+ os.environ["USE_SAM2"] = os.getenv("USE_SAM2", "true")
63
+ os.environ["SELF_CHECK_MODE"] = os.getenv("SELF_CHECK_MODE", "false")
64
 
65
  # =========================
66
  # Imports
 
318
  return make_solid(w, h, (240, 240, 240))
319
 
320
  # =========================
321
+ # MatAnyone wrapper (robust)
322
  # =========================
323
  class OptimizedMatAnyoneProcessor:
324
  """
325
+ Wrapper around MatAnyone's InferenceCore.
326
+ Tries HF repo-id first; if that fails (or the local class expects a `network`),
327
+ falls back to downloading `pretrained_models/matanyone.pth` and building the model.
328
  """
329
  def __init__(self):
330
  self.processor = None
331
  self.device = "cuda" if (TORCH_AVAILABLE and CUDA_AVAILABLE) else "cpu"
332
  self.initialized = False
333
  self.verified = False
334
+ self.last_error = None # for diagnostics
335
 
336
  def initialize(self) -> bool:
337
  if not MATANYONE_IMPORTED:
 
339
  return False
340
  if self.initialized and self.processor is not None:
341
  return True
342
+
343
+ self.last_error = None
344
+
345
+ # 1) Preferred path: HF repo-id (documented usage)
346
  try:
347
+ print(f"Initializing MatAnyone (HF repo-id) on {self.device}…")
348
+ self.processor = MatAnyInferenceCore("PeiqingYang/MatAnyone") # per README
349
  self.verified = hasattr(self.processor, "process_video")
350
  if self.device == "cuda":
351
  torch.cuda.empty_cache()
352
+ _ = torch.rand(1, device="cuda") * 0.0 # warmup
353
  self.initialized = True
354
+ print("✅ MatAnyone initialized via HF repo-id.")
355
  return True
356
+ except TypeError as e:
357
+ # Classic sign of older API: __init__ requires a `network` object
358
+ if "network" in str(e).lower():
359
+ print("MatAnyone InferenceCore expects a `network` (older API); will build network locally.")
360
+ else:
361
+ print(f"HF init TypeError: {e}")
362
+ self.last_error = f"HF init TypeError: {e}"
363
  except Exception as e:
364
+ self.last_error = f"HF init failed: {type(e).__name__}: {e}"
365
+ print(self.last_error)
366
+
367
+ # 2) Fallback: local checkpoint → network object → InferenceCore(network)
368
+ try:
369
+ print("Falling back to local checkpoint init for MatAnyone…")
370
+ from pathlib import Path
371
+ import requests
372
+ from matanyone.utils.get_default_model import get_matanyone_model
373
+
374
+ ckpt_dir = Path("./pretrained_models")
375
+ ckpt_dir.mkdir(parents=True, exist_ok=True)
376
+ ckpt_path = ckpt_dir / "matanyone.pth"
377
+
378
+ if not ckpt_path.exists():
379
+ url = "https://github.com/pq-yang/MatAnyone/releases/download/v1.0.0/matanyone.pth"
380
+ print(f"Downloading MatAnyone checkpoint from: {url}")
381
+ with requests.get(url, stream=True, timeout=180) as r:
382
+ r.raise_for_status()
383
+ with open(ckpt_path, "wb") as f:
384
+ for chunk in r.iter_content(chunk_size=8192):
385
+ if chunk:
386
+ f.write(chunk)
387
+ print(f"Checkpoint saved to {ckpt_path}")
388
+
389
+ # Build the actual network then wrap with InferenceCore
390
+ network = get_matanyone_model(
391
+ str(ckpt_path),
392
+ device=("cuda" if CUDA_AVAILABLE else "cpu")
393
+ )
394
+ self.processor = MatAnyInferenceCore(network)
395
+ self.verified = hasattr(self.processor, "process_video")
396
+
397
+ if self.device == "cuda":
398
+ torch.cuda.empty_cache()
399
+ _ = torch.rand(1, device="cuda") * 0.0
400
+
401
+ self.initialized = True
402
+ print("✅ MatAnyone initialized via local checkpoint.")
403
+ return True
404
+
405
+ except Exception as e:
406
+ self.last_error = f"Local init failed: {type(e).__name__}: {e}"
407
+ print(f"MatAnyone initialization failed: {self.last_error}")
408
  import traceback; traceback.print_exc()
409
  return False
410
 
 
433
  except Exception as e:
434
  print(f"SAM2 mask creation failed; fallback rectangle. Error: {e}")
435
 
436
+ # Fallback: centered box
437
  h, w = frame.shape[:2]
438
  mask = np.zeros((h, w), dtype=np.uint8)
439
  mx, my = int(w * 0.15), int(h * 0.10)
 
634
 
635
  def self_test_matanyone() -> (bool, str):
636
  try:
637
+ ok_init = matanyone_processor.initialize()
638
+ if not ok_init:
639
+ return False, f"MatAnyone init failed: {getattr(matanyone_processor, 'last_error', 'no details')}"
640
  if not matanyone_processor.verified:
641
  return False, "MatAnyone missing process_video API"
642
  # Create a tiny real video + mask, then run process_video
 
648
  x = 8 + t*4
649
  cv2.rectangle(frame, (x, 20), (x+12, 44), (200, 200, 200), -1)
650
  frames.append(frame)
651
+
652
+ # Write temp video and a visible copy you can inspect
653
  vid_path = os.path.join(td, "tiny_input.mp4")
654
+ clip = ImageSequenceClip(frames, fps=8)
655
+ clip.write_videofile(vid_path, audio=False, logger=None)
656
+ visible_test_clip = CHECKPOINTS_DIR / "selftest_clip.mp4"
657
+ clip.write_videofile(str(visible_test_clip), audio=False, logger=None)
658
+ clip.close()
659
+ print(f"📹 Self-test clip saved to {visible_test_clip}")
660
 
661
  # Simple central mask (seed)
662
  mask = np.zeros((64, 64), dtype=np.uint8)
 
770
  def gradio_run_self_test():
771
  return run_self_test()
772
 
773
+ def show_matanyone_diag():
774
+ ok = matanyone_processor.initialized and matanyone_processor.verified
775
+ return "READY ✅" if ok else (matanyone_processor.last_error or "Not initialized yet")
776
+
777
  # =========================
778
  # UI
779
  # =========================
 
827
  selftest_btn = gr.Button("Run Self-Test")
828
  selftest_out = gr.Textbox(label="Self-Test Report", lines=16)
829
 
830
+ gr.Markdown("### 🛠 MatAnyone Diagnostics")
831
+ mat_diag_btn = gr.Button("Show MatAnyone Diagnostics")
832
+ mat_diag_out = gr.Textbox(label="MatAnyone Last Error / Status", lines=6)
833
+
834
  with gr.Column():
835
  output_video = gr.Video(label="✨ Result")
836
  download_file = gr.File(label="💾 Download")
 
848
  outputs=[selftest_out],
849
  )
850
 
851
+ mat_diag_btn.click(
852
+ fn=show_matanyone_diag,
853
+ inputs=[],
854
+ outputs=[mat_diag_out],
855
+ )
856
+
857
  gr.Markdown("---")
858
  gr.Markdown("""
859
  **Notes**
860
  - SAM2 shows ✅ only after a real micro-inference passes.
861
  - MatAnyone shows ✅ only if initialization succeeded and `process_video` exists; the self-test also runs a tiny real video through it.
862
+ - The self-test saves a visible clip to `checkpoints/selftest_clip.mp4` so you can inspect what was used.
863
  - FFmpeg/MoviePy, CUDA, and rembg are validated by actually running them.
864
  """)
865