from __future__ import annotations import json import shutil import subprocess import textwrap import time import urllib.request from pathlib import Path import cv2 import numpy as np ROOT = Path(__file__).resolve().parents[1] DEMO_DIR = ROOT / "storage" / "demos" SAMPLE_VIDEO = ROOT / "storage" / "samples" / "synthetic_guidewire.mp4" HOME_SCREENSHOT = DEMO_DIR / "01_home.png" RESPONSE_JSON = DEMO_DIR / "latest_demo_response.json" OUTPUT_VIDEO = DEMO_DIR / "isiseg_usage_demo.mp4" BASE_URL = "http://127.0.0.1:8001" def run(command: list[str], timeout: int = 120) -> subprocess.CompletedProcess[str]: return subprocess.run( command, cwd=ROOT, check=True, text=True, capture_output=True, timeout=timeout, ) def wait_for_service() -> None: deadline = time.time() + 20 while time.time() < deadline: try: with urllib.request.urlopen(f"{BASE_URL}/api/health", timeout=2) as response: if response.status == 200: return except OSError: time.sleep(0.5) raise RuntimeError(f"ISISeg service is not reachable at {BASE_URL}") def ensure_sample() -> None: if not SAMPLE_VIDEO.exists(): run(["bash", "scripts/generate_sample.sh"]) def capture_homepage() -> None: chrome = shutil.which("google-chrome") or shutil.which("chromium") or shutil.which("chromium-browser") if not chrome: raise RuntimeError("Chrome/Chromium is required to capture the UI screenshot") run( [ chrome, "--headless=new", "--no-sandbox", "--disable-gpu", "--hide-scrollbars", "--window-size=1440,1000", f"--screenshot={HOME_SCREENSHOT}", BASE_URL, ], timeout=60, ) def call_segmentation() -> dict: command = [ "curl", "-s", "-X", "POST", "-F", f"file=@{SAMPLE_VIDEO}", "-F", "method=fusion", "-F", "sensitivity=0.68", "-F", "frame_stride=12", "-F", "max_frames=4", f"{BASE_URL}/api/segment", ] completed = run(command, timeout=180) payload = json.loads(completed.stdout) RESPONSE_JSON.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") return payload def fit_image(image: np.ndarray, box_w: int, box_h: int) -> np.ndarray: h, w = image.shape[:2] scale = min(box_w / w, box_h / h) resized = cv2.resize(image, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA) canvas = np.full((box_h, box_w, 3), 18, dtype=np.uint8) y = (box_h - resized.shape[0]) // 2 x = (box_w - resized.shape[1]) // 2 canvas[y : y + resized.shape[0], x : x + resized.shape[1]] = resized return canvas def put_lines(frame: np.ndarray, lines: list[str], x: int, y: int, size: float = 0.82, color=(238, 245, 239)) -> None: for line in lines: cv2.putText(frame, line, (x, y), cv2.FONT_HERSHEY_SIMPLEX, size, color, 2, cv2.LINE_AA) y += int(34 * size / 0.82) def wrap(text: str, width: int = 44) -> list[str]: return textwrap.wrap(text, width=width, break_long_words=False) def slate(title: str, subtitle: str, width: int = 1280, height: int = 720) -> np.ndarray: frame = np.zeros((height, width, 3), dtype=np.uint8) frame[:, :] = (18, 20, 18) cv2.rectangle(frame, (0, 0), (width, height), (48, 64, 56), 18) cv2.line(frame, (70, 90), (1210, 90), (102, 209, 185), 3) cv2.putText(frame, title, (70, 185), cv2.FONT_HERSHEY_SIMPLEX, 1.35, (255, 209, 102), 3, cv2.LINE_AA) put_lines(frame, wrap(subtitle, 58), 74, 260, size=0.82, color=(226, 234, 226)) cv2.putText(frame, BASE_URL, (74, 640), cv2.FONT_HERSHEY_SIMPLEX, 0.78, (102, 209, 185), 2, cv2.LINE_AA) return frame def screenshot_slide() -> np.ndarray: image = cv2.imread(str(HOME_SCREENSHOT), cv2.IMREAD_COLOR) frame = slate("1. Open Web Console", "Browser opens the ISISeg guidewire segmentation workspace.") fitted = fit_image(image, 1120, 500) frame[160:660, 80:1200] = fitted return frame def result_slide(payload: dict, frame_payload: dict, index: int) -> np.ndarray: frame = slate( f"2.{index} Segmentation Result", "The demo uploads the synthetic guidewire video and renders real fusion masks returned by the API.", ) overlay = cv2.imread(str(ROOT / frame_payload["overlay_url"].lstrip("/")), cv2.IMREAD_COLOR) mask = cv2.imread(str(ROOT / frame_payload["mask_url"].lstrip("/")), cv2.IMREAD_GRAYSCALE) mask_color = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) frame[178:598, 75:635] = fit_image(overlay, 560, 420) frame[178:598, 645:1205] = fit_image(mask_color, 560, 420) metrics = frame_payload["metrics"] lines = [ f"job: {payload['job_id']} frame: {frame_payload['frame_index']}", f"coverage: {metrics['coverage'] * 100:.3f}% skeleton: {metrics['skeleton_length']} components: {metrics['components']}", ] put_lines(frame, lines, 75, 650, size=0.62, color=(238, 245, 239)) return frame def write_video(slides: list[np.ndarray]) -> None: writer = cv2.VideoWriter(str(OUTPUT_VIDEO), cv2.VideoWriter_fourcc(*"mp4v"), 24.0, (1280, 720)) for slide in slides: for _ in range(72): writer.write(slide) writer.release() def main() -> None: DEMO_DIR.mkdir(parents=True, exist_ok=True) wait_for_service() ensure_sample() capture_homepage() payload = call_segmentation() slides = [ slate( "ISISeg Usage Demo", "A runnable Web system for guidewire segmentation: open the console, upload a fluoroscopy-like video, choose fusion mode, and inspect overlay/mask outputs.", ), screenshot_slide(), ] for index, frame_payload in enumerate(payload["frames"][:4], start=1): slides.append(result_slide(payload, frame_payload, index)) slides.append( slate( "Ready to Use", f"Demo output video and API artifacts are stored under storage/demos and storage/jobs. Overlay video URL: {payload.get('video_url')}", ) ) write_video(slides) print(OUTPUT_VIDEO) print(RESPONSE_JSON) if __name__ == "__main__": main()