supervisely 6.73.277__py3-none-any.whl → 6.73.279__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 +2 -0
- supervisely/annotation/annotation.py +138 -1
- supervisely/convert/__init__.py +12 -56
- supervisely/convert/base_converter.py +12 -1
- supervisely/convert/image/__init__.py +22 -0
- supervisely/convert/image/coco/coco_helper.py +494 -2
- supervisely/convert/image/pascal_voc/pascal_voc_helper.py +417 -11
- supervisely/convert/image/yolo/yolo_helper.py +339 -4
- supervisely/convert/pointcloud/__init__.py +8 -0
- supervisely/convert/pointcloud_episodes/__init__.py +9 -0
- supervisely/convert/video/__init__.py +3 -0
- supervisely/convert/volume/__init__.py +3 -0
- supervisely/nn/training/gui/gui.py +25 -1
- supervisely/nn/training/gui/hyperparameters_selector.py +11 -1
- supervisely/nn/training/gui/model_selector.py +8 -2
- supervisely/nn/training/gui/training_artifacts.py +1 -1
- supervisely/nn/training/train_app.py +151 -46
- supervisely/project/project.py +311 -2
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/METADATA +1 -1
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/RECORD +24 -24
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/LICENSE +0 -0
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/WHEEL +0 -0
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.277.dist-info → supervisely-6.73.279.dist-info}/top_level.txt +0 -0
|
@@ -25,6 +25,7 @@ import supervisely.io.json as sly_json
|
|
|
25
25
|
from supervisely import (
|
|
26
26
|
Api,
|
|
27
27
|
Application,
|
|
28
|
+
Dataset,
|
|
28
29
|
DatasetInfo,
|
|
29
30
|
OpenMode,
|
|
30
31
|
Project,
|
|
@@ -32,6 +33,7 @@ from supervisely import (
|
|
|
32
33
|
ProjectMeta,
|
|
33
34
|
WorkflowMeta,
|
|
34
35
|
WorkflowSettings,
|
|
36
|
+
batched,
|
|
35
37
|
download_project,
|
|
36
38
|
is_development,
|
|
37
39
|
is_production,
|
|
@@ -340,22 +342,6 @@ class TrainApp:
|
|
|
340
342
|
"""
|
|
341
343
|
return self.gui.model_selector.get_model_info()
|
|
342
344
|
|
|
343
|
-
@property
|
|
344
|
-
def model_meta(self) -> ProjectMeta:
|
|
345
|
-
"""
|
|
346
|
-
Returns the model metadata.
|
|
347
|
-
|
|
348
|
-
:return: Model metadata.
|
|
349
|
-
:rtype: dict
|
|
350
|
-
"""
|
|
351
|
-
project_meta_json = self.project_meta.to_json()
|
|
352
|
-
model_meta = {
|
|
353
|
-
"classes": [
|
|
354
|
-
item for item in project_meta_json["classes"] if item["title"] in self.classes
|
|
355
|
-
]
|
|
356
|
-
}
|
|
357
|
-
return ProjectMeta.from_json(model_meta)
|
|
358
|
-
|
|
359
345
|
@property
|
|
360
346
|
def device(self) -> str:
|
|
361
347
|
"""
|
|
@@ -505,8 +491,7 @@ class TrainApp:
|
|
|
505
491
|
self._download_project()
|
|
506
492
|
# Step 3. Split Project
|
|
507
493
|
self._split_project()
|
|
508
|
-
# Step 4.
|
|
509
|
-
# Step 5. Download Model files
|
|
494
|
+
# Step 4. Download Model files
|
|
510
495
|
self._download_model()
|
|
511
496
|
|
|
512
497
|
def _finalize(self, experiment_info: dict) -> None:
|
|
@@ -528,13 +513,14 @@ class TrainApp:
|
|
|
528
513
|
experiment_info = self._preprocess_artifacts(experiment_info)
|
|
529
514
|
|
|
530
515
|
# Step3. Postprocess splits
|
|
531
|
-
|
|
516
|
+
train_splits_data = self._postprocess_splits()
|
|
532
517
|
|
|
533
518
|
# Step 3. Upload artifacts
|
|
534
519
|
self._set_text_status("uploading")
|
|
535
520
|
remote_dir, file_info = self._upload_artifacts()
|
|
536
521
|
|
|
537
522
|
# Step 4. Run Model Benchmark
|
|
523
|
+
model_meta = self.create_model_meta(experiment_info["task_type"])
|
|
538
524
|
mb_eval_lnk_file_info, mb_eval_report, mb_eval_report_id, eval_metrics = (
|
|
539
525
|
None,
|
|
540
526
|
None,
|
|
@@ -543,6 +529,15 @@ class TrainApp:
|
|
|
543
529
|
)
|
|
544
530
|
if self.is_model_benchmark_enabled:
|
|
545
531
|
try:
|
|
532
|
+
# Convert GT project
|
|
533
|
+
if self._app_options.get("auto_convert_classes", True):
|
|
534
|
+
self._set_text_status("convert_gt_project")
|
|
535
|
+
gt_project_id, bm_splits_data = self._convert_and_split_gt_project(
|
|
536
|
+
experiment_info["task_type"]
|
|
537
|
+
)
|
|
538
|
+
else:
|
|
539
|
+
gt_project_id, bm_splits_data = None, train_splits_data
|
|
540
|
+
|
|
546
541
|
self._set_text_status("benchmark")
|
|
547
542
|
(
|
|
548
543
|
mb_eval_lnk_file_info,
|
|
@@ -550,7 +545,12 @@ class TrainApp:
|
|
|
550
545
|
mb_eval_report_id,
|
|
551
546
|
eval_metrics,
|
|
552
547
|
) = self._run_model_benchmark(
|
|
553
|
-
self.output_dir,
|
|
548
|
+
self.output_dir,
|
|
549
|
+
remote_dir,
|
|
550
|
+
experiment_info,
|
|
551
|
+
bm_splits_data,
|
|
552
|
+
model_meta,
|
|
553
|
+
gt_project_id,
|
|
554
554
|
)
|
|
555
555
|
except Exception as e:
|
|
556
556
|
logger.error(f"Model benchmark failed: {e}")
|
|
@@ -571,8 +571,8 @@ class TrainApp:
|
|
|
571
571
|
)
|
|
572
572
|
self._generate_app_state(remote_dir, experiment_info)
|
|
573
573
|
self._generate_hyperparameters(remote_dir, experiment_info)
|
|
574
|
-
self._generate_train_val_splits(remote_dir,
|
|
575
|
-
self._generate_model_meta(remote_dir,
|
|
574
|
+
self._generate_train_val_splits(remote_dir, train_splits_data)
|
|
575
|
+
self._generate_model_meta(remote_dir, model_meta)
|
|
576
576
|
self._upload_demo_files(remote_dir)
|
|
577
577
|
|
|
578
578
|
# Step 7. Set output widgets
|
|
@@ -1200,9 +1200,14 @@ class TrainApp:
|
|
|
1200
1200
|
logger.debug("Validation successful")
|
|
1201
1201
|
return True, None
|
|
1202
1202
|
|
|
1203
|
-
def _postprocess_splits(self) -> dict:
|
|
1203
|
+
def _postprocess_splits(self, project_id: Optional[int] = None) -> dict:
|
|
1204
1204
|
"""
|
|
1205
1205
|
Processes the train and val splits to generate the necessary data for the experiment_info.json file.
|
|
1206
|
+
|
|
1207
|
+
:param project_id: ID of the ground truth project for model benchmark. Provide only when cv task convertion is required.
|
|
1208
|
+
:type project_id: Optional[int]
|
|
1209
|
+
:return: Splits data.
|
|
1210
|
+
:rtype: dict
|
|
1206
1211
|
"""
|
|
1207
1212
|
val_dataset_ids = None
|
|
1208
1213
|
val_images_ids = None
|
|
@@ -1212,10 +1217,30 @@ class TrainApp:
|
|
|
1212
1217
|
split_method = self.gui.train_val_splits_selector.get_split_method()
|
|
1213
1218
|
train_set, val_set = self._train_split, self._val_split
|
|
1214
1219
|
if split_method == "Based on datasets":
|
|
1215
|
-
|
|
1216
|
-
|
|
1220
|
+
if project_id is None:
|
|
1221
|
+
val_dataset_ids = self.gui.train_val_splits_selector.get_val_dataset_ids()
|
|
1222
|
+
train_dataset_ids = self.gui.train_val_splits_selector.get_train_dataset_ids()
|
|
1223
|
+
else:
|
|
1224
|
+
src_datasets_map = {
|
|
1225
|
+
dataset.id: dataset
|
|
1226
|
+
for _, dataset in self._api.dataset.tree(self.project_info.id)
|
|
1227
|
+
}
|
|
1228
|
+
val_dataset_ids = self.gui.train_val_splits_selector.get_val_dataset_ids()
|
|
1229
|
+
train_dataset_ids = self.gui.train_val_splits_selector.get_train_dataset_ids()
|
|
1230
|
+
|
|
1231
|
+
train_dataset_names = [src_datasets_map[ds_id].name for ds_id in train_dataset_ids]
|
|
1232
|
+
val_dataset_names = [src_datasets_map[ds_id].name for ds_id in val_dataset_ids]
|
|
1233
|
+
|
|
1234
|
+
gt_datasets_map = {
|
|
1235
|
+
dataset.name: dataset.id for _, dataset in self._api.dataset.tree(project_id)
|
|
1236
|
+
}
|
|
1237
|
+
train_dataset_ids = [gt_datasets_map[ds_name] for ds_name in train_dataset_names]
|
|
1238
|
+
val_dataset_ids = [gt_datasets_map[ds_name] for ds_name in val_dataset_names]
|
|
1217
1239
|
else:
|
|
1218
|
-
|
|
1240
|
+
if project_id is None:
|
|
1241
|
+
project_id = self.project_id
|
|
1242
|
+
|
|
1243
|
+
dataset_infos = [dataset for _, dataset in self._api.dataset.tree(project_id)]
|
|
1219
1244
|
ds_infos_dict = {}
|
|
1220
1245
|
for dataset in dataset_infos:
|
|
1221
1246
|
if dataset.parent_id is not None:
|
|
@@ -1232,18 +1257,19 @@ class TrainApp:
|
|
|
1232
1257
|
image_infos = []
|
|
1233
1258
|
for dataset_name, image_names in image_names_per_dataset.items():
|
|
1234
1259
|
ds_info = ds_infos_dict[dataset_name]
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1260
|
+
for names_batch in batched(image_names, 200):
|
|
1261
|
+
image_infos.extend(
|
|
1262
|
+
self._api.image.get_list(
|
|
1263
|
+
ds_info.id,
|
|
1264
|
+
filters=[
|
|
1265
|
+
{
|
|
1266
|
+
"field": "name",
|
|
1267
|
+
"operator": "in",
|
|
1268
|
+
"value": names_batch,
|
|
1269
|
+
}
|
|
1270
|
+
],
|
|
1271
|
+
)
|
|
1245
1272
|
)
|
|
1246
|
-
)
|
|
1247
1273
|
return image_infos
|
|
1248
1274
|
|
|
1249
1275
|
val_image_infos = get_image_infos_by_split(ds_infos_dict, val_set)
|
|
@@ -1373,7 +1399,7 @@ class TrainApp:
|
|
|
1373
1399
|
f"Uploading '{self._train_val_split_file}' to Team Files",
|
|
1374
1400
|
)
|
|
1375
1401
|
|
|
1376
|
-
def _generate_model_meta(self, remote_dir: str,
|
|
1402
|
+
def _generate_model_meta(self, remote_dir: str, model_meta: ProjectMeta) -> None:
|
|
1377
1403
|
"""
|
|
1378
1404
|
Generates and uploads the model_meta.json file to the output directory.
|
|
1379
1405
|
|
|
@@ -1382,17 +1408,31 @@ class TrainApp:
|
|
|
1382
1408
|
:param experiment_info: Information about the experiment results.
|
|
1383
1409
|
:type experiment_info: dict
|
|
1384
1410
|
"""
|
|
1385
|
-
# @TODO: Handle tags for classification tasks
|
|
1386
1411
|
local_path = join(self.output_dir, self._model_meta_file)
|
|
1387
1412
|
remote_path = join(remote_dir, self._model_meta_file)
|
|
1388
1413
|
|
|
1389
|
-
sly_json.dump_json_file(
|
|
1414
|
+
sly_json.dump_json_file(model_meta.to_json(), local_path)
|
|
1390
1415
|
self._upload_file_to_team_files(
|
|
1391
1416
|
local_path,
|
|
1392
1417
|
remote_path,
|
|
1393
1418
|
f"Uploading '{self._model_meta_file}' to Team Files",
|
|
1394
1419
|
)
|
|
1395
1420
|
|
|
1421
|
+
def create_model_meta(self, task_type: str):
|
|
1422
|
+
"""
|
|
1423
|
+
Convert project meta according to task type.
|
|
1424
|
+
"""
|
|
1425
|
+
names_to_delete = [
|
|
1426
|
+
c.name for c in self.project_meta.obj_classes if c.name not in self.classes
|
|
1427
|
+
]
|
|
1428
|
+
model_meta = self.project_meta.delete_obj_classes(names_to_delete)
|
|
1429
|
+
|
|
1430
|
+
if task_type == TaskType.OBJECT_DETECTION:
|
|
1431
|
+
model_meta, _ = model_meta.to_detection_task(True)
|
|
1432
|
+
elif task_type in [TaskType.INSTANCE_SEGMENTATION, TaskType.SEMANTIC_SEGMENTATION]:
|
|
1433
|
+
model_meta, _ = model_meta.to_segmentation_task() # @TODO: check background class
|
|
1434
|
+
return model_meta
|
|
1435
|
+
|
|
1396
1436
|
def _generate_experiment_info(
|
|
1397
1437
|
self,
|
|
1398
1438
|
remote_dir: str,
|
|
@@ -1674,8 +1714,9 @@ class TrainApp:
|
|
|
1674
1714
|
self.gui.training_artifacts.model_benchmark_report_thumbnail.show()
|
|
1675
1715
|
self.gui.training_artifacts.model_benchmark_report_field.show()
|
|
1676
1716
|
else:
|
|
1677
|
-
self.gui.
|
|
1678
|
-
|
|
1717
|
+
if self.gui.hyperparameters_selector.get_model_benchmark_checkbox_value():
|
|
1718
|
+
self.gui.training_artifacts.model_benchmark_fail_text.show()
|
|
1719
|
+
self.gui.training_artifacts.model_benchmark_report_field.show()
|
|
1679
1720
|
# ---------------------------- #
|
|
1680
1721
|
|
|
1681
1722
|
# Set instruction to GUI
|
|
@@ -1739,6 +1780,8 @@ class TrainApp:
|
|
|
1739
1780
|
remote_artifacts_dir: str,
|
|
1740
1781
|
experiment_info: dict,
|
|
1741
1782
|
splits_data: dict,
|
|
1783
|
+
model_meta: ProjectInfo,
|
|
1784
|
+
gt_project_id: int = None,
|
|
1742
1785
|
) -> tuple:
|
|
1743
1786
|
"""
|
|
1744
1787
|
Runs the Model Benchmark evaluation process. Model benchmark runs only in production mode.
|
|
@@ -1751,6 +1794,10 @@ class TrainApp:
|
|
|
1751
1794
|
:type experiment_info: dict
|
|
1752
1795
|
:param splits_data: Information about the train and val splits.
|
|
1753
1796
|
:type splits_data: dict
|
|
1797
|
+
:param model_meta: Model meta with object classes.
|
|
1798
|
+
:type model_meta: ProjectInfo
|
|
1799
|
+
:param gt_project_id: Ground truth project ID with converted shapes.
|
|
1800
|
+
:type gt_project_id: int
|
|
1754
1801
|
:return: Evaluation report, report ID and evaluation metrics.
|
|
1755
1802
|
:rtype: tuple
|
|
1756
1803
|
"""
|
|
@@ -1766,6 +1813,7 @@ class TrainApp:
|
|
|
1766
1813
|
supported_task_types = [
|
|
1767
1814
|
TaskType.OBJECT_DETECTION,
|
|
1768
1815
|
TaskType.INSTANCE_SEGMENTATION,
|
|
1816
|
+
TaskType.SEMANTIC_SEGMENTATION,
|
|
1769
1817
|
]
|
|
1770
1818
|
task_type = experiment_info["task_type"]
|
|
1771
1819
|
if task_type not in supported_task_types:
|
|
@@ -1806,7 +1854,7 @@ class TrainApp:
|
|
|
1806
1854
|
"artifacts_dir": remote_artifacts_dir,
|
|
1807
1855
|
"model_name": experiment_info["model_name"],
|
|
1808
1856
|
"framework_name": self.framework_name,
|
|
1809
|
-
"model_meta":
|
|
1857
|
+
"model_meta": model_meta.to_json(),
|
|
1810
1858
|
}
|
|
1811
1859
|
|
|
1812
1860
|
logger.info(f"Deploy parameters: {self._benchmark_params}")
|
|
@@ -1826,12 +1874,15 @@ class TrainApp:
|
|
|
1826
1874
|
train_images_ids = splits_data["train"]["images_ids"]
|
|
1827
1875
|
|
|
1828
1876
|
bm = None
|
|
1877
|
+
if gt_project_id is None:
|
|
1878
|
+
gt_project_id = self.project_info.id
|
|
1879
|
+
|
|
1829
1880
|
if task_type == TaskType.OBJECT_DETECTION:
|
|
1830
1881
|
eval_params = ObjectDetectionEvaluator.load_yaml_evaluation_params()
|
|
1831
1882
|
eval_params = yaml.safe_load(eval_params)
|
|
1832
1883
|
bm = ObjectDetectionBenchmark(
|
|
1833
1884
|
self._api,
|
|
1834
|
-
|
|
1885
|
+
gt_project_id,
|
|
1835
1886
|
output_dir=benchmark_dir,
|
|
1836
1887
|
gt_dataset_ids=benchmark_dataset_ids,
|
|
1837
1888
|
gt_images_ids=benchmark_images_ids,
|
|
@@ -1845,7 +1896,7 @@ class TrainApp:
|
|
|
1845
1896
|
eval_params = yaml.safe_load(eval_params)
|
|
1846
1897
|
bm = InstanceSegmentationBenchmark(
|
|
1847
1898
|
self._api,
|
|
1848
|
-
|
|
1899
|
+
gt_project_id,
|
|
1849
1900
|
output_dir=benchmark_dir,
|
|
1850
1901
|
gt_dataset_ids=benchmark_dataset_ids,
|
|
1851
1902
|
gt_images_ids=benchmark_images_ids,
|
|
@@ -1859,7 +1910,7 @@ class TrainApp:
|
|
|
1859
1910
|
eval_params = yaml.safe_load(eval_params)
|
|
1860
1911
|
bm = SemanticSegmentationBenchmark(
|
|
1861
1912
|
self._api,
|
|
1862
|
-
|
|
1913
|
+
gt_project_id,
|
|
1863
1914
|
output_dir=benchmark_dir,
|
|
1864
1915
|
gt_dataset_ids=benchmark_dataset_ids,
|
|
1865
1916
|
gt_images_ids=benchmark_images_ids,
|
|
@@ -2279,6 +2330,7 @@ class TrainApp:
|
|
|
2279
2330
|
"metadata",
|
|
2280
2331
|
"export_onnx",
|
|
2281
2332
|
"export_trt",
|
|
2333
|
+
"convert_gt_project",
|
|
2282
2334
|
],
|
|
2283
2335
|
):
|
|
2284
2336
|
|
|
@@ -2312,6 +2364,8 @@ class TrainApp:
|
|
|
2312
2364
|
self.gui.training_process.validator_text.set("Validating experiment...", "info")
|
|
2313
2365
|
elif status == "metadata":
|
|
2314
2366
|
self.gui.training_process.validator_text.set("Generating training metadata...", "info")
|
|
2367
|
+
elif status == "convert_gt_project":
|
|
2368
|
+
self.gui.training_process.validator_text.set("Converting GT project...", "info")
|
|
2315
2369
|
|
|
2316
2370
|
def _set_ws_progress_status(
|
|
2317
2371
|
self,
|
|
@@ -2390,3 +2444,54 @@ class TrainApp:
|
|
|
2390
2444
|
for runtime, path in export_weights.items()
|
|
2391
2445
|
}
|
|
2392
2446
|
return remote_export_weights
|
|
2447
|
+
|
|
2448
|
+
def _convert_and_split_gt_project(self, task_type: str):
|
|
2449
|
+
# 1. Convert GT project to cv task
|
|
2450
|
+
Project.download(
|
|
2451
|
+
self._api, self.project_info.id, "tmp_project", save_images=False, save_image_info=True
|
|
2452
|
+
)
|
|
2453
|
+
project = Project("tmp_project", OpenMode.READ)
|
|
2454
|
+
|
|
2455
|
+
pr_prefix = ""
|
|
2456
|
+
if task_type == TaskType.OBJECT_DETECTION:
|
|
2457
|
+
Project.to_detection_task(project.directory, inplace=True)
|
|
2458
|
+
pr_prefix = "[detection]: "
|
|
2459
|
+
# @TODO: dont convert segmentation?
|
|
2460
|
+
elif (
|
|
2461
|
+
task_type == TaskType.INSTANCE_SEGMENTATION
|
|
2462
|
+
or task_type == TaskType.SEMANTIC_SEGMENTATION
|
|
2463
|
+
):
|
|
2464
|
+
Project.to_segmentation_task(project.directory, inplace=True)
|
|
2465
|
+
pr_prefix = "[segmentation]: "
|
|
2466
|
+
|
|
2467
|
+
gt_project_info = self._api.project.create(
|
|
2468
|
+
self.workspace_id,
|
|
2469
|
+
f"{pr_prefix}{self.project_info.name}",
|
|
2470
|
+
description=(
|
|
2471
|
+
f"Converted ground truth project for trainig session: '{self.task_id}'. "
|
|
2472
|
+
f"Original project id: '{self.project_info.id}. "
|
|
2473
|
+
"Removing this project will affect model benchmark evaluation report."
|
|
2474
|
+
),
|
|
2475
|
+
change_name_if_conflict=True,
|
|
2476
|
+
)
|
|
2477
|
+
|
|
2478
|
+
# 3. Upload gt project to benchmark workspace
|
|
2479
|
+
project = Project("tmp_project", OpenMode.READ)
|
|
2480
|
+
self._api.project.update_meta(gt_project_info.id, project.meta)
|
|
2481
|
+
for dataset in project.datasets:
|
|
2482
|
+
dataset: Dataset
|
|
2483
|
+
ds_info = self._api.dataset.create(
|
|
2484
|
+
gt_project_info.id, dataset.name, change_name_if_conflict=True
|
|
2485
|
+
)
|
|
2486
|
+
for batch_names in batched(dataset.get_items_names(), 100):
|
|
2487
|
+
img_infos = [dataset.get_item_info(name) for name in batch_names]
|
|
2488
|
+
img_ids = [img_info.id for img_info in img_infos]
|
|
2489
|
+
anns = [dataset.get_ann(name, project.meta) for name in batch_names]
|
|
2490
|
+
|
|
2491
|
+
img_infos = self._api.image.copy_batch(ds_info.id, img_ids)
|
|
2492
|
+
img_ids = [img_info.id for img_info in img_infos]
|
|
2493
|
+
self._api.annotation.upload_anns(img_ids, anns)
|
|
2494
|
+
|
|
2495
|
+
# 4. Match splits with original project
|
|
2496
|
+
gt_split_data = self._postprocess_splits(gt_project_info.id)
|
|
2497
|
+
return gt_project_info.id, gt_split_data
|