Skip to content

Commit

Permalink
Merge pull request #4826 from kemaleren/seg_mask_off_by_one
Browse files Browse the repository at this point in the history
fix off-by-one errors parsing stuff and thing instances
  • Loading branch information
brimoor authored Sep 20, 2024
2 parents e5a0107 + bac2d2a commit dacedbc
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 14 deletions.
28 changes: 14 additions & 14 deletions fiftyone/core/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -1855,7 +1855,7 @@ def _parse_stuff_instance(mask, offset=None, frame_size=None):
h = (ymax - ymin + 1) / height

bbox = [x, y, w, h]
instance_mask = mask[ymin:ymax, xmin:xmax]
instance_mask = mask[ymin : (ymax + 1), xmin : (xmax + 1)]

return bbox, instance_mask

Expand All @@ -1873,23 +1873,23 @@ def _parse_thing_instances(mask, offset=None, frame_size=None):

labeled = skm.label(mask)
objects = _find_slices(labeled)

instances = []
for idx, (yslice, xslice) in objects.items():
for target, slc in objects.items():
yslice, xslice = slc
xmin = xslice.start
xmax = xslice.stop
ymin = yslice.start
ymax = yslice.stop

x = (xmin + x_offset) / width
y = (ymin + y_offset) / height
w = (xmax - xmin) / width
h = (ymax - ymin) / height

bbox = [x, y, w, h]
instance_mask = mask[ymin:ymax, xmin:xmax]
instance_offset = (
offset[0] + xmin,
offset[1] + ymin,
)

instances.append((bbox, instance_mask))
# use the labeled image mask so `_parse_stuff_instance()`
# can be re-used here
instance_mask = labeled[slc] == target
instance = _parse_stuff_instance(
instance_mask, instance_offset, frame_size
)
instances.append(instance)

return instances

Expand Down
32 changes: 32 additions & 0 deletions tests/unittests/label_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,38 @@ def poly_bounds(p):
decimal=1,
)

@drop_datasets
def test_parse_stuff_instance(self):
mask = np.ones((3, 3), dtype=bool)
offset = (0, 0)
frame_size = (6, 6)
bbox, instance_mask = focl._parse_stuff_instance(
mask, offset, frame_size
)
self.assertEqual(bbox, [0.0, 0.0, 0.5, 0.5])
nptest.assert_array_equal(instance_mask, mask)

@drop_datasets
def test_parse_thing_instances(self):
# test on multiple disconnected objects with overlapping
# bounding boxes
mask = np.eye(5, dtype=bool)
mask[0, -1] = True
offset = (0, 0)
frame_size = (10, 10)
results = focl._parse_thing_instances(mask, offset, frame_size)
self.assertEqual(len(results), 2)

bbox, instance_mask = max(results, key=lambda x: x[1].size)
self.assertEqual(bbox, [0, 0, 0.5, 0.5])
expected_mask = np.eye(5, dtype=bool)
nptest.assert_array_equal(instance_mask, expected_mask)

bbox, instance_mask = min(results, key=lambda x: x[1].size)
self.assertEqual(bbox, [0.4, 0, 0.1, 0.1])
expected_mask = np.eye(1, dtype=bool)
nptest.assert_array_equal(instance_mask, expected_mask)

@drop_datasets
def test_transform_mask(self):
# int to int
Expand Down

0 comments on commit dacedbc

Please sign in to comment.