Spaces:
Sleeping
Sleeping
| """Demo-side image helpers (star marker + mask overlay). | |
| Slim version: keeps only the functions imported by the GLaMM / Sa2VA demos. | |
| The full hm_utils used at training time (S3/EXR loaders, ISP augmentation, | |
| multi-channel readers, etc.) is not shipped in the release. | |
| """ | |
| from typing import Optional, Tuple | |
| import cv2 | |
| import numpy as np | |
| import torch | |
| COLOR_MAP = { | |
| "red": (1.0, 0.0, 0.0), | |
| "green": (0.0, 1.0, 0.0), | |
| "blue": (0.0, 0.0, 1.0), | |
| "cyan": (0.0, 1.0, 1.0), | |
| "magenta": (1.0, 0.0, 1.0), | |
| "yellow": (1.0, 1.0, 0.0), | |
| "orange": (1.0, 0.5, 0.0), | |
| "purple": (0.5, 0.0, 1.0), | |
| "pink": (1.0, 0.5, 0.8), | |
| "turquoise": (0.0, 0.8, 0.8), | |
| } | |
| def ensure_three_channel(image: np.ndarray) -> np.ndarray: | |
| if image.ndim == 2: | |
| return np.stack([image, image, image], axis=-1) | |
| if image.ndim == 3 and image.shape[-1] == 1: | |
| return np.repeat(image, 3, axis=-1) | |
| return image | |
| def select_contrasting_color(avg_color) -> Tuple[str, Tuple[float, float, float]]: | |
| """Pick the COLOR_MAP entry farthest (L2 in RGB) from ``avg_color``.""" | |
| if isinstance(avg_color, torch.Tensor): | |
| avg_color = avg_color.detach().cpu().numpy() | |
| avg_color = np.asarray(avg_color, dtype=np.float32).reshape(-1) | |
| if avg_color.size < 3: | |
| avg_color = np.pad(avg_color, (0, 3 - avg_color.size), constant_values=0) | |
| best_contrast = -1.0 | |
| best_name = "red" | |
| best_rgb = COLOR_MAP[best_name] | |
| for name, rgb in COLOR_MAP.items(): | |
| contrast = sum((rgb[i] - float(avg_color[i])) ** 2 for i in range(3)) | |
| if contrast > best_contrast: | |
| best_contrast = contrast | |
| best_name = name | |
| best_rgb = rgb | |
| return best_name, best_rgb | |
| def create_mask_overlay( | |
| original: np.ndarray, | |
| mask_tensor: Optional[torch.Tensor], | |
| alpha: float = 0.45, | |
| ) -> Tuple[np.ndarray, str]: | |
| """Blend a binary mask onto a uint8 RGB image with an auto-contrast colour.""" | |
| base_img = ensure_three_channel(original) | |
| if mask_tensor is None: | |
| return base_img.copy(), "red" | |
| mask_np = mask_tensor.detach().cpu().numpy() | |
| if mask_np.ndim == 3: | |
| mask_np = mask_np[0] | |
| mask_np = mask_np.astype(bool) | |
| normalized = base_img.astype(np.float32) / 255.0 | |
| if mask_np.any(): | |
| avg_color = normalized[mask_np].mean(axis=0) | |
| else: | |
| avg_color = normalized.mean(axis=(0, 1)) | |
| color_name, color_rgb = select_contrasting_color(avg_color) | |
| color_arr = np.asarray(color_rgb, dtype=np.float32) | |
| blended = normalized.copy() | |
| blended[mask_np] = blended[mask_np] * (1 - alpha) + color_arr * alpha | |
| overlay_img = np.clip(blended * 255.0, 0, 255).astype(np.uint8) | |
| return overlay_img, color_name | |
| def augment_data(image, mat_label, size, flip=True, test=False, crop=True, | |
| resize=False, point_hw=None): | |
| """Crop/flip augmentation on CHW float tensors. | |
| image: [C, H, W] float in [0, 1] | |
| mat_label: [N, H, W] float mask(s) | |
| size: int — output spatial size (square) | |
| """ | |
| import random as _random | |
| import torchvision.transforms.functional as TF | |
| from torchvision.transforms.functional import InterpolationMode | |
| C, H, W = image.shape | |
| out_h, out_w = (size, size) if isinstance(size, int) else (size[0], size[1]) | |
| if flip and not test and _random.random() < 0.5: | |
| image = TF.hflip(image) | |
| mat_label = TF.hflip(mat_label) | |
| if crop: | |
| if test or point_hw is None: | |
| top = max(0, (H - out_h) // 2) | |
| left = max(0, (W - out_w) // 2) | |
| else: | |
| ph, pw = point_hw | |
| top = max(0, min(H - out_h, ph - out_h // 2)) | |
| left = max(0, min(W - out_w, pw - out_w // 2)) | |
| image = TF.crop(image, top, left, min(out_h, H), min(out_w, W)) | |
| mat_label = TF.crop(mat_label, top, left, min(out_h, H), min(out_w, W)) | |
| cur_h, cur_w = image.shape[-2], image.shape[-1] | |
| if cur_h != out_h or cur_w != out_w: | |
| image = TF.resize(image, [out_h, out_w], antialias=True) | |
| mat_label = TF.resize(mat_label, [out_h, out_w], interpolation=InterpolationMode.NEAREST_EXACT) | |
| return image, mat_label | |
| def add_star_marker(image, h, w, size=None, color=None): | |
| """Draw a filled 5-point star at (h, w) on a CHW float tensor in [0, 1]. | |
| Args: | |
| image: torch.Tensor of shape [C, H, W]. | |
| h, w: integer pixel centre (h = row, w = column). | |
| size: bounding-box size in pixels. Default 10. | |
| color: one of: None (auto-pick contrasting), a key in COLOR_MAP, | |
| or an (R, G, B) tuple / tensor in [0, 1]. | |
| Returns: | |
| (marked_image, color_name). | |
| """ | |
| marked_image = image.clone() | |
| C, H, W = image.shape | |
| if size is None: | |
| size = 10 | |
| outer_radius = size // 2 | |
| inner_radius = outer_radius * 0.4 | |
| best_color_name = "red" | |
| color_rgb = COLOR_MAP[best_color_name] | |
| if color is None: | |
| sample_size = size | |
| y0, y1 = max(0, h - sample_size), min(H, h + sample_size) | |
| x0, x1 = max(0, w - sample_size), min(W, w + sample_size) | |
| nearby = image[:, y0:y1, x0:x1] | |
| if nearby.numel() > 0: | |
| avg_color = nearby.mean(dim=(1, 2)) | |
| best_color_name, color_rgb = select_contrasting_color(avg_color) | |
| elif isinstance(color, str): | |
| if color in COLOR_MAP: | |
| best_color_name = color | |
| color_rgb = COLOR_MAP[color] | |
| else: | |
| best_color_name, color_rgb = select_contrasting_color(color) | |
| points = [] | |
| for i in range(10): | |
| angle = i * np.pi / 5 - np.pi / 2 | |
| r = outer_radius if i % 2 == 0 else inner_radius | |
| py = max(0, min(H - 1, h + int(r * np.sin(angle)))) | |
| px = max(0, min(W - 1, w + int(r * np.cos(angle)))) | |
| points.append((py, px)) | |
| points_np = np.array([(p[1], p[0]) for p in points], dtype=np.int32) | |
| mask_np = np.zeros((H, W), dtype=np.uint8) | |
| cv2.fillPoly(mask_np, [points_np], 1) | |
| mask = torch.from_numpy(mask_np).bool().to(marked_image.device) | |
| color_tensor = torch.tensor( | |
| color_rgb, dtype=marked_image.dtype, device=marked_image.device | |
| ).view(3, 1) | |
| marked_image[:, mask] = color_tensor | |
| return marked_image, best_color_name | |