From 6cabe4d30a38e905b370fe072d0f3f3a874f1687 Mon Sep 17 00:00:00 2001 From: neo Date: Sun, 15 Dec 2019 16:14:28 +0900 Subject: [PATCH 1/3] add upload annotation function to cli --- utils/cli/cli.py | 3 ++- utils/cli/core/core.py | 20 ++++++++++++++++++++ utils/cli/core/definition.py | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/utils/cli/cli.py b/utils/cli/cli.py index 96b6881fdf8a..f22bf81520c2 100755 --- a/utils/cli/cli.py +++ b/utils/cli/cli.py @@ -23,7 +23,8 @@ def main(): 'delete': CLI.tasks_delete, 'ls': CLI.tasks_list, 'frames': CLI.tasks_frame, - 'dump': CLI.tasks_dump} + 'dump': CLI.tasks_dump, + 'upload': CLI.tasks_upload} args = parser.parse_args() config_log(args.loglevel) with requests.Session() as session: diff --git a/utils/cli/core/core.py b/utils/cli/core/core.py index 374d8e640210..ef0b76e659b6 100644 --- a/utils/cli/core/core.py +++ b/utils/cli/core/core.py @@ -112,6 +112,22 @@ def tasks_dump(self, task_id, fileformat, filename, **kwargs): with open(filename, 'wb') as fp: fp.write(response.content) + def tasks_upload(self, task_id, fileformat, filename, **kwargs): + """ Upload annotations for a task in the specified format + (e.g. 'YOLO ZIP 1.0').""" + url = self.api.tasks_id_annotations_format(task_id, fileformat) + while True: + response = self.session.put( + url, + files={'annotation_file':open(filename, 'rb')} + ) + response.raise_for_status() + if response.status_code == 201: + break + + log.info('Upload job for Task ID {} \ + with annotation file {} finished'.format(task_id, filename)) + class CVAT_API_V1(): """ Build parameterized API URLs """ @@ -135,6 +151,10 @@ def tasks_id_data(self, task_id): def tasks_id_frame_id(self, task_id, frame_id): return self.tasks_id(task_id) + '/frames/{}'.format(frame_id) + def tasks_id_annotations_format(self, task_id, fileformat): + return self.tasks_id(task_id) + '/annotations?format={}' \ + .format(fileformat) + def tasks_id_annotations_filename(self, task_id, name, fileformat): return self.tasks_id(task_id) + '/annotations/{}?format={}' \ .format(name, fileformat) diff --git a/utils/cli/core/definition.py b/utils/cli/core/definition.py index c9e32e256d6d..e3d1a6222d59 100644 --- a/utils/cli/core/definition.py +++ b/utils/cli/core/definition.py @@ -208,3 +208,29 @@ def argparse(s): default='CVAT XML 1.1 for images', help='annotation format (default: %(default)s)' ) + +####################################################################### +# Upload Annotations +####################################################################### + +upload_parser = task_subparser.add_parser( + 'upload', + description='Upload annotations for a CVAT task.' +) +upload_parser.add_argument( + 'task_id', + type=int, + help='task ID' +) +upload_parser.add_argument( + 'filename', + type=str, + help='upload file' +) +upload_parser.add_argument( + '--format', + dest='fileformat', + type=str, + default='CVAT XML 1.1', + help='annotation format (default: %(default)s)' +) From 87dfaa70a19b3b1a0d2ddf57ea28eadfda826953 Mon Sep 17 00:00:00 2001 From: Seungwon Jeong Date: Sun, 15 Dec 2019 16:28:37 +0900 Subject: [PATCH 2/3] Update core.py Removing whitespace --- utils/cli/core/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/cli/core/core.py b/utils/cli/core/core.py index ef0b76e659b6..7ef0a35ece10 100644 --- a/utils/cli/core/core.py +++ b/utils/cli/core/core.py @@ -124,7 +124,7 @@ def tasks_upload(self, task_id, fileformat, filename, **kwargs): response.raise_for_status() if response.status_code == 201: break - + log.info('Upload job for Task ID {} \ with annotation file {} finished'.format(task_id, filename)) From 218adeefafe671f12ced349e862a43a77621245d Mon Sep 17 00:00:00 2001 From: Seungwon Jeong Date: Thu, 19 Dec 2019 22:45:59 +0900 Subject: [PATCH 3/3] Add test code for upload function --- utils/cli/core/core.py | 5 +-- utils/cli/tests/test_cli.py | 62 ++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/utils/cli/core/core.py b/utils/cli/core/core.py index 7ef0a35ece10..f213044332dd 100644 --- a/utils/cli/core/core.py +++ b/utils/cli/core/core.py @@ -125,8 +125,9 @@ def tasks_upload(self, task_id, fileformat, filename, **kwargs): if response.status_code == 201: break - log.info('Upload job for Task ID {} \ - with annotation file {} finished'.format(task_id, filename)) + logger_string = "Upload job for Task ID {} ".format(task_id) +\ + "with annotation file {} finished".format(filename) + log.info(logger_string) class CVAT_API_V1(): diff --git a/utils/cli/tests/test_cli.py b/utils/cli/tests/test_cli.py index cbc0a63b4fc4..cf1dcc82ed81 100644 --- a/utils/cli/tests/test_cli.py +++ b/utils/cli/tests/test_cli.py @@ -10,6 +10,7 @@ from rest_framework.test import APITestCase, RequestsClient from cvat.apps.engine.tests.test_rest_api import create_db_users from cvat.apps.engine.tests.test_rest_api import generate_image_file +from PIL import Image class TestCLI(APITestCase): @@ -22,7 +23,7 @@ def setUp(self, mock_stdout): self.cli = CLI(self.client, self.api) self.taskname = 'test_task' self.cli.tasks_create(self.taskname, - [], + [{'name' : 'car'}, {'name': 'person'}], '', ResourceType.LOCAL, [self.img_file]) @@ -69,3 +70,62 @@ def test_tasks_frame(self): self.cli.tasks_frame(1, [0], outdir=settings.SHARE_ROOT) self.assertTrue(os.path.exists(path)) os.remove(path) + + def test_tasks_upload(self): + test_image = Image.open(self.img_file) + width, height = test_image.size + + # Using generate_coco_anno() from: + # https://github.com/opencv/cvat/blob/develop/cvat/apps/engine/tests/test_rest_api.py + 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_cli.jpg", + "height": %d, + "width": %d + } + ], + "annotations": [ + { + "category_id": 1, + "id": 1, + "image_id": 0, + "iscrowd": 0, + "segmentation": [ + [] + ], + "area": 17702.0, + "bbox": [ + 574.0, + 407.0, + 167.0, + 106.0 + ] + } + ] + }""" + content = generate_coco_anno() % (height, width) + path = os.path.join(settings.SHARE_ROOT, 'test_cli.json') + with open(path, "wb") as coco: + coco.write(content) + self.cli.tasks_upload(1, 'COCO JSON 1.0', path) + self.assertRegex(self.mock_stdout.getvalue(), '.*{}.*'.format("annotation file")) + os.remove(path)