supervisely 6.73.420__py3-none-any.whl → 6.73.421__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.
- supervisely/api/api.py +10 -5
- supervisely/api/app_api.py +71 -4
- supervisely/api/module_api.py +4 -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/project_api.py +35 -6
- supervisely/api/task_api.py +5 -1
- supervisely/app/widgets/__init__.py +8 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/deploy_model/__init__.py +0 -0
- supervisely/app/widgets/deploy_model/deploy_model.py +729 -0
- 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 +190 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +447 -264
- supervisely/app/widgets/fast_table/fast_table.py +402 -74
- supervisely/app/widgets/fast_table/script.js +364 -96
- supervisely/app/widgets/fast_table/style.css +24 -0
- supervisely/app/widgets/fast_table/template.html +43 -3
- supervisely/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +18 -0
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -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/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +160 -94
- 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 +91 -0
- supervisely/nn/inference/predict_app/gui/gui.py +710 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +165 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +79 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +139 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +184 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +282 -0
- supervisely/nn/inference/predict_app/predict_app.py +184 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/prediction.py +2 -0
- supervisely/nn/model/prediction_session.py +20 -3
- supervisely/nn/training/gui/gui.py +131 -44
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +122 -70
- supervisely/nn/training/gui/training_artifacts.py +0 -5
- supervisely/nn/training/train_app.py +161 -44
- supervisely/template/experiment/experiment.html.jinja +74 -17
- 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-6.73.420.dist-info → supervisely-6.73.421.dist-info}/METADATA +3 -1
- {supervisely-6.73.420.dist-info → supervisely-6.73.421.dist-info}/RECORD +74 -56
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- {supervisely-6.73.420.dist-info → supervisely-6.73.421.dist-info}/LICENSE +0 -0
- {supervisely-6.73.420.dist-info → supervisely-6.73.421.dist-info}/WHEEL +0 -0
- {supervisely-6.73.420.dist-info → supervisely-6.73.421.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.420.dist-info → supervisely-6.73.421.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
|
|
3
|
+
from supervisely.app.widgets import (
|
|
4
|
+
Button,
|
|
5
|
+
Card,
|
|
6
|
+
Container,
|
|
7
|
+
OneOf,
|
|
8
|
+
RadioGroup,
|
|
9
|
+
RadioTable,
|
|
10
|
+
SelectDatasetTree,
|
|
11
|
+
Text,
|
|
12
|
+
)
|
|
13
|
+
from supervisely.project.project import ProjectType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InputSelector:
|
|
17
|
+
title = "Select Input"
|
|
18
|
+
description = "Select input data on which to run model for prediction"
|
|
19
|
+
lock_message = None
|
|
20
|
+
|
|
21
|
+
def __init__(self, workspace_id: int):
|
|
22
|
+
# Init Step
|
|
23
|
+
self.workspace_id = workspace_id
|
|
24
|
+
self.display_widgets: List[Any] = []
|
|
25
|
+
# -------------------------------- #
|
|
26
|
+
|
|
27
|
+
# Init Base Widgets
|
|
28
|
+
self.validator_text = None
|
|
29
|
+
self.button = None
|
|
30
|
+
self.container = None
|
|
31
|
+
self.card = None
|
|
32
|
+
# -------------------------------- #
|
|
33
|
+
|
|
34
|
+
# Init Step Widgets
|
|
35
|
+
# Images
|
|
36
|
+
self.select_dataset_for_images = None
|
|
37
|
+
self.select_image_container = None
|
|
38
|
+
# Videos
|
|
39
|
+
self.select_dataset_for_video = None
|
|
40
|
+
self.select_video = None
|
|
41
|
+
self.select_video_container = None
|
|
42
|
+
# Selector
|
|
43
|
+
self.radio = None
|
|
44
|
+
self.one_of = None
|
|
45
|
+
# -------------------------------- #
|
|
46
|
+
|
|
47
|
+
# Images
|
|
48
|
+
self.select_dataset_for_images = SelectDatasetTree(
|
|
49
|
+
multiselect=True,
|
|
50
|
+
flat=True,
|
|
51
|
+
select_all_datasets=False,
|
|
52
|
+
allowed_project_types=[ProjectType.IMAGES],
|
|
53
|
+
always_open=False,
|
|
54
|
+
compact=False,
|
|
55
|
+
team_is_selectable=False,
|
|
56
|
+
workspace_is_selectable=False,
|
|
57
|
+
show_select_all_datasets_checkbox=True,
|
|
58
|
+
)
|
|
59
|
+
self.select_image_container = Container(widgets=[self.select_dataset_for_images])
|
|
60
|
+
self._radio_item_images = RadioGroup.Item(
|
|
61
|
+
ProjectType.IMAGES.value, "Images", content=self.select_image_container
|
|
62
|
+
)
|
|
63
|
+
# -------------------------------- #
|
|
64
|
+
|
|
65
|
+
# Videos
|
|
66
|
+
self.select_dataset_for_video = SelectDatasetTree(
|
|
67
|
+
flat=True,
|
|
68
|
+
select_all_datasets=False,
|
|
69
|
+
allowed_project_types=[ProjectType.VIDEOS],
|
|
70
|
+
always_open=False,
|
|
71
|
+
compact=False,
|
|
72
|
+
team_is_selectable=False,
|
|
73
|
+
workspace_is_selectable=False,
|
|
74
|
+
show_select_all_datasets_checkbox=False,
|
|
75
|
+
)
|
|
76
|
+
self.select_video = RadioTable(columns=["id", "name", "dataset"], rows=[])
|
|
77
|
+
self.select_video_container = Container(
|
|
78
|
+
widgets=[self.select_dataset_for_video, self.select_video]
|
|
79
|
+
)
|
|
80
|
+
self._radio_item_videos = RadioGroup.Item(
|
|
81
|
+
ProjectType.VIDEOS.value, "Videos", content=self.select_video_container
|
|
82
|
+
)
|
|
83
|
+
# -------------------------------- #
|
|
84
|
+
|
|
85
|
+
# Data type Radio Selector
|
|
86
|
+
# self.radio = RadioGroup(items=[self._radio_item_images, self._radio_item_videos])
|
|
87
|
+
self.radio = RadioGroup(items=[self._radio_item_images])
|
|
88
|
+
self.radio.hide()
|
|
89
|
+
self.one_of = OneOf(conditional_widget=self.radio)
|
|
90
|
+
# Add widgets to display ------------ #
|
|
91
|
+
self.display_widgets.extend([self.radio, self.one_of])
|
|
92
|
+
# ----------------------------------- #
|
|
93
|
+
|
|
94
|
+
# Base Widgets
|
|
95
|
+
self.validator_text = Text("")
|
|
96
|
+
self.validator_text.hide()
|
|
97
|
+
self.button = Button("Select")
|
|
98
|
+
# Add widgets to display ------------ #
|
|
99
|
+
self.display_widgets.extend([self.validator_text, self.button])
|
|
100
|
+
# ----------------------------------- #
|
|
101
|
+
|
|
102
|
+
# Card Layout
|
|
103
|
+
self.container = Container(self.display_widgets)
|
|
104
|
+
self.card = Card(
|
|
105
|
+
title=self.title,
|
|
106
|
+
description=self.description,
|
|
107
|
+
content=self.container,
|
|
108
|
+
lock_message=self.lock_message,
|
|
109
|
+
)
|
|
110
|
+
# ----------------------------------- #
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def widgets_to_disable(self) -> list:
|
|
114
|
+
return [
|
|
115
|
+
self.select_dataset_for_images,
|
|
116
|
+
self.select_dataset_for_video,
|
|
117
|
+
self.select_video,
|
|
118
|
+
self.radio,
|
|
119
|
+
self.one_of,
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
def get_settings(self) -> Dict[str, Any]:
|
|
123
|
+
if self.radio.get_value() == ProjectType.IMAGES.value:
|
|
124
|
+
return {
|
|
125
|
+
"project_id": self.select_dataset_for_images.get_selected_project_id(),
|
|
126
|
+
"dataset_ids": self.select_dataset_for_images.get_selected_ids(),
|
|
127
|
+
}
|
|
128
|
+
if self.radio.get_value() == ProjectType.VIDEOS.value:
|
|
129
|
+
return {"video_id": self.select_video.get_selected_row()}
|
|
130
|
+
|
|
131
|
+
def load_from_json(self, data):
|
|
132
|
+
if "project_id" in data:
|
|
133
|
+
self.select_dataset_for_images.set_project_id(data["project_id"])
|
|
134
|
+
self.select_dataset_for_images.select_all()
|
|
135
|
+
self.radio.set_value(ProjectType.IMAGES.value)
|
|
136
|
+
if "dataset_ids" in data:
|
|
137
|
+
self.select_dataset_for_images.set_dataset_ids(data["dataset_ids"])
|
|
138
|
+
self.radio.set_value(ProjectType.IMAGES.value)
|
|
139
|
+
if "video_id" in data:
|
|
140
|
+
self.select_video.select_row_by_value("id", data["video_id"])
|
|
141
|
+
self.radio.set_value(ProjectType.VIDEOS.value)
|
|
142
|
+
|
|
143
|
+
def validate_step(self) -> bool:
|
|
144
|
+
self.validator_text.hide()
|
|
145
|
+
if self.radio.get_value() == ProjectType.IMAGES.value:
|
|
146
|
+
if len(self.select_dataset_for_images.get_selected_ids()) == 0:
|
|
147
|
+
self.validator_text.set(text="Select at least one dataset", status="error")
|
|
148
|
+
self.validator_text.show()
|
|
149
|
+
return False
|
|
150
|
+
if self.radio.get_value() == ProjectType.VIDEOS.value:
|
|
151
|
+
if self.select_dataset_for_video.get_selected_id() is None:
|
|
152
|
+
self.validator_text.set(text="Select a dataset", status="error")
|
|
153
|
+
self.validator_text.show()
|
|
154
|
+
return False
|
|
155
|
+
if len(self.select_video.rows) == 0:
|
|
156
|
+
self.validator_text.set(
|
|
157
|
+
text="No videos found in the selected dataset", status="error"
|
|
158
|
+
)
|
|
159
|
+
self.validator_text.show()
|
|
160
|
+
return False
|
|
161
|
+
if self.select_video.get_selected_row() == []:
|
|
162
|
+
self.validator_text.set(text="Select a video", status="error")
|
|
163
|
+
self.validator_text.show()
|
|
164
|
+
return False
|
|
165
|
+
return True
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
|
|
3
|
+
from supervisely.api.api import Api
|
|
4
|
+
from supervisely.app.widgets import Button, Card, Container, DeployModel, Text
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ModelSelector:
|
|
8
|
+
title = "Select Model"
|
|
9
|
+
description = "Connect to deployed model or deploy new model"
|
|
10
|
+
lock_message = "Select previous step to unlock"
|
|
11
|
+
|
|
12
|
+
def __init__(self, api: Api, team_id: int):
|
|
13
|
+
# Init Step
|
|
14
|
+
self.api = api
|
|
15
|
+
self.team_id = team_id
|
|
16
|
+
self.display_widgets: List[Any] = []
|
|
17
|
+
# -------------------------------- #
|
|
18
|
+
|
|
19
|
+
# Init Base Widgets
|
|
20
|
+
self.validator_text = None
|
|
21
|
+
self.button = None
|
|
22
|
+
self.container = None
|
|
23
|
+
self.card = None
|
|
24
|
+
# -------------------------------- #
|
|
25
|
+
|
|
26
|
+
# Init Step Widgets
|
|
27
|
+
self.model: DeployModel = None
|
|
28
|
+
# -------------------------------- #
|
|
29
|
+
|
|
30
|
+
# Model Selector
|
|
31
|
+
self.model = DeployModel(api=self.api, team_id=self.team_id)
|
|
32
|
+
# Add widgets to display ------------ #
|
|
33
|
+
self.display_widgets.extend([self.model])
|
|
34
|
+
# ----------------------------------- #
|
|
35
|
+
|
|
36
|
+
# Base Widgets
|
|
37
|
+
self.validator_text = Text("")
|
|
38
|
+
self.validator_text.hide()
|
|
39
|
+
self.button = Button("Select")
|
|
40
|
+
# Add widgets to display ------------ #
|
|
41
|
+
self.display_widgets.extend([self.validator_text, self.button])
|
|
42
|
+
# ----------------------------------- #
|
|
43
|
+
|
|
44
|
+
# Card Layout
|
|
45
|
+
self.container = Container(self.display_widgets)
|
|
46
|
+
self.card = Card(
|
|
47
|
+
title=self.title,
|
|
48
|
+
description=self.description,
|
|
49
|
+
content=self.container,
|
|
50
|
+
lock_message=self.lock_message,
|
|
51
|
+
)
|
|
52
|
+
self.card.lock()
|
|
53
|
+
# ----------------------------------- #
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def widgets_to_disable(self) -> list:
|
|
57
|
+
return [
|
|
58
|
+
self.model,
|
|
59
|
+
self.model.connect_button,
|
|
60
|
+
self.model.deploy_button,
|
|
61
|
+
self.model.stop_button,
|
|
62
|
+
self.model.disconnect_button,
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
def get_settings(self) -> Dict[str, Any]:
|
|
66
|
+
return self.model.get_deploy_parameters()
|
|
67
|
+
|
|
68
|
+
def load_from_json(self, data):
|
|
69
|
+
self.model.load_from_json(data)
|
|
70
|
+
|
|
71
|
+
def validate_step(self) -> bool:
|
|
72
|
+
self.validator_text.hide()
|
|
73
|
+
|
|
74
|
+
if self.model.model_api is None:
|
|
75
|
+
self.validator_text.set(text="Please connect or deploy a model", status="error")
|
|
76
|
+
self.validator_text.show()
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
return True
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
|
|
3
|
+
from supervisely import logger
|
|
4
|
+
from supervisely.api.api import Api
|
|
5
|
+
from supervisely.app.widgets import (
|
|
6
|
+
Button,
|
|
7
|
+
Card,
|
|
8
|
+
Checkbox,
|
|
9
|
+
Container,
|
|
10
|
+
Field,
|
|
11
|
+
Input,
|
|
12
|
+
Progress,
|
|
13
|
+
ProjectThumbnail,
|
|
14
|
+
Text,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OutputSelector:
|
|
19
|
+
title = "Select Output"
|
|
20
|
+
description = "Select the output mode"
|
|
21
|
+
lock_message = "Select previous step to unlock"
|
|
22
|
+
|
|
23
|
+
def __init__(self, api: Api):
|
|
24
|
+
# Init Step
|
|
25
|
+
self.api = api
|
|
26
|
+
self.display_widgets: List[Any] = []
|
|
27
|
+
# -------------------------------- #
|
|
28
|
+
|
|
29
|
+
# Init Base Widgets
|
|
30
|
+
self.validator_text = None
|
|
31
|
+
self.start_button = None
|
|
32
|
+
self.stop_button = None
|
|
33
|
+
self.container = None
|
|
34
|
+
self.card = None
|
|
35
|
+
# -------------------------------- #
|
|
36
|
+
|
|
37
|
+
# Init Step Widgets
|
|
38
|
+
self.stop_serving_on_finish = None
|
|
39
|
+
self.stop_self_on_finish = None
|
|
40
|
+
self.project_name_input = None
|
|
41
|
+
self.project_name_field = None
|
|
42
|
+
self.progress = None
|
|
43
|
+
self.project_thumbnail = None
|
|
44
|
+
# -------------------------------- #
|
|
45
|
+
|
|
46
|
+
# TODO: Implement option later
|
|
47
|
+
# Stop Apps on Finish
|
|
48
|
+
# self.stop_serving_on_finish = Checkbox("Stop Serving App on prediction finish", False)
|
|
49
|
+
# self.stop_self_on_finish = Checkbox("Stop Predict App on prediction finish", True)
|
|
50
|
+
# Add widgets to display ------------ #
|
|
51
|
+
# self.display_widgets.extend([self.stop_serving_on_finish, self.stop_self_on_finish])
|
|
52
|
+
# ----------------------------------- #
|
|
53
|
+
|
|
54
|
+
# Project Name
|
|
55
|
+
self.project_name_input = Input(minlength=1, maxlength=255, placeholder="New Project Name")
|
|
56
|
+
self.project_name_field = Field(
|
|
57
|
+
content=self.project_name_input,
|
|
58
|
+
title="New Project Name",
|
|
59
|
+
description="Name of the new project to create for the results. The created project will have the same dataset structure as the input project.",
|
|
60
|
+
)
|
|
61
|
+
# Add widgets to display ------------ #
|
|
62
|
+
self.display_widgets.extend([self.project_name_field])
|
|
63
|
+
# ----------------------------------- #
|
|
64
|
+
|
|
65
|
+
# Base Widgets
|
|
66
|
+
self.validator_text = Text("", status="text")
|
|
67
|
+
self.validator_text.hide()
|
|
68
|
+
self.start_button = Button("Run", icon="zmdi zmdi-play")
|
|
69
|
+
self.stop_button = Button("Stop", icon="zmdi zmdi-stop")
|
|
70
|
+
# Add widgets to display ------------ #
|
|
71
|
+
self.display_widgets.extend([self.start_button, self.validator_text])
|
|
72
|
+
# ----------------------------------- #
|
|
73
|
+
|
|
74
|
+
# Progress
|
|
75
|
+
self.progress = Progress(hide_on_finish=False)
|
|
76
|
+
self.progress.hide()
|
|
77
|
+
# Add widgets to display ------------ #
|
|
78
|
+
self.display_widgets.extend([self.progress])
|
|
79
|
+
# ----------------------------------- #
|
|
80
|
+
|
|
81
|
+
# Result
|
|
82
|
+
self.project_thumbnail = ProjectThumbnail()
|
|
83
|
+
self.project_thumbnail.hide()
|
|
84
|
+
# Add widgets to display ------------ #
|
|
85
|
+
self.display_widgets.extend([self.project_thumbnail])
|
|
86
|
+
# ----------------------------------- #
|
|
87
|
+
|
|
88
|
+
# Card Layout
|
|
89
|
+
self.container = Container(self.display_widgets)
|
|
90
|
+
self.card = Card(
|
|
91
|
+
title=self.title,
|
|
92
|
+
description=self.description,
|
|
93
|
+
content=self.container,
|
|
94
|
+
lock_message=self.lock_message,
|
|
95
|
+
)
|
|
96
|
+
self.card.lock()
|
|
97
|
+
# ----------------------------------- #
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def widgets_to_disable(self) -> list:
|
|
101
|
+
return [self.project_name_input]
|
|
102
|
+
|
|
103
|
+
def set_result_thumbnail(self, project_id: int):
|
|
104
|
+
try:
|
|
105
|
+
project_info = self.api.project.get_info_by_id(project_id)
|
|
106
|
+
self.project_thumbnail.set(project_info)
|
|
107
|
+
self.project_thumbnail.show()
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error(f"Failed to set result thumbnail: {str(e)}")
|
|
110
|
+
self.project_thumbnail.hide()
|
|
111
|
+
|
|
112
|
+
def get_settings(self) -> Dict[str, Any]:
|
|
113
|
+
settings = {}
|
|
114
|
+
settings["project_name"] = self.project_name_input.get_value()
|
|
115
|
+
return settings
|
|
116
|
+
|
|
117
|
+
def should_stop_serving_on_finish(self) -> bool:
|
|
118
|
+
if self.stop_serving_on_finish is not None:
|
|
119
|
+
return self.stop_serving_on_finish.is_checked()
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
def should_stop_self_on_finish(self) -> bool:
|
|
123
|
+
if self.stop_self_on_finish is not None:
|
|
124
|
+
return self.stop_self_on_finish.is_checked()
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
def load_from_json(self, data):
|
|
128
|
+
project_name = data.get("project_name", None)
|
|
129
|
+
if project_name:
|
|
130
|
+
self.project_name_input.set_value(project_name)
|
|
131
|
+
|
|
132
|
+
def validate_step(self) -> bool:
|
|
133
|
+
self.validator_text.hide()
|
|
134
|
+
if self.project_name_input.get_value() == "":
|
|
135
|
+
self.validator_text.set(text="Project name is required", status="error")
|
|
136
|
+
self.validator_text.show()
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
return True
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List, Any, Dict
|
|
3
|
+
|
|
4
|
+
from supervisely.api.api import Api
|
|
5
|
+
from supervisely.app.widgets import Button, Container, Card, Text, GridGallery
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Preview:
|
|
9
|
+
title = "Preview"
|
|
10
|
+
description = "Preview the model output"
|
|
11
|
+
lock_message = None
|
|
12
|
+
|
|
13
|
+
def __init__(self, api: Api, static_dir: str):
|
|
14
|
+
# Init Step
|
|
15
|
+
self.api = api
|
|
16
|
+
self.display_widgets: List[Any] = []
|
|
17
|
+
self.static_dir = static_dir
|
|
18
|
+
self.inference_settings = None
|
|
19
|
+
# -------------------------------- #
|
|
20
|
+
|
|
21
|
+
# Init Base Widgets
|
|
22
|
+
self.validator_text = None
|
|
23
|
+
self.button = None
|
|
24
|
+
self.container = None
|
|
25
|
+
self.card = None
|
|
26
|
+
# -------------------------------- #
|
|
27
|
+
|
|
28
|
+
# Init Step Widgets
|
|
29
|
+
self.gallery = None
|
|
30
|
+
# -------------------------------- #
|
|
31
|
+
|
|
32
|
+
# Preview Directory
|
|
33
|
+
self.preview_dir = os.path.join(self.static_dir, "preview")
|
|
34
|
+
os.makedirs(self.preview_dir, exist_ok=True)
|
|
35
|
+
self.preview_path = os.path.join(self.preview_dir, "preview.jpg")
|
|
36
|
+
self.peview_url = f"/static/preview/preview.jpg"
|
|
37
|
+
# ----------------------------------- #
|
|
38
|
+
|
|
39
|
+
# Preview Widget
|
|
40
|
+
self.gallery = GridGallery(
|
|
41
|
+
2,
|
|
42
|
+
sync_views=True,
|
|
43
|
+
enable_zoom=True,
|
|
44
|
+
resize_on_zoom=True,
|
|
45
|
+
empty_message="Click 'Preview' to see the model output.",
|
|
46
|
+
)
|
|
47
|
+
# Add widgets to display ------------ #
|
|
48
|
+
self.display_widgets.extend([self.gallery])
|
|
49
|
+
# ----------------------------------- #
|
|
50
|
+
|
|
51
|
+
# Base Widgets
|
|
52
|
+
self.validator_text = Text("")
|
|
53
|
+
self.validator_text.hide()
|
|
54
|
+
self.button = Button("Preview", icon="zmdi zmdi-eye")
|
|
55
|
+
# Add widgets to display ------------ #
|
|
56
|
+
self.display_widgets.extend([self.validator_text, self.button])
|
|
57
|
+
# ----------------------------------- #
|
|
58
|
+
|
|
59
|
+
# Card Layout
|
|
60
|
+
self.container = Container(self.display_widgets)
|
|
61
|
+
self.card = Card(
|
|
62
|
+
title="Preview",
|
|
63
|
+
content=self.container,
|
|
64
|
+
lock_message=self.lock_message,
|
|
65
|
+
)
|
|
66
|
+
self.card.lock()
|
|
67
|
+
# ----------------------------------- #
|
|
68
|
+
|
|
69
|
+
@self.button.click
|
|
70
|
+
def button_click():
|
|
71
|
+
self.run_preview()
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def widgets_to_disable(self) -> list:
|
|
75
|
+
return [self.gallery]
|
|
76
|
+
|
|
77
|
+
def load_from_json(self, data: Dict[str, Any]) -> None:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
def get_settings(self) -> Dict[str, Any]:
|
|
81
|
+
return {
|
|
82
|
+
"preview_path": self.preview_path,
|
|
83
|
+
"preview_url": self.peview_url,
|
|
84
|
+
"inference_settings": self.inference_settings,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def validate_step(self) -> bool:
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
def run_preview(self) -> None:
|
|
91
|
+
raise NotImplementedError(
|
|
92
|
+
"run_preview must be implemented by subclasses or injected at runtime"
|
|
93
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
|
|
3
|
+
import yaml
|
|
4
|
+
|
|
5
|
+
from supervisely.app.widgets import (
|
|
6
|
+
Button,
|
|
7
|
+
Card,
|
|
8
|
+
Checkbox,
|
|
9
|
+
Container,
|
|
10
|
+
Editor,
|
|
11
|
+
Field,
|
|
12
|
+
Input,
|
|
13
|
+
Select,
|
|
14
|
+
Text,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InferenceMode:
|
|
19
|
+
FULL_IMAGE = "Full Image"
|
|
20
|
+
SLIDING_WINDOW = "Sliding Window"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AddPredictionsMode:
|
|
24
|
+
MERGE_WITH_EXISTING_LABELS = "Merge with existing labels"
|
|
25
|
+
REPLACE_EXISTING_LABELS = "Replace existing labels"
|
|
26
|
+
REPLACE_EXISTING_LABELS_AND_SAVE_IMAGE_TAGS = "Replace existing labels and save image tags"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SettingsSelector:
|
|
30
|
+
title = "Settings Selector"
|
|
31
|
+
description = "Select additional settings for model inference"
|
|
32
|
+
lock_message = "Select previous step to unlock"
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
# Init Step
|
|
36
|
+
self.display_widgets: List[Any] = []
|
|
37
|
+
# -------------------------------- #
|
|
38
|
+
|
|
39
|
+
# Init Base Widgets
|
|
40
|
+
self.validator_text = None
|
|
41
|
+
self.button = None
|
|
42
|
+
self.container = None
|
|
43
|
+
self.card = None
|
|
44
|
+
# -------------------------------- #
|
|
45
|
+
|
|
46
|
+
# Init Step Widgets
|
|
47
|
+
self.inference_mode_selector = None
|
|
48
|
+
self.inference_mode_field = None
|
|
49
|
+
self.model_prediction_suffix_input = None
|
|
50
|
+
self.model_prediction_suffix_field = None
|
|
51
|
+
# self.model_prediction_suffix_checkbox = None
|
|
52
|
+
self.predictions_mode_selector = None
|
|
53
|
+
self.predictions_mode_field = None
|
|
54
|
+
self.inference_settings = None
|
|
55
|
+
# -------------------------------- #
|
|
56
|
+
|
|
57
|
+
# Inference Mode
|
|
58
|
+
self.inference_modes = [InferenceMode.FULL_IMAGE, InferenceMode.SLIDING_WINDOW]
|
|
59
|
+
self.inference_mode_selector = Select(
|
|
60
|
+
items=[Select.Item(mode) for mode in self.inference_modes]
|
|
61
|
+
)
|
|
62
|
+
self.inference_mode_selector.set_value(self.inference_modes[0])
|
|
63
|
+
self.inference_mode_field = Field(
|
|
64
|
+
content=self.inference_mode_selector,
|
|
65
|
+
title="Inference mode",
|
|
66
|
+
description="Select how to process images: full images or using sliding window.",
|
|
67
|
+
)
|
|
68
|
+
# Add widgets to display ------------ #
|
|
69
|
+
self.display_widgets.extend([self.inference_mode_field])
|
|
70
|
+
# ----------------------------------- #
|
|
71
|
+
|
|
72
|
+
# Class / Tag Suffix
|
|
73
|
+
self.model_prediction_suffix_input = Input(
|
|
74
|
+
value="_model", minlength=1, placeholder="Enter suffix e.g: _model"
|
|
75
|
+
)
|
|
76
|
+
self.model_prediction_suffix_field = Field(
|
|
77
|
+
content=self.model_prediction_suffix_input,
|
|
78
|
+
title="Class and tag suffix",
|
|
79
|
+
description=(
|
|
80
|
+
"Suffix that will be added to conflicting class and tag names. "
|
|
81
|
+
"E.g. your project has a class 'person' with shape 'bitmap' and model has class 'person' with shape 'rectangle', "
|
|
82
|
+
"then suffix will be added to the model predictions to avoid conflicts. E.g. 'person_model'."
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
# self.model_prediction_suffix_checkbox = Checkbox("Always add suffix to model predictions")
|
|
86
|
+
# Add widgets to display ------------ #
|
|
87
|
+
self.display_widgets.extend(
|
|
88
|
+
[self.model_prediction_suffix_field] # , self.model_prediction_suffix_checkbox]
|
|
89
|
+
)
|
|
90
|
+
# ----------------------------------- #
|
|
91
|
+
|
|
92
|
+
# Prediction Mode
|
|
93
|
+
self.prediction_modes = [
|
|
94
|
+
AddPredictionsMode.MERGE_WITH_EXISTING_LABELS,
|
|
95
|
+
AddPredictionsMode.REPLACE_EXISTING_LABELS,
|
|
96
|
+
# AddPredictionsMode.REPLACE_EXISTING_LABELS_AND_SAVE_IMAGE_TAGS, # @TODO: Implement later
|
|
97
|
+
]
|
|
98
|
+
self.predictions_mode_selector = Select(
|
|
99
|
+
items=[Select.Item(mode) for mode in self.prediction_modes]
|
|
100
|
+
)
|
|
101
|
+
self.predictions_mode_selector.set_value(self.prediction_modes[0])
|
|
102
|
+
self.predictions_mode_field = Field(
|
|
103
|
+
content=self.predictions_mode_selector,
|
|
104
|
+
title="Add predictions mode",
|
|
105
|
+
description="Select how to add predictions to the project: by merging with existing labels or by replacing them.",
|
|
106
|
+
)
|
|
107
|
+
# Add widgets to display ------------ #
|
|
108
|
+
self.display_widgets.extend([self.predictions_mode_field])
|
|
109
|
+
# ----------------------------------- #
|
|
110
|
+
|
|
111
|
+
# Inference Settings
|
|
112
|
+
self.inference_settings = Editor("", language_mode="yaml", height_px=300)
|
|
113
|
+
# Add widgets to display ------------ #
|
|
114
|
+
self.display_widgets.extend([self.inference_settings])
|
|
115
|
+
# ----------------------------------- #
|
|
116
|
+
|
|
117
|
+
# Base Widgets
|
|
118
|
+
self.validator_text = Text("")
|
|
119
|
+
self.validator_text.hide()
|
|
120
|
+
self.button = Button("Select")
|
|
121
|
+
# Add widgets to display ------------ #
|
|
122
|
+
self.display_widgets.extend([self.validator_text, self.button])
|
|
123
|
+
# ----------------------------------- #
|
|
124
|
+
|
|
125
|
+
# Card Layout
|
|
126
|
+
self.container = Container(self.display_widgets)
|
|
127
|
+
self.card = Card(
|
|
128
|
+
title=self.title,
|
|
129
|
+
description=self.description,
|
|
130
|
+
content=self.container,
|
|
131
|
+
lock_message=self.lock_message,
|
|
132
|
+
)
|
|
133
|
+
self.card.lock()
|
|
134
|
+
# ----------------------------------- #
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def widgets_to_disable(self) -> list:
|
|
138
|
+
return [
|
|
139
|
+
self.inference_mode_selector,
|
|
140
|
+
self.model_prediction_suffix_input,
|
|
141
|
+
# self.model_prediction_suffix_checkbox,
|
|
142
|
+
self.predictions_mode_selector,
|
|
143
|
+
self.inference_settings,
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
def set_inference_settings(self, settings: Dict[str, Any]):
|
|
147
|
+
if isinstance(settings, str):
|
|
148
|
+
self.inference_settings.set_text(settings)
|
|
149
|
+
else:
|
|
150
|
+
self.inference_settings.set_text(yaml.safe_dump(settings))
|
|
151
|
+
|
|
152
|
+
def get_inference_settings(self) -> Dict:
|
|
153
|
+
settings = yaml.safe_load(self.inference_settings.get_text())
|
|
154
|
+
if settings:
|
|
155
|
+
return settings
|
|
156
|
+
return {}
|
|
157
|
+
|
|
158
|
+
def get_settings(self) -> Dict[str, Any]:
|
|
159
|
+
return {
|
|
160
|
+
"inference_mode": self.inference_mode_selector.get_value(),
|
|
161
|
+
"model_prediction_suffix": self.model_prediction_suffix_input.get_value(),
|
|
162
|
+
"predictions_mode": self.predictions_mode_selector.get_value(),
|
|
163
|
+
"inference_settings": self.get_inference_settings(),
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
def load_from_json(self, data):
|
|
167
|
+
inference_mode = data.get("inference_mode", None)
|
|
168
|
+
if inference_mode:
|
|
169
|
+
self.inference_mode_selector.set_value(inference_mode)
|
|
170
|
+
|
|
171
|
+
model_prediction_suffix = data.get("model_prediction_suffix", None)
|
|
172
|
+
if model_prediction_suffix is not None:
|
|
173
|
+
self.model_prediction_suffix_input.set_value(model_prediction_suffix)
|
|
174
|
+
|
|
175
|
+
predictions_mode = data.get("predictions_mode", None)
|
|
176
|
+
if predictions_mode:
|
|
177
|
+
self.predictions_mode_selector.set_value(predictions_mode)
|
|
178
|
+
|
|
179
|
+
inference_settings = data.get("inference_settings", None)
|
|
180
|
+
if inference_settings is not None:
|
|
181
|
+
self.set_inference_settings(inference_settings)
|
|
182
|
+
|
|
183
|
+
def validate_step(self) -> bool:
|
|
184
|
+
return True
|