From de622082d33155b736b55d8143fb82ea44e1b356 Mon Sep 17 00:00:00 2001 From: eisuke-ueta <39715045+eisuke-ueta@users.noreply.github.com> Date: Sun, 23 May 2021 11:58:51 +0900 Subject: [PATCH 1/8] Update Readme.md --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef265ad..82d2afd 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ _If you are using FastLabel prototype, please install version 0.2.2._ +## Table of Contents + +- [Installation](#installation) +- [Usage](#usage) + - [Limitation](#limitation) +- [Task](#task) + - [Image](#image) + - [Image Classification](#image-classification) + - [Multi Image](#multi-image) + - [Video](#multi-image) +- [Converter](#converter) + - [COCO](#coco) + ## Installation ```bash @@ -25,7 +38,7 @@ import fastlabel client = fastlabel.Client() ``` -## Limitation +### Limitation API is allowed to call 10000 times per 10 minutes. If you create/delete a large size of tasks, please wait a second for every requests. From cd5b878f12b7be97c691fae1142c16cb66316f40 Mon Sep 17 00:00:00 2001 From: eisuke-ueta <39715045+eisuke-ueta@users.noreply.github.com> Date: Sun, 23 May 2021 12:00:06 +0900 Subject: [PATCH 2/8] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82d2afd..93ec23f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Image](#image) - [Image Classification](#image-classification) - [Multi Image](#multi-image) - - [Video](#multi-image) + - [Video](#video) + - [Common](#common) - [Converter](#converter) - [COCO](#coco) From eecbd4c4f9899ec388630bd894956e335e4822c9 Mon Sep 17 00:00:00 2001 From: eisuke-ueta <39715045+eisuke-ueta@users.noreply.github.com> Date: Sun, 23 May 2021 12:33:11 +0900 Subject: [PATCH 3/8] remove $ from snippet --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93ec23f..754706d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ _If you are using FastLabel prototype, please install version 0.2.2._ ## Installation ```bash -$ pip install --upgrade fastlabel +pip install --upgrade fastlabel ``` > Python version 3.7 or greater is required From 1cece409dd9bb28c72099567065df3604cc68a96 Mon Sep 17 00:00:00 2001 From: faycute Date: Mon, 24 May 2021 11:25:33 +0900 Subject: [PATCH 4/8] initial commit --- fastlabel/__init__.py | 72 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 4c34e79..a11a621 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -11,7 +11,10 @@ logger = getLogger(__name__) -FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" +# FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" + +FASTLABEL_ENDPOINT = "http://localhost:4000/v1/" +# export FASTLABEL_ACCESS_TOKEN=OTc3MGUxODgtZDU5My00NWFlLWJlNzAtYjY1NDI3NzhmNWM1 class Client: @@ -134,6 +137,18 @@ def find_task(self, task_id: str) -> dict: endpoint = "tasks/" + task_id return self.__getrequest(endpoint) + def find_task_by_name(self, project: str, task_name: str) -> dict: + """ + Find a signle task by name. + + project is slug of your project. (Required) + task_name is a task name. (Required) + """ + tasks = self.get_tasks(project=project, task_name=task_name) + if not tasks: + return None + return tasks[0] + def find_multi_image_task(self, task_id: str) -> dict: """ Find a signle multi image task. @@ -141,11 +156,25 @@ def find_multi_image_task(self, task_id: str) -> dict: endpoint = "tasks/multi/image/" + task_id return self.__getrequest(endpoint) + def find_multi_image_task_by_name(self, project: str, task_name: str) -> dict: + """ + Find a signle multi image task by name. + + project is slug of your project. (Required) + task_name is a task name. (Required) + """ + tasks = self.get_multi_image_tasks( + project=project, task_name=task_name) + if not tasks: + return None + return tasks[0] + def get_tasks( self, project: str, status: str = None, tags: list = [], + task_name: str = None, offset: int = None, limit: int = 100, ) -> list: @@ -156,15 +185,21 @@ def get_tasks( project is slug of your project. (Required) status can be 'registered', 'in_progress', 'completed', 'skipped', 'in_review', 'send_backed', 'approved', 'customer_in_review', 'customer_send_backed', 'customer_approved'. (Optional) tags is a list of tag. (Optional) + task_name is a task name. (Optional) offset is the starting position number to fetch. (Optional) limit is the max number to fetch. (Optional) """ + if limit > 1000: + raise FastLabelInvalidException( + "Limit must be less than or equal to 1000.", 422) endpoint = "tasks" params = {"project": project} if status: params["status"] = status if tags: params["tags"] = tags + if task_name: + params["taskName"] = task_name if offset: params["offset"] = offset if limit: @@ -176,6 +211,7 @@ def get_multi_image_tasks( project: str, status: str = None, tags: list = [], + task_name: str = None, offset: int = None, limit: int = 10, ) -> dict: @@ -186,6 +222,7 @@ def get_multi_image_tasks( project is slug of your project. (Required) status can be 'registered', 'in_progress', 'completed', 'skipped', 'in_review', 'send_backed', 'approved', 'customer_in_review', 'customer_send_backed', 'customer_approved'. (Optional) tags is a list of tag. (Optional) + task_name is a task name. (Optional) offset is the starting position number to fetch. (Optional) limit is the max number to fetch. (Optional) """ @@ -198,6 +235,36 @@ def get_multi_image_tasks( params["status"] = status if tags: params["tags"] = tags + if task_name: + params["taskName"] = task_name + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.__getrequest(endpoint, params=params) + + def get_task_id_name_map( + self, project: str, + offset: int = None, + limit: int = 1000, + ) -> list: + """ + Returns a list of task ids and names. + e.g.) [ + {"id": "88e74507-07b5-4607-a130-cb6316ca872c", "name": "01_cat.jpg"} + {"id": "fe2c24a4-8270-46eb-9c78-bb7281c8bdgs", "name": "02_cat.jpg"} + ] + Returns up to 1000 at a time, to get more, set offset as the starting position to fetch. + + project is slug of your project. (Required) + offset is the starting position number to fetch. (Optional) + limit is the max number to fetch. (Optional) + """ + if limit > 1000: + raise FastLabelInvalidException( + "Limit must be less than or equal to 1000.", 422) + endpoint = "tasks/map/id-name" + params = {"project": project} if offset: params["offset"] = offset if limit: @@ -312,6 +379,9 @@ def delete_task(self, task_id: str) -> None: self.__deleterequest(endpoint) def to_coco(self, tasks: list) -> dict: + """ + Convert tasks to COCO format. + """ # Get categories categories = self.__get_categories(tasks) From 4109787440beb6c8c8f17b07daeeefa9aea6d388 Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 25 May 2021 14:49:13 +0900 Subject: [PATCH 5/8] update --- README.md | 195 ++++++++++++++++++++++++++++++++++++++++-- fastlabel/__init__.py | 154 +++++++++++++++++++++++++++++++-- fastlabel/api.py | 5 +- 3 files changed, 338 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 754706d..5e8b0ad 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,16 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Installation](#installation) - [Usage](#usage) - - [Limitation](#limitation) + - [Limitation](#limitation) - [Task](#task) - - [Image](#image) - - [Image Classification](#image-classification) - - [Multi Image](#multi-image) - - [Video](#video) - - [Common](#common) + - [Image](#image) + - [Image Classification](#image-classification) + - [Multi Image](#multi-image) + - [Video](#video) + - [Common](#common) +- [Annotation](#annotation) - [Converter](#converter) - - [COCO](#coco) + - [COCO](#coco) ## Installation @@ -104,6 +105,12 @@ task_id = client.create_image_task( task = client.find_image_task(task_id="YOUR_TASK_ID") ``` +- Find a single task by name. + +```python +tasks = client.find_image_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks - Get tasks. (Up to 1000 tasks) @@ -480,6 +487,180 @@ task_id = client.update_task( client.delete_task(task_id="YOUR_TASK_ID") ``` +#### Get Tasks Id and Name map + +```python +map = client.get_task_id_name_map(project="YOUR_PROJECT_SLUG") +``` + +## Annotation + +### Create Annotaion + +- Create a new annotation. + +```python +annotation_id = client.create_annotation( + project="YOUR_PROJECT_SLUG", type="bbox", value="cat", title="Cat", color="#FF0000") +``` + +- Create a new annotation with attributes. + +```python +attributes = [ + { + "type": "text", + "name": "Kind", + "key": "kind" + }, + { + "type": "select", + "name": "Size", + "key": "size", + "options": [ # select, radio and checkbox type needs options + { + "title": "Large", + "value": "large" + }, + { + "title": "Small", + "value": "small" + }, + ] + }, +] +annotation_id = client.create_annotation( + project="YOUR_PROJECT_SLUG", type="bbox", value="cat", title="Cat", color="#FF0000", attributes=attributes) +``` + +- Create a new classification annotation. + +```python +annotation_id = client.create_classification_annotation( + project="YOUR_PROJECT_SLUG", attributes=attributes) +``` + +### Find Annotation + +- Find an annotation. + +```python +annotaion = client.find_annotation(annotation_id="YOUR_ANNOTATIPN_ID") +``` + +- Find an annotation by value. + +```python +annotaion = client.find_annotation_by_value(project="YOUR_PROJECT_SLUG", value="cat") +``` + +- Find an annotation by value in classification project. + +```python +annotaion = client.find_annotation_by_value( + project="YOUR_PROJECT_SLUG", value="classification") # "classification" is fixed value +``` + +### Get Annotations + +- Get annotations. (Up to 1000 annotations) + +```python +annotatios = client.get_annotations(project="YOUR_PROJECT_SLUG") +``` + +### Response + +- Example of an annotation object + +```python +{ + "id": "YOUR_ANNOTATION_ID", + "type": "bbox", + "value": "cat", + "title": "Cat", + "color": "#FF0000", + "attributes": [ + { + "id": "YOUR_ATTRIBUTE_ID", + "key": "kind", + "name": "Kind", + "options": [], + "type": "text", + "value": "" + }, + { + "id": "YOUR_ATTRIBUTE_ID", + "key": "size", + "name": "Size", + "options": [ + {"title": "Large", "value": "large"}, + {"title": "Small", "value": "small"} + ], + "type": "select", + "value": "" + } + ], + "createdAt": "2021-05-25T05:36:50.459Z", + "updatedAt": "2021-05-25T05:36:50.459Z" +} +``` + +### Update Annotation + +- Update an annotation. + +```python +annotation_id = client.update_annotation( + annotation_id="YOUR_ANNOTATION_ID", value="cat2", title="Cat2", color="#FF0000") +``` + +- Update an annotation with attributes. + +```python +attributes = [ + { + "id": "YOUR_ATTRIBUTE_ID", # check by sdk get methods + "type": "text", + "name": "Kind2", + "key": "kind2" + }, + { + "id": "YOUR_ATTRIBUTE_ID", + "type": "select", + "name": "Size2", + "key": "size2", + "options": [ + { + "title": "Large2", + "value": "large2" + }, + { + "title": "Small2", + "value": "small2" + }, + ] + }, +] +annotation_id = client.update_annotation( + annotation_id="YOUR_ANNOTATION_ID", value="cat2", title="Cat2", color="#FF0000", attributes=attributes) +``` + +- Update a classification annotation. + +```python +annotation_id = client.update_classification_annotation( + project="YOUR_PROJECT_SLUG", attributes=attributes) +``` + +### Delete Annotation + +- Delete an annotation. + +```python +client.delete_annotation(annotation_id="YOUR_ANNOTATIPN_ID") +``` + ## Converter ### COCO diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 2d3d577..2198ab9 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -249,10 +249,10 @@ def get_task_id_name_map( ) -> list: """ Returns a list of task ids and names. - e.g.) [ - {"id": "88e74507-07b5-4607-a130-cb6316ca872c", "name": "01_cat.jpg"} - {"id": "fe2c24a4-8270-46eb-9c78-bb7281c8bdgs", "name": "02_cat.jpg"} - ] + e.g.) { + "88e74507-07b5-4607-a130-cb6316ca872c", "01_cat.jpg", + "fe2c24a4-8270-46eb-9c78-bb7281c8bdgs", "02_cat.jpg" + } Returns up to 1000 at a time, to get more, set offset as the starting position to fetch. project is slug of your project. (Required) @@ -445,7 +445,7 @@ def delete_task(self, task_id: str) -> None: endpoint = "tasks/" + task_id self.api.delete_request(endpoint) - # Convert + # Task Convert def to_coco(self, tasks: list) -> dict: """ @@ -453,3 +453,147 @@ def to_coco(self, tasks: list) -> dict: """ return converters.to_coco(tasks) + + # Annotation + + def find_annotation(self, annotation_id: str) -> dict: + """ + Find an annotation. + """ + endpoint = "annotations/" + annotation_id + return self.api.get_request(endpoint) + + def find_annotation_by_value(self, project: str, value: str) -> dict: + """ + Find an annotation by value. + """ + annotations = self.get_annotations(project=project, value=value) + if not annotations: + return None + return annotations[0] + + def get_annotations( + self, + project: str, + value: str = None, + offset: int = None, + limit: int = 10, + ) -> list: + """ + Returns a list of annotations. + Returns up to 1000 at a time, to get more, set offset as the starting position to fetch. + + project is slug of your project. (Required) + value is an unique identifier of annotation in your project. (Required) + offset is the starting position number to fetch. (Optional) + limit is the max number to fetch. (Optional) + """ + if limit > 1000: + raise FastLabelInvalidException( + "Limit must be less than or equal to 1000.", 422) + endpoint = "annotations" + params = {"project": project} + if value: + params["value"] = value + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.api.get_request(endpoint, params=params) + + def create_annotation( + self, + project: str, + type: str, + value: str, + title: str, + color: str, + attributes: list = [] + ) -> str: + """ + Create an annotation. + + project is slug of your project. (Required) + type can be 'bbox', 'polygon', 'keypoint', 'classification', 'line', 'segmentation'. (Required) + value is an unique identifier of annotation in your project. (Required) + title is a display name of value. (Required) + color is hex color code like #ffffff. (Required) + attributes is a list of attribute. (Optional) + """ + endpoint = "annotations" + payload = { + "project": project, + "type": type, + "value": value, + "title": title, + "color": color + } + if attributes: + payload["attributes"] = attributes + return self.api.post_request(endpoint, payload=payload) + + def create_classification_annotation( + self, + project: str, + attributes: list = [] + ) -> str: + """ + Create a classification annotation. + + project is slug of your project. (Required) + attributes is a list of attribute. (Required) + """ + endpoint = "annotations/classification" + payload = {"project": project, "attributes": attributes} + return self.api.post_request(endpoint, payload=payload) + + def update_annotation( + self, + annotation_id: str, + value: str = None, + title: str = None, + color: str = None, + attributes: list = [] + ) -> str: + """ + Update an annotation. + + annotation_id is an id of the annotation. (Required) + value is an unique identifier of annotation in your project. (Optional) + title is a display name of value. (Optional) + color is hex color code like #ffffff. (Optional) + attributes is a list of attribute. (Optional) + """ + endpoint = "annotations/" + annotation_id + payload = {} + if value: + payload["value"] = value + if title: + payload["title"] = title + if color: + payload["color"] = color + if attributes: + payload["attributes"] = attributes + return self.api.put_request(endpoint, payload=payload) + + def update_classification_annotation( + self, + annotation_id: str, + attributes: list + ) -> str: + """ + Update a classification annotation. + + annotation_id is an id of the annotation. (Required) + attributes is a list of attribute. (Required) + """ + endpoint = "annotations/classification/" + annotation_id + payload = {"attributes": attributes} + return self.api.put_request(endpoint, payload=payload) + + def delete_annotation(self, annotation_id: str) -> None: + """ + Delete an annotation. + """ + endpoint = "annotations/" + annotation_id + self.api.delete_request(endpoint) diff --git a/fastlabel/api.py b/fastlabel/api.py index 7f1fb52..fafca89 100644 --- a/fastlabel/api.py +++ b/fastlabel/api.py @@ -3,10 +3,7 @@ from .exceptions import FastLabelException, FastLabelInvalidException -# FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" - -FASTLABEL_ENDPOINT = "http://localhost:4000/v1/" -# export FASTLABEL_ACCESS_TOKEN=OTc3MGUxODgtZDU5My00NWFlLWJlNzAtYjY1NDI3NzhmNWM1 +FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" class Api: From f0e8517c04d34ef85ea9aa229dd2f8e1c4572be1 Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 25 May 2021 16:03:48 +0900 Subject: [PATCH 6/8] fix --- fastlabel/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 2198ab9..f3f80bd 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -248,7 +248,7 @@ def get_task_id_name_map( limit: int = 1000, ) -> list: """ - Returns a list of task ids and names. + Returns a map of task ids and names. e.g.) { "88e74507-07b5-4607-a130-cb6316ca872c", "01_cat.jpg", "fe2c24a4-8270-46eb-9c78-bb7281c8bdgs", "02_cat.jpg" @@ -535,7 +535,7 @@ def create_annotation( def create_classification_annotation( self, project: str, - attributes: list = [] + attributes: list ) -> str: """ Create a classification annotation. From e4dc99907d3fc42f4b3b918aacf78892f776a7bf Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 25 May 2021 16:04:32 +0900 Subject: [PATCH 7/8] update setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9779540..ab109ed 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="fastlabel", - version="0.7.0", + version="0.7.1", author="eisuke-ueta", author_email="eisuke.ueta@fastlabel.ai", description="The official Python SDK for FastLabel API, the Data Platform for AI", From 4ad778cb257f9778f189894272459ff6ebce0e85 Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 25 May 2021 16:17:08 +0900 Subject: [PATCH 8/8] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e8b0ad..6807c19 100644 --- a/README.md +++ b/README.md @@ -517,7 +517,7 @@ attributes = [ "type": "select", "name": "Size", "key": "size", - "options": [ # select, radio and checkbox type needs options + "options": [ # select, radio and checkbox type requires options { "title": "Large", "value": "large"