fix: filter low res masks before upsampling + rename nms and iou thresh vars

#1
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
- nms_threshold=0.5,
53
- iou_threshold=0.7,
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.nms_threshold = nms_threshold
74
- self.iou_threshold = iou_threshold
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
- # Upsample masks to original image size
349
- pred_masks = self._postprocess_masks(pred_masks, original_size_list[img_idx])
 
 
 
 
 
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.iou_threshold
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, pred_masks.shape[-2], pred_masks.shape[-1]),
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(binary_masks)
389
- nms_keep = torchvision.ops.nms(boxes, iou_scores, self.config.nms_threshold)
390
- binary_masks = binary_masks[nms_keep]
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,