From 6abe39e0505b334df2c9a6f52c7b715bf2426381 Mon Sep 17 00:00:00 2001 From: Anastasia Yasakova Date: Fri, 4 Jun 2021 21:16:17 +0300 Subject: [PATCH] Add test for bug with attribute in WiderFace (#3288) * add test for bug with attribute in WiderFace * update Datumaro to 0.1.9 --- .../tests/assets/annotations.json | 29 +++++++ .../dataset_manager/tests/assets/tasks.json | 44 ++++++++++ .../tests/test_rest_api_formats.py | 86 +++++++++++++++++-- cvat/requirements/base.txt | 2 +- 4 files changed, 154 insertions(+), 7 deletions(-) diff --git a/cvat/apps/dataset_manager/tests/assets/annotations.json b/cvat/apps/dataset_manager/tests/assets/annotations.json index cc82dbcd..251d7111 100644 --- a/cvat/apps/dataset_manager/tests/assets/annotations.json +++ b/cvat/apps/dataset_manager/tests/assets/annotations.json @@ -42,5 +42,34 @@ "attributes": [] } ] + }, + "WiderFace 1.0": { + "version": 0, + "tags": [], + "shapes": [ + { + "type": "rectangle", + "occluded": false, + "z_order": 0, + "points": [7.55, 9.75, 16.44, 15.85], + "frame": 0, + "label_id": null, + "group": 0, + "source": "manual", + "attributes": [] + }, + { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [3.55, 27.75, 11.33, 33.71], + "frame": 1, + "label_id": null, + "group": 0, + "source": "manual", + "attributes": [] + } + ], + "tracks": [] } } diff --git a/cvat/apps/dataset_manager/tests/assets/tasks.json b/cvat/apps/dataset_manager/tests/assets/tasks.json index 2fd5f663..1cc2d312 100644 --- a/cvat/apps/dataset_manager/tests/assets/tasks.json +++ b/cvat/apps/dataset_manager/tests/assets/tasks.json @@ -53,5 +53,49 @@ "attributes": [] } ] + }, + "widerface with all attributes": { + "name": "widerface task", + "overlap": 0, + "segment_size": 100, + "owner_id": 1, + "assignee_id": 2, + "labels": [ + { + "name": "face", + "attributes": [ + { + "name": "blur", + "mutable": false, + "input_type": "select", + "values": ["0", "1", "2"] + }, + { + "name": "expression", + "mutable": false, + "input_type": "select", + "values": ["0", "1"] + }, + { + "name": "illumination", + "mutable": false, + "input_type": "select", + "values": ["0", "1"] + }, + { + "name": "pose", + "mutable": false, + "input_type": "select", + "values": ["0", "1"] + }, + { + "name": "invalid", + "mutable": false, + "input_type": "select", + "values": ["0", "1"] + } + ] + } + ] } } diff --git a/cvat/apps/dataset_manager/tests/test_rest_api_formats.py b/cvat/apps/dataset_manager/tests/test_rest_api_formats.py index 0abbf889..aa761514 100644 --- a/cvat/apps/dataset_manager/tests/test_rest_api_formats.py +++ b/cvat/apps/dataset_manager/tests/test_rest_api_formats.py @@ -1,4 +1,3 @@ - # Copyright (C) 2021 Intel Corporation # # SPDX-License-Identifier: MIT @@ -11,18 +10,23 @@ import xml.etree.ElementTree as ET import zipfile from io import BytesIO -from datumaro.util.test_utils import TestDir +from datumaro.components.dataset import Dataset +from datumaro.util.test_utils import TestDir, compare_datasets from django.contrib.auth.models import Group, User from PIL import Image from rest_framework import status from rest_framework.test import APIClient, APITestCase -path = osp.join(osp.dirname(__file__), 'assets', 'tasks.json') -with open(path) as f: +from cvat.apps.dataset_manager.bindings import CvatTaskDataExtractor, TaskData +from cvat.apps.dataset_manager.task import TaskAnnotation +from cvat.apps.engine.models import Task + +tasks_path = osp.join(osp.dirname(__file__), 'assets', 'tasks.json') +with open(tasks_path) as f: tasks = json.load(f) -path = osp.join(osp.dirname(__file__), 'assets', 'annotations.json') -with open(path) as f: +annotations_path = osp.join(osp.dirname(__file__), 'assets', 'annotations.json') +with open(annotations_path) as f: annotations = json.load(f) def generate_image_file(filename, size=(100, 50)): @@ -104,6 +108,16 @@ class _DbTestBase(APITestCase): response = self.client.get(path, data) return response + def _put_request_with_data(self, path, data, user): + with ForceLogin(user, self.client): + response = self.client.put(path, data) + return response + + def _delete_request(self, path, user): + with ForceLogin(user, self.client): + response = self.client.delete(path) + return response + def _create_annotations(self, task, name_ann, key_get_values): tmp_annotations = copy.deepcopy(annotations[name_ann]) @@ -152,6 +166,12 @@ class _DbTestBase(APITestCase): with open(file_name, "wb") as f: f.write(content.getvalue()) + def _upload_file(self, url, data, user): + response = self._put_request_with_data(url, {"annotation_file": data}, user) + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + response = self._put_request_with_data(url, {}, user) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def _check_downloaded_file(self, file_name): if not osp.exists(file_name): raise FileNotFoundError(f"File '{file_name}' was not downloaded") @@ -159,6 +179,14 @@ class _DbTestBase(APITestCase): def _generate_url_dump_tasks_annotations(self, task_id): return f"/api/v1/tasks/{task_id}/annotations" + def _generate_url_upload_tasks_annotations(self, task_id, upload_format_name): + return f"/api/v1/tasks/{task_id}/annotations?format={upload_format_name}" + + def _remove_annotations(self, url, user): + response = self._delete_request(url, user) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + return response + class TaskDumpUploadTest(_DbTestBase): def test_api_v1_check_duplicated_polygon_points(self): test_name = self._testMethodName @@ -189,3 +217,49 @@ class TaskDumpUploadTest(_DbTestBase): polygon_points = polygon.attrib["points"].replace(",", ";") polygon_points = [float(p) for p in polygon_points.split(";")] self.assertEqual(polygon_points, annotation_points) + + def test_api_v1_check_widerface_with_all_attributes(self): + test_name = self._testMethodName + dump_format_name = "WiderFace 1.0" + upload_format_name = "WiderFace 1.0" + + for include_images in (False, True): + with self.subTest(): + # create task with annotations + images = self._generate_task_images(3) + task = self._create_task(tasks["widerface with all attributes"], images) + self._create_annotations(task, f'{dump_format_name}', "random") + + task_id = task["id"] + task_ann = TaskAnnotation(task_id) + task_ann.init_from_db() + task_data = TaskData(task_ann.ir_data, Task.objects.get(pk=task_id)) + extractor = CvatTaskDataExtractor(task_data, include_images=include_images) + data_from_task_before_upload = Dataset.from_extractors(extractor) + + # dump annotations + url = self._generate_url_dump_tasks_annotations(task_id) + data = { + "format": dump_format_name, + "action": "download", + } + with TestDir() as test_dir: + file_zip_name = osp.join(test_dir, f'{test_name}_{dump_format_name}.zip') + self._download_file(url, data, self.admin, file_zip_name) + self._check_downloaded_file(file_zip_name) + + # remove annotations + self._remove_annotations(url, self.admin) + + # upload annotations + url = self._generate_url_upload_tasks_annotations(task_id, upload_format_name) + with open(file_zip_name, 'rb') as binary_file: + self._upload_file(url, binary_file, self.admin) + + # equals annotations + task_ann = TaskAnnotation(task_id) + task_ann.init_from_db() + task_data = TaskData(task_ann.ir_data, Task.objects.get(pk=task_id)) + extractor = CvatTaskDataExtractor(task_data, include_images=include_images) + data_from_task_after_upload = Dataset.from_extractors(extractor) + compare_datasets(self, data_from_task_before_upload, data_from_task_after_upload) diff --git a/cvat/requirements/base.txt b/cvat/requirements/base.txt index c9037583..08d61d6f 100644 --- a/cvat/requirements/base.txt +++ b/cvat/requirements/base.txt @@ -50,4 +50,4 @@ open3d==0.11.2 # --no-binary=pycocotools: workaround for binary incompatibility on numpy 1.20 # of pycocotools and tensorflow 2.4.1 # when pycocotools is installed by wheel in python 3.8+ -datumaro==0.1.8 --no-binary=datumaro --no-binary=pycocotools +datumaro==0.1.9 --no-binary=datumaro --no-binary=pycocotools