Image Segmentation
Transformers
Safetensors
sam2
instance-segmentation
panoptic-segmentation
semantic-segmentation
zero-shot
open-vocabulary
beit3
fiftyone
Instructions to use Voxel51/openworld-sam with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Voxel51/openworld-sam with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-segmentation", model="Voxel51/openworld-sam")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Voxel51/openworld-sam", dtype="auto") - sam2
How to use Voxel51/openworld-sam with sam2:
# Use SAM2 with images import torch from sam2.sam2_image_predictor import SAM2ImagePredictor predictor = SAM2ImagePredictor.from_pretrained(Voxel51/openworld-sam) with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16): predictor.set_image(<your_image>) masks, _, _ = predictor.predict(<input_prompts>)# Use SAM2 with videos import torch from sam2.sam2_video_predictor import SAM2VideoPredictor predictor = SAM2VideoPredictor.from_pretrained(Voxel51/openworld-sam) with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16): state = predictor.init_state(<your_video>) # add new prompts and instantly get the output on the same frame frame_idx, object_ids, masks = predictor.add_new_points(state, <your_prompts>): # propagate the prompts to get masklets throughout the video for frame_idx, object_ids, masks in predictor.propagate_in_video(state): ... - Notebooks
- Google Colab
- Kaggle
fix: filter low res masks before upsampling + rename nms and iou thresh vars
#1
by neerajaabhyankar - opened
- configuration_openworld_sam.py +4 -4
- modeling_openworld_sam.py +20 -12
configuration_openworld_sam.py
CHANGED
|
@@ -49,8 +49,8 @@ class OpenWorldSAMConfig(PretrainedConfig):
|
|
| 49 |
pixel_std=(58.395, 57.120, 57.375),
|
| 50 |
# Inference thresholds (ADE20K instance config defaults)
|
| 51 |
nms_on=True,
|
| 52 |
-
|
| 53 |
-
|
| 54 |
top_k_on=False,
|
| 55 |
detections_per_image=100,
|
| 56 |
# Class vocabulary
|
|
@@ -70,8 +70,8 @@ class OpenWorldSAMConfig(PretrainedConfig):
|
|
| 70 |
self.pixel_mean = list(pixel_mean)
|
| 71 |
self.pixel_std = list(pixel_std)
|
| 72 |
self.nms_on = nms_on
|
| 73 |
-
self.
|
| 74 |
-
self.
|
| 75 |
self.top_k_on = top_k_on
|
| 76 |
self.detections_per_image = detections_per_image
|
| 77 |
self.stuff_classes = stuff_classes if stuff_classes is not None else ADE20K_150_CLASSES
|
|
|
|
| 49 |
pixel_std=(58.395, 57.120, 57.375),
|
| 50 |
# Inference thresholds (ADE20K instance config defaults)
|
| 51 |
nms_on=True,
|
| 52 |
+
nms_thresh=0.5,
|
| 53 |
+
iou_thresh=0.7,
|
| 54 |
top_k_on=False,
|
| 55 |
detections_per_image=100,
|
| 56 |
# Class vocabulary
|
|
|
|
| 70 |
self.pixel_mean = list(pixel_mean)
|
| 71 |
self.pixel_std = list(pixel_std)
|
| 72 |
self.nms_on = nms_on
|
| 73 |
+
self.nms_thresh = nms_thresh
|
| 74 |
+
self.iou_thresh = iou_thresh
|
| 75 |
self.top_k_on = top_k_on
|
| 76 |
self.detections_per_image = detections_per_image
|
| 77 |
self.stuff_classes = stuff_classes if stuff_classes is not None else ADE20K_150_CLASSES
|
modeling_openworld_sam.py
CHANGED
|
@@ -345,10 +345,14 @@ class OpenWorldSAMModel(PreTrainedModel):
|
|
| 345 |
dtype=torch.long, device=self.device,
|
| 346 |
)
|
| 347 |
|
| 348 |
-
#
|
| 349 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
|
| 351 |
-
instances = self._instance_inference(pred_masks, pred_logits, class_labels)
|
| 352 |
processed_results.append({"instances": instances})
|
| 353 |
|
| 354 |
return processed_results
|
|
@@ -358,7 +362,7 @@ class OpenWorldSAMModel(PreTrainedModel):
|
|
| 358 |
masks.float().unsqueeze(0), orig_hw, mode="bilinear", align_corners=False
|
| 359 |
).squeeze(0)
|
| 360 |
|
| 361 |
-
def _instance_inference(self, pred_masks, iou_scores, class_labels):
|
| 362 |
"""Returns dict with keys: masks (bool), scores (float), class_ids (long)."""
|
| 363 |
pred_masks = pred_masks.squeeze(1) if pred_masks.ndim == 4 else pred_masks
|
| 364 |
|
|
@@ -369,28 +373,32 @@ class OpenWorldSAMModel(PreTrainedModel):
|
|
| 369 |
pred_masks, iou_scores, class_labels = pred_masks[idx], iou_scores[idx], class_labels[idx]
|
| 370 |
|
| 371 |
# IoU threshold filter
|
| 372 |
-
keep = iou_scores >= self.config.
|
| 373 |
pred_masks, iou_scores, class_labels = pred_masks[keep], iou_scores[keep], class_labels[keep]
|
| 374 |
|
| 375 |
if pred_masks.shape[0] == 0:
|
| 376 |
empty = torch.empty(0, device=self.device)
|
| 377 |
return {
|
| 378 |
-
"masks": torch.empty((0,
|
| 379 |
-
dtype=torch.bool, device=self.device),
|
| 380 |
"scores": empty,
|
| 381 |
"class_ids": empty.long(),
|
| 382 |
}
|
| 383 |
|
| 384 |
-
binary_masks = pred_masks > 0
|
| 385 |
|
| 386 |
-
# NMS
|
|
|
|
|
|
|
| 387 |
if self.config.nms_on:
|
| 388 |
-
boxes = _masks_to_boxes(
|
| 389 |
-
nms_keep = torchvision.ops.nms(boxes, iou_scores, self.config.
|
| 390 |
-
|
| 391 |
iou_scores = iou_scores[nms_keep]
|
| 392 |
class_labels = class_labels[nms_keep]
|
| 393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
return {
|
| 395 |
"masks": binary_masks,
|
| 396 |
"scores": iou_scores,
|
|
|
|
| 345 |
dtype=torch.long, device=self.device,
|
| 346 |
)
|
| 347 |
|
| 348 |
+
# Filter on low-res masks first; only the survivors get upsampled
|
| 349 |
+
# to the original image size (upsampling the full query set before
|
| 350 |
+
# filtering allocates one [num_queries, H, W] float32 tensor that
|
| 351 |
+
# can reach double-digit GiB for large vocabularies).
|
| 352 |
+
instances = self._instance_inference(
|
| 353 |
+
pred_masks, pred_logits, class_labels, original_size_list[img_idx]
|
| 354 |
+
)
|
| 355 |
|
|
|
|
| 356 |
processed_results.append({"instances": instances})
|
| 357 |
|
| 358 |
return processed_results
|
|
|
|
| 362 |
masks.float().unsqueeze(0), orig_hw, mode="bilinear", align_corners=False
|
| 363 |
).squeeze(0)
|
| 364 |
|
| 365 |
+
def _instance_inference(self, pred_masks, iou_scores, class_labels, orig_hw):
|
| 366 |
"""Returns dict with keys: masks (bool), scores (float), class_ids (long)."""
|
| 367 |
pred_masks = pred_masks.squeeze(1) if pred_masks.ndim == 4 else pred_masks
|
| 368 |
|
|
|
|
| 373 |
pred_masks, iou_scores, class_labels = pred_masks[idx], iou_scores[idx], class_labels[idx]
|
| 374 |
|
| 375 |
# IoU threshold filter
|
| 376 |
+
keep = iou_scores >= self.config.iou_thresh
|
| 377 |
pred_masks, iou_scores, class_labels = pred_masks[keep], iou_scores[keep], class_labels[keep]
|
| 378 |
|
| 379 |
if pred_masks.shape[0] == 0:
|
| 380 |
empty = torch.empty(0, device=self.device)
|
| 381 |
return {
|
| 382 |
+
"masks": torch.empty((0, *orig_hw), dtype=torch.bool, device=self.device),
|
|
|
|
| 383 |
"scores": empty,
|
| 384 |
"class_ids": empty.long(),
|
| 385 |
}
|
| 386 |
|
|
|
|
| 387 |
|
| 388 |
+
# NMS on low-res masks — box IoU is scale-invariant, so this doesn't
|
| 389 |
+
# need the full-res masks either.
|
| 390 |
+
low_res_binary_masks = pred_masks > 0
|
| 391 |
if self.config.nms_on:
|
| 392 |
+
boxes = _masks_to_boxes(low_res_binary_masks)
|
| 393 |
+
nms_keep = torchvision.ops.nms(boxes, iou_scores, self.config.nms_thresh)
|
| 394 |
+
pred_masks = pred_masks[nms_keep]
|
| 395 |
iou_scores = iou_scores[nms_keep]
|
| 396 |
class_labels = class_labels[nms_keep]
|
| 397 |
|
| 398 |
+
# Upsample only the surviving masks to the original image size
|
| 399 |
+
pred_masks = self._postprocess_masks(pred_masks, orig_hw)
|
| 400 |
+
binary_masks = pred_masks > 0
|
| 401 |
+
|
| 402 |
return {
|
| 403 |
"masks": binary_masks,
|
| 404 |
"scores": iou_scores,
|