Skip to content

Commit 98a9718

Browse files
authoredMay 17, 2020
[Datumaro] Change alignment in mask parsing (#1547)
1 parent 4299090 commit 98a9718

File tree

5 files changed

+57
-13
lines changed

5 files changed

+57
-13
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4040
- Task/Job buttons has no "Open in new tab" option (<https://github.com/opencv/cvat/pull/1419>)
4141
- Delete point context menu option has no shortcut hint (<https://github.com/opencv/cvat/pull/1416>)
4242
- Fixed issue with unnecessary tag activation in cvat-canvas (<https://github.com/opencv/cvat/issues/1540>)
43+
- Fixed an issue with large number of instances in instance mask (https://github.com/opencv/cvat/issues/1539)
4344
- Fixed full COCO dataset import error with conflicting labels in keypoints and detection (https://github.com/opencv/cvat/pull/1548)
4445
- Fixed COCO keypoints skeleton parsing and saving (https://github.com/opencv/cvat/issues/1539)
4546

‎datumaro/datumaro/components/extractor.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def from_instance_masks(instance_masks,
270270
if instance_ids is not None:
271271
assert len(instance_ids) == len(instance_masks)
272272
else:
273-
instance_ids = [1 + i for i in range(len(instance_masks))]
273+
instance_ids = range(1, len(instance_masks) + 1)
274274

275275
if instance_labels is not None:
276276
assert len(instance_labels) == len(instance_masks)
@@ -310,15 +310,13 @@ def instance_mask(self):
310310
def instance_count(self):
311311
return int(self.instance_mask.max())
312312

313-
def get_instance_labels(self, class_count=None):
314-
if class_count is None:
315-
class_count = np.max(self.class_mask) + 1
316-
317-
m = self.class_mask * class_count + self.instance_mask
318-
m = m.astype(int)
313+
def get_instance_labels(self):
314+
class_shift = 16
315+
m = (self.class_mask.astype(np.uint32) << class_shift) \
316+
+ self.instance_mask.astype(np.uint32)
319317
keys = np.unique(m)
320-
instance_labels = {k % class_count: k // class_count
321-
for k in keys if k % class_count != 0
318+
instance_labels = {k & ((1 << class_shift) - 1): k >> class_shift
319+
for k in keys if k & ((1 << class_shift) - 1) != 0
322320
}
323321
return instance_labels
324322

@@ -783,4 +781,4 @@ def categories(self):
783781
return self._extractor.categories()
784782

785783
def transform_item(self, item):
786-
raise NotImplementedError()
784+
raise NotImplementedError()

‎datumaro/datumaro/plugins/voc_format/extractor.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,7 @@ def _load_annotations(self, item_id):
251251

252252
if class_mask is not None:
253253
label_cat = self._categories[AnnotationType.label]
254-
instance_labels = compiled_mask.get_instance_labels(
255-
class_count=len(label_cat.items))
254+
instance_labels = compiled_mask.get_instance_labels()
256255
else:
257256
instance_labels = {i: None
258257
for i in range(compiled_mask.instance_count)}

‎datumaro/tests/test_masks.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from unittest import TestCase
44

55
import datumaro.util.mask_tools as mask_tools
6+
from datumaro.components.extractor import CompiledMask
67

78

89
class PolygonConversionsTest(TestCase):
@@ -183,4 +184,14 @@ def test_can_merge_masks(self):
183184
actual = mask_tools.merge_masks(masks)
184185

185186
self.assertTrue(np.array_equal(expected, actual),
186-
'%s\nvs.\n%s' % (expected, actual))
187+
'%s\nvs.\n%s' % (expected, actual))
188+
189+
def test_can_decode_compiled_mask(self):
190+
class_idx = 1000
191+
instance_idx = 10000
192+
mask = np.array([1])
193+
compiled_mask = CompiledMask(mask * class_idx, mask * instance_idx)
194+
195+
labels = compiled_mask.get_instance_labels()
196+
197+
self.assertEqual({instance_idx: class_idx}, labels)

‎datumaro/tests/test_voc_format.py

+35
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,41 @@ def __iter__(self):
444444
VocSegmentationConverter(label_map='voc'), test_dir,
445445
target_dataset=DstExtractor())
446446

447+
def test_can_save_voc_segm_with_many_instances(self):
448+
def bit(x, y, shape):
449+
mask = np.zeros(shape)
450+
mask[y, x] = 1
451+
return mask
452+
453+
class TestExtractor(TestExtractorBase):
454+
def __iter__(self):
455+
return iter([
456+
DatasetItem(id=1, subset='a', annotations=[
457+
Mask(image=bit(x, y, shape=[10, 10]),
458+
label=self._label(VOC.VocLabel(3).name),
459+
z_order=10 * y + x + 1
460+
)
461+
for y in range(10) for x in range(10)
462+
]),
463+
])
464+
465+
class DstExtractor(TestExtractorBase):
466+
def __iter__(self):
467+
return iter([
468+
DatasetItem(id=1, subset='a', annotations=[
469+
Mask(image=bit(x, y, shape=[10, 10]),
470+
label=self._label(VOC.VocLabel(3).name),
471+
group=10 * y + x + 1
472+
)
473+
for y in range(10) for x in range(10)
474+
]),
475+
])
476+
477+
with TestDir() as test_dir:
478+
self._test_save_and_load(TestExtractor(),
479+
VocSegmentationConverter(label_map='voc'), test_dir,
480+
target_dataset=DstExtractor())
481+
447482
def test_can_save_voc_layout(self):
448483
class TestExtractor(TestExtractorBase):
449484
def __iter__(self):

0 commit comments

Comments
 (0)
Please sign in to comment.