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.
Files changed (256) hide show
  1. label_studio_sdk/__init__.py +206 -9
  2. label_studio_sdk/_extensions/label_studio_tools/__init__.py +0 -0
  3. label_studio_sdk/_extensions/label_studio_tools/core/__init__.py +0 -0
  4. label_studio_sdk/_extensions/label_studio_tools/core/label_config.py +163 -0
  5. label_studio_sdk/_extensions/label_studio_tools/core/utils/__init__.py +0 -0
  6. label_studio_sdk/_extensions/label_studio_tools/core/utils/exceptions.py +2 -0
  7. label_studio_sdk/_extensions/label_studio_tools/core/utils/io.py +228 -0
  8. label_studio_sdk/_extensions/label_studio_tools/core/utils/params.py +45 -0
  9. label_studio_sdk/_extensions/label_studio_tools/etl/__init__.py +1 -0
  10. label_studio_sdk/_extensions/label_studio_tools/etl/beam.py +34 -0
  11. label_studio_sdk/_extensions/label_studio_tools/etl/example.py +17 -0
  12. label_studio_sdk/_extensions/label_studio_tools/etl/registry.py +67 -0
  13. label_studio_sdk/_extensions/label_studio_tools/postprocessing/__init__.py +0 -0
  14. label_studio_sdk/_extensions/label_studio_tools/postprocessing/video.py +97 -0
  15. label_studio_sdk/_legacy/__init__.py +11 -0
  16. label_studio_sdk/_legacy/client.py +471 -0
  17. label_studio_sdk/_legacy/label_interface/data_examples.json +96 -0
  18. label_studio_sdk/{label_interface → _legacy/label_interface}/interface.py +9 -6
  19. label_studio_sdk/{project.py → _legacy/project.py} +2 -2
  20. label_studio_sdk/actions/__init__.py +2 -0
  21. label_studio_sdk/actions/client.py +150 -0
  22. label_studio_sdk/annotations/__init__.py +2 -0
  23. label_studio_sdk/annotations/client.py +750 -0
  24. label_studio_sdk/client.py +162 -450
  25. label_studio_sdk/converter/__init__.py +7 -0
  26. label_studio_sdk/converter/audio.py +56 -0
  27. label_studio_sdk/converter/brush.py +452 -0
  28. label_studio_sdk/converter/converter.py +1175 -0
  29. label_studio_sdk/converter/exports/__init__.py +0 -0
  30. label_studio_sdk/converter/exports/csv.py +82 -0
  31. label_studio_sdk/converter/exports/csv2.py +103 -0
  32. label_studio_sdk/converter/funsd.py +85 -0
  33. label_studio_sdk/converter/imports/__init__.py +0 -0
  34. label_studio_sdk/converter/imports/coco.py +314 -0
  35. label_studio_sdk/converter/imports/colors.py +198 -0
  36. label_studio_sdk/converter/imports/label_config.py +45 -0
  37. label_studio_sdk/converter/imports/pathtrack.py +269 -0
  38. label_studio_sdk/converter/imports/yolo.py +236 -0
  39. label_studio_sdk/converter/main.py +202 -0
  40. label_studio_sdk/converter/utils.py +473 -0
  41. label_studio_sdk/core/__init__.py +33 -0
  42. label_studio_sdk/core/api_error.py +15 -0
  43. label_studio_sdk/core/client_wrapper.py +55 -0
  44. label_studio_sdk/core/datetime_utils.py +28 -0
  45. label_studio_sdk/core/file.py +38 -0
  46. label_studio_sdk/core/http_client.py +443 -0
  47. label_studio_sdk/core/jsonable_encoder.py +99 -0
  48. label_studio_sdk/core/pagination.py +87 -0
  49. label_studio_sdk/core/pydantic_utilities.py +28 -0
  50. label_studio_sdk/core/query_encoder.py +33 -0
  51. label_studio_sdk/core/remove_none_from_dict.py +11 -0
  52. label_studio_sdk/core/request_options.py +32 -0
  53. label_studio_sdk/environment.py +7 -0
  54. label_studio_sdk/errors/__init__.py +6 -0
  55. label_studio_sdk/errors/bad_request_error.py +8 -0
  56. label_studio_sdk/errors/internal_server_error.py +8 -0
  57. label_studio_sdk/export_storage/__init__.py +28 -0
  58. label_studio_sdk/export_storage/azure/__init__.py +5 -0
  59. label_studio_sdk/export_storage/azure/client.py +722 -0
  60. label_studio_sdk/export_storage/azure/types/__init__.py +6 -0
  61. label_studio_sdk/export_storage/azure/types/azure_create_response.py +52 -0
  62. label_studio_sdk/export_storage/azure/types/azure_update_response.py +52 -0
  63. label_studio_sdk/export_storage/client.py +107 -0
  64. label_studio_sdk/export_storage/gcs/__init__.py +5 -0
  65. label_studio_sdk/export_storage/gcs/client.py +722 -0
  66. label_studio_sdk/export_storage/gcs/types/__init__.py +6 -0
  67. label_studio_sdk/export_storage/gcs/types/gcs_create_response.py +52 -0
  68. label_studio_sdk/export_storage/gcs/types/gcs_update_response.py +52 -0
  69. label_studio_sdk/export_storage/local/__init__.py +5 -0
  70. label_studio_sdk/export_storage/local/client.py +688 -0
  71. label_studio_sdk/export_storage/local/types/__init__.py +6 -0
  72. label_studio_sdk/export_storage/local/types/local_create_response.py +47 -0
  73. label_studio_sdk/export_storage/local/types/local_update_response.py +47 -0
  74. label_studio_sdk/export_storage/redis/__init__.py +5 -0
  75. label_studio_sdk/export_storage/redis/client.py +714 -0
  76. label_studio_sdk/export_storage/redis/types/__init__.py +6 -0
  77. label_studio_sdk/export_storage/redis/types/redis_create_response.py +57 -0
  78. label_studio_sdk/export_storage/redis/types/redis_update_response.py +57 -0
  79. label_studio_sdk/export_storage/s3/__init__.py +5 -0
  80. label_studio_sdk/export_storage/s3/client.py +820 -0
  81. label_studio_sdk/export_storage/s3/types/__init__.py +6 -0
  82. label_studio_sdk/export_storage/s3/types/s3create_response.py +74 -0
  83. label_studio_sdk/export_storage/s3/types/s3update_response.py +74 -0
  84. label_studio_sdk/export_storage/types/__init__.py +5 -0
  85. label_studio_sdk/export_storage/types/export_storage_list_types_response_item.py +30 -0
  86. label_studio_sdk/files/__init__.py +2 -0
  87. label_studio_sdk/files/client.py +556 -0
  88. label_studio_sdk/import_storage/__init__.py +28 -0
  89. label_studio_sdk/import_storage/azure/__init__.py +5 -0
  90. label_studio_sdk/import_storage/azure/client.py +812 -0
  91. label_studio_sdk/import_storage/azure/types/__init__.py +6 -0
  92. label_studio_sdk/import_storage/azure/types/azure_create_response.py +72 -0
  93. label_studio_sdk/import_storage/azure/types/azure_update_response.py +72 -0
  94. label_studio_sdk/import_storage/client.py +107 -0
  95. label_studio_sdk/import_storage/gcs/__init__.py +5 -0
  96. label_studio_sdk/import_storage/gcs/client.py +812 -0
  97. label_studio_sdk/import_storage/gcs/types/__init__.py +6 -0
  98. label_studio_sdk/import_storage/gcs/types/gcs_create_response.py +72 -0
  99. label_studio_sdk/import_storage/gcs/types/gcs_update_response.py +72 -0
  100. label_studio_sdk/import_storage/local/__init__.py +5 -0
  101. label_studio_sdk/import_storage/local/client.py +690 -0
  102. label_studio_sdk/import_storage/local/types/__init__.py +6 -0
  103. label_studio_sdk/import_storage/local/types/local_create_response.py +47 -0
  104. label_studio_sdk/import_storage/local/types/local_update_response.py +47 -0
  105. label_studio_sdk/import_storage/redis/__init__.py +5 -0
  106. label_studio_sdk/import_storage/redis/client.py +768 -0
  107. label_studio_sdk/import_storage/redis/types/__init__.py +6 -0
  108. label_studio_sdk/import_storage/redis/types/redis_create_response.py +62 -0
  109. label_studio_sdk/import_storage/redis/types/redis_update_response.py +62 -0
  110. label_studio_sdk/import_storage/s3/__init__.py +5 -0
  111. label_studio_sdk/import_storage/s3/client.py +912 -0
  112. label_studio_sdk/import_storage/s3/types/__init__.py +6 -0
  113. label_studio_sdk/import_storage/s3/types/s3create_response.py +99 -0
  114. label_studio_sdk/import_storage/s3/types/s3update_response.py +99 -0
  115. label_studio_sdk/import_storage/types/__init__.py +5 -0
  116. label_studio_sdk/import_storage/types/import_storage_list_types_response_item.py +30 -0
  117. label_studio_sdk/ml/__init__.py +19 -0
  118. label_studio_sdk/ml/client.py +981 -0
  119. label_studio_sdk/ml/types/__init__.py +17 -0
  120. label_studio_sdk/ml/types/ml_create_request_auth_method.py +5 -0
  121. label_studio_sdk/ml/types/ml_create_response.py +78 -0
  122. label_studio_sdk/ml/types/ml_create_response_auth_method.py +5 -0
  123. label_studio_sdk/ml/types/ml_update_request_auth_method.py +5 -0
  124. label_studio_sdk/ml/types/ml_update_response.py +78 -0
  125. label_studio_sdk/ml/types/ml_update_response_auth_method.py +5 -0
  126. label_studio_sdk/predictions/__init__.py +2 -0
  127. label_studio_sdk/predictions/client.py +638 -0
  128. label_studio_sdk/projects/__init__.py +6 -0
  129. label_studio_sdk/projects/client.py +1053 -0
  130. label_studio_sdk/projects/exports/__init__.py +2 -0
  131. label_studio_sdk/projects/exports/client.py +930 -0
  132. label_studio_sdk/projects/types/__init__.py +7 -0
  133. label_studio_sdk/projects/types/projects_create_response.py +96 -0
  134. label_studio_sdk/projects/types/projects_import_tasks_response.py +71 -0
  135. label_studio_sdk/projects/types/projects_list_response.py +33 -0
  136. label_studio_sdk/py.typed +0 -0
  137. label_studio_sdk/tasks/__init__.py +5 -0
  138. label_studio_sdk/tasks/client.py +811 -0
  139. label_studio_sdk/tasks/types/__init__.py +6 -0
  140. label_studio_sdk/tasks/types/tasks_list_request_fields.py +5 -0
  141. label_studio_sdk/tasks/types/tasks_list_response.py +48 -0
  142. label_studio_sdk/types/__init__.py +115 -0
  143. label_studio_sdk/types/annotation.py +116 -0
  144. label_studio_sdk/types/annotation_filter_options.py +42 -0
  145. label_studio_sdk/types/annotation_last_action.py +19 -0
  146. label_studio_sdk/types/azure_blob_export_storage.py +112 -0
  147. label_studio_sdk/types/azure_blob_export_storage_status.py +7 -0
  148. label_studio_sdk/types/azure_blob_import_storage.py +113 -0
  149. label_studio_sdk/types/azure_blob_import_storage_status.py +7 -0
  150. label_studio_sdk/types/base_task.py +113 -0
  151. label_studio_sdk/types/base_user.py +42 -0
  152. label_studio_sdk/types/converted_format.py +36 -0
  153. label_studio_sdk/types/converted_format_status.py +5 -0
  154. label_studio_sdk/types/export.py +48 -0
  155. label_studio_sdk/types/export_convert.py +32 -0
  156. label_studio_sdk/types/export_create.py +54 -0
  157. label_studio_sdk/types/export_create_status.py +5 -0
  158. label_studio_sdk/types/export_status.py +5 -0
  159. label_studio_sdk/types/file_upload.py +30 -0
  160. label_studio_sdk/types/filter.py +53 -0
  161. label_studio_sdk/types/filter_group.py +35 -0
  162. label_studio_sdk/types/gcs_export_storage.py +112 -0
  163. label_studio_sdk/types/gcs_export_storage_status.py +7 -0
  164. label_studio_sdk/types/gcs_import_storage.py +113 -0
  165. label_studio_sdk/types/gcs_import_storage_status.py +7 -0
  166. label_studio_sdk/types/local_files_export_storage.py +97 -0
  167. label_studio_sdk/types/local_files_export_storage_status.py +7 -0
  168. label_studio_sdk/types/local_files_import_storage.py +92 -0
  169. label_studio_sdk/types/local_files_import_storage_status.py +7 -0
  170. label_studio_sdk/types/ml_backend.py +89 -0
  171. label_studio_sdk/types/ml_backend_auth_method.py +5 -0
  172. label_studio_sdk/types/ml_backend_state.py +5 -0
  173. label_studio_sdk/types/prediction.py +78 -0
  174. label_studio_sdk/types/project.py +198 -0
  175. label_studio_sdk/types/project_import.py +63 -0
  176. label_studio_sdk/types/project_import_status.py +5 -0
  177. label_studio_sdk/types/project_label_config.py +32 -0
  178. label_studio_sdk/types/project_sampling.py +7 -0
  179. label_studio_sdk/types/project_skip_queue.py +5 -0
  180. label_studio_sdk/types/redis_export_storage.py +117 -0
  181. label_studio_sdk/types/redis_export_storage_status.py +7 -0
  182. label_studio_sdk/types/redis_import_storage.py +112 -0
  183. label_studio_sdk/types/redis_import_storage_status.py +7 -0
  184. label_studio_sdk/types/s3export_storage.py +134 -0
  185. label_studio_sdk/types/s3export_storage_status.py +7 -0
  186. label_studio_sdk/types/s3import_storage.py +140 -0
  187. label_studio_sdk/types/s3import_storage_status.py +7 -0
  188. label_studio_sdk/types/serialization_option.py +36 -0
  189. label_studio_sdk/types/serialization_options.py +45 -0
  190. label_studio_sdk/types/task.py +157 -0
  191. label_studio_sdk/types/task_filter_options.py +49 -0
  192. label_studio_sdk/types/user_simple.py +37 -0
  193. label_studio_sdk/types/view.py +55 -0
  194. label_studio_sdk/types/webhook.py +67 -0
  195. label_studio_sdk/types/webhook_actions_item.py +21 -0
  196. label_studio_sdk/types/webhook_serializer_for_update.py +67 -0
  197. label_studio_sdk/types/webhook_serializer_for_update_actions_item.py +21 -0
  198. label_studio_sdk/users/__init__.py +5 -0
  199. label_studio_sdk/users/client.py +830 -0
  200. label_studio_sdk/users/types/__init__.py +6 -0
  201. label_studio_sdk/users/types/users_get_token_response.py +36 -0
  202. label_studio_sdk/users/types/users_reset_token_response.py +36 -0
  203. label_studio_sdk/version.py +4 -0
  204. label_studio_sdk/views/__init__.py +31 -0
  205. label_studio_sdk/views/client.py +564 -0
  206. label_studio_sdk/views/types/__init__.py +29 -0
  207. label_studio_sdk/views/types/views_create_request_data.py +43 -0
  208. label_studio_sdk/views/types/views_create_request_data_filters.py +43 -0
  209. label_studio_sdk/views/types/views_create_request_data_filters_conjunction.py +5 -0
  210. label_studio_sdk/views/types/views_create_request_data_filters_items_item.py +47 -0
  211. label_studio_sdk/views/types/views_create_request_data_ordering_item.py +38 -0
  212. label_studio_sdk/views/types/views_create_request_data_ordering_item_direction.py +5 -0
  213. label_studio_sdk/views/types/views_update_request_data.py +43 -0
  214. label_studio_sdk/views/types/views_update_request_data_filters.py +43 -0
  215. label_studio_sdk/views/types/views_update_request_data_filters_conjunction.py +5 -0
  216. label_studio_sdk/views/types/views_update_request_data_filters_items_item.py +47 -0
  217. label_studio_sdk/views/types/views_update_request_data_ordering_item.py +38 -0
  218. label_studio_sdk/views/types/views_update_request_data_ordering_item_direction.py +5 -0
  219. label_studio_sdk/webhooks/__init__.py +5 -0
  220. label_studio_sdk/webhooks/client.py +636 -0
  221. label_studio_sdk/webhooks/types/__init__.py +5 -0
  222. label_studio_sdk/webhooks/types/webhooks_update_request_actions_item.py +21 -0
  223. label_studio_sdk-1.0.0.dist-info/METADATA +307 -0
  224. label_studio_sdk-1.0.0.dist-info/RECORD +239 -0
  225. {label_studio_sdk-0.0.34.dist-info → label_studio_sdk-1.0.0.dist-info}/WHEEL +1 -2
  226. label_studio_sdk-0.0.34.dist-info/LICENSE +0 -201
  227. label_studio_sdk-0.0.34.dist-info/METADATA +0 -24
  228. label_studio_sdk-0.0.34.dist-info/RECORD +0 -37
  229. label_studio_sdk-0.0.34.dist-info/top_level.txt +0 -2
  230. tests/test_client.py +0 -37
  231. tests/test_export.py +0 -105
  232. tests/test_interface/__init__.py +0 -1
  233. tests/test_interface/configs.py +0 -137
  234. tests/test_interface/mockups.py +0 -22
  235. tests/test_interface/test_compat.py +0 -64
  236. tests/test_interface/test_control_tags.py +0 -55
  237. tests/test_interface/test_data_generation.py +0 -45
  238. tests/test_interface/test_lpi.py +0 -15
  239. tests/test_interface/test_main.py +0 -196
  240. tests/test_interface/test_object_tags.py +0 -36
  241. tests/test_interface/test_region.py +0 -36
  242. tests/test_interface/test_validate_summary.py +0 -35
  243. tests/test_interface/test_validation.py +0 -59
  244. {tests → label_studio_sdk/_extensions}/__init__.py +0 -0
  245. /label_studio_sdk/{exceptions.py → _legacy/exceptions.py} +0 -0
  246. /label_studio_sdk/{label_interface → _legacy/label_interface}/__init__.py +0 -0
  247. /label_studio_sdk/{label_interface → _legacy/label_interface}/base.py +0 -0
  248. /label_studio_sdk/{label_interface → _legacy/label_interface}/control_tags.py +0 -0
  249. /label_studio_sdk/{label_interface → _legacy/label_interface}/label_tags.py +0 -0
  250. /label_studio_sdk/{label_interface → _legacy/label_interface}/object_tags.py +0 -0
  251. /label_studio_sdk/{label_interface → _legacy/label_interface}/region.py +0 -0
  252. /label_studio_sdk/{objects.py → _legacy/objects.py} +0 -0
  253. /label_studio_sdk/{schema → _legacy/schema}/label_config_schema.json +0 -0
  254. /label_studio_sdk/{users.py → _legacy/users.py} +0 -0
  255. /label_studio_sdk/{utils.py → _legacy/utils.py} +0 -0
  256. /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()