File size: 3,201 Bytes
626e708
5e38374
626e708
5e38374
 
 
 
626e708
 
 
5e38374
 
626e708
 
 
5e38374
 
 
 
 
626e708
5e38374
 
626e708
 
5e38374
 
626e708
5e38374
 
 
 
 
 
 
626e708
5e38374
626e708
 
5e38374
 
 
 
 
 
 
 
 
 
 
 
626e708
 
5e38374
 
 
 
 
 
 
 
 
 
626e708
 
5e38374
 
 
 
 
 
 
 
 
 
 
 
626e708
5e38374
 
 
 
 
 
 
626e708
5e38374
 
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
#!/usr/bin/env python3
# tools/startup_selfcheck.py
"""
Quiet Startup Self-Check for Hugging Face Space
- Verifies SAM2 + MatAnyone can load and run a tiny inference
- Defaults to ASYNC so UI launches immediately; logs results
- Switch to SYNC by setting env SELF_CHECK_MODE=sync
"""

import logging
import os
import threading
import numpy as np
import torch

# Keep imports lazy so a busted loader doesn't break the Space
def _safe_imports():
    from models.loaders.sam2_loader import SAM2Loader
    from models.loaders.matanyone_loader import MatAnyoneLoader
    return SAM2Loader, MatAnyoneLoader

log = logging.getLogger("selfcheck")
if not log.handlers:
    logging.basicConfig(level=logging.INFO)

def run_selfcheck() -> None:
    """Do the actual check; log results. Non-throwing."""
    try:
        SAM2Loader, MatAnyoneLoader = _safe_imports()
    except Exception as e:
        log.error(f"❌ Self-check import failure: {e}", exc_info=True)
        return

    try:
        # tiny dummy frame + mask
        img = np.zeros((64, 64, 3), dtype=np.uint8)
        mask = np.ones((64, 64), dtype=np.float32)

        # --- SAM2 ---
        try:
            sam_loader = SAM2Loader(device="cuda" if torch.cuda.is_available() else "cpu")
            sam = sam_loader.load("tiny")
            if sam:
                sam.set_image(img)
                out = sam.predict(point_coords=None, point_labels=None)
                m = out["masks"]
                log.info(f"βœ… SAM2 OK β€” masks {m.shape} {m.dtype} [{m.min():.3f},{m.max():.3f}]")
            else:
                log.error("❌ SAM2 failed to load (adapter is None)")
        except Exception as e:
            log.error(f"❌ SAM2 self-check error: {e}", exc_info=True)

        # --- MatAnyone ---
        try:
            mat_loader = MatAnyoneLoader(device="cuda" if torch.cuda.is_available() else "cpu")
            session = mat_loader.load()
            if session:
                alpha = session(img, mask)  # first frame needs a mask (fallback ok)
                log.info(f"βœ… MatAnyone OK β€” alpha {alpha.shape} {alpha.dtype} [{alpha.min():.3f},{alpha.max():.3f}]")
            else:
                log.error("❌ MatAnyone failed to load (session is None)")
        except Exception as e:
            log.error(f"❌ MatAnyone self-check error: {e}", exc_info=True)

    except Exception as e:
        log.error(f"❌ Self-check unexpected error: {e}", exc_info=True)

def schedule_startup_selfcheck(mode: str = "async") -> None:
    """
    mode: "async" (default) β†’ run in a daemon thread; UI doesn't wait
          "sync"           β†’ block until check completes (slower startup)
    """
    mode = (mode or "async").lower()
    if mode == "sync":
        log.info("πŸ”Ž Running model self-check (sync)…")
        run_selfcheck()
        return

    def _worker():
        try:
            log.info("πŸ”Ž Running model self-check (async)…")
            run_selfcheck()
        except Exception:
            # Make absolutely sure this never crashes the app
            log.exception("Self-check thread crashed")

    t = threading.Thread(target=_worker, name="startup-selfcheck", daemon=True)
    t.start()