from pathlib import Path from config import ( ALL_EXAMPLE_IMAGES, REAL_WORLD_VIDEO_MASK_DIRS, DIGITAL_TWIN_VIDEO_MASK_DIRS, REAL_WORLD_EXAMPLE_VIDEOS, DIGITAL_TWIN_EXAMPLE_VIDEOS ) ALL_EXAMPLE_VIDEOS: set[str] = set(REAL_WORLD_EXAMPLE_VIDEOS.values()) | set(DIGITAL_TWIN_EXAMPLE_VIDEOS.values()) def _build_example_image_lookup() -> dict[str, str]: """ Build a lookup table mapping filenames to their original example image paths. This allows matching example images even when Gradio transforms their paths (e.g., copying to /tmp/gradio/...). :return: Dictionary mapping filename -> original path """ lookup = {} for path in ALL_EXAMPLE_IMAGES: filename = Path(path).name # If there's a collision, prefer the first one (shouldn't happen with unique names) if filename not in lookup: lookup[filename] = path return lookup _EXAMPLE_IMAGE_LOOKUP = _build_example_image_lookup() def get_original_example_path(image_path: str | Path) -> str | None: """ Get the original example image path for a given image path. This handles the case where Gradio has copied the image to a temporary location. Matching is done by filename. :param image_path: Path to check (may be a temp path from Gradio) :return: Original example image path if found, None otherwise """ image_path = Path(image_path) filename = image_path.name str_path = str(image_path) if str_path in ALL_EXAMPLE_IMAGES: return str_path try: resolved = str(image_path.resolve()) for example_path in ALL_EXAMPLE_IMAGES: if str(Path(example_path).resolve()) == resolved: return example_path except (OSError, RuntimeError): pass return _EXAMPLE_IMAGE_LOOKUP.get(filename) def get_mask_path_for_image(image_path: str | Path) -> Path | None: """ Get the corresponding segmentation mask path for an example image. The mask is expected to be in the 'segmented' subdirectory with .png extension. This function handles: 1. Direct paths to example images 2. Gradio-transformed paths (matched by filename) :param image_path: Path to the original image :return: Path to the mask file if it exists, None otherwise """ image_path = Path(image_path) original_path = get_original_example_path(image_path) if original_path: original_path = Path(original_path) parent = original_path.parent segmented_dir = parent / "segmented" mask_path = segmented_dir / f"{original_path.stem}.png" if mask_path.exists(): return mask_path parent = image_path.parent segmented_dir = parent / "segmented" mask_path = segmented_dir / f"{image_path.stem}.png" if mask_path.exists(): return mask_path return None def is_example_image(image_path: str | Path) -> bool: """ Check if the given image path is one of the predefined example images. :param image_path: Path to check :return: True if it's an example image, False otherwise """ result = get_original_example_path(image_path) is not None return result def is_example_video(video_path: str | Path) -> bool: """ Check if the given video path is one of the predefined example videos. This handles the case where Gradio may have copied the video to a temporary location. Matching is done by both full path comparison and filename matching. :param video_path: Path to check (may be a temp path from Gradio) :return: True if it's an example video, False otherwise """ video_path = Path(video_path) str_path = str(video_path) if str_path in ALL_EXAMPLE_VIDEOS: return True try: resolved = str(video_path.resolve()) for example_path in ALL_EXAMPLE_VIDEOS: if str(Path(example_path).resolve()) == resolved: return True except (OSError, RuntimeError): pass filename = video_path.name for example_path in ALL_EXAMPLE_VIDEOS: if Path(example_path).name == filename: return True return False def get_video_mask_dir(video_path: str | Path, video_label: str | None = None) -> Path | None: """ Get the directory containing frame masks for a video. Only returns mask directories for predefined example videos. User-uploaded videos will not match, even if they have similar names. :param video_path: Path to the video file :param video_label: Optional label (Down-Left, Front, Right) if known :return: Path to mask directory if it exists, None otherwise """ video_path = Path(video_path) if not is_example_video(video_path): return None video_name = video_path.stem parent_name = video_path.parent.name if "real-world" in parent_name or "real-world" in video_name: mask_dirs = REAL_WORLD_VIDEO_MASK_DIRS elif "digital-twin" in parent_name or "digital-twin" in video_name: mask_dirs = DIGITAL_TWIN_VIDEO_MASK_DIRS else: return None if video_label and video_label in mask_dirs: mask_dir = mask_dirs[video_label] if mask_dir.exists(): return mask_dir for label, mask_dir in mask_dirs.items(): if label.lower().replace("-", "") in video_name.lower().replace("-", ""): if mask_dir.exists(): return mask_dir return None def get_frame_mask_path(mask_dir: Path, frame_idx: int) -> Path | None: """ Get the mask file path for a specific video frame. The mask files follow the naming pattern: frame_{frame_idx:06d}_{timestamp}.png :param mask_dir: Directory containing the mask files :param frame_idx: Frame index (0-based) :return: Path to the mask file if found, None otherwise """ if not mask_dir.exists(): return None frame_prefix = f"frame_{frame_idx:06d}_" for mask_file in mask_dir.glob(f"{frame_prefix}*.png"): return mask_file return None def build_frame_mask_index(mask_dir: Path) -> dict[int, Path]: """ Build an index mapping frame numbers to their mask file paths. Masks are sorted by their original frame number and re-indexed starting from 0 to match cut video frame indices. :param mask_dir: Directory containing mask files :return: Dictionary mapping frame_idx -> mask_path """ if not mask_dir.exists(): return {} masks_with_original_idx = [] for mask_file in mask_dir.glob("frame_*.png"): parts = mask_file.stem.split("_") if len(parts) >= 2: try: original_frame_idx = int(parts[1]) masks_with_original_idx.append((original_frame_idx, mask_file)) except ValueError: continue masks_with_original_idx.sort(key=lambda x: x[0]) return {i: mask_file for i, (_, mask_file) in enumerate(masks_with_original_idx)}