label-studio-sdk 0.0.34__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- label_studio_sdk/__init__.py +206 -9
- label_studio_sdk/_extensions/label_studio_tools/__init__.py +0 -0
- label_studio_sdk/_extensions/label_studio_tools/core/__init__.py +0 -0
- label_studio_sdk/_extensions/label_studio_tools/core/label_config.py +163 -0
- label_studio_sdk/_extensions/label_studio_tools/core/utils/__init__.py +0 -0
- label_studio_sdk/_extensions/label_studio_tools/core/utils/exceptions.py +2 -0
- label_studio_sdk/_extensions/label_studio_tools/core/utils/io.py +228 -0
- label_studio_sdk/_extensions/label_studio_tools/core/utils/params.py +45 -0
- label_studio_sdk/_extensions/label_studio_tools/etl/__init__.py +1 -0
- label_studio_sdk/_extensions/label_studio_tools/etl/beam.py +34 -0
- label_studio_sdk/_extensions/label_studio_tools/etl/example.py +17 -0
- label_studio_sdk/_extensions/label_studio_tools/etl/registry.py +67 -0
- label_studio_sdk/_extensions/label_studio_tools/postprocessing/__init__.py +0 -0
- label_studio_sdk/_extensions/label_studio_tools/postprocessing/video.py +97 -0
- label_studio_sdk/_legacy/__init__.py +11 -0
- label_studio_sdk/_legacy/client.py +471 -0
- label_studio_sdk/_legacy/label_interface/data_examples.json +96 -0
- label_studio_sdk/{label_interface → _legacy/label_interface}/interface.py +9 -6
- label_studio_sdk/{project.py → _legacy/project.py} +2 -2
- label_studio_sdk/actions/__init__.py +2 -0
- label_studio_sdk/actions/client.py +150 -0
- label_studio_sdk/annotations/__init__.py +2 -0
- label_studio_sdk/annotations/client.py +750 -0
- label_studio_sdk/client.py +162 -450
- label_studio_sdk/converter/__init__.py +7 -0
- label_studio_sdk/converter/audio.py +56 -0
- label_studio_sdk/converter/brush.py +452 -0
- label_studio_sdk/converter/converter.py +1175 -0
- label_studio_sdk/converter/exports/__init__.py +0 -0
- label_studio_sdk/converter/exports/csv.py +82 -0
- label_studio_sdk/converter/exports/csv2.py +103 -0
- label_studio_sdk/converter/funsd.py +85 -0
- label_studio_sdk/converter/imports/__init__.py +0 -0
- label_studio_sdk/converter/imports/coco.py +314 -0
- label_studio_sdk/converter/imports/colors.py +198 -0
- label_studio_sdk/converter/imports/label_config.py +45 -0
- label_studio_sdk/converter/imports/pathtrack.py +269 -0
- label_studio_sdk/converter/imports/yolo.py +236 -0
- label_studio_sdk/converter/main.py +202 -0
- label_studio_sdk/converter/utils.py +473 -0
- label_studio_sdk/core/__init__.py +33 -0
- label_studio_sdk/core/api_error.py +15 -0
- label_studio_sdk/core/client_wrapper.py +55 -0
- label_studio_sdk/core/datetime_utils.py +28 -0
- label_studio_sdk/core/file.py +38 -0
- label_studio_sdk/core/http_client.py +443 -0
- label_studio_sdk/core/jsonable_encoder.py +99 -0
- label_studio_sdk/core/pagination.py +87 -0
- label_studio_sdk/core/pydantic_utilities.py +28 -0
- label_studio_sdk/core/query_encoder.py +33 -0
- label_studio_sdk/core/remove_none_from_dict.py +11 -0
- label_studio_sdk/core/request_options.py +32 -0
- label_studio_sdk/environment.py +7 -0
- label_studio_sdk/errors/__init__.py +6 -0
- label_studio_sdk/errors/bad_request_error.py +8 -0
- label_studio_sdk/errors/internal_server_error.py +8 -0
- label_studio_sdk/export_storage/__init__.py +28 -0
- label_studio_sdk/export_storage/azure/__init__.py +5 -0
- label_studio_sdk/export_storage/azure/client.py +722 -0
- label_studio_sdk/export_storage/azure/types/__init__.py +6 -0
- label_studio_sdk/export_storage/azure/types/azure_create_response.py +52 -0
- label_studio_sdk/export_storage/azure/types/azure_update_response.py +52 -0
- label_studio_sdk/export_storage/client.py +107 -0
- label_studio_sdk/export_storage/gcs/__init__.py +5 -0
- label_studio_sdk/export_storage/gcs/client.py +722 -0
- label_studio_sdk/export_storage/gcs/types/__init__.py +6 -0
- label_studio_sdk/export_storage/gcs/types/gcs_create_response.py +52 -0
- label_studio_sdk/export_storage/gcs/types/gcs_update_response.py +52 -0
- label_studio_sdk/export_storage/local/__init__.py +5 -0
- label_studio_sdk/export_storage/local/client.py +688 -0
- label_studio_sdk/export_storage/local/types/__init__.py +6 -0
- label_studio_sdk/export_storage/local/types/local_create_response.py +47 -0
- label_studio_sdk/export_storage/local/types/local_update_response.py +47 -0
- label_studio_sdk/export_storage/redis/__init__.py +5 -0
- label_studio_sdk/export_storage/redis/client.py +714 -0
- label_studio_sdk/export_storage/redis/types/__init__.py +6 -0
- label_studio_sdk/export_storage/redis/types/redis_create_response.py +57 -0
- label_studio_sdk/export_storage/redis/types/redis_update_response.py +57 -0
- label_studio_sdk/export_storage/s3/__init__.py +5 -0
- label_studio_sdk/export_storage/s3/client.py +820 -0
- label_studio_sdk/export_storage/s3/types/__init__.py +6 -0
- label_studio_sdk/export_storage/s3/types/s3create_response.py +74 -0
- label_studio_sdk/export_storage/s3/types/s3update_response.py +74 -0
- label_studio_sdk/export_storage/types/__init__.py +5 -0
- label_studio_sdk/export_storage/types/export_storage_list_types_response_item.py +30 -0
- label_studio_sdk/files/__init__.py +2 -0
- label_studio_sdk/files/client.py +556 -0
- label_studio_sdk/import_storage/__init__.py +28 -0
- label_studio_sdk/import_storage/azure/__init__.py +5 -0
- label_studio_sdk/import_storage/azure/client.py +812 -0
- label_studio_sdk/import_storage/azure/types/__init__.py +6 -0
- label_studio_sdk/import_storage/azure/types/azure_create_response.py +72 -0
- label_studio_sdk/import_storage/azure/types/azure_update_response.py +72 -0
- label_studio_sdk/import_storage/client.py +107 -0
- label_studio_sdk/import_storage/gcs/__init__.py +5 -0
- label_studio_sdk/import_storage/gcs/client.py +812 -0
- label_studio_sdk/import_storage/gcs/types/__init__.py +6 -0
- label_studio_sdk/import_storage/gcs/types/gcs_create_response.py +72 -0
- label_studio_sdk/import_storage/gcs/types/gcs_update_response.py +72 -0
- label_studio_sdk/import_storage/local/__init__.py +5 -0
- label_studio_sdk/import_storage/local/client.py +690 -0
- label_studio_sdk/import_storage/local/types/__init__.py +6 -0
- label_studio_sdk/import_storage/local/types/local_create_response.py +47 -0
- label_studio_sdk/import_storage/local/types/local_update_response.py +47 -0
- label_studio_sdk/import_storage/redis/__init__.py +5 -0
- label_studio_sdk/import_storage/redis/client.py +768 -0
- label_studio_sdk/import_storage/redis/types/__init__.py +6 -0
- label_studio_sdk/import_storage/redis/types/redis_create_response.py +62 -0
- label_studio_sdk/import_storage/redis/types/redis_update_response.py +62 -0
- label_studio_sdk/import_storage/s3/__init__.py +5 -0
- label_studio_sdk/import_storage/s3/client.py +912 -0
- label_studio_sdk/import_storage/s3/types/__init__.py +6 -0
- label_studio_sdk/import_storage/s3/types/s3create_response.py +99 -0
- label_studio_sdk/import_storage/s3/types/s3update_response.py +99 -0
- label_studio_sdk/import_storage/types/__init__.py +5 -0
- label_studio_sdk/import_storage/types/import_storage_list_types_response_item.py +30 -0
- label_studio_sdk/ml/__init__.py +19 -0
- label_studio_sdk/ml/client.py +981 -0
- label_studio_sdk/ml/types/__init__.py +17 -0
- label_studio_sdk/ml/types/ml_create_request_auth_method.py +5 -0
- label_studio_sdk/ml/types/ml_create_response.py +78 -0
- label_studio_sdk/ml/types/ml_create_response_auth_method.py +5 -0
- label_studio_sdk/ml/types/ml_update_request_auth_method.py +5 -0
- label_studio_sdk/ml/types/ml_update_response.py +78 -0
- label_studio_sdk/ml/types/ml_update_response_auth_method.py +5 -0
- label_studio_sdk/predictions/__init__.py +2 -0
- label_studio_sdk/predictions/client.py +638 -0
- label_studio_sdk/projects/__init__.py +6 -0
- label_studio_sdk/projects/client.py +1053 -0
- label_studio_sdk/projects/exports/__init__.py +2 -0
- label_studio_sdk/projects/exports/client.py +930 -0
- label_studio_sdk/projects/types/__init__.py +7 -0
- label_studio_sdk/projects/types/projects_create_response.py +96 -0
- label_studio_sdk/projects/types/projects_import_tasks_response.py +71 -0
- label_studio_sdk/projects/types/projects_list_response.py +33 -0
- label_studio_sdk/py.typed +0 -0
- label_studio_sdk/tasks/__init__.py +5 -0
- label_studio_sdk/tasks/client.py +811 -0
- label_studio_sdk/tasks/types/__init__.py +6 -0
- label_studio_sdk/tasks/types/tasks_list_request_fields.py +5 -0
- label_studio_sdk/tasks/types/tasks_list_response.py +48 -0
- label_studio_sdk/types/__init__.py +115 -0
- label_studio_sdk/types/annotation.py +116 -0
- label_studio_sdk/types/annotation_filter_options.py +42 -0
- label_studio_sdk/types/annotation_last_action.py +19 -0
- label_studio_sdk/types/azure_blob_export_storage.py +112 -0
- label_studio_sdk/types/azure_blob_export_storage_status.py +7 -0
- label_studio_sdk/types/azure_blob_import_storage.py +113 -0
- label_studio_sdk/types/azure_blob_import_storage_status.py +7 -0
- label_studio_sdk/types/base_task.py +113 -0
- label_studio_sdk/types/base_user.py +42 -0
- label_studio_sdk/types/converted_format.py +36 -0
- label_studio_sdk/types/converted_format_status.py +5 -0
- label_studio_sdk/types/export.py +48 -0
- label_studio_sdk/types/export_convert.py +32 -0
- label_studio_sdk/types/export_create.py +54 -0
- label_studio_sdk/types/export_create_status.py +5 -0
- label_studio_sdk/types/export_status.py +5 -0
- label_studio_sdk/types/file_upload.py +30 -0
- label_studio_sdk/types/filter.py +53 -0
- label_studio_sdk/types/filter_group.py +35 -0
- label_studio_sdk/types/gcs_export_storage.py +112 -0
- label_studio_sdk/types/gcs_export_storage_status.py +7 -0
- label_studio_sdk/types/gcs_import_storage.py +113 -0
- label_studio_sdk/types/gcs_import_storage_status.py +7 -0
- label_studio_sdk/types/local_files_export_storage.py +97 -0
- label_studio_sdk/types/local_files_export_storage_status.py +7 -0
- label_studio_sdk/types/local_files_import_storage.py +92 -0
- label_studio_sdk/types/local_files_import_storage_status.py +7 -0
- label_studio_sdk/types/ml_backend.py +89 -0
- label_studio_sdk/types/ml_backend_auth_method.py +5 -0
- label_studio_sdk/types/ml_backend_state.py +5 -0
- label_studio_sdk/types/prediction.py +78 -0
- label_studio_sdk/types/project.py +198 -0
- label_studio_sdk/types/project_import.py +63 -0
- label_studio_sdk/types/project_import_status.py +5 -0
- label_studio_sdk/types/project_label_config.py +32 -0
- label_studio_sdk/types/project_sampling.py +7 -0
- label_studio_sdk/types/project_skip_queue.py +5 -0
- label_studio_sdk/types/redis_export_storage.py +117 -0
- label_studio_sdk/types/redis_export_storage_status.py +7 -0
- label_studio_sdk/types/redis_import_storage.py +112 -0
- label_studio_sdk/types/redis_import_storage_status.py +7 -0
- label_studio_sdk/types/s3export_storage.py +134 -0
- label_studio_sdk/types/s3export_storage_status.py +7 -0
- label_studio_sdk/types/s3import_storage.py +140 -0
- label_studio_sdk/types/s3import_storage_status.py +7 -0
- label_studio_sdk/types/serialization_option.py +36 -0
- label_studio_sdk/types/serialization_options.py +45 -0
- label_studio_sdk/types/task.py +157 -0
- label_studio_sdk/types/task_filter_options.py +49 -0
- label_studio_sdk/types/user_simple.py +37 -0
- label_studio_sdk/types/view.py +55 -0
- label_studio_sdk/types/webhook.py +67 -0
- label_studio_sdk/types/webhook_actions_item.py +21 -0
- label_studio_sdk/types/webhook_serializer_for_update.py +67 -0
- label_studio_sdk/types/webhook_serializer_for_update_actions_item.py +21 -0
- label_studio_sdk/users/__init__.py +5 -0
- label_studio_sdk/users/client.py +830 -0
- label_studio_sdk/users/types/__init__.py +6 -0
- label_studio_sdk/users/types/users_get_token_response.py +36 -0
- label_studio_sdk/users/types/users_reset_token_response.py +36 -0
- label_studio_sdk/version.py +4 -0
- label_studio_sdk/views/__init__.py +31 -0
- label_studio_sdk/views/client.py +564 -0
- label_studio_sdk/views/types/__init__.py +29 -0
- label_studio_sdk/views/types/views_create_request_data.py +43 -0
- label_studio_sdk/views/types/views_create_request_data_filters.py +43 -0
- label_studio_sdk/views/types/views_create_request_data_filters_conjunction.py +5 -0
- label_studio_sdk/views/types/views_create_request_data_filters_items_item.py +47 -0
- label_studio_sdk/views/types/views_create_request_data_ordering_item.py +38 -0
- label_studio_sdk/views/types/views_create_request_data_ordering_item_direction.py +5 -0
- label_studio_sdk/views/types/views_update_request_data.py +43 -0
- label_studio_sdk/views/types/views_update_request_data_filters.py +43 -0
- label_studio_sdk/views/types/views_update_request_data_filters_conjunction.py +5 -0
- label_studio_sdk/views/types/views_update_request_data_filters_items_item.py +47 -0
- label_studio_sdk/views/types/views_update_request_data_ordering_item.py +38 -0
- label_studio_sdk/views/types/views_update_request_data_ordering_item_direction.py +5 -0
- label_studio_sdk/webhooks/__init__.py +5 -0
- label_studio_sdk/webhooks/client.py +636 -0
- label_studio_sdk/webhooks/types/__init__.py +5 -0
- label_studio_sdk/webhooks/types/webhooks_update_request_actions_item.py +21 -0
- label_studio_sdk-1.0.0.dist-info/METADATA +307 -0
- label_studio_sdk-1.0.0.dist-info/RECORD +239 -0
- {label_studio_sdk-0.0.34.dist-info → label_studio_sdk-1.0.0.dist-info}/WHEEL +1 -2
- label_studio_sdk-0.0.34.dist-info/LICENSE +0 -201
- label_studio_sdk-0.0.34.dist-info/METADATA +0 -24
- label_studio_sdk-0.0.34.dist-info/RECORD +0 -37
- label_studio_sdk-0.0.34.dist-info/top_level.txt +0 -2
- tests/test_client.py +0 -37
- tests/test_export.py +0 -105
- tests/test_interface/__init__.py +0 -1
- tests/test_interface/configs.py +0 -137
- tests/test_interface/mockups.py +0 -22
- tests/test_interface/test_compat.py +0 -64
- tests/test_interface/test_control_tags.py +0 -55
- tests/test_interface/test_data_generation.py +0 -45
- tests/test_interface/test_lpi.py +0 -15
- tests/test_interface/test_main.py +0 -196
- tests/test_interface/test_object_tags.py +0 -36
- tests/test_interface/test_region.py +0 -36
- tests/test_interface/test_validate_summary.py +0 -35
- tests/test_interface/test_validation.py +0 -59
- {tests → label_studio_sdk/_extensions}/__init__.py +0 -0
- /label_studio_sdk/{exceptions.py → _legacy/exceptions.py} +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/__init__.py +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/base.py +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/control_tags.py +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/label_tags.py +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/object_tags.py +0 -0
- /label_studio_sdk/{label_interface → _legacy/label_interface}/region.py +0 -0
- /label_studio_sdk/{objects.py → _legacy/objects.py} +0 -0
- /label_studio_sdk/{schema → _legacy/schema}/label_config_schema.json +0 -0
- /label_studio_sdk/{users.py → _legacy/users.py} +0 -0
- /label_studio_sdk/{utils.py → _legacy/utils.py} +0 -0
- /label_studio_sdk/{workspaces.py → _legacy/workspaces.py} +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json # better to use "imports ujson as json" for the best performance
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from PIL import Image
|
|
8
|
+
from typing import Optional, Tuple
|
|
9
|
+
from urllib.request import (
|
|
10
|
+
pathname2url,
|
|
11
|
+
) # for converting "+","*", etc. in file paths to appropriate urls
|
|
12
|
+
|
|
13
|
+
from label_studio_sdk.converter.utils import ExpandFullPath
|
|
14
|
+
from label_studio_sdk.converter.imports.label_config import generate_label_config
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("root")
|
|
17
|
+
default_image_root_url = "/data/local-files/?d=images"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def convert_yolo_to_ls(
|
|
21
|
+
input_dir,
|
|
22
|
+
out_file,
|
|
23
|
+
to_name="image",
|
|
24
|
+
from_name="label",
|
|
25
|
+
out_type="annotations",
|
|
26
|
+
image_root_url=default_image_root_url,
|
|
27
|
+
image_ext=".jpg,.jpeg,.png",
|
|
28
|
+
image_dims: Optional[Tuple[int, int]] = None,
|
|
29
|
+
):
|
|
30
|
+
"""Convert YOLO labeling to Label Studio JSON
|
|
31
|
+
:param input_dir: directory with YOLO where images, labels, notes.json are located
|
|
32
|
+
:param out_file: output file with Label Studio JSON tasks
|
|
33
|
+
:param to_name: object name from Label Studio labeling config
|
|
34
|
+
:param from_name: control tag name from Label Studio labeling config
|
|
35
|
+
:param out_type: annotation type - "annotations" or "predictions"
|
|
36
|
+
:param image_root_url: root URL path where images will be hosted, e.g.: http://example.com/images
|
|
37
|
+
:param image_ext: image extension/s - single string or comma separated list to search, eg. .jpeg or .jpg, .png and so on.
|
|
38
|
+
:param image_dims: image dimensions - optional tuple of integers specifying the image width and height of *all* images in the dataset. Defaults to opening the image to determine it's width and height, which is slower. This should only be used in the special case where you dataset has uniform image dimesions.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
tasks = []
|
|
42
|
+
logger.info("Reading YOLO notes and categories from %s", input_dir)
|
|
43
|
+
|
|
44
|
+
# build categories=>labels dict
|
|
45
|
+
notes_file = os.path.join(input_dir, "classes.txt")
|
|
46
|
+
with open(notes_file) as f:
|
|
47
|
+
lines = [line.strip() for line in f.readlines()]
|
|
48
|
+
categories = {i: line for i, line in enumerate(lines)}
|
|
49
|
+
logger.info(f"Found {len(categories)} categories")
|
|
50
|
+
|
|
51
|
+
# generate and save labeling config
|
|
52
|
+
label_config_file = out_file.replace(".json", "") + ".label_config.xml"
|
|
53
|
+
generate_label_config(
|
|
54
|
+
categories,
|
|
55
|
+
{from_name: "RectangleLabels"},
|
|
56
|
+
to_name,
|
|
57
|
+
from_name,
|
|
58
|
+
label_config_file,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# define directories
|
|
62
|
+
labels_dir = os.path.join(input_dir, "labels")
|
|
63
|
+
images_dir = os.path.join(input_dir, "images")
|
|
64
|
+
logger.info("Converting labels from %s", labels_dir)
|
|
65
|
+
|
|
66
|
+
# build array out of provided comma separated image_extns (str -> array)
|
|
67
|
+
image_ext = [x.strip() for x in image_ext.split(",")]
|
|
68
|
+
logger.info(f"image extensions->, {image_ext}")
|
|
69
|
+
|
|
70
|
+
# loop through images
|
|
71
|
+
for f in os.listdir(images_dir):
|
|
72
|
+
image_file_found_flag = False
|
|
73
|
+
for ext in image_ext:
|
|
74
|
+
if f.endswith(ext):
|
|
75
|
+
image_file = f
|
|
76
|
+
image_file_base = os.path.splitext(f)[0]
|
|
77
|
+
image_file_found_flag = True
|
|
78
|
+
break
|
|
79
|
+
if not image_file_found_flag:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
image_root_url += "" if image_root_url.endswith("/") else "/"
|
|
83
|
+
task = {
|
|
84
|
+
"data": {
|
|
85
|
+
# eg. '../../foo+you.py' -> '../../foo%2Byou.py'
|
|
86
|
+
"image": image_root_url
|
|
87
|
+
+ str(pathname2url(image_file))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# define coresponding label file and check existence
|
|
92
|
+
label_file = os.path.join(labels_dir, image_file_base + ".txt")
|
|
93
|
+
|
|
94
|
+
if os.path.exists(label_file):
|
|
95
|
+
task[out_type] = [
|
|
96
|
+
{
|
|
97
|
+
"result": [],
|
|
98
|
+
"ground_truth": False,
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# read image sizes
|
|
103
|
+
if image_dims is None:
|
|
104
|
+
# default to opening file if we aren't given image dims. slow!
|
|
105
|
+
with Image.open(os.path.join(images_dir, image_file)) as im:
|
|
106
|
+
image_width, image_height = im.size
|
|
107
|
+
else:
|
|
108
|
+
image_width, image_height = image_dims
|
|
109
|
+
|
|
110
|
+
with open(label_file) as file:
|
|
111
|
+
# convert all bounding boxes to Label Studio Results
|
|
112
|
+
lines = file.readlines()
|
|
113
|
+
for line in lines:
|
|
114
|
+
values = line.split()
|
|
115
|
+
label_id, x, y, width, height = values[0:5]
|
|
116
|
+
score = float(values[5]) if len(values) >= 6 else None
|
|
117
|
+
x, y, width, height = (
|
|
118
|
+
float(x),
|
|
119
|
+
float(y),
|
|
120
|
+
float(width),
|
|
121
|
+
float(height),
|
|
122
|
+
)
|
|
123
|
+
item = {
|
|
124
|
+
"id": uuid.uuid4().hex[0:10],
|
|
125
|
+
"type": "rectanglelabels",
|
|
126
|
+
"value": {
|
|
127
|
+
"x": (x - width / 2) * 100,
|
|
128
|
+
"y": (y - height / 2) * 100,
|
|
129
|
+
"width": width * 100,
|
|
130
|
+
"height": height * 100,
|
|
131
|
+
"rotation": 0,
|
|
132
|
+
"rectanglelabels": [categories[int(label_id)]],
|
|
133
|
+
},
|
|
134
|
+
"to_name": to_name,
|
|
135
|
+
"from_name": from_name,
|
|
136
|
+
"image_rotation": 0,
|
|
137
|
+
"original_width": image_width,
|
|
138
|
+
"original_height": image_height,
|
|
139
|
+
}
|
|
140
|
+
if score:
|
|
141
|
+
item["score"] = score
|
|
142
|
+
task[out_type][0]["result"].append(item)
|
|
143
|
+
|
|
144
|
+
tasks.append(task)
|
|
145
|
+
|
|
146
|
+
if len(tasks) > 0:
|
|
147
|
+
logger.info("Saving Label Studio JSON to %s", out_file)
|
|
148
|
+
with open(out_file, "w") as out:
|
|
149
|
+
json.dump(tasks, out)
|
|
150
|
+
|
|
151
|
+
help_root_dir = ""
|
|
152
|
+
if image_root_url == default_image_root_url:
|
|
153
|
+
help_root_dir = (
|
|
154
|
+
f"Set environment variables LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true and "
|
|
155
|
+
f"LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT={input_dir} for Label Studio run,\n"
|
|
156
|
+
f"add Local Storage with Absolute local path = {input_dir}/images"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
print(
|
|
160
|
+
"\n"
|
|
161
|
+
f" 1. Create a new project in Label Studio\n"
|
|
162
|
+
f' 2. Use Labeling Config from "{label_config_file}"\n'
|
|
163
|
+
f" 3. Setup serving for images\n"
|
|
164
|
+
f" E.g. you can use Local Storage (or others):\n"
|
|
165
|
+
f" https://labelstud.io/guide/storage.html#Local-storage\n"
|
|
166
|
+
f" See tutorial here:\nhttps://github.com/HumanSignal/label-studio-converter/tree/master?tab=readme-ov-file#yolo-to-label-studio-converter\n"
|
|
167
|
+
f" {help_root_dir}\n"
|
|
168
|
+
f' 4. Import "{out_file}" to the project\n'
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
logger.error("No labels converted")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def add_parser(subparsers):
|
|
175
|
+
yolo = subparsers.add_parser("yolo")
|
|
176
|
+
|
|
177
|
+
yolo.add_argument(
|
|
178
|
+
"-i",
|
|
179
|
+
"--input",
|
|
180
|
+
dest="input",
|
|
181
|
+
required=True,
|
|
182
|
+
help="directory with YOLO where images, labels, notes.json are located",
|
|
183
|
+
action=ExpandFullPath,
|
|
184
|
+
)
|
|
185
|
+
yolo.add_argument(
|
|
186
|
+
"-o",
|
|
187
|
+
"--output",
|
|
188
|
+
dest="output",
|
|
189
|
+
help="output file with Label Studio JSON tasks",
|
|
190
|
+
default="output.json",
|
|
191
|
+
action=ExpandFullPath,
|
|
192
|
+
)
|
|
193
|
+
yolo.add_argument(
|
|
194
|
+
"--to-name",
|
|
195
|
+
dest="to_name",
|
|
196
|
+
help="object name from Label Studio labeling config",
|
|
197
|
+
default="image",
|
|
198
|
+
)
|
|
199
|
+
yolo.add_argument(
|
|
200
|
+
"--from-name",
|
|
201
|
+
dest="from_name",
|
|
202
|
+
help="control tag name from Label Studio labeling config",
|
|
203
|
+
default="label",
|
|
204
|
+
)
|
|
205
|
+
yolo.add_argument(
|
|
206
|
+
"--out-type",
|
|
207
|
+
dest="out_type",
|
|
208
|
+
help='annotation type - "annotations" or "predictions"',
|
|
209
|
+
default="annotations",
|
|
210
|
+
)
|
|
211
|
+
yolo.add_argument(
|
|
212
|
+
"--image-root-url",
|
|
213
|
+
dest="image_root_url",
|
|
214
|
+
help="root URL path where images will be hosted, e.g.: http://example.com/images",
|
|
215
|
+
default=default_image_root_url,
|
|
216
|
+
)
|
|
217
|
+
yolo.add_argument(
|
|
218
|
+
"--image-ext",
|
|
219
|
+
dest="image_ext",
|
|
220
|
+
help="image extension to search: .jpeg or .jpg, .png",
|
|
221
|
+
default=".jpg",
|
|
222
|
+
)
|
|
223
|
+
yolo.add_argument(
|
|
224
|
+
"--image-dims",
|
|
225
|
+
dest="image_dims",
|
|
226
|
+
type=int,
|
|
227
|
+
nargs=2,
|
|
228
|
+
help=(
|
|
229
|
+
"optional tuple of integers specifying the image width and height of *all* "
|
|
230
|
+
"images in the dataset. Defaults to opening the image to determine it's width "
|
|
231
|
+
"and height, which is slower. This should only be used in the special "
|
|
232
|
+
"case where you dataset has uniform image dimesions. e.g. `--image-dims 600 800` "
|
|
233
|
+
"if all your images are of dimensions width=600, height=800"
|
|
234
|
+
),
|
|
235
|
+
default=None,
|
|
236
|
+
)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from label_studio_sdk.converter.converter import (
|
|
6
|
+
Converter,
|
|
7
|
+
Format,
|
|
8
|
+
FormatNotSupportedError,
|
|
9
|
+
)
|
|
10
|
+
from label_studio_sdk.converter.exports.csv import ExportToCSV
|
|
11
|
+
from label_studio_sdk.converter.imports import yolo as import_yolo, coco as import_coco
|
|
12
|
+
from label_studio_sdk.converter.utils import ExpandFullPath
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(level=logging.INFO)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_export_args(parser):
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"-i",
|
|
20
|
+
"--input",
|
|
21
|
+
dest="input",
|
|
22
|
+
required=True,
|
|
23
|
+
help='Directory or JSON file with annotations (e.g. "/<project_path>/annotations")',
|
|
24
|
+
action=ExpandFullPath,
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"-c",
|
|
28
|
+
"--config",
|
|
29
|
+
dest="config",
|
|
30
|
+
help='Project config (e.g. "/<project_path>/config.xml")',
|
|
31
|
+
action=ExpandFullPath,
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"-o",
|
|
35
|
+
"--output",
|
|
36
|
+
dest="output",
|
|
37
|
+
help="Output file or directory (will be created if not exists)",
|
|
38
|
+
default=os.path.join(os.path.dirname(__file__), "output"),
|
|
39
|
+
action=ExpandFullPath,
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"-f",
|
|
43
|
+
"--format",
|
|
44
|
+
dest="format",
|
|
45
|
+
metavar="FORMAT",
|
|
46
|
+
help="Output format: " + ", ".join(f.name for f in Format),
|
|
47
|
+
type=Format.from_string,
|
|
48
|
+
choices=list(Format),
|
|
49
|
+
default=Format.JSON,
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--csv-separator",
|
|
53
|
+
dest="csv_separator",
|
|
54
|
+
help="Separator used in CSV format",
|
|
55
|
+
default=",",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--csv-no-header",
|
|
59
|
+
dest="csv_no_header",
|
|
60
|
+
help="Whether to omit header in CSV output file",
|
|
61
|
+
action="store_true",
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--image-dir",
|
|
65
|
+
dest="image_dir",
|
|
66
|
+
help="In case of image outputs (COCO, VOC, ...), specifies output image directory where downloaded images will "
|
|
67
|
+
"be stored. (If not specified, local image paths left untouched)",
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--project-dir",
|
|
71
|
+
dest="project_dir",
|
|
72
|
+
default=None,
|
|
73
|
+
help="Label Studio project directory path",
|
|
74
|
+
)
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"--heartex-format",
|
|
77
|
+
dest="heartex_format",
|
|
78
|
+
action="store_true",
|
|
79
|
+
default=True,
|
|
80
|
+
help="Set this flag if your annotations are in one JSON file instead of multiple JSON files from directory",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_all_args():
|
|
85
|
+
parser = argparse.ArgumentParser()
|
|
86
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
87
|
+
subparsers.required = False
|
|
88
|
+
|
|
89
|
+
# Export
|
|
90
|
+
parser_export = subparsers.add_parser(
|
|
91
|
+
"export",
|
|
92
|
+
help="Converter from Label Studio JSON annotations to external formats",
|
|
93
|
+
)
|
|
94
|
+
get_export_args(parser_export)
|
|
95
|
+
|
|
96
|
+
# Import
|
|
97
|
+
parser_import = subparsers.add_parser(
|
|
98
|
+
"import",
|
|
99
|
+
help="Converter from external formats to Label Studio JSON annotations",
|
|
100
|
+
)
|
|
101
|
+
import_format = parser_import.add_subparsers(dest="import_format")
|
|
102
|
+
import_yolo.add_parser(import_format)
|
|
103
|
+
import_coco.add_parser(import_format)
|
|
104
|
+
|
|
105
|
+
return parser.parse_args()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def export(args):
|
|
109
|
+
c = Converter(args.config, project_dir=args.project_dir)
|
|
110
|
+
|
|
111
|
+
if args.format == Format.JSON:
|
|
112
|
+
c.convert_to_json(args.input, args.output)
|
|
113
|
+
elif args.format == Format.CSV:
|
|
114
|
+
header = not args.csv_no_header
|
|
115
|
+
sep = args.csv_separator
|
|
116
|
+
c.convert_to_csv(
|
|
117
|
+
args.input,
|
|
118
|
+
args.output,
|
|
119
|
+
sep=sep,
|
|
120
|
+
header=header,
|
|
121
|
+
is_dir=not args.heartex_format,
|
|
122
|
+
)
|
|
123
|
+
elif args.format == Format.CSV_OLD:
|
|
124
|
+
header = not args.csv_no_header
|
|
125
|
+
sep = args.csv_separator
|
|
126
|
+
ExportToCSV(args.input).to_file(
|
|
127
|
+
args.output, sep=sep, header=header, index=False
|
|
128
|
+
)
|
|
129
|
+
elif args.format == Format.TSV:
|
|
130
|
+
header = not args.csv_no_header
|
|
131
|
+
sep = "\t"
|
|
132
|
+
c.convert_to_csv(
|
|
133
|
+
args.input,
|
|
134
|
+
args.output,
|
|
135
|
+
sep=sep,
|
|
136
|
+
header=header,
|
|
137
|
+
is_dir=not args.heartex_format,
|
|
138
|
+
)
|
|
139
|
+
elif args.format == Format.CONLL2003:
|
|
140
|
+
c.convert_to_conll2003(args.input, args.output, is_dir=not args.heartex_format)
|
|
141
|
+
elif args.format == Format.COCO:
|
|
142
|
+
c.convert_to_coco(
|
|
143
|
+
args.input,
|
|
144
|
+
args.output,
|
|
145
|
+
output_image_dir=args.image_dir,
|
|
146
|
+
is_dir=not args.heartex_format,
|
|
147
|
+
)
|
|
148
|
+
elif args.format == Format.VOC:
|
|
149
|
+
c.convert_to_voc(
|
|
150
|
+
args.input,
|
|
151
|
+
args.output,
|
|
152
|
+
output_image_dir=args.image_dir,
|
|
153
|
+
is_dir=not args.heartex_format,
|
|
154
|
+
)
|
|
155
|
+
elif args.format == Format.YOLO:
|
|
156
|
+
c.convert_to_yolo(args.input, args.output, is_dir=not args.heartex_format)
|
|
157
|
+
elif args.format == Format.YOLO_OBB:
|
|
158
|
+
c.convert_to_yolo(
|
|
159
|
+
args.input, args.output, is_dir=not args.heartex_format, is_obb=True
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
raise FormatNotSupportedError()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def imports(args):
|
|
166
|
+
if args.import_format == "yolo":
|
|
167
|
+
import_yolo.convert_yolo_to_ls(
|
|
168
|
+
input_dir=args.input,
|
|
169
|
+
out_file=args.output,
|
|
170
|
+
to_name=args.to_name,
|
|
171
|
+
from_name=args.from_name,
|
|
172
|
+
out_type=args.out_type,
|
|
173
|
+
image_root_url=args.image_root_url,
|
|
174
|
+
image_ext=args.image_ext,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
elif args.import_format == "coco":
|
|
178
|
+
import_coco.convert_coco_to_ls(
|
|
179
|
+
input_file=args.input,
|
|
180
|
+
out_file=args.output,
|
|
181
|
+
to_name=args.to_name,
|
|
182
|
+
from_name=args.from_name,
|
|
183
|
+
out_type=args.out_type,
|
|
184
|
+
image_root_url=args.image_root_url,
|
|
185
|
+
point_width=args.point_width,
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
raise FormatNotSupportedError()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def main():
|
|
192
|
+
args = get_all_args()
|
|
193
|
+
if args.command == "export":
|
|
194
|
+
export(args)
|
|
195
|
+
elif args.command == "import":
|
|
196
|
+
imports(args)
|
|
197
|
+
else:
|
|
198
|
+
print('Please, use "import" or "export" or "-h" command')
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
main()
|