提交 befe5efb 编写于 作者: A Andrey Zhavoronkov 提交者: Nikita Manovich

Fix upload anno for COCO (#788)

* COCO: load bbox as rectangle if segmentation field is empty
* added unit test for coco format (case: object segment field is empty)
上级 1487ceaf
......@@ -315,7 +315,8 @@ It may take some time.
#### COCO loader description
- uploaded file: single unpacked `*.json`.
- supported shapes: Polygons (the `segmentation` must not be empty)
- supported shapes: object is interpreted as Polygon if the `segmentation` field of annotation is not empty
else as Rectangle with coordinates from `bbox` field.
- additional comments: the CVAT task should be created with the full label set that may be in the annotation files
#### How to create a task from MS COCO dataset
......
......@@ -344,11 +344,12 @@ def load(file_object, annotations):
for ann in anns:
group = 0
label_name = labels[ann['category_id']]
polygons = []
if 'segmentation' in ann:
polygons = []
# polygon
if ann['iscrowd'] == 0:
polygons = ann['segmentation']
# filter non-empty polygons
polygons = [polygon for polygon in ann['segmentation'] if polygon]
# mask
else:
if isinstance(ann['segmentation']['counts'], list):
......@@ -375,3 +376,18 @@ def load(file_object, annotations):
attributes=[],
group=group,
))
if not polygons and 'bbox' in ann and isinstance(ann['bbox'], list):
xtl = ann['bbox'][0]
ytl = ann['bbox'][1]
xbr = xtl + ann['bbox'][2]
ybr = ytl + ann['bbox'][3]
annotations.add_shape(annotations.LabeledShape(
type='rectangle',
frame=frame_number,
label=label_name,
points=[xtl, ytl, xbr, ybr],
occluded=False,
attributes=[],
group=group,
))
......@@ -280,9 +280,9 @@ class FloatArrayField(models.TextField):
separator = ","
def from_db_value(self, value, expression, connection):
if value is None:
return value
return [float(v) for v in value.split(self.separator)]
if not value:
return value
return [float(v) for v in value.split(self.separator)]
def to_python(self, value):
if isinstance(value, list):
......
......@@ -366,7 +366,8 @@ class ShapeSerializer(serializers.Serializer):
occluded = serializers.BooleanField()
z_order = serializers.IntegerField(default=0)
points = serializers.ListField(
child=serializers.FloatField(min_value=0)
child=serializers.FloatField(min_value=0),
allow_empty=False,
)
class LabeledShapeSerializer(ShapeSerializer, AnnotationSerializer):
......
......@@ -2779,6 +2779,84 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
elif annotation_format_name == "MASK":
self.assertTrue(zipfile.is_zipfile(content))
def _run_coco_annotation_upload_test(self, user):
def generate_coco_anno():
return b"""{
"categories": [
{
"id": 1,
"name": "car",
"supercategory": ""
},
{
"id": 2,
"name": "person",
"supercategory": ""
}
],
"images": [
{
"coco_url": "",
"date_captured": "",
"flickr_url": "",
"license": 0,
"id": 0,
"file_name": "test_1.jpg",
"height": 720,
"width": 1280
}
],
"annotations": [
{
"category_id": 1,
"id": 1,
"image_id": 0,
"iscrowd": 0,
"segmentation": [
[]
],
"area": 17702.0,
"bbox": [
574.0,
407.0,
167.0,
106.0
]
}
]
}"""
response = self._get_annotation_formats(user)
self.assertEqual(response.status_code, status.HTTP_200_OK)
supported_formats = response.data
self.assertTrue(isinstance(supported_formats, list) and supported_formats)
coco_format = None
for f in response.data:
if f["name"] == "COCO":
coco_format = f
break
self.assertTrue(coco_format)
loader = coco_format["loaders"][0]
task, _ = self._create_task(user, user)
content = io.BytesIO(generate_coco_anno())
content.seek(0)
uploaded_data = {
"annotation_file": content,
}
response = self._upload_api_v1_tasks_id_annotations(task["id"], user, uploaded_data, "format={}".format(loader["display_name"]))
self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
response = self._upload_api_v1_tasks_id_annotations(task["id"], user, {}, "format={}".format(loader["display_name"]))
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self._get_api_v1_tasks_id_annotations(task["id"], user)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_api_v1_tasks_id_annotations_admin(self):
self._run_api_v1_tasks_id_annotations(self.admin, self.assignee,
self.assignee)
......@@ -2801,6 +2879,9 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
def test_api_v1_tasks_id_annotations_dump_load_no_auth(self):
self._run_api_v1_tasks_id_annotations_dump_load(self.user, self.assignee, None)
def test_api_v1_tasks_id_annotations_upload_coco_user(self):
self._run_coco_annotation_upload_test(self.user)
class ServerShareAPITestCase(APITestCase):
def setUp(self):
self.client = APIClient()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册