|
|
|
|
@ -496,6 +496,7 @@ class TaskExportTest(_DbTestBase):
|
|
|
|
|
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()
|
|
|
|
|
@ -586,3 +587,327 @@ class FrameMatchingTest(_DbTestBase):
|
|
|
|
|
|
|
|
|
|
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)
|