diff --git a/CHANGELOG.md b/CHANGELOG.md index 696c02f8d2ff..0da60518f6b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ### Changed -- +- VOC task export now does not use official label map by default, but takes one + from the source task to avoid primary-class and class part name + clashing ([#1275](https://github.com/opencv/cvat/issues/1275)) ### Deprecated - @@ -19,6 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `occluded` and `z_order` annotation attributes are now correctly passed to Datumaro ([#1271](https://github.com/opencv/cvat/pull/1271)) +- Annotation-less tasks now can be exported as empty datasets in COCO ([#1277](https://github.com/opencv/cvat/issues/1277)) +- Frame name matching for video annotations import - + allowed `frame_XXXXXX[.ext]` format ([#1274](https://github.com/opencv/cvat/pull/1274)) ### Security - Bump acorn from 6.3.0 to 6.4.1 in /cvat-ui ([#1270](https://github.com/opencv/cvat/pull/1270)) diff --git a/cvat/apps/annotation/pascal_voc.py b/cvat/apps/annotation/pascal_voc.py index 2dd0aa48f510..b6bcfaa33eeb 100644 --- a/cvat/apps/annotation/pascal_voc.py +++ b/cvat/apps/annotation/pascal_voc.py @@ -74,7 +74,7 @@ def dump(file_object, annotations): extractor = CvatAnnotationsExtractor('', annotations) extractor = extractor.transform(id_from_image) extractor = Dataset.from_extractors(extractor) # apply lazy transforms - converter = env.make_converter('voc') + converter = env.make_converter('voc', label_map='source') with TemporaryDirectory() as temp_dir: converter(extractor, save_dir=temp_dir) make_zip_archive(temp_dir, file_object) \ No newline at end of file diff --git a/cvat/apps/dataset_manager/bindings.py b/cvat/apps/dataset_manager/bindings.py index dc1db5740d7c..d1d98af279aa 100644 --- a/cvat/apps/dataset_manager/bindings.py +++ b/cvat/apps/dataset_manager/bindings.py @@ -181,6 +181,8 @@ def __init__(self, url, db_task, user): def match_frame(item, cvat_task_anno): + is_video = cvat_task_anno.meta['task']['mode'] == 'interpolation' + frame_number = None if frame_number is None: try: @@ -197,6 +199,8 @@ def match_frame(item, cvat_task_anno): frame_number = int(item.id) except Exception: pass + if frame_number is None and is_video and item.id.startswith('frame_'): + frame_number = int(item.id[len('frame_'):]) if not frame_number in cvat_task_anno.frame_info: raise Exception("Could not match item id: '%s' with any task frame" % item.id) diff --git a/datumaro/datumaro/plugins/coco_format/converter.py b/datumaro/datumaro/plugins/coco_format/converter.py index 39fe7b154027..403a6a83eb6c 100644 --- a/datumaro/datumaro/plugins/coco_format/converter.py +++ b/datumaro/datumaro/plugins/coco_format/converter.py @@ -329,20 +329,24 @@ def save_categories(self, dataset): label_categories = dataset.categories().get(AnnotationType.label) if label_categories is None: return - points_categories = dataset.categories().get(AnnotationType.points) - if points_categories is None: - return - - for idx, kp_cat in points_categories.items.items(): - label_cat = label_categories.items[idx] + point_categories = dataset.categories().get(AnnotationType.points) + for idx, label_cat in enumerate(label_categories.items): cat = { 'id': 1 + idx, 'name': _cast(label_cat.name, str, ''), 'supercategory': _cast(label_cat.parent, str, ''), - 'keypoints': [str(l) for l in kp_cat.labels], - 'skeleton': [int(i) for i in kp_cat.adjacent], + 'keypoints': [], + 'skeleton': [], } + + if point_categories is not None: + kp_cat = point_categories.items.get(idx) + if kp_cat is not None: + cat.update({ + 'keypoints': [str(l) for l in kp_cat.labels], + 'skeleton': [int(i) for i in kp_cat.adjacent], + }) self.categories.append(cat) def save_annotations(self, item): @@ -447,14 +451,19 @@ class _Converter: def __init__(self, extractor, save_dir, tasks=None, save_images=False, segmentation_mode=None, crop_covered=False): - assert tasks is None or isinstance(tasks, (CocoTask, list)) + assert tasks is None or isinstance(tasks, (CocoTask, list, str)) if tasks is None: tasks = list(self._TASK_CONVERTER) elif isinstance(tasks, CocoTask): tasks = [tasks] + elif isinstance(tasks, str): + tasks = [CocoTask[tasks]] else: - for t in tasks: - assert t in CocoTask + for i, t in enumerate(tasks): + if isinstance(t, str): + tasks[i] = CocoTask[t] + else: + assert t in CocoTask, t self._tasks = tasks self._extractor = extractor @@ -546,9 +555,8 @@ def convert(self): task_conv.save_annotations(item) for task, task_conv in task_converters.items(): - if not task_conv.is_empty(): - task_conv.write(osp.join(self._ann_dir, - '%s_%s.json' % (task.name, subset_name))) + task_conv.write(osp.join(self._ann_dir, + '%s_%s.json' % (task.name, subset_name))) class CocoConverter(Converter, CliPlugin): @staticmethod diff --git a/datumaro/datumaro/plugins/yolo_format/extractor.py b/datumaro/datumaro/plugins/yolo_format/extractor.py index 7840b26c5ca1..11e829d4a5bd 100644 --- a/datumaro/datumaro/plugins/yolo_format/extractor.py +++ b/datumaro/datumaro/plugins/yolo_format/extractor.py @@ -90,7 +90,9 @@ def __init__(self, config_path, image_info=None): subset = YoloExtractor.Subset(subset_name, self) with open(list_path, 'r') as f: subset.items = OrderedDict( - (osp.splitext(osp.basename(p))[0], p.strip()) for p in f) + (osp.splitext(osp.basename(p.strip()))[0], p.strip()) + for p in f + ) for item_id, image_path in subset.items.items(): image_path = self._make_local_path(image_path) diff --git a/datumaro/tests/test_coco_format.py b/datumaro/tests/test_coco_format.py index 724fdc5a4afd..f9340b659e8b 100644 --- a/datumaro/tests/test_coco_format.py +++ b/datumaro/tests/test_coco_format.py @@ -632,10 +632,13 @@ def __iter__(self): def categories(self): label_cat = LabelCategories() + point_cat = PointsCategories() for label in range(10): label_cat.add('label_' + str(label)) + point_cat.add(label) return { AnnotationType.label: label_cat, + AnnotationType.points: point_cat, } with TestDir() as test_dir: @@ -651,4 +654,4 @@ def __iter__(self): with TestDir() as test_dir: self._test_save_and_load(TestExtractor(), - CocoConverter(), test_dir) \ No newline at end of file + CocoConverter(tasks='image_info'), test_dir) \ No newline at end of file