{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f400486b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Copyright (c) Meta Platforms, Inc. and affiliates."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1ae39ff",
   "metadata": {},
   "source": [
    "# Object masks in images from prompts with SAM 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4a4b25c",
   "metadata": {},
   "source": [
    "Segment Anything Model 2 (SAM 2) predicts object masks given prompts that indicate the desired object. The model first converts the image into an image embedding that allows high quality masks to be efficiently produced from a prompt. \n",
    "\n",
    "The `SAM2ImagePredictor` class provides an easy interface to the model for prompting the model. It allows the user to first set an image using the `set_image` method, which calculates the necessary image embeddings. Then, prompts can be provided via the `predict` method to efficiently predict masks from those prompts. The model can take as input both point and box prompts, as well as masks from the previous iteration of prediction."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "644532a8",
   "metadata": {},
   "source": [
    "## Environment Set-up"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07fabfee",
   "metadata": {},
   "source": [
    "If running locally using jupyter, first install `segment-anything-2` in your environment using the [installation instructions](https://github.com/facebookresearch/segment-anything-2#installation) in the repository."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0be845da",
   "metadata": {},
   "source": [
    "## Set-up"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33681dd1",
   "metadata": {},
   "source": [
    "Necessary imports and helper functions for displaying points, boxes, and masks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "69b28288",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from PIL import Image\n",
    "from sam2.build_sam import build_sam2\n",
    "from sam2.sam2_image_predictor import SAM2ImagePredictor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "33a15e2f-c7e1-4e5d-862f-fcb751a60b89",
   "metadata": {},
   "outputs": [],
   "source": [
    "# use bfloat16 for the entire notebook\n",
    "torch.autocast(device_type=\"cuda\", dtype=torch.bfloat16).__enter__()\n",
    "\n",
    "if torch.cuda.get_device_properties(0).major >= 8:\n",
    "    # turn on tfloat32 for Ampere GPUs (https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices)\n",
    "    torch.backends.cuda.matmul.allow_tf32 = True\n",
    "    torch.backends.cudnn.allow_tf32 = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "29bc90d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def show_mask(mask, ax, random_color=False, borders = True):\n",
    "    if random_color:\n",
    "        color = np.concatenate([np.random.random(3), np.array([0.6])], axis=0)\n",
    "    else:\n",
    "        color = np.array([30/255, 144/255, 255/255, 0.6])\n",
    "    h, w = mask.shape[-2:]\n",
    "    mask = mask.astype(np.uint8)\n",
    "    mask_image =  mask.reshape(h, w, 1) * color.reshape(1, 1, -1)\n",
    "    if borders:\n",
    "        import cv2\n",
    "        contours, _ = cv2.findContours(mask,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) \n",
    "        # Try to smooth contours\n",
    "        contours = [cv2.approxPolyDP(contour, epsilon=0.01, closed=True) for contour in contours]\n",
    "        mask_image = cv2.drawContours(mask_image, contours, -1, (1, 1, 1, 0.5), thickness=2) \n",
    "    ax.imshow(mask_image)\n",
    "\n",
    "def show_points(coords, labels, ax, marker_size=375):\n",
    "    pos_points = coords[labels==1]\n",
    "    neg_points = coords[labels==0]\n",
    "    ax.scatter(pos_points[:, 0], pos_points[:, 1], color='green', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)\n",
    "    ax.scatter(neg_points[:, 0], neg_points[:, 1], color='red', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)   \n",
    "\n",
    "def show_box(box, ax):\n",
    "    x0, y0 = box[0], box[1]\n",
    "    w, h = box[2] - box[0], box[3] - box[1]\n",
    "    ax.add_patch(plt.Rectangle((x0, y0), w, h, edgecolor='green', facecolor=(0, 0, 0, 0), lw=2))    \n",
    "\n",
    "def show_masks(image, masks, scores, point_coords=None, box_coords=None, input_labels=None, borders=True):\n",
    "    for i, (mask, score) in enumerate(zip(masks, scores)):\n",
    "        plt.figure(figsize=(10, 10))\n",
    "        plt.imshow(image)\n",
    "        show_mask(mask, plt.gca(), borders=borders)\n",
    "        if point_coords is not None:\n",
    "            assert input_labels is not None\n",
    "            show_points(point_coords, input_labels, plt.gca())\n",
    "        if box_coords is not None:\n",
    "            # boxes\n",
    "            show_box(box_coords, plt.gca())\n",
    "        if len(scores) > 1:\n",
    "            plt.title(f\"Mask {i+1}, Score: {score:.3f}\", fontsize=18)\n",
    "        plt.axis('off')\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23842fb2",
   "metadata": {},
   "source": [
    "## Example image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3c2e4f6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "image = Image.open('images/truck.jpg')\n",
    "image = np.array(image.convert(\"RGB\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e30125fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 637248 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10, 10))\n",
    "plt.imshow(image)\n",
    "plt.axis('on')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98b228b8",
   "metadata": {},
   "source": [
    "## Selecting objects with SAM 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bb1927b",
   "metadata": {},
   "source": [
    "First, load the SAM 2 model and predictor. Change the path below to point to the SAM 2 checkpoint. Running on CUDA and using the default model are recommended for best results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7e28150b",
   "metadata": {},
   "outputs": [],
   "source": [
    "sam2_checkpoint = \"../checkpoints/sam2_hiera_large.pt\"\n",
    "model_cfg = \"sam2_hiera_l.yaml\"\n",
    "\n",
    "sam2_model = build_sam2(model_cfg, sam2_checkpoint, device=\"cuda\")\n",
    "\n",
    "predictor = SAM2ImagePredictor(sam2_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c925e829",
   "metadata": {},
   "source": [
    "Process the image to produce an image embedding by calling `SAM2ImagePredictor.set_image`. `SAM2ImagePredictor` remembers this embedding and will use it for subsequent mask prediction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d95d48dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "predictor.set_image(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8fc7a46",
   "metadata": {},
   "source": [
    "To select the truck, choose a point on it. Points are input to the model in (x,y) format and come with labels 1 (foreground point) or 0 (background point). Multiple points can be input; here we use only one. The chosen point will be shown as a star on the image."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "5c69570c",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_point = np.array([[500, 375]])\n",
    "input_label = np.array([1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a91ba973",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 638600 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10, 10))\n",
    "plt.imshow(image)\n",
    "show_points(input_point, input_label, plt.gca())\n",
    "plt.axis('on')\n",
    "plt.show()  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "1073a838",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 256, 64, 64]) torch.Size([256, 64, 64])\n"
     ]
    }
   ],
   "source": [
    "print(predictor._features[\"image_embed\"].shape, predictor._features[\"image_embed\"][-1].shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c765e952",
   "metadata": {},
   "source": [
    "Predict with `SAM2ImagePredictor.predict`. The model returns masks, quality predictions for those masks, and low resolution mask logits that can be passed to the next iteration of prediction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "5373fd68",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, logits = predictor.predict(\n",
    "    point_coords=input_point,\n",
    "    point_labels=input_label,\n",
    "    multimask_output=True,\n",
    ")\n",
    "sorted_ind = np.argsort(scores)[::-1]\n",
    "masks = masks[sorted_ind]\n",
    "scores = scores[sorted_ind]\n",
    "logits = logits[sorted_ind]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7f0e938",
   "metadata": {},
   "source": [
    "With `multimask_output=True` (the default setting), SAM 2 outputs 3 masks, where `scores` gives the model's own estimation of the quality of these masks. This setting is intended for ambiguous input prompts, and helps the model disambiguate different objects consistent with the prompt. When `False`, it will return a single mask. For ambiguous prompts such as a single point, it is recommended to use `multimask_output=True` even if only a single mask is desired; the best single mask can be chosen by picking the one with the highest score returned in `scores`. This will often result in a better mask."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "47821187",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3, 1200, 1800)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "masks.shape  # (number_of_masks) x H x W"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "e9c227a6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 633520 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 633336 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 628276 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_masks(image, masks, scores, point_coords=input_point, input_labels=input_label, borders=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fa31f7c",
   "metadata": {},
   "source": [
    "## Specifying a specific object with additional points"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88d6d29a",
   "metadata": {},
   "source": [
    "The single input point is ambiguous, and the model has returned multiple objects consistent with it. To obtain a single object, multiple points can be provided. If available, a mask from a previous iteration can also be supplied to the model to aid in prediction. When specifying a single object with multiple prompts, a single mask can be requested by setting `multimask_output=False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f6923b94",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_point = np.array([[500, 375], [1125, 625]])\n",
    "input_label = np.array([1, 1])\n",
    "\n",
    "mask_input = logits[np.argmax(scores), :, :]  # Choose the model's best mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d98f96a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, _ = predictor.predict(\n",
    "    point_coords=input_point,\n",
    "    point_labels=input_label,\n",
    "    mask_input=mask_input[None, :, :],\n",
    "    multimask_output=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0ce8b82f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1200, 1800)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "masks.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "e06d5c8d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 620920 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_masks(image, masks, scores, point_coords=input_point, input_labels=input_label)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c93e2087",
   "metadata": {},
   "source": [
    "To exclude the car and specify just the window, a background point (with label 0, here shown in red) can be supplied."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "9a196f68",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_point = np.array([[500, 375], [1125, 625]])\n",
    "input_label = np.array([1, 0])\n",
    "\n",
    "mask_input = logits[np.argmax(scores), :, :]  # Choose the model's best mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "81a52282",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, _ = predictor.predict(\n",
    "    point_coords=input_point,\n",
    "    point_labels=input_label,\n",
    "    mask_input=mask_input[None, :, :],\n",
    "    multimask_output=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "bfca709f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 626844 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_masks(image, masks, scores, point_coords=input_point, input_labels=input_label)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41e2d5a9",
   "metadata": {},
   "source": [
    "## Specifying a specific object with a box"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d61ca7ac",
   "metadata": {},
   "source": [
    "The model can also take a box as input, provided in xyxy format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "8ea92a7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_box = np.array([425, 600, 700, 875])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "b35a8814",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, _ = predictor.predict(\n",
    "    point_coords=None,\n",
    "    point_labels=None,\n",
    "    box=input_box[None, :],\n",
    "    multimask_output=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "3ffb4906",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 624240 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_masks(image, masks, scores, box_coords=input_box)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1ed9f0a",
   "metadata": {},
   "source": [
    "## Combining points and boxes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8455d1c5",
   "metadata": {},
   "source": [
    "Points and boxes may be combined, just by including both types of prompts to the predictor. Here this can be used to select just the trucks's tire, instead of the entire wheel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "90e2e547",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_box = np.array([425, 600, 700, 875])\n",
    "input_point = np.array([[575, 750]])\n",
    "input_label = np.array([0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "6956d8c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, logits = predictor.predict(\n",
    "    point_coords=input_point,\n",
    "    point_labels=input_label,\n",
    "    box=input_box,\n",
    "    multimask_output=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "eb519a31",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 626832 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_masks(image, masks, scores, box_coords=input_box, point_coords=input_point, input_labels=input_label)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45ddbca3",
   "metadata": {},
   "source": [
    "## Batched prompt inputs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df6f18a0",
   "metadata": {},
   "source": [
    "`SAM2ImagePredictor` can take multiple input prompts for the same image, using `predict` method. For example, imagine we have several box outputs from an object detector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "0a06681b",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_boxes = np.array([\n",
    "    [75, 275, 1725, 850],\n",
    "    [425, 600, 700, 875],\n",
    "    [1375, 550, 1650, 800],\n",
    "    [1240, 675, 1400, 750],\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "117521a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks, scores, _ = predictor.predict(\n",
    "    point_coords=None,\n",
    "    point_labels=None,\n",
    "    box=input_boxes,\n",
    "    multimask_output=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "6a8f5d49",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(4, 1, 1200, 1800)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "masks.shape  # (batch_size) x (num_predicted_masks_per_input) x H x W"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "c00c3681",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 592840 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10, 10))\n",
    "plt.imshow(image)\n",
    "for mask in masks:\n",
    "    show_mask(mask.squeeze(0), plt.gca(), random_color=True)\n",
    "for box in input_boxes:\n",
    "    show_box(box, plt.gca())\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9a27b5d",
   "metadata": {},
   "source": [
    "## End-to-end batched inference\n",
    "If all prompts are available in advance, it is possible to run SAM 2 directly in an end-to-end fashion. This also allows batching over images."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "d485f75b",
   "metadata": {},
   "outputs": [],
   "source": [
    "image1 = image  # truck.jpg from above\n",
    "image1_boxes = np.array([\n",
    "    [75, 275, 1725, 850],\n",
    "    [425, 600, 700, 875],\n",
    "    [1375, 550, 1650, 800],\n",
    "    [1240, 675, 1400, 750],\n",
    "])\n",
    "\n",
    "image2 = Image.open('images/groceries.jpg')\n",
    "image2 = np.array(image2.convert(\"RGB\"))\n",
    "image2_boxes = np.array([\n",
    "    [450, 170, 520, 350],\n",
    "    [350, 190, 450, 350],\n",
    "    [500, 170, 580, 350],\n",
    "    [580, 170, 640, 350],\n",
    "])\n",
    "\n",
    "img_batch = [image1, image2]\n",
    "boxes_batch = [image1_boxes, image2_boxes]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "47932c99",
   "metadata": {},
   "outputs": [],
   "source": [
    "predictor.set_image_batch(img_batch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "97af3c54",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks_batch, scores_batch, _ = predictor.predict_batch(\n",
    "    None,\n",
    "    None, \n",
    "    box_batch=boxes_batch, \n",
    "    multimask_output=False\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "226df881",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 609416 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 801180 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for image, boxes, masks in zip(img_batch, boxes_batch, masks_batch):\n",
    "    plt.figure(figsize=(10, 10))\n",
    "    plt.imshow(image)   \n",
    "    for mask in masks:\n",
    "        show_mask(mask.squeeze(0), plt.gca(), random_color=True)\n",
    "    for box in boxes:\n",
    "        show_box(box, plt.gca())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46f30085",
   "metadata": {},
   "source": [
    "Similarly, we can have a batch of point prompts defined over a batch of images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "1ab929fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "image1 = image  # truck.jpg from above\n",
    "image1_pts = np.array([\n",
    "    [[500, 375]],\n",
    "    [[650, 750]]\n",
    "    ]) # Bx1x2 where B corresponds to number of objects \n",
    "image1_labels = np.array([[1], [1]])\n",
    "\n",
    "image2_pts = np.array([\n",
    "    [[400, 300]],\n",
    "    [[630, 300]],\n",
    "])\n",
    "image2_labels = np.array([[1], [1]])\n",
    "\n",
    "pts_batch = [image1_pts, image2_pts]\n",
    "labels_batch = [image1_labels, image2_labels]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "848f8287",
   "metadata": {},
   "outputs": [],
   "source": [
    "masks_batch, scores_batch, _ = predictor.predict_batch(pts_batch, labels_batch, box_batch=None, multimask_output=True)\n",
    "\n",
    "# Select the best single mask per object\n",
    "best_masks = []\n",
    "for masks, scores in zip(masks_batch,scores_batch):\n",
    "    best_masks.append(masks[range(len(masks)), np.argmax(scores, axis=-1)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "99b15c6c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 640572 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 816732 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for image, points, labels, masks in zip(img_batch, pts_batch, labels_batch, best_masks):\n",
    "    plt.figure(figsize=(10, 10))\n",
    "    plt.imshow(image)   \n",
    "    for mask in masks:\n",
    "        show_mask(mask, plt.gca(), random_color=True)\n",
    "    show_points(points, labels, plt.gca())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
