label-studio-sdk 0.0.34__py3-none-any.whl → 1.0.1__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.
Potentially problematic release.
This version of label-studio-sdk might be problematic. Click here for more details.
- label_studio_sdk/__init__.py +232 -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/_extensions/pager_ext.py +49 -0
- label_studio_sdk/_legacy/__init__.py +11 -0
- label_studio_sdk/_legacy/client.py +471 -0
- label_studio_sdk/_legacy/objects.py +74 -0
- label_studio_sdk/{project.py → _legacy/project.py} +2 -2
- label_studio_sdk/{schema → _legacy/schema}/label_config_schema.json +14 -14
- label_studio_sdk/actions/__init__.py +27 -0
- label_studio_sdk/actions/client.py +223 -0
- label_studio_sdk/actions/types/__init__.py +25 -0
- label_studio_sdk/actions/types/actions_create_request_filters.py +43 -0
- label_studio_sdk/actions/types/actions_create_request_filters_conjunction.py +5 -0
- label_studio_sdk/actions/types/actions_create_request_filters_items_item.py +49 -0
- label_studio_sdk/actions/types/actions_create_request_filters_items_item_filter.py +31 -0
- label_studio_sdk/actions/types/actions_create_request_filters_items_item_operator.py +23 -0
- label_studio_sdk/actions/types/actions_create_request_id.py +19 -0
- label_studio_sdk/actions/types/actions_create_request_ordering_item.py +31 -0
- label_studio_sdk/actions/types/actions_create_request_selected_items.py +10 -0
- label_studio_sdk/actions/types/actions_create_request_selected_items_excluded.py +39 -0
- label_studio_sdk/actions/types/actions_create_request_selected_items_included.py +39 -0
- label_studio_sdk/annotations/__init__.py +2 -0
- label_studio_sdk/annotations/client.py +750 -0
- label_studio_sdk/base_client.py +183 -0
- label_studio_sdk/client.py +17 -463
- 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 +447 -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 +888 -0
- label_studio_sdk/export_storage/azure/types/__init__.py +6 -0
- label_studio_sdk/export_storage/azure/types/azure_create_response.py +67 -0
- label_studio_sdk/export_storage/azure/types/azure_update_response.py +67 -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 +888 -0
- label_studio_sdk/export_storage/gcs/types/__init__.py +6 -0
- label_studio_sdk/export_storage/gcs/types/gcs_create_response.py +67 -0
- label_studio_sdk/export_storage/gcs/types/gcs_update_response.py +67 -0
- label_studio_sdk/export_storage/local/__init__.py +5 -0
- label_studio_sdk/export_storage/local/client.py +834 -0
- label_studio_sdk/export_storage/local/types/__init__.py +6 -0
- label_studio_sdk/export_storage/local/types/local_create_response.py +57 -0
- label_studio_sdk/export_storage/local/types/local_update_response.py +57 -0
- label_studio_sdk/export_storage/redis/__init__.py +5 -0
- label_studio_sdk/export_storage/redis/client.py +918 -0
- label_studio_sdk/export_storage/redis/types/__init__.py +6 -0
- label_studio_sdk/export_storage/redis/types/redis_create_response.py +72 -0
- label_studio_sdk/export_storage/redis/types/redis_update_response.py +72 -0
- label_studio_sdk/export_storage/s3/__init__.py +5 -0
- label_studio_sdk/export_storage/s3/client.py +1008 -0
- label_studio_sdk/export_storage/s3/types/__init__.py +6 -0
- label_studio_sdk/export_storage/s3/types/s3create_response.py +89 -0
- label_studio_sdk/export_storage/s3/types/s3update_response.py +89 -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 +988 -0
- label_studio_sdk/import_storage/azure/types/__init__.py +6 -0
- label_studio_sdk/import_storage/azure/types/azure_create_response.py +82 -0
- label_studio_sdk/import_storage/azure/types/azure_update_response.py +82 -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 +988 -0
- label_studio_sdk/import_storage/gcs/types/__init__.py +6 -0
- label_studio_sdk/import_storage/gcs/types/gcs_create_response.py +82 -0
- label_studio_sdk/import_storage/gcs/types/gcs_update_response.py +82 -0
- label_studio_sdk/import_storage/local/__init__.py +5 -0
- label_studio_sdk/import_storage/local/client.py +836 -0
- label_studio_sdk/import_storage/local/types/__init__.py +6 -0
- label_studio_sdk/import_storage/local/types/local_create_response.py +57 -0
- label_studio_sdk/import_storage/local/types/local_update_response.py +57 -0
- label_studio_sdk/import_storage/redis/__init__.py +5 -0
- label_studio_sdk/import_storage/redis/client.py +924 -0
- label_studio_sdk/import_storage/redis/types/__init__.py +6 -0
- label_studio_sdk/import_storage/redis/types/redis_create_response.py +72 -0
- label_studio_sdk/import_storage/redis/types/redis_update_response.py +72 -0
- label_studio_sdk/import_storage/s3/__init__.py +5 -0
- label_studio_sdk/import_storage/s3/client.py +1138 -0
- label_studio_sdk/import_storage/s3/types/__init__.py +6 -0
- label_studio_sdk/import_storage/s3/types/s3create_response.py +109 -0
- label_studio_sdk/import_storage/s3/types/s3update_response.py +109 -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/label_interface/base.py +10 -0
- label_studio_sdk/label_interface/control_tags.py +109 -71
- label_studio_sdk/label_interface/data_examples.json +96 -0
- label_studio_sdk/label_interface/interface.py +102 -53
- label_studio_sdk/label_interface/object_tags.py +8 -13
- label_studio_sdk/label_interface/region.py +33 -1
- 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 +1055 -0
- label_studio_sdk/projects/client_ext.py +19 -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 +835 -0
- label_studio_sdk/tasks/client_ext.py +18 -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 +35 -0
- label_studio_sdk/views/client.py +564 -0
- label_studio_sdk/views/types/__init__.py +33 -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 +49 -0
- label_studio_sdk/views/types/views_create_request_data_filters_items_item_filter.py +31 -0
- label_studio_sdk/views/types/views_create_request_data_filters_items_item_operator.py +23 -0
- label_studio_sdk/views/types/views_create_request_data_ordering_item.py +31 -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 +49 -0
- label_studio_sdk/views/types/views_update_request_data_filters_items_item_filter.py +31 -0
- label_studio_sdk/views/types/views_update_request_data_filters_items_item_operator.py +23 -0
- label_studio_sdk/views/types/views_update_request_data_ordering_item.py +31 -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.1.dist-info/METADATA +163 -0
- label_studio_sdk-1.0.1.dist-info/RECORD +256 -0
- {label_studio_sdk-0.0.34.dist-info → label_studio_sdk-1.0.1.dist-info}/WHEEL +1 -2
- label_studio_sdk/objects.py +0 -35
- 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/{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,34 @@
|
|
|
1
|
+
import apache_beam as beam
|
|
2
|
+
from registry import dataloader, call_dataloader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CallDataLoader(beam.DoFn):
|
|
6
|
+
def __init__(self, dataloader_name):
|
|
7
|
+
self.dataloader_name = dataloader_name
|
|
8
|
+
|
|
9
|
+
def setup(self):
|
|
10
|
+
print("Setup!")
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
def process(self, element, *args, **kwargs):
|
|
14
|
+
return call_dataloader(self.dataloader_name, element, *args, **kwargs)
|
|
15
|
+
|
|
16
|
+
def teardown(self):
|
|
17
|
+
print("Teardown!")
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataloader("test")
|
|
22
|
+
def f(i, k):
|
|
23
|
+
print("I!!", k)
|
|
24
|
+
yield i
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
with beam.Pipeline() as pipeline:
|
|
28
|
+
results = (
|
|
29
|
+
pipeline
|
|
30
|
+
| "Get storage keys" >> beam.io.gcp.gcsio.list_files("bucket")
|
|
31
|
+
| "Run dataloader"
|
|
32
|
+
>> beam.FlatMap(fn=CallDataLoader("test"), **dataloader_kwargs)
|
|
33
|
+
| "Load into Vector DB" >> beam.ParDo(fn=LoadDataRecordInDB)
|
|
34
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from label_studio_sdk._extensions.label_studio_tools.etl.registry import (
|
|
2
|
+
transform,
|
|
3
|
+
call_dataloader,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@transform.datarecords("read_base64_from_blob")
|
|
8
|
+
def gcs_blob_to_base64(
|
|
9
|
+
key: str,
|
|
10
|
+
bucket_name: str,
|
|
11
|
+
google_project_id: str = None,
|
|
12
|
+
):
|
|
13
|
+
print(key, bucket_name)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
call_dataloader("gcs_blob_reader", key=12, bucket_name=34)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Callable, Iterable, Dict, Any
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class extract:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class transform:
|
|
14
|
+
DataRecords = Iterable[Dict[str, Any]]
|
|
15
|
+
DataRecordsProducer = Callable[..., DataRecords]
|
|
16
|
+
|
|
17
|
+
class NameAlreadyExists(KeyError):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
class NameNotFound(NotImplementedError):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
class WrongOutput(ValueError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
registry = {}
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def datarecords(name: str = None):
|
|
30
|
+
"""Class method to register transform function to produce DataRecords
|
|
31
|
+
with specified name to the internal registry.
|
|
32
|
+
Can output any iterable for dicts (generators, iterators, list, tuples)
|
|
33
|
+
For example:
|
|
34
|
+
|
|
35
|
+
@transform.datarecords("DataRecords from GCS file")
|
|
36
|
+
def read_image(bucket_name, key):
|
|
37
|
+
yield {"image": blob.download_as_string()}
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def register_transform_func(
|
|
41
|
+
transform_func: transform.DataRecordsProducer,
|
|
42
|
+
) -> transform.DataRecordsProducer:
|
|
43
|
+
if name in transform.registry:
|
|
44
|
+
raise transform.NameAlreadyExists(
|
|
45
|
+
f"Dataloader {name} already exists. Will replace it"
|
|
46
|
+
)
|
|
47
|
+
transform.registry[name] = transform_func
|
|
48
|
+
|
|
49
|
+
@wraps(transform_func)
|
|
50
|
+
def inner_call_transform_func(*args, **kwargs) -> transform.DataRecords:
|
|
51
|
+
data_records = transform_func(*args, **kwargs)
|
|
52
|
+
# TODO: assert Data record type in a proper way, typing.assert_type is supported in Python 3.11
|
|
53
|
+
return data_records
|
|
54
|
+
|
|
55
|
+
return inner_call_transform_func
|
|
56
|
+
|
|
57
|
+
return register_transform_func
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def invoke(name, *args, **kwargs):
|
|
61
|
+
if name not in transform.registry:
|
|
62
|
+
raise transform.NameNotFound(name)
|
|
63
|
+
return transform.registry[name](*args, **kwargs)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class load:
|
|
67
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from operator import itemgetter
|
|
3
|
+
|
|
4
|
+
from label_studio_sdk._extensions.label_studio_tools.core.label_config import (
|
|
5
|
+
_VIDEO_TRACKING_TAGS,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def extract_key_frames(results):
|
|
10
|
+
"""
|
|
11
|
+
Extract frames from key frames from video annotation results
|
|
12
|
+
:param results: Annotation results
|
|
13
|
+
:return: Frames in LS format
|
|
14
|
+
"""
|
|
15
|
+
final_results = []
|
|
16
|
+
for result in results:
|
|
17
|
+
temp = deepcopy(result)
|
|
18
|
+
if result["type"].lower() not in _VIDEO_TRACKING_TAGS:
|
|
19
|
+
final_results.append(result)
|
|
20
|
+
continue
|
|
21
|
+
sequence = result["value"]["sequence"]
|
|
22
|
+
if len(sequence) < 1:
|
|
23
|
+
continue
|
|
24
|
+
sequence = sorted(sequence, key=lambda d: d["frame"])
|
|
25
|
+
exclude_first = False
|
|
26
|
+
for i in range(len(sequence)):
|
|
27
|
+
frame_a = sequence[i]
|
|
28
|
+
frame_b = {} if i == len(sequence) - 1 else sequence[i + 1]
|
|
29
|
+
temp["value"]["sequence"].extend(
|
|
30
|
+
_construct_result_from_frames(
|
|
31
|
+
frame1=frame_a,
|
|
32
|
+
frame2=frame_b,
|
|
33
|
+
frameCount=result["value"].get("framesCount", 0),
|
|
34
|
+
duration=result["value"].get("duration", 0),
|
|
35
|
+
exclude_first=exclude_first,
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
exclude_first = frame_a["enabled"]
|
|
39
|
+
temp["value"]["sequence"] = sorted(
|
|
40
|
+
temp["value"]["sequence"], key=itemgetter("frame")
|
|
41
|
+
)
|
|
42
|
+
final_results.append(temp)
|
|
43
|
+
return final_results
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _construct_result_from_frames(
|
|
47
|
+
frame1, frame2, frameCount=0, duration=0, exclude_first=True
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Construct frames between 2 keyframes
|
|
51
|
+
:param frame1: First frame in sequence
|
|
52
|
+
:param frame2: Next frame in sequence
|
|
53
|
+
:param frameCount: Total frame count in the video
|
|
54
|
+
:param duration: Total duration of the video
|
|
55
|
+
:param exclude_first: Exclude first result to deduplicate results
|
|
56
|
+
:return: List of frames
|
|
57
|
+
"""
|
|
58
|
+
final_results = []
|
|
59
|
+
if not frame1["enabled"]:
|
|
60
|
+
return []
|
|
61
|
+
if len(frame2) > 0:
|
|
62
|
+
if frame1["frame"] > frame2["frame"]:
|
|
63
|
+
return []
|
|
64
|
+
frame_count = frame2["frame"] - frame1["frame"] + 1
|
|
65
|
+
else:
|
|
66
|
+
frame_count = frameCount - frame1["frame"] + 1
|
|
67
|
+
start_i = 1 if exclude_first else 0
|
|
68
|
+
for i in range(start_i, frame_count):
|
|
69
|
+
frame_number = i + frame1["frame"]
|
|
70
|
+
delta = i / max(1, (frame_count - 1))
|
|
71
|
+
deltas = {}
|
|
72
|
+
for v in ["x", "y", "rotation", "width", "height", "time"]:
|
|
73
|
+
deltas[v] = (
|
|
74
|
+
0
|
|
75
|
+
if (frame1[v] == frame2.get(v) or not frame2)
|
|
76
|
+
else (frame2.get(v, 0) - frame1[v]) * delta
|
|
77
|
+
)
|
|
78
|
+
result = deepcopy(frame1)
|
|
79
|
+
result.update(
|
|
80
|
+
{
|
|
81
|
+
"x": frame1["x"] + deltas["x"],
|
|
82
|
+
"y": frame1["y"] + deltas["y"],
|
|
83
|
+
"width": frame1["width"] + deltas["width"],
|
|
84
|
+
"height": frame1["height"] + deltas["height"],
|
|
85
|
+
"rotation": frame1["rotation"] + deltas["rotation"],
|
|
86
|
+
"frame": frame_number,
|
|
87
|
+
"time": round(frame1["time"] + deltas["time"], 2),
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
if frame_number not in [frame1.get("frame"), frame2.get("frame")]:
|
|
91
|
+
result["auto"] = True
|
|
92
|
+
if deltas["time"] == 0 and duration > 0:
|
|
93
|
+
result["time"] = round(
|
|
94
|
+
frame1["time"] + delta * (duration - frame1["time"]), 2
|
|
95
|
+
)
|
|
96
|
+
final_results.append(result)
|
|
97
|
+
return final_results
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from label_studio_sdk.core.pagination import SyncPager, AsyncPager, T
|
|
3
|
+
from label_studio_sdk.core.api_error import ApiError
|
|
4
|
+
|
|
5
|
+
# This is a custom extension of the autogenerated SyncPager and AsyncPager classes
|
|
6
|
+
# that works with the Label Studio SDK's default pagination behavior
|
|
7
|
+
# that throws 404 errors at the end of the pagination.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SyncPagerExt(SyncPager, typing.Generic[T]):
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def from_sync_pager(cls, sync_pager: SyncPager) -> 'SyncPagerExt':
|
|
14
|
+
return cls(
|
|
15
|
+
get_next=sync_pager.get_next,
|
|
16
|
+
has_next=sync_pager.has_next,
|
|
17
|
+
items=sync_pager.items
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def __iter__(self) -> typing.Iterator[T]: # type: ignore
|
|
21
|
+
# Extends the iterator to catch 404 errors at the end of the pagination
|
|
22
|
+
try:
|
|
23
|
+
for item in super().__iter__():
|
|
24
|
+
yield item
|
|
25
|
+
except ApiError as exc:
|
|
26
|
+
if exc.status_code == 404:
|
|
27
|
+
return
|
|
28
|
+
raise
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AsyncPagerExt(AsyncPager, typing.Generic[T]):
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
async def from_async_pager(cls, async_pager: AsyncPager) -> 'AsyncPagerExt':
|
|
35
|
+
return cls(
|
|
36
|
+
get_next=async_pager.get_next,
|
|
37
|
+
has_next=async_pager.has_next,
|
|
38
|
+
items=async_pager.items
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async def __aiter__(self) -> typing.AsyncIterator[T]: # type: ignore
|
|
42
|
+
# Extends the iterator to catch 404 errors at the end of the pagination
|
|
43
|
+
try:
|
|
44
|
+
async for item in super().__aiter__():
|
|
45
|
+
yield item
|
|
46
|
+
except ApiError as exc:
|
|
47
|
+
if exc.status_code == 404:
|
|
48
|
+
return
|
|
49
|
+
raise
|