Skip to content

Commit

Permalink
Merge pull request #3091 from openvinotoolkit/sk/fix-import-annotatio…
Browse files Browse the repository at this point in the history
…ns-for-YOLO-format

Fix import annotations for frames with dots in name
  • Loading branch information
Kirill Sizov authored Apr 20, 2021
2 parents f267f8a + b36c5f2 commit 4db05fd
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 5 deletions.
7 changes: 4 additions & 3 deletions cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,9 @@ def db_task(self):
def _get_filename(path):
return osp.splitext(path)[0]

def match_frame(self, path, root_hint=None):
path = self._get_filename(path)
def match_frame(self, path, root_hint=None, path_has_ext=True):
if path_has_ext:
path = self._get_filename(path)
match = self._frame_mapping.get(path)
if not match and root_hint and not path.startswith(root_hint):
path = osp.join(root_hint, path)
Expand Down Expand Up @@ -611,7 +612,7 @@ def match_dm_item(item, task_data, root_hint=None):
if frame_number is None and item.has_image:
frame_number = task_data.match_frame(item.id + item.image.ext, root_hint)
if frame_number is None:
frame_number = task_data.match_frame(item.id, root_hint)
frame_number = task_data.match_frame(item.id, root_hint, path_has_ext=False)
if frame_number is None:
frame_number = cast(item.attributes.get('frame', item.id), int)
if frame_number is None and is_video:
Expand Down
5 changes: 3 additions & 2 deletions cvat/apps/dataset_manager/formats/cvat.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,9 @@ def load(file_object, annotations):
elif el.tag == 'image':
image_is_opened = True
frame_id = annotations.abs_frame_id(match_dm_item(
DatasetItem(id=el.attrib['name'],
attributes={'frame': el.attrib['id']}
DatasetItem(id=osp.splitext(el.attrib['name'])[0],
attributes={'frame': el.attrib['id']},
image=el.attrib['name']
),
task_data=annotations
))
Expand Down
325 changes: 325 additions & 0 deletions cvat/apps/dataset_manager/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ def test_frames_outside_are_not_generated(self):
self.assertTrue(frame.frame in range(6, 10))
self.assertEqual(i + 1, 4)


class FrameMatchingTest(_DbTestBase):
def _generate_task_images(self, paths): # pylint: disable=no-self-use
f = BytesIO()
Expand Down Expand Up @@ -586,3 +587,327 @@ def test_dataset_root(self):

root = find_dataset_root(dataset, task_data)
self.assertEqual(expected, root)

class TaskAnnotationsImportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v1_task_id_annotations(task["id"], annotations)
return annotations

def _generate_task_images(self, count, name="image"):
images = {
"client_files[%d]" % i: generate_image_file("image_%d.jpg" % i)
for i in range(count)
}
images["image_quality"] = 75
return images

def _generate_task(self, images, annotation_format, **overrides):
labels = []
if annotation_format in ["ICDAR Recognition 1.0",
"ICDAR Localization 1.0"]:
labels = [{
"name": "icdar",
"attributes": [{
"name": "text",
"mutable": False,
"input_type": "text",
"values": ["word1", "word2"]
}]
}]
elif annotation_format == "ICDAR Segmentation 1.0":
labels = [{
"name": "icdar",
"attributes": [
{
"name": "text",
"mutable": False,
"input_type": "text",
"values": ["word_1", "word_2", "word_3"]
},
{
"name": "index",
"mutable": False,
"input_type": "number",
"values": ["0", "1", "2"]
},
{
"name": "color",
"mutable": False,
"input_type": "text",
"values": ["100 110 240", "10 15 20", "120 128 64"]
},
{
"name": "center",
"mutable": False,
"input_type": "text",
"values": ["1 2", "2 4", "10 45"]
},
]
}]
elif annotation_format == "Market-1501 1.0":
labels = [{
"name": "market-1501",
"attributes": [
{
"name": "query",
"mutable": False,
"input_type": "select",
"values": ["True", "False"]
},
{
"name": "camera_id",
"mutable": False,
"input_type": "number",
"values": ["0", "1", "2", "3"]
},
{
"name": "person_id",
"mutable": False,
"input_type": "number",
"values": ["1", "2", "3"]
},
]
}]
else:
labels = [
{
"name": "car",
"attributes": [
{
"name": "model",
"mutable": False,
"input_type": "select",
"default_value": "mazda",
"values": ["bmw", "mazda", "renault"]
},
{
"name": "parked",
"mutable": True,
"input_type": "checkbox",
"default_value": False
}
]
},
{"name": "person"}
]

task = {
"name": "my task #1",
"overlap": 0,
"segment_size": 100,
"labels": labels
}
task.update(overrides)
return self._create_task(task, images)

def _generate_annotations(self, task, annotation_format):
shapes = []
tracks = []
tags = []

if annotation_format in ["ICDAR Recognition 1.0",
"ICDAR Localization 1.0"]:
shapes = [{
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}]
elif annotation_format == "Market-1501 1.0":
tags = [{
"frame": 1,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["values"][2]
},
{
"spec_id": task["labels"][0]["attributes"][2]["id"],
"value": task["labels"][0]["attributes"][2]["values"][0]
}
],
}]
elif annotation_format == "ICDAR Segmentation 1.0":
shapes = [{
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][2]["id"],
"value": task["labels"][0]["attributes"][2]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][3]["id"],
"value": task["labels"][0]["attributes"][3]["values"][2]
}
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}]
elif annotation_format == "VGGFace2 1.0":
shapes = [{
"frame": 1,
"label_id": task["labels"][1]["id"],
"group": None,
"source": "manual",
"attributes": [],
"points": [2.0, 2.1, 40, 50.7],
"type": "rectangle",
"occluded": False
}]
else:
rectangle_shape_wo_attrs = {
"frame": 1,
"label_id": task["labels"][1]["id"],
"group": 0,
"source": "manual",
"attributes": [],
"points": [2.0, 2.1, 40, 50.7],
"type": "rectangle",
"occluded": False,
}

rectangle_shape_with_attrs = {
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"]
}
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}

track_wo_attrs = {
"frame": 0,
"label_id": task["labels"][1]["id"],
"group": 0,
"source": "manual",
"attributes": [],
"shapes": [
{
"frame": 0,
"attributes": [],
"points": [1.0, 2.1, 100, 300.222],
"type": "polygon",
"occluded": False,
"outside": False
}
]
}

tag_wo_attrs = {
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": None,
"attributes": []
}

tag_with_attrs = {
"frame": 1,
"label_id": task["labels"][0]["id"],
"group": 3,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"]
}
],
}

if annotation_format == "VGGFace2 1.0":
shapes = rectangle_shape_wo_attrs
elif annotation_format == "CVAT 1.1":
shapes = [rectangle_shape_wo_attrs,
rectangle_shape_with_attrs]
tags = [tag_with_attrs, tag_wo_attrs]
elif annotation_format == "MOTS PNG 1.0":
tracks = [track_wo_attrs]
else:
shapes = [rectangle_shape_wo_attrs,
rectangle_shape_with_attrs]
tags = tag_wo_attrs
tracks = track_wo_attrs

annotations = {
"version": 0,
"tags": tags,
"shapes": shapes,
"tracks": tracks
}

return self._generate_custom_annotations(annotations, task)

def _test_can_import_annotations(self, task, import_format):
with tempfile.TemporaryDirectory() as temp_dir:
file_path = osp.join(temp_dir, import_format)

export_format = import_format
if import_format == "CVAT 1.1":
export_format = "CVAT for images 1.1"

dm.task.export_task(task["id"], file_path, export_format)
expected_ann = TaskAnnotation(task["id"])
expected_ann.init_from_db()

dm.task.import_task_annotations(task["id"],
file_path, import_format)
actual_ann = TaskAnnotation(task["id"])
actual_ann.init_from_db()

self.assertEqual(len(expected_ann.data), len(actual_ann.data))

def test_can_import_annotations_for_image_with_dots_in_filename(self):
for f in dm.views.get_import_formats():
format_name = f.DISPLAY_NAME

images = self._generate_task_images(3, "img0.0.0")
task = self._generate_task(images, format_name)
self._generate_annotations(task, format_name)

with self.subTest(format=format_name):
if not f.ENABLED:
self.skipTest("Format is disabled")

self._test_can_import_annotations(task, format_name)

0 comments on commit 4db05fd

Please sign in to comment.