Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import torch | |
| import numpy as np | |
| from PIL import Image | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'sam2')) | |
| from sam2.build_sam import build_sam2 | |
| from sam2.sam2_image_predictor import SAM2ImagePredictor | |
| MODEL_CFG = "configs/sam2.1/sam2.1_hiera_s.yaml" | |
| CHECKPOINT = os.path.join(os.path.dirname(__file__), "models", "sam2.1_hiera_small.pt") | |
| DEVICE = "cuda" if torch.cuda.is_available() else "cpu" | |
| SAM2_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sam2') | |
| print(f"[SAM2] Using device: {DEVICE}") | |
| def load_sam2_model(): | |
| os.chdir(SAM2_DIR) | |
| model = build_sam2(MODEL_CFG, CHECKPOINT, device=DEVICE) | |
| predictor = SAM2ImagePredictor(model) | |
| print("[SAM2] Model loaded successfully") | |
| return predictor | |
| def get_mask_for_box(predictor, image_rgb: np.ndarray, box: list) -> np.ndarray: | |
| """ | |
| Get precise pixel mask for a single bounding box using SAM2. | |
| box = [x1, y1, x2, y2] in absolute pixels. | |
| Returns boolean mask same size as image. | |
| """ | |
| predictor.set_image(image_rgb) | |
| box_array = np.array(box, dtype=np.float32) | |
| with torch.inference_mode(): | |
| with torch.autocast(device_type=DEVICE, dtype=torch.float16): | |
| masks, scores, _ = predictor.predict( | |
| point_coords = None, | |
| point_labels = None, | |
| box = box_array[None, :], # SAM2 expects (1, 4) | |
| multimask_output = True, | |
| ) | |
| # pick highest confidence mask | |
| best_idx = scores.argmax() | |
| best_mask = masks[best_idx].astype(bool) | |
| return best_mask | |
| def mask_to_transparent_png(image_rgb: np.ndarray, mask: np.ndarray) -> Image.Image: | |
| """ | |
| Apply mask to image — keep masked pixels, make everything else transparent. | |
| Returns RGBA PIL image. | |
| """ | |
| rgba = np.zeros((*image_rgb.shape[:2], 4), dtype=np.uint8) | |
| rgba[..., :3] = image_rgb | |
| rgba[..., 3] = (mask * 255).astype(np.uint8) # alpha = 255 where object is | |
| # crop to bounding box of mask to minimise image size | |
| rows = np.where(mask.any(axis=1))[0] | |
| cols = np.where(mask.any(axis=0))[0] | |
| if len(rows) == 0 or len(cols) == 0: | |
| return Image.fromarray(rgba, 'RGBA') | |
| y1, y2 = int(rows.min()), int(rows.max()) | |
| x1, x2 = int(cols.min()), int(cols.max()) | |
| cropped = rgba[y1:y2+1, x1:x2+1] | |
| return Image.fromarray(cropped, 'RGBA') | |