Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Foundation model for image segmentation with zero-shot transfer. Use when you need to segment any object in images using points, boxes, or masks as prompts, or automatically generate all object masks in an image.
.claude/skills/graniet-segment-anything/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 197% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 101% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 178% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 719% | 0% |
This skill is repo-local and stays inactive until explicitly activated.
When the original instructions refer to legacy tool names, use these Kheish mappings:
terminal => bashweb_extract => web_fetch, plus web_search when discovery is neededsearch_files => grep_search and glob_searchbrowser_* tools require a browser-capable surfaced tool or MCP; if none is available, use the closest available surface and say so explicitlyWhen the instructions mention local helper files, resolve them from ${KHEISH_SKILL_DIR}.
Comprehensive guide to using Meta AI's Segment Anything Model for zero-shot image segmentation.
Use SAM when:
Key features:
Use alternatives instead:
bash# From GitHub pip install git+https://github.com/facebookresearch/segment-anything.git # Optional dependencies pip install opencv-python pycocotools matplotlib # Or use HuggingFace transformers pip install transformers
bash# ViT-H (largest, most accurate) - 2.4GB wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth # ViT-L (medium) - 1.2GB wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_l_0b3195.pth # ViT-B (smallest, fastest) - 375MB wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth
pythonimport numpy as np from segment_anything import sam_model_registry, SamPredictor # Load model sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth") sam.to(device="cuda") # Create predictor predictor = SamPredictor(sam) # Set image (computes embeddings once) image = cv2.imread("image.jpg") image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) predictor.set_image(image) # Predict with point prompts input_point = np.array([[500, 375]]) # (x, y) coordinates input_label = np.array([1]) # 1 = foreground, 0 = background masks, scores, logits = predictor.predict( point_coords=input_point, point_labels=input_label, multimask_output=True # Returns 3 mask options ) # Select best mask best_mask = masks[np.argmax(scores)]
pythonimport torch from PIL import Image from transformers import SamModel, SamProcessor # Load model and processor model = SamModel.from_pretrained("facebook/sam-vit-huge") processor = SamProcessor.from_pretrained("facebook/sam-vit-huge") model.to("cuda") # Process image with point prompt image = Image.open("image.jpg") input_points = [[[450, 600]]] # Batch of points inputs = processor(image, input_points=input_points, return_tensors="pt") inputs = {k: v.to("cuda") for k, v in inputs.items()} # Generate masks with torch.no_grad(): outputs = model(**inputs) # Post-process masks to original size masks = processor.image_processor.post_process_masks( outputs.pred_masks.cpu(), inputs["original_sizes"].cpu(), inputs["reshaped_input_sizes"].cpu() )
SAM Architecture:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Image Encoder │────▶│ Prompt Encoder │────▶│ Mask Decoder │
│ (ViT) │ │ (Points/Boxes) │ │ (Transformer) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
Image Embeddings Prompt Embeddings Masks + IoU
(computed once) (per prompt) predictions| Model | Checkpoint | Size | Speed | Accuracy | |-------|------------|------|-------|----------| | ViT-H | vit_h | 2.4 GB | Slowest | Best | | ViT-L | vit_l | 1.2 GB | Medium | Good | | ViT-B | vit_b | 375 MB | Fastest | Good |
| Prompt | Description | Use Case | |--------|-------------|----------| | Point (foreground) | Click on object | Single object selection | | Point (background) | Click outside object | Exclude regions | | Bounding box | Rectangle around object | Larger objects | | Previous mask | Low-res mask input | Iterative refinement |
python# Single foreground point input_point = np.array([[500, 375]]) input_label = np.array([1]) masks, scores, logits = predictor.predict( point_coords=input_point, point_labels=input_label, multimask_output=True ) # Multiple points (foreground + background) input_points = np.array([[500, 375], [600, 400], [450, 300]]) input_labels = np.array([1, 1, 0]) # 2 foreground, 1 background masks, scores, logits = predictor.predict( point_coords=input_points, point_labels=input_labels, multimask_output=False # Single mask when prompts are clear )
python# Bounding box [x1, y1, x2, y2] input_box = np.array([425, 600, 700, 875]) masks, scores, logits = predictor.predict( box=input_box, multimask_output=False )
python# Box + points for precise control masks, scores, logits = predictor.predict( point_coords=np.array([[500, 375]]), point_labels=np.array([1]), box=np.array([400, 300, 700, 600]), multimask_output=False )
python# Initial prediction masks, scores, logits = predictor.predict( point_coords=np.array([[500, 375]]), point_labels=np.array([1]), multimask_output=True ) # Refine with additional point using previous mask masks, scores, logits = predictor.predict( point_coords=np.array([[500, 375], [550, 400]]), point_labels=np.array([1, 0]), # Add background point mask_input=logits[np.argmax(scores)][None, :, :], # Use best mask multimask_output=False )
pythonfrom segment_anything import SamAutomaticMaskGenerator # Create generator mask_generator = SamAutomaticMaskGenerator(sam) # Generate all masks masks = mask_generator.generate(image) # Each mask contains: # - segmentation: binary mask # - bbox: [x, y, w, h] # - area: pixel count # - predicted_iou: quality score # - stability_score: robustness score # - point_coords: generating point
pythonmask_generator = SamAutomaticMaskGenerator( model=sam, points_per_side=32, # Grid density (more = more masks) pred_iou_thresh=0.88, # Quality threshold stability_score_thresh=0.95, # Stability threshold crop_n_layers=1, # Multi-scale crops crop_n_points_downscale_factor=2, min_mask_region_area=100, # Remove tiny masks ) masks = mask_generator.generate(image)
python# Sort by area (largest first) masks = sorted(masks, key=lambda x: x['area'], reverse=True) # Filter by predicted IoU high_quality = [m for m in masks if m['predicted_iou'] > 0.9] # Filter by stability score stable_masks = [m for m in masks if m['stability_score'] > 0.95]
python# Process multiple images efficiently images = [cv2.imread(f"image_{i}.jpg") for i in range(10)] all_masks = [] for image in images: predictor.set_image(image) masks, _, _ = predictor.predict( point_coords=np.array([[500, 375]]), point_labels=np.array([1]), multimask_output=True ) all_masks.append(masks)
python# Process multiple prompts efficiently (one image encoding) predictor.set_image(image) # Batch of point prompts points = [ np.array([[100, 100]]), np.array([[200, 200]]), np.array([[300, 300]]) ] all_masks = [] for point in points: masks, scores, _ = predictor.predict( point_coords=point, point_labels=np.array([1]), multimask_output=True ) all_masks.append(masks[np.argmax(scores)])
This imported skill does not bundle a local ONNX export helper. Use the upstream Segment Anything export utility or your own checked-in export script, and record the exact command alongside the resulting .onnx artifact.
pythonimport onnxruntime # Load ONNX model ort_session = onnxruntime.InferenceSession("sam_onnx.onnx") # Run inference (image embeddings computed separately) masks = ort_session.run( None, { "image_embeddings": image_embeddings, "point_coords": point_coords, "point_labels": point_labels, "mask_input": np.zeros((1, 1, 256, 256), dtype=np.float32), "has_mask_input": np.array([0], dtype=np.float32), "orig_im_size": np.array([h, w], dtype=np.float32) } )
pythonimport cv2 # Load model predictor = SamPredictor(sam) predictor.set_image(image) def on_click(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: # Foreground point masks, scores, _ = predictor.predict( point_coords=np.array([[x, y]]), point_labels=np.array([1]), multimask_output=True ) # Display best mask display_mask(masks[np.argmax(scores)])
pythondef extract_object(image, point): """Extract object at point with transparent background.""" predictor.set_image(image) masks, scores, _ = predictor.predict( point_coords=np.array([point]), point_labels=np.array([1]), multimask_output=True ) best_mask = masks[np.argmax(scores)] # Create RGBA output rgba = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8) rgba[:, :, :3] = image rgba[:, :, 3] = best_mask * 255 return rgba
python# Process medical images (grayscale to RGB) medical_image = cv2.imread("scan.png", cv2.IMREAD_GRAYSCALE) rgb_image = cv2.cvtColor(medical_image, cv2.COLOR_GRAY2RGB) predictor.set_image(rgb_image) # Segment region of interest masks, scores, _ = predictor.predict( box=np.array([x1, y1, x2, y2]), # ROI bounding box multimask_output=True )
python# SamAutomaticMaskGenerator output { "segmentation": np.ndarray, # H×W binary mask "bbox": [x, y, w, h], # Bounding box "area": int, # Pixel count "predicted_iou": float, # 0-1 quality score "stability_score": float, # 0-1 robustness score "crop_box": [x, y, w, h], # Generation crop region "point_coords": [[x, y]], # Input point }
pythonfrom pycocotools import mask as mask_utils # Encode mask to RLE rle = mask_utils.encode(np.asfortranarray(mask.astype(np.uint8))) rle["counts"] = rle["counts"].decode("utf-8") # Decode RLE to mask decoded_mask = mask_utils.decode(rle)
python# Use smaller model for limited VRAM sam = sam_model_registry["vit_b"](checkpoint="sam_vit_b_01ec64.pth") # Process images in batches # Clear CUDA cache between large batches torch.cuda.empty_cache()
python# Use half precision sam = sam.half() # Reduce points for automatic generation mask_generator = SamAutomaticMaskGenerator( model=sam, points_per_side=16, # Default is 32 ) # Use ONNX for deployment # Export with --return-single-mask for faster inference
| Issue | Solution | |-------|----------| | Out of memory | Use ViT-B model, reduce image size | | Slow inference | Use ViT-B, reduce points_per_side | | Poor mask quality | Try different prompts, use box + points | | Edge artifacts | Use stability_score filtering | | Small objects missed | Increase points_per_side |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 11,224 | 17,504 | +56% | 1 | 1 | 0% | 1,993 | 5,910 | +197% | 0 | 0 | — |
case-02 | pass→pass | 17,189 | 9,116 | -47% | 1 | 1 | 0% | 2,759 | 5,552 | +101% | 0 | 0 | — |
case-03 | pass→pass | 10,656 | 7,952 | -25% | 1 | 1 | 0% | 1,898 | 5,282 | +178% | 0 | 0 | — |
case-04 | fail→pass | 15,382 | 4,546 | -70% | 1 | 1 | 0% | 2,661 | 4,884 | +84% | 0 | 0 | — |
case-05 | pass→pass | 2,979 | 3,422 | +15% | 1 | 1 | 0% | 566 | 4,635 | +719% | 0 | 0 | — |
case-06 | pass→pass | 5,043 | 4,039 | -20% | 1 | 1 | 0% | 1,016 | 4,828 | +375% | 0 | 0 | — |
case-07 | pass→pass | 5,581 | 4,139 | -26% | 1 | 1 | 0% | 1,021 | 4,762 | +366% | 0 | 0 | — |
case-08 | pass→pass | 5,219 | 3,422 | -34% | 1 | 1 | 0% | 949 | 4,607 | +385% | 0 | 0 | — |
case-09 | pass→pass | 9,705 | 6,144 | -37% | 1 | 1 | 0% | 1,801 | 5,090 | +183% | 0 | 0 | — |
case-10 | pass→pass | 5,085 | 3,185 | -37% | 1 | 1 | 0% | 964 | 4,564 | +373% | 0 | 0 | — |
case-11 | pass→pass | 3,760 | 3,294 | -12% | 1 | 1 | 0% | 609 | 4,599 | +655% | 0 | 0 | — |
case-12 | pass→pass | 5,761 | 3,189 | -45% | 1 | 1 | 0% | 1,085 | 4,636 | +327% | 0 | 0 | — |
case-13 | pass→pass | 4,169 | 2,554 | -39% | 1 | 1 | 0% | 736 | 4,475 | +508% | 0 | 0 | — |
case-14 | pass→pass | 3,627 | 2,023 | -44% | 1 | 1 | 0% | 643 | 4,274 | +565% | 0 | 0 | — |
case-15 | pass→pass | 10,386 | 6,113 | -41% | 1 | 1 | 0% | 1,786 | 5,101 | +186% | 0 | 0 | — |
case-16 | pass→pass | 4,878 | 3,939 | -19% | 1 | 1 | 0% | 920 | 4,797 | +421% | 0 | 0 | — |
case-17 | pass→pass | 3,437 | 5,273 | +53% | 1 | 1 | 0% | 724 | 4,803 | +563% | 0 | 0 | — |
case-18 | pass→pass | 2,926 | 2,399 | -18% | 1 | 1 | 0% | 423 | 4,436 | +949% | 0 | 0 | — |
case-19 | pass→pass | 12,316 | 10,610 | -14% | 1 | 1 | 0% | 2,456 | 6,114 | +149% | 0 | 0 | — |
case-20 | pass→pass | 3,200 | 1,793 | -44% | 1 | 1 | 0% | 615 | 4,288 | +597% | 0 | 0 | — |
case-21 | pass→pass | 3,960 | 2,011 | -49% | 1 | 1 | 0% | 749 | 4,413 | +489% | 0 | 0 | — |
case-22 | pass→pass | 3,092 | 2,761 | -11% | 1 | 1 | 0% | 603 | 4,380 | +626% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.