supervisely 6.73.410__py3-none-any.whl → 6.73.470__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 supervisely might be problematic. Click here for more details.
- supervisely/__init__.py +136 -1
- supervisely/_utils.py +81 -0
- supervisely/annotation/json_geometries_map.py +2 -0
- supervisely/annotation/label.py +80 -3
- supervisely/api/annotation_api.py +9 -9
- supervisely/api/api.py +67 -43
- supervisely/api/app_api.py +72 -5
- supervisely/api/dataset_api.py +108 -33
- supervisely/api/entity_annotation/figure_api.py +113 -49
- supervisely/api/image_api.py +82 -0
- supervisely/api/module_api.py +10 -0
- supervisely/api/nn/deploy_api.py +15 -9
- supervisely/api/nn/ecosystem_models_api.py +201 -0
- supervisely/api/nn/neural_network_api.py +12 -3
- supervisely/api/pointcloud/pointcloud_api.py +38 -0
- supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
- supervisely/api/project_api.py +213 -6
- supervisely/api/task_api.py +11 -1
- supervisely/api/video/video_annotation_api.py +4 -2
- supervisely/api/video/video_api.py +79 -1
- supervisely/api/video/video_figure_api.py +24 -11
- supervisely/api/volume/volume_api.py +38 -0
- supervisely/app/__init__.py +1 -1
- supervisely/app/content.py +14 -6
- supervisely/app/fastapi/__init__.py +1 -0
- supervisely/app/fastapi/custom_static_files.py +1 -1
- supervisely/app/fastapi/multi_user.py +88 -0
- supervisely/app/fastapi/subapp.py +175 -42
- supervisely/app/fastapi/templating.py +1 -1
- supervisely/app/fastapi/websocket.py +77 -9
- supervisely/app/singleton.py +21 -0
- supervisely/app/v1/app_service.py +18 -2
- supervisely/app/v1/constants.py +7 -1
- supervisely/app/widgets/__init__.py +11 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/card/card.py +20 -0
- supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
- supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
- supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
- supervisely/app/widgets/dialog/dialog.py +12 -0
- supervisely/app/widgets/dialog/template.html +2 -1
- supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
- supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
- supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
- supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
- supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
- supervisely/app/widgets/fast_table/fast_table.py +713 -126
- supervisely/app/widgets/fast_table/script.js +492 -95
- supervisely/app/widgets/fast_table/style.css +54 -0
- supervisely/app/widgets/fast_table/template.html +45 -5
- supervisely/app/widgets/heatmap/__init__.py +0 -0
- supervisely/app/widgets/heatmap/heatmap.py +523 -0
- supervisely/app/widgets/heatmap/script.js +378 -0
- supervisely/app/widgets/heatmap/style.css +227 -0
- supervisely/app/widgets/heatmap/template.html +21 -0
- supervisely/app/widgets/input_tag/input_tag.py +102 -15
- supervisely/app/widgets/input_tag_list/__init__.py +0 -0
- supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
- supervisely/app/widgets/input_tag_list/template.html +70 -0
- supervisely/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
- supervisely/app/widgets/radio_tabs/template.html +1 -0
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
- supervisely/app/widgets/table/table.py +68 -13
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -1
- supervisely/app/widgets/transfer/style.css +3 -0
- supervisely/app/widgets/transfer/template.html +3 -1
- supervisely/app/widgets/transfer/transfer.py +48 -45
- supervisely/app/widgets/tree_select/tree_select.py +2 -0
- supervisely/convert/image/csv/csv_converter.py +24 -15
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
- supervisely/convert/video/video_converter.py +2 -2
- supervisely/geometry/polyline_3d.py +110 -0
- supervisely/io/env.py +161 -1
- supervisely/nn/artifacts/__init__.py +1 -1
- supervisely/nn/artifacts/artifacts.py +10 -2
- supervisely/nn/artifacts/detectron2.py +1 -0
- supervisely/nn/artifacts/hrda.py +1 -0
- supervisely/nn/artifacts/mmclassification.py +20 -0
- supervisely/nn/artifacts/mmdetection.py +5 -3
- supervisely/nn/artifacts/mmsegmentation.py +1 -0
- supervisely/nn/artifacts/ritm.py +1 -0
- supervisely/nn/artifacts/rtdetr.py +1 -0
- supervisely/nn/artifacts/unet.py +1 -0
- supervisely/nn/artifacts/utils.py +3 -0
- supervisely/nn/artifacts/yolov5.py +2 -0
- supervisely/nn/artifacts/yolov8.py +1 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
- supervisely/nn/experiments.py +9 -0
- supervisely/nn/inference/cache.py +37 -17
- supervisely/nn/inference/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +953 -211
- supervisely/nn/inference/inference_request.py +15 -8
- supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
- supervisely/nn/inference/object_detection/object_detection.py +1 -0
- supervisely/nn/inference/predict_app/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
- supervisely/nn/inference/predict_app/gui/gui.py +915 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +399 -0
- supervisely/nn/inference/predict_app/predict_app.py +176 -0
- supervisely/nn/inference/session.py +47 -39
- supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
- supervisely/nn/inference/tracking/point_tracking.py +5 -1
- supervisely/nn/inference/tracking/tracker_interface.py +4 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/model_api.py +44 -22
- supervisely/nn/model/prediction.py +15 -1
- supervisely/nn/model/prediction_session.py +70 -14
- supervisely/nn/prediction_dto.py +7 -0
- supervisely/nn/tracker/__init__.py +6 -8
- supervisely/nn/tracker/base_tracker.py +54 -0
- supervisely/nn/tracker/botsort/__init__.py +1 -0
- supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
- supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
- supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
- supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
- supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
- supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
- supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
- supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
- supervisely/nn/tracker/botsort_tracker.py +273 -0
- supervisely/nn/tracker/calculate_metrics.py +264 -0
- supervisely/nn/tracker/utils.py +273 -0
- supervisely/nn/tracker/visualize.py +520 -0
- supervisely/nn/training/gui/gui.py +152 -49
- supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
- supervisely/nn/training/gui/training_artifacts.py +3 -1
- supervisely/nn/training/train_app.py +225 -46
- supervisely/project/pointcloud_episode_project.py +12 -8
- supervisely/project/pointcloud_project.py +12 -8
- supervisely/project/project.py +221 -75
- supervisely/template/experiment/experiment.html.jinja +105 -55
- supervisely/template/experiment/experiment_generator.py +258 -112
- supervisely/template/experiment/header.html.jinja +31 -13
- supervisely/template/experiment/sly-style.css +7 -2
- supervisely/versions.json +3 -1
- supervisely/video/sampling.py +42 -20
- supervisely/video/video.py +41 -12
- supervisely/video_annotation/video_figure.py +38 -4
- supervisely/volume/stl_converter.py +2 -0
- supervisely/worker_api/agent_rpc.py +24 -1
- supervisely/worker_api/rpc_servicer.py +31 -7
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
- supervisely_lib/__init__.py +6 -1
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- supervisely/nn/tracker/bot_sort/__init__.py +0 -21
- supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
- supervisely/nn/tracker/bot_sort/matching.py +0 -127
- supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
- supervisely/nn/tracker/deep_sort/__init__.py +0 -6
- supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
- supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
- supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
- supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
- supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
- supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
- supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
- supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
- supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
- supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
- supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
- supervisely/nn/tracker/tracker.py +0 -285
- supervisely/nn/tracker/utils/kalman_filter.py +0 -492
- supervisely/nn/tracking/__init__.py +0 -1
- supervisely/nn/tracking/boxmot.py +0 -114
- supervisely/nn/tracking/tracking.py +0 -24
- /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
supervisely/api/image_api.py
CHANGED
|
@@ -70,6 +70,11 @@ from supervisely.api.module_api import (
|
|
|
70
70
|
_get_single_item,
|
|
71
71
|
)
|
|
72
72
|
from supervisely.imaging import image as sly_image
|
|
73
|
+
from supervisely.io.env import (
|
|
74
|
+
add_uploaded_ids_to_env,
|
|
75
|
+
app_categories,
|
|
76
|
+
increment_upload_count,
|
|
77
|
+
)
|
|
73
78
|
from supervisely.io.fs import (
|
|
74
79
|
OFFSETS_PKL_BATCH_SIZE,
|
|
75
80
|
OFFSETS_PKL_SUFFIX,
|
|
@@ -392,6 +397,9 @@ class ImageInfo(NamedTuple):
|
|
|
392
397
|
#: Format: "YYYY-MM-DDTHH:MM:SS.sssZ"
|
|
393
398
|
embeddings_updated_at: Optional[str] = None
|
|
394
399
|
|
|
400
|
+
#: :class:`int`: :class:`Dataset<supervisely.project.project.Project>` ID in Supervisely.
|
|
401
|
+
project_id: int = None
|
|
402
|
+
|
|
395
403
|
# DO NOT DELETE THIS COMMENT
|
|
396
404
|
#! New fields must be added with default values to keep backward compatibility.
|
|
397
405
|
|
|
@@ -471,6 +479,7 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
471
479
|
ApiField.OFFSET_END,
|
|
472
480
|
ApiField.AI_SEARCH_META,
|
|
473
481
|
ApiField.EMBEDDINGS_UPDATED_AT,
|
|
482
|
+
ApiField.PROJECT_ID,
|
|
474
483
|
]
|
|
475
484
|
|
|
476
485
|
@staticmethod
|
|
@@ -2613,6 +2622,16 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
2613
2622
|
info_json_copy[ApiField.EXT] = info_json[ApiField.MIME].split("/")[1]
|
|
2614
2623
|
# results.append(self.InfoType(*[info_json_copy[field_name] for field_name in self.info_sequence()]))
|
|
2615
2624
|
results.append(self._convert_json_info(info_json_copy))
|
|
2625
|
+
|
|
2626
|
+
try:
|
|
2627
|
+
if "import" in app_categories():
|
|
2628
|
+
ids = [info.id for info in results[-len(batch_names) :]]
|
|
2629
|
+
if len(ids) > 0:
|
|
2630
|
+
increment_upload_count(dataset_id, len(ids))
|
|
2631
|
+
add_uploaded_ids_to_env(dataset_id, ids)
|
|
2632
|
+
except:
|
|
2633
|
+
pass
|
|
2634
|
+
|
|
2616
2635
|
break
|
|
2617
2636
|
except HTTPError as e:
|
|
2618
2637
|
error_details = e.response.json().get("details", {})
|
|
@@ -5508,3 +5527,66 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
5508
5527
|
method,
|
|
5509
5528
|
{ApiField.IMAGES: images},
|
|
5510
5529
|
)
|
|
5530
|
+
|
|
5531
|
+
def get_subsequent_image_ids(
|
|
5532
|
+
self,
|
|
5533
|
+
image_id: int,
|
|
5534
|
+
images_count: Optional[int] = None,
|
|
5535
|
+
job_id: Optional[int] = None,
|
|
5536
|
+
params: Optional[dict] = None,
|
|
5537
|
+
dataset_id: Optional[int] = None,
|
|
5538
|
+
project_id: Optional[int] = None,
|
|
5539
|
+
) -> List[int]:
|
|
5540
|
+
"""
|
|
5541
|
+
Get list of subsequent image IDs after the specified image ID.
|
|
5542
|
+
|
|
5543
|
+
:param image_id: Image ID in Supervisely.
|
|
5544
|
+
:type image_id: int
|
|
5545
|
+
:param images_count: Number of subsequent images to retrieve. If None, retrieves all subsequent images.
|
|
5546
|
+
:type images_count: int, optional
|
|
5547
|
+
:param job_id: Job ID to filter images. If None, does not filter by job ID.
|
|
5548
|
+
:type job_id: int, optional
|
|
5549
|
+
:param params: Additional parameters for filtering and sorting images.
|
|
5550
|
+
:type params: dict, optional
|
|
5551
|
+
:param dataset_id: Dataset ID to filter images.
|
|
5552
|
+
:type dataset_id: int, optional
|
|
5553
|
+
:param project_id: Project ID to filter images. If None, makes a request to retrieve it from the specified image.
|
|
5554
|
+
:type project_id: int, optional
|
|
5555
|
+
"""
|
|
5556
|
+
data = {
|
|
5557
|
+
"recursive": True,
|
|
5558
|
+
"projectId": project_id,
|
|
5559
|
+
"filters": [],
|
|
5560
|
+
"sort": "name",
|
|
5561
|
+
"sort_order": "asc",
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5564
|
+
if params is not None:
|
|
5565
|
+
data.update(params)
|
|
5566
|
+
|
|
5567
|
+
if data["projectId"] is None:
|
|
5568
|
+
image_info = self.get_info_by_id(image_id)
|
|
5569
|
+
if image_info is None:
|
|
5570
|
+
raise ValueError(f"Image with ID {image_id} not found.")
|
|
5571
|
+
project_id = self._api.dataset.get_info_by_id(image_info.dataset_id).project_id
|
|
5572
|
+
if job_id is not None:
|
|
5573
|
+
self._api.add_header("x-job-id", str(job_id))
|
|
5574
|
+
if dataset_id is not None:
|
|
5575
|
+
data["datasetId"] = dataset_id
|
|
5576
|
+
|
|
5577
|
+
image_infos = self.get_list_all_pages(
|
|
5578
|
+
"images.list",
|
|
5579
|
+
data,
|
|
5580
|
+
limit=None,
|
|
5581
|
+
return_first_response=False,
|
|
5582
|
+
)
|
|
5583
|
+
self._api.headers.pop("x-job-id", None)
|
|
5584
|
+
image_ids = [img_info.id for img_info in image_infos]
|
|
5585
|
+
if len(image_ids) == 0:
|
|
5586
|
+
raise ValueError("No images found with the specified criteria.")
|
|
5587
|
+
elif image_id not in image_ids:
|
|
5588
|
+
raise ValueError(f"Image with ID {image_id} not found in the specified entity.")
|
|
5589
|
+
|
|
5590
|
+
target_idx = image_ids.index(image_id) + 1
|
|
5591
|
+
to_idx = target_idx + images_count if images_count is not None else len(image_ids)
|
|
5592
|
+
return image_ids[target_idx:to_idx]
|
supervisely/api/module_api.py
CHANGED
|
@@ -691,6 +691,10 @@ class ApiField:
|
|
|
691
691
|
""""""
|
|
692
692
|
UPDATE_STRATEGY = "updateStrategy"
|
|
693
693
|
""""""
|
|
694
|
+
FILE_PATH = "filePath"
|
|
695
|
+
""""""
|
|
696
|
+
FILE_KEY = "fileKey"
|
|
697
|
+
""""""
|
|
694
698
|
LOCAL_ENTITIES_COUNT = "localEntitiesCount"
|
|
695
699
|
""""""
|
|
696
700
|
REMOTE_ENTITIES_COUNT = "remoteEntitiesCount"
|
|
@@ -703,6 +707,12 @@ class ApiField:
|
|
|
703
707
|
""""""
|
|
704
708
|
ERROR_MESSAGE = "errorMessage"
|
|
705
709
|
""""""
|
|
710
|
+
UNIQUE_ITEMS = "uniqueItems"
|
|
711
|
+
""""""
|
|
712
|
+
NN_CREATED = "nnCreated"
|
|
713
|
+
""""""
|
|
714
|
+
NN_UPDATED = "nnUpdated"
|
|
715
|
+
""""""
|
|
706
716
|
|
|
707
717
|
|
|
708
718
|
def _get_single_item(items):
|
supervisely/api/nn/deploy_api.py
CHANGED
|
@@ -157,16 +157,15 @@ class DeployApi:
|
|
|
157
157
|
"device": device,
|
|
158
158
|
"model_source": ModelSource.CUSTOM,
|
|
159
159
|
"model_files": {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
).as_posix(),
|
|
163
|
-
"config": Path(
|
|
164
|
-
experiment_info.artifacts_dir, experiment_info.model_files["config"]
|
|
165
|
-
).as_posix(),
|
|
160
|
+
key: Path(experiment_info.artifacts_dir, value).as_posix()
|
|
161
|
+
for key, value in experiment_info.model_files.items()
|
|
166
162
|
},
|
|
167
163
|
"model_info": experiment_info.to_json(),
|
|
168
164
|
"runtime": runtime,
|
|
169
165
|
}
|
|
166
|
+
deploy_params["model_files"]["checkpoint"] = Path(
|
|
167
|
+
experiment_info.artifacts_dir, "checkpoints", checkpoint_name
|
|
168
|
+
).as_posix()
|
|
170
169
|
self._load_model_from_api(session_id, deploy_params)
|
|
171
170
|
|
|
172
171
|
def _find_agent(self, team_id: int = None, public=True, gpu=True):
|
|
@@ -239,6 +238,7 @@ class DeployApi:
|
|
|
239
238
|
RTDETR,
|
|
240
239
|
Detectron2,
|
|
241
240
|
MMClassification,
|
|
241
|
+
MMPretrain,
|
|
242
242
|
MMDetection,
|
|
243
243
|
MMDetection3,
|
|
244
244
|
MMSegmentation,
|
|
@@ -262,6 +262,7 @@ class DeployApi:
|
|
|
262
262
|
RTDETR(team_id).framework_name: RTDETR(team_id).serve_slug,
|
|
263
263
|
Detectron2(team_id).framework_name: Detectron2(team_id).serve_slug,
|
|
264
264
|
MMClassification(team_id).framework_name: MMClassification(team_id).serve_slug,
|
|
265
|
+
MMPretrain(team_id).framework_name: MMPretrain(team_id).serve_slug,
|
|
265
266
|
MMDetection(team_id).framework_name: MMDetection(team_id).serve_slug,
|
|
266
267
|
MMDetection3(team_id).framework_name: MMDetection3(team_id).serve_slug,
|
|
267
268
|
MMSegmentation(team_id).framework_name: MMSegmentation(team_id).serve_slug,
|
|
@@ -547,7 +548,6 @@ class DeployApi:
|
|
|
547
548
|
_attempt_delay_sec = 1
|
|
548
549
|
_attempts = timeout // _attempt_delay_sec
|
|
549
550
|
|
|
550
|
-
# @TODO: Run app in team?
|
|
551
551
|
if workspace_id is None:
|
|
552
552
|
workspace_id = env.workspace_id()
|
|
553
553
|
kwargs = get_valid_kwargs(
|
|
@@ -768,10 +768,14 @@ class DeployApi:
|
|
|
768
768
|
for file_key, file_path in experiment_info.model_files.items():
|
|
769
769
|
full_file_path = os.path.join(experiment_info.artifacts_dir, file_path)
|
|
770
770
|
if not self._api.file.exists(team_id, full_file_path):
|
|
771
|
-
logger.debug(
|
|
771
|
+
logger.debug(
|
|
772
|
+
f"Model file not found: '{full_file_path}'. Trying to find it by checkpoint path."
|
|
773
|
+
)
|
|
772
774
|
full_file_path = os.path.join(artifacts_dir, file_path)
|
|
773
775
|
if not self._api.file.exists(team_id, full_file_path):
|
|
774
|
-
raise ValueError(
|
|
776
|
+
raise ValueError(
|
|
777
|
+
f"Model file not found: '{full_file_path}'. Make sure that the file exists in the artifacts directory."
|
|
778
|
+
)
|
|
775
779
|
deploy_params["model_files"][file_key] = full_file_path
|
|
776
780
|
logger.debug(f"Model file added: {full_file_path}")
|
|
777
781
|
return module_id, serve_app_name, deploy_params
|
|
@@ -845,6 +849,7 @@ class DeployApi:
|
|
|
845
849
|
RTDETR,
|
|
846
850
|
Detectron2,
|
|
847
851
|
MMClassification,
|
|
852
|
+
MMPretrain,
|
|
848
853
|
MMDetection,
|
|
849
854
|
MMDetection3,
|
|
850
855
|
MMSegmentation,
|
|
@@ -863,6 +868,7 @@ class DeployApi:
|
|
|
863
868
|
frameworks = {
|
|
864
869
|
"/detectron2": Detectron2,
|
|
865
870
|
"/mmclassification": MMClassification,
|
|
871
|
+
"/mmclassification-v2": MMPretrain,
|
|
866
872
|
"/mmdetection": MMDetection,
|
|
867
873
|
"/mmdetection-3": MMDetection3,
|
|
868
874
|
"/mmsegmentation": MMSegmentation,
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from typing import List, Literal
|
|
2
|
+
|
|
3
|
+
from supervisely.api.api import Api
|
|
4
|
+
from supervisely.api.module_api import ApiField, ModuleApi
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ModelApiField:
|
|
8
|
+
NAME = "name"
|
|
9
|
+
FRAMEWORK = "framework"
|
|
10
|
+
TASK_TYPE = "task"
|
|
11
|
+
MODALITY = "modality"
|
|
12
|
+
TRAIN_MODULE_ID = "trainModuleId"
|
|
13
|
+
SERVE_MODULE_ID = "serveModuleId"
|
|
14
|
+
ARCHITECTURE = "architecture"
|
|
15
|
+
PRETRAINED = "pretrained"
|
|
16
|
+
NUM_CLASSES = "numClasses"
|
|
17
|
+
SIZE = "size"
|
|
18
|
+
PARAMS_M = "paramsM"
|
|
19
|
+
GFLOPS = "GFLOPs"
|
|
20
|
+
TAGS = "tags"
|
|
21
|
+
RUNTIMES = "runtimes"
|
|
22
|
+
FILES = "files"
|
|
23
|
+
SPEED_TESTS = "speedTests"
|
|
24
|
+
EVALUATION = "evaluation"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class EcosystemModelsApi(ModuleApi):
|
|
28
|
+
|
|
29
|
+
def __init__(self, api: Api):
|
|
30
|
+
self._api = api
|
|
31
|
+
|
|
32
|
+
def _convert_json_info(self, json_info):
|
|
33
|
+
return json_info
|
|
34
|
+
|
|
35
|
+
def get_list_all_pages(
|
|
36
|
+
self,
|
|
37
|
+
method,
|
|
38
|
+
data,
|
|
39
|
+
progress_cb=None,
|
|
40
|
+
convert_json_info_cb=None,
|
|
41
|
+
limit: int = None,
|
|
42
|
+
return_first_response: bool = False,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Get list of all or limited quantity entities from the Supervisely server.
|
|
46
|
+
|
|
47
|
+
:param method: Request method name
|
|
48
|
+
:type method: str
|
|
49
|
+
:param data: Dictionary with request body info
|
|
50
|
+
:type data: dict
|
|
51
|
+
:param progress_cb: Function for tracking download progress.
|
|
52
|
+
:type progress_cb: Progress, optional
|
|
53
|
+
:param convert_json_info_cb: Function for convert json info
|
|
54
|
+
:type convert_json_info_cb: Callable, optional
|
|
55
|
+
:param limit: Number of entity to retrieve
|
|
56
|
+
:type limit: int, optional
|
|
57
|
+
:param return_first_response: Specify if return first response
|
|
58
|
+
:type return_first_response: bool, optional
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
if convert_json_info_cb is None:
|
|
62
|
+
convert_func = self._convert_json_info
|
|
63
|
+
else:
|
|
64
|
+
convert_func = convert_json_info_cb
|
|
65
|
+
|
|
66
|
+
if ApiField.SORT not in data:
|
|
67
|
+
data = self._add_sort_param(data)
|
|
68
|
+
first_response = self._api.get(method, {}, data=data).json()
|
|
69
|
+
total = first_response["total"]
|
|
70
|
+
per_page = first_response["perPage"]
|
|
71
|
+
pages_count = first_response["pagesCount"]
|
|
72
|
+
|
|
73
|
+
limit_exceeded = False
|
|
74
|
+
results = first_response["entities"]
|
|
75
|
+
if limit is not None and len(results) > limit:
|
|
76
|
+
limit_exceeded = True
|
|
77
|
+
|
|
78
|
+
if progress_cb is not None:
|
|
79
|
+
progress_cb(len(results))
|
|
80
|
+
if (pages_count == 1 and len(results) == total) or limit_exceeded is True:
|
|
81
|
+
pass
|
|
82
|
+
else:
|
|
83
|
+
for page_idx in range(2, pages_count + 1):
|
|
84
|
+
temp_resp = self._api.get(
|
|
85
|
+
method, {}, data={**data, "page": page_idx, "per_page": per_page}
|
|
86
|
+
)
|
|
87
|
+
temp_items = temp_resp.json()["entities"]
|
|
88
|
+
results.extend(temp_items)
|
|
89
|
+
if progress_cb is not None:
|
|
90
|
+
progress_cb(len(temp_items))
|
|
91
|
+
if limit is not None and len(results) > limit:
|
|
92
|
+
limit_exceeded = True
|
|
93
|
+
break
|
|
94
|
+
|
|
95
|
+
if len(results) != total and limit is None:
|
|
96
|
+
raise RuntimeError(
|
|
97
|
+
"Method {!r}: error during pagination, some items are missed".format(method)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if limit is not None:
|
|
101
|
+
results = results[:limit]
|
|
102
|
+
if return_first_response:
|
|
103
|
+
return [convert_func(item) for item in results], first_response
|
|
104
|
+
return [convert_func(item) for item in results]
|
|
105
|
+
|
|
106
|
+
def list_models(self, local=False):
|
|
107
|
+
method = "ecosystem.models.list"
|
|
108
|
+
data = {"localModels": local}
|
|
109
|
+
return self.get_list_all_pages(method, data=data)
|
|
110
|
+
|
|
111
|
+
def add(
|
|
112
|
+
self,
|
|
113
|
+
name: str,
|
|
114
|
+
framework: str,
|
|
115
|
+
task_type: str,
|
|
116
|
+
tain_module_id: int,
|
|
117
|
+
serve_module_id: int,
|
|
118
|
+
modality: Literal["images", "videos"] = "images",
|
|
119
|
+
architecture: str = None,
|
|
120
|
+
pretrained: bool = False,
|
|
121
|
+
num_classes: int = None,
|
|
122
|
+
size: int = None,
|
|
123
|
+
params_m: int = None,
|
|
124
|
+
gflops: float = None,
|
|
125
|
+
tags: List[str] = None,
|
|
126
|
+
runtimes: List[str] = None,
|
|
127
|
+
files: List[str] = None,
|
|
128
|
+
speed_tests: List = None,
|
|
129
|
+
evaluation: dict = None,
|
|
130
|
+
):
|
|
131
|
+
method = "ecosystem.models.add"
|
|
132
|
+
data = {
|
|
133
|
+
ModelApiField.NAME: name,
|
|
134
|
+
ModelApiField.FRAMEWORK: framework,
|
|
135
|
+
ModelApiField.TASK_TYPE: task_type,
|
|
136
|
+
ModelApiField.MODALITY: modality,
|
|
137
|
+
ModelApiField.TRAIN_MODULE_ID: tain_module_id,
|
|
138
|
+
ModelApiField.SERVE_MODULE_ID: serve_module_id,
|
|
139
|
+
}
|
|
140
|
+
optional_fields = {
|
|
141
|
+
ModelApiField.ARCHITECTURE: architecture,
|
|
142
|
+
ModelApiField.PRETRAINED: pretrained,
|
|
143
|
+
ModelApiField.NUM_CLASSES: num_classes,
|
|
144
|
+
ModelApiField.SIZE: size,
|
|
145
|
+
ModelApiField.PARAMS_M: params_m,
|
|
146
|
+
ModelApiField.GFLOPS: gflops,
|
|
147
|
+
ModelApiField.TAGS: tags,
|
|
148
|
+
ModelApiField.RUNTIMES: runtimes,
|
|
149
|
+
ModelApiField.FILES: files,
|
|
150
|
+
ModelApiField.SPEED_TESTS: speed_tests,
|
|
151
|
+
ModelApiField.EVALUATION: evaluation,
|
|
152
|
+
}
|
|
153
|
+
for key, value in optional_fields.items():
|
|
154
|
+
if value is not None:
|
|
155
|
+
data[key] = value
|
|
156
|
+
return self._api.post(method, data=data)
|
|
157
|
+
|
|
158
|
+
def update_model(
|
|
159
|
+
self,
|
|
160
|
+
model_id: int,
|
|
161
|
+
name: str = None,
|
|
162
|
+
framework: str = None,
|
|
163
|
+
task_type: str = None,
|
|
164
|
+
tain_module_id: int = None,
|
|
165
|
+
serve_module_id: int = None,
|
|
166
|
+
modality: Literal["images", "videos"] = None,
|
|
167
|
+
architecture: str = None,
|
|
168
|
+
pretrained: bool = False,
|
|
169
|
+
num_classes: int = None,
|
|
170
|
+
size: int = None,
|
|
171
|
+
params_m: int = None,
|
|
172
|
+
gflops: float = None,
|
|
173
|
+
tags: List[str] = None,
|
|
174
|
+
runtimes: List[str] = None,
|
|
175
|
+
files: List[str] = None,
|
|
176
|
+
speed_tests: List = None,
|
|
177
|
+
evaluation: dict = None,
|
|
178
|
+
):
|
|
179
|
+
data = {
|
|
180
|
+
ModelApiField.NAME: name,
|
|
181
|
+
ModelApiField.FRAMEWORK: framework,
|
|
182
|
+
ModelApiField.TASK_TYPE: task_type,
|
|
183
|
+
ModelApiField.MODALITY: modality,
|
|
184
|
+
ModelApiField.TRAIN_MODULE_ID: tain_module_id,
|
|
185
|
+
ModelApiField.SERVE_MODULE_ID: serve_module_id,
|
|
186
|
+
ModelApiField.ARCHITECTURE: architecture,
|
|
187
|
+
ModelApiField.PRETRAINED: pretrained,
|
|
188
|
+
ModelApiField.NUM_CLASSES: num_classes,
|
|
189
|
+
ModelApiField.SIZE: size,
|
|
190
|
+
ModelApiField.PARAMS_M: params_m,
|
|
191
|
+
ModelApiField.GFLOPS: gflops,
|
|
192
|
+
ModelApiField.TAGS: tags,
|
|
193
|
+
ModelApiField.RUNTIMES: runtimes,
|
|
194
|
+
ModelApiField.FILES: files,
|
|
195
|
+
ModelApiField.SPEED_TESTS: speed_tests,
|
|
196
|
+
ModelApiField.EVALUATION: evaluation,
|
|
197
|
+
}
|
|
198
|
+
data = {k: v for k, v in data.items() if v is not None}
|
|
199
|
+
method = "ecosystem.models.update"
|
|
200
|
+
data["id"] = model_id
|
|
201
|
+
return self._api.post(method, data=data)
|
|
@@ -23,9 +23,11 @@ class NeuralNetworkApi:
|
|
|
23
23
|
|
|
24
24
|
def __init__(self, api: "Api"):
|
|
25
25
|
from supervisely.api.nn.deploy_api import DeployApi
|
|
26
|
+
from supervisely.api.nn.ecosystem_models_api import EcosystemModelsApi
|
|
26
27
|
|
|
27
28
|
self._api = api
|
|
28
29
|
self._deploy_api = DeployApi(api)
|
|
30
|
+
self.ecosystem_models_api = EcosystemModelsApi(api)
|
|
29
31
|
|
|
30
32
|
def deploy(
|
|
31
33
|
self,
|
|
@@ -168,7 +170,7 @@ class NeuralNetworkApi:
|
|
|
168
170
|
workspaces = [workspace_id]
|
|
169
171
|
elif team_id is not None:
|
|
170
172
|
workspaces = self._api.workspace.get_list(team_id)
|
|
171
|
-
workspaces = [workspace
|
|
173
|
+
workspaces = [workspace.id for workspace in workspaces]
|
|
172
174
|
else:
|
|
173
175
|
workspace_id = env.workspace_id(raise_not_found=False)
|
|
174
176
|
if workspace_id is None:
|
|
@@ -178,7 +180,7 @@ class NeuralNetworkApi:
|
|
|
178
180
|
"Workspace ID and Team ID are not specified and cannot be found in the environment."
|
|
179
181
|
)
|
|
180
182
|
workspaces = self._api.workspace.get_list(team_id)
|
|
181
|
-
workspaces = [workspace
|
|
183
|
+
workspaces = [workspace.id for workspace in workspaces]
|
|
182
184
|
else:
|
|
183
185
|
workspaces = [workspace_id]
|
|
184
186
|
|
|
@@ -202,7 +204,14 @@ class NeuralNetworkApi:
|
|
|
202
204
|
# get deploy infos and filter results
|
|
203
205
|
result = []
|
|
204
206
|
for task in all_tasks:
|
|
205
|
-
|
|
207
|
+
try:
|
|
208
|
+
deploy_info = self._deploy_api.get_deploy_info(task["id"])
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.warning(
|
|
211
|
+
f"Failed to get deploy info for task {task['id']}: {e}",
|
|
212
|
+
exc_info=True,
|
|
213
|
+
)
|
|
214
|
+
continue
|
|
206
215
|
if model is not None:
|
|
207
216
|
checkpoint = deploy_info["checkpoint_name"]
|
|
208
217
|
deployed_model = deploy_info["model_name"]
|
|
@@ -1431,3 +1431,41 @@ class PointcloudApi(RemoveableBulkModuleApi):
|
|
|
1431
1431
|
)
|
|
1432
1432
|
tasks.append(task)
|
|
1433
1433
|
await asyncio.gather(*tasks)
|
|
1434
|
+
|
|
1435
|
+
def rename(
|
|
1436
|
+
self,
|
|
1437
|
+
id: int,
|
|
1438
|
+
name: str,
|
|
1439
|
+
) -> PointcloudInfo:
|
|
1440
|
+
"""Renames Pointcloud with given ID to a new name.
|
|
1441
|
+
|
|
1442
|
+
:param id: Pointcloud ID in Supervisely.
|
|
1443
|
+
:type id: int
|
|
1444
|
+
:param name: New Pointcloud name.
|
|
1445
|
+
:type name: str
|
|
1446
|
+
:return: Information about updated Pointcloud.
|
|
1447
|
+
:rtype: :class:`PointcloudInfo`
|
|
1448
|
+
|
|
1449
|
+
:Usage example:
|
|
1450
|
+
|
|
1451
|
+
.. code-block:: python
|
|
1452
|
+
|
|
1453
|
+
import supervisely as sly
|
|
1454
|
+
|
|
1455
|
+
api = sly.Api.from_env()
|
|
1456
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
1457
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
1458
|
+
|
|
1459
|
+
pointcloud_id = 123456
|
|
1460
|
+
new_pointcloud_name = "3333_new.pcd"
|
|
1461
|
+
|
|
1462
|
+
api.pointcloud.rename(id=pointcloud_id, name=new_pointcloud_name)
|
|
1463
|
+
"""
|
|
1464
|
+
|
|
1465
|
+
data = {
|
|
1466
|
+
ApiField.ID: id,
|
|
1467
|
+
ApiField.NAME: name,
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
response = self._api.post("images.editInfo", data)
|
|
1471
|
+
return self._convert_json_info(response.json())
|
|
@@ -107,6 +107,9 @@ class PointcloudEpisodeAnnotationAPI(EntityAnnotationAPI):
|
|
|
107
107
|
"""
|
|
108
108
|
|
|
109
109
|
response = self._api.post(self._method_download, {ApiField.DATASET_ID: dataset_id})
|
|
110
|
+
result = response.json()
|
|
111
|
+
if len(result) == 0:
|
|
112
|
+
return PointcloudEpisodeAnnotation().to_json()
|
|
110
113
|
return response.json()[0]
|
|
111
114
|
|
|
112
115
|
def download_bulk(self, dataset_id, entity_ids):
|