Skip to content

Fix mask visualization to exclude holes from polygon drawing#5509

Open
Mr-Neutr0n wants to merge 1 commit into
facebookresearch:mainfrom
Mr-Neutr0n:fix-mask-holes-issue-1771
Open

Fix mask visualization to exclude holes from polygon drawing#5509
Mr-Neutr0n wants to merge 1 commit into
facebookresearch:mainfrom
Mr-Neutr0n:fix-mask-holes-issue-1771

Conversation

@Mr-Neutr0n
Copy link
Copy Markdown

@Mr-Neutr0n Mr-Neutr0n commented Feb 5, 2026

Summary

Fixes #1771

When drawing segmentation masks with holes, the GenericMask.mask_to_polygons() method was returning all contours including internal hole contours. When these polygons were drawn, the holes were incorrectly filled in.

Problem

As reported in #1771:

  1. Model predicts segmentation masks with holes (donut shapes, etc.)
  2. Visualizer.draw_instance_predictions() creates GenericMask and calls overlay_instances
  3. mask_to_polygons uses cv2.findContours with cv2.RETR_CCOMP which returns both external and internal (hole) contours
  4. All contours are returned as polygons without filtering
  5. When drawing, hole contours are drawn as filled polygons, incorrectly filling in the holes

Before fix:
Holes incorrectly filled

Solution

Filter out hole contours by checking the hierarchy returned from cv2.findContours. With cv2.RETR_CCOMP:

  • External contours have hierarchy[i][3] == -1 (no parent)
  • Hole contours have hierarchy[i][3] >= 0 (have a parent)

Now only external contours (boundaries) are returned as polygons, while holes are correctly left empty.

Changes

Modified GenericMask.mask_to_polygons() to filter contours based on hierarchy:

# Filter out hole contours (those with a parent, i.e., hierarchy[i][3] >= 0)
# Only keep external contours (hierarchy[i][3] == -1)
res = [contours[i].flatten() for i in range(len(contours)) if hierarchy[i][3] < 0]

Test plan

import numpy as np
from detectron2.utils.visualizer import GenericMask

# Create a mask with a hole (donut shape)
mask = np.zeros((100, 100), dtype=np.uint8)
mask[20:80, 20:80] = 1  # outer square
mask[35:65, 35:65] = 0  # inner hole

gm = GenericMask(mask, 100, 100)
print(f"Has holes: {gm.has_holes}")  # Should be True
print(f"Number of polygons: {len(gm.polygons)}")  # Should be 1 (only outer boundary)

Fixes facebookresearch#1771

When drawing segmentation masks with holes, the GenericMask.mask_to_polygons()
method was returning all contours including internal hole contours. When these
polygons were drawn, the holes were incorrectly filled in.

The fix filters out hole contours by checking the hierarchy returned from
cv2.findContours. With cv2.RETR_CCOMP:
- External contours have hierarchy[i][3] == -1 (no parent)
- Hole contours have hierarchy[i][3] >= 0 (have a parent)

Now only external contours (boundaries) are returned as polygons, while holes
are correctly left empty.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 5, 2026
@Mr-Neutr0n
Copy link
Copy Markdown
Author

hi, wanted to follow up on this. the polygon drawing bug causes holes to incorrectly show up as filled regions in mask visualizations. the fix is minimal — just filters out inner contours. let me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Potential bug on drawing segmentation mask with holes

1 participant