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.

Files changed (190) hide show
  1. supervisely/__init__.py +136 -1
  2. supervisely/_utils.py +81 -0
  3. supervisely/annotation/json_geometries_map.py +2 -0
  4. supervisely/annotation/label.py +80 -3
  5. supervisely/api/annotation_api.py +9 -9
  6. supervisely/api/api.py +67 -43
  7. supervisely/api/app_api.py +72 -5
  8. supervisely/api/dataset_api.py +108 -33
  9. supervisely/api/entity_annotation/figure_api.py +113 -49
  10. supervisely/api/image_api.py +82 -0
  11. supervisely/api/module_api.py +10 -0
  12. supervisely/api/nn/deploy_api.py +15 -9
  13. supervisely/api/nn/ecosystem_models_api.py +201 -0
  14. supervisely/api/nn/neural_network_api.py +12 -3
  15. supervisely/api/pointcloud/pointcloud_api.py +38 -0
  16. supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
  17. supervisely/api/project_api.py +213 -6
  18. supervisely/api/task_api.py +11 -1
  19. supervisely/api/video/video_annotation_api.py +4 -2
  20. supervisely/api/video/video_api.py +79 -1
  21. supervisely/api/video/video_figure_api.py +24 -11
  22. supervisely/api/volume/volume_api.py +38 -0
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +14 -6
  25. supervisely/app/fastapi/__init__.py +1 -0
  26. supervisely/app/fastapi/custom_static_files.py +1 -1
  27. supervisely/app/fastapi/multi_user.py +88 -0
  28. supervisely/app/fastapi/subapp.py +175 -42
  29. supervisely/app/fastapi/templating.py +1 -1
  30. supervisely/app/fastapi/websocket.py +77 -9
  31. supervisely/app/singleton.py +21 -0
  32. supervisely/app/v1/app_service.py +18 -2
  33. supervisely/app/v1/constants.py +7 -1
  34. supervisely/app/widgets/__init__.py +11 -1
  35. supervisely/app/widgets/agent_selector/template.html +1 -0
  36. supervisely/app/widgets/card/card.py +20 -0
  37. supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
  38. supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
  39. supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
  40. supervisely/app/widgets/dialog/dialog.py +12 -0
  41. supervisely/app/widgets/dialog/template.html +2 -1
  42. supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
  43. supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
  44. supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
  45. supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
  46. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
  47. supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
  48. supervisely/app/widgets/fast_table/fast_table.py +713 -126
  49. supervisely/app/widgets/fast_table/script.js +492 -95
  50. supervisely/app/widgets/fast_table/style.css +54 -0
  51. supervisely/app/widgets/fast_table/template.html +45 -5
  52. supervisely/app/widgets/heatmap/__init__.py +0 -0
  53. supervisely/app/widgets/heatmap/heatmap.py +523 -0
  54. supervisely/app/widgets/heatmap/script.js +378 -0
  55. supervisely/app/widgets/heatmap/style.css +227 -0
  56. supervisely/app/widgets/heatmap/template.html +21 -0
  57. supervisely/app/widgets/input_tag/input_tag.py +102 -15
  58. supervisely/app/widgets/input_tag_list/__init__.py +0 -0
  59. supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
  60. supervisely/app/widgets/input_tag_list/template.html +70 -0
  61. supervisely/app/widgets/radio_table/radio_table.py +10 -2
  62. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  63. supervisely/app/widgets/radio_tabs/template.html +1 -0
  64. supervisely/app/widgets/select/select.py +6 -4
  65. supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
  66. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
  67. supervisely/app/widgets/table/table.py +68 -13
  68. supervisely/app/widgets/tabs/tabs.py +22 -6
  69. supervisely/app/widgets/tabs/template.html +5 -1
  70. supervisely/app/widgets/transfer/style.css +3 -0
  71. supervisely/app/widgets/transfer/template.html +3 -1
  72. supervisely/app/widgets/transfer/transfer.py +48 -45
  73. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  74. supervisely/convert/image/csv/csv_converter.py +24 -15
  75. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
  76. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
  77. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
  78. supervisely/convert/video/video_converter.py +2 -2
  79. supervisely/geometry/polyline_3d.py +110 -0
  80. supervisely/io/env.py +161 -1
  81. supervisely/nn/artifacts/__init__.py +1 -1
  82. supervisely/nn/artifacts/artifacts.py +10 -2
  83. supervisely/nn/artifacts/detectron2.py +1 -0
  84. supervisely/nn/artifacts/hrda.py +1 -0
  85. supervisely/nn/artifacts/mmclassification.py +20 -0
  86. supervisely/nn/artifacts/mmdetection.py +5 -3
  87. supervisely/nn/artifacts/mmsegmentation.py +1 -0
  88. supervisely/nn/artifacts/ritm.py +1 -0
  89. supervisely/nn/artifacts/rtdetr.py +1 -0
  90. supervisely/nn/artifacts/unet.py +1 -0
  91. supervisely/nn/artifacts/utils.py +3 -0
  92. supervisely/nn/artifacts/yolov5.py +2 -0
  93. supervisely/nn/artifacts/yolov8.py +1 -0
  94. supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
  95. supervisely/nn/experiments.py +9 -0
  96. supervisely/nn/inference/cache.py +37 -17
  97. supervisely/nn/inference/gui/serving_gui_template.py +39 -13
  98. supervisely/nn/inference/inference.py +953 -211
  99. supervisely/nn/inference/inference_request.py +15 -8
  100. supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
  101. supervisely/nn/inference/object_detection/object_detection.py +1 -0
  102. supervisely/nn/inference/predict_app/__init__.py +0 -0
  103. supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
  104. supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
  105. supervisely/nn/inference/predict_app/gui/gui.py +915 -0
  106. supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
  107. supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
  108. supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
  109. supervisely/nn/inference/predict_app/gui/preview.py +93 -0
  110. supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
  111. supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
  112. supervisely/nn/inference/predict_app/gui/utils.py +399 -0
  113. supervisely/nn/inference/predict_app/predict_app.py +176 -0
  114. supervisely/nn/inference/session.py +47 -39
  115. supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
  116. supervisely/nn/inference/tracking/point_tracking.py +5 -1
  117. supervisely/nn/inference/tracking/tracker_interface.py +4 -0
  118. supervisely/nn/inference/uploader.py +9 -5
  119. supervisely/nn/model/model_api.py +44 -22
  120. supervisely/nn/model/prediction.py +15 -1
  121. supervisely/nn/model/prediction_session.py +70 -14
  122. supervisely/nn/prediction_dto.py +7 -0
  123. supervisely/nn/tracker/__init__.py +6 -8
  124. supervisely/nn/tracker/base_tracker.py +54 -0
  125. supervisely/nn/tracker/botsort/__init__.py +1 -0
  126. supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
  127. supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
  128. supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
  129. supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
  130. supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
  131. supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
  132. supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
  133. supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
  134. supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
  135. supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
  136. supervisely/nn/tracker/botsort_tracker.py +273 -0
  137. supervisely/nn/tracker/calculate_metrics.py +264 -0
  138. supervisely/nn/tracker/utils.py +273 -0
  139. supervisely/nn/tracker/visualize.py +520 -0
  140. supervisely/nn/training/gui/gui.py +152 -49
  141. supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
  142. supervisely/nn/training/gui/model_selector.py +8 -6
  143. supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
  144. supervisely/nn/training/gui/training_artifacts.py +3 -1
  145. supervisely/nn/training/train_app.py +225 -46
  146. supervisely/project/pointcloud_episode_project.py +12 -8
  147. supervisely/project/pointcloud_project.py +12 -8
  148. supervisely/project/project.py +221 -75
  149. supervisely/template/experiment/experiment.html.jinja +105 -55
  150. supervisely/template/experiment/experiment_generator.py +258 -112
  151. supervisely/template/experiment/header.html.jinja +31 -13
  152. supervisely/template/experiment/sly-style.css +7 -2
  153. supervisely/versions.json +3 -1
  154. supervisely/video/sampling.py +42 -20
  155. supervisely/video/video.py +41 -12
  156. supervisely/video_annotation/video_figure.py +38 -4
  157. supervisely/volume/stl_converter.py +2 -0
  158. supervisely/worker_api/agent_rpc.py +24 -1
  159. supervisely/worker_api/rpc_servicer.py +31 -7
  160. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
  161. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
  162. supervisely_lib/__init__.py +6 -1
  163. supervisely/app/widgets/experiment_selector/style.css +0 -27
  164. supervisely/app/widgets/experiment_selector/template.html +0 -61
  165. supervisely/nn/tracker/bot_sort/__init__.py +0 -21
  166. supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
  167. supervisely/nn/tracker/bot_sort/matching.py +0 -127
  168. supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
  169. supervisely/nn/tracker/deep_sort/__init__.py +0 -6
  170. supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
  171. supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
  172. supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
  173. supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
  174. supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
  175. supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
  176. supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
  177. supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
  178. supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
  179. supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
  180. supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
  181. supervisely/nn/tracker/tracker.py +0 -285
  182. supervisely/nn/tracker/utils/kalman_filter.py +0 -492
  183. supervisely/nn/tracking/__init__.py +0 -1
  184. supervisely/nn/tracking/boxmot.py +0 -114
  185. supervisely/nn/tracking/tracking.py +0 -24
  186. /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
  187. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
  188. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
  189. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
  190. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,344 @@
1
+ import threading
2
+ from typing import Any, Dict, List
3
+
4
+ from supervisely.api.api import Api
5
+ from supervisely.api.dataset_api import DatasetInfo
6
+ from supervisely.api.project_api import ProjectInfo
7
+ from supervisely.api.video.video_api import VideoInfo
8
+ from supervisely.app.widgets import (
9
+ Button,
10
+ Card,
11
+ Container,
12
+ FastTable,
13
+ OneOf,
14
+ RadioGroup,
15
+ SelectDatasetTree,
16
+ Text,
17
+ )
18
+ from supervisely.app.widgets.widget import Widget
19
+ from supervisely.project.project import ProjectType
20
+
21
+
22
+ class InputSelector:
23
+ title = "Input data"
24
+ description = "Select input data on which to run model for prediction"
25
+ lock_message = None
26
+
27
+ def __init__(self, workspace_id: int, api: Api):
28
+ # Init Step
29
+ self.workspace_id = workspace_id
30
+ self.api = api
31
+ self.display_widgets: List[Any] = []
32
+ # -------------------------------- #
33
+
34
+ # Init Base Widgets
35
+ self.validator_text = None
36
+ self.button = None
37
+ self.container = None
38
+ self.card = None
39
+ # -------------------------------- #
40
+
41
+ # Init Step Widgets
42
+ # Images
43
+ self.select_dataset_for_images = None
44
+ self.select_image_container = None
45
+ # Videos
46
+ self.select_dataset_for_video = None
47
+ self.select_video = None
48
+ self.select_video_container = None
49
+ # Selector
50
+ self.radio = None
51
+ self.one_of = None
52
+ # -------------------------------- #
53
+
54
+ # Images
55
+ self.select_dataset_for_images = SelectDatasetTree(
56
+ multiselect=True,
57
+ flat=True,
58
+ select_all_datasets=True,
59
+ allowed_project_types=[ProjectType.IMAGES],
60
+ always_open=False,
61
+ compact=False,
62
+ team_is_selectable=False,
63
+ workspace_is_selectable=False,
64
+ show_select_all_datasets_checkbox=True,
65
+ )
66
+ self.select_image_container = Container(widgets=[self.select_dataset_for_images])
67
+ self._radio_item_images = RadioGroup.Item(
68
+ ProjectType.IMAGES.value, "Images", content=self.select_image_container
69
+ )
70
+ # -------------------------------- #
71
+
72
+ # Videos
73
+ self.select_dataset_for_video = SelectDatasetTree(
74
+ multiselect=True,
75
+ flat=True,
76
+ select_all_datasets=True,
77
+ allowed_project_types=[ProjectType.VIDEOS],
78
+ always_open=False,
79
+ compact=False,
80
+ team_is_selectable=False,
81
+ workspace_is_selectable=False,
82
+ show_select_all_datasets_checkbox=True,
83
+ )
84
+ self._video_table_columns = [
85
+ "Video id",
86
+ "Video name",
87
+ "Size",
88
+ "Duration",
89
+ "FPS",
90
+ "Frames count",
91
+ "Dataset name",
92
+ "Dataset id",
93
+ ]
94
+ self.select_video = FastTable(
95
+ columns=self._video_table_columns,
96
+ is_selectable=True,
97
+ )
98
+ self.select_video.hide()
99
+ self.select_video_container = Container(
100
+ widgets=[self.select_dataset_for_video, self.select_video]
101
+ )
102
+ self._radio_item_videos = RadioGroup.Item(
103
+ ProjectType.VIDEOS.value, "Videos", content=self.select_video_container
104
+ )
105
+ # -------------------------------- #
106
+
107
+ # Data type Radio Selector
108
+ self.radio = RadioGroup(items=[self._radio_item_images, self._radio_item_videos])
109
+ # self.radio = RadioGroup(items=[self._radio_item_images])
110
+ # self.radio.hide()
111
+ self.one_of = OneOf(conditional_widget=self.radio)
112
+ # Add widgets to display ------------ #
113
+ self.display_widgets.extend([self.radio, self.one_of])
114
+ # ----------------------------------- #
115
+
116
+ # Base Widgets
117
+ self.validator_text = Text("")
118
+ self.validator_text.hide()
119
+ self.button = Button("Select")
120
+ # Add widgets to display ------------ #
121
+ self.display_widgets.extend([self.validator_text, self.button])
122
+ # ----------------------------------- #
123
+
124
+ # Card Layout
125
+ self.container = Container(self.display_widgets)
126
+ self.card = Card(
127
+ title=self.title,
128
+ description=self.description,
129
+ content=self.container,
130
+ lock_message=self.lock_message,
131
+ )
132
+ # ----------------------------------- #
133
+
134
+ self._refresh_table_lock = threading.Lock()
135
+ self._refresh_table_thread: threading.Thread = None
136
+ self._refresh_called = False
137
+
138
+ @self.radio.value_changed
139
+ def input_selector_type_changed(value: str):
140
+ self.validator_text.hide()
141
+
142
+ @self.select_dataset_for_images.project_changed
143
+ def _images_project_changed(project_id):
144
+ self.validator_text.hide()
145
+
146
+ @self.select_dataset_for_images.value_changed
147
+ def _images_dataset_changed(dataset_ids):
148
+ self.validator_text.hide()
149
+
150
+ @self.select_dataset_for_video.project_changed
151
+ def _videos_project_changed(project_id: int):
152
+ self._refresh_video_table_called()
153
+
154
+ @self.select_dataset_for_video.value_changed
155
+ def _videos_dataset_changed(datasets_ids):
156
+ self._refresh_video_table_called()
157
+
158
+ def _refresh_video_table_called(self):
159
+ with self._refresh_table_lock:
160
+ self._refresh_called = True
161
+ if self._refresh_table_thread is None or not self._refresh_table_thread.is_alive():
162
+ self._refresh_table_thread = threading.Thread(target=self._refresh_video_table_loop)
163
+ if self._refresh_table_thread is not None and not self._refresh_table_thread.is_alive():
164
+ self._refresh_table_thread.start()
165
+
166
+ def _refresh_video_table_loop(self):
167
+ while self._refresh_called:
168
+ with self._refresh_table_lock:
169
+ self._refresh_called = False
170
+ self.select_video.loading = True
171
+ self._refresh_video_table()
172
+ if not self._refresh_called:
173
+ self.select_video.loading = False
174
+
175
+ def _refresh_video_table(self):
176
+ self.validator_text.hide()
177
+ self.select_video.clear()
178
+ selected_datasets = self.select_dataset_for_video.get_selected_ids()
179
+ if not selected_datasets:
180
+ self.select_video.hide()
181
+ else:
182
+ rows = []
183
+ self.select_video.show()
184
+ for dataset_id in selected_datasets:
185
+ dataset_info = self.api.dataset.get_info_by_id(dataset_id)
186
+ videos = self.api.video.get_list(dataset_id)
187
+ for video in videos:
188
+ size = f"{video.frame_height}x{video.frame_width}"
189
+ try:
190
+ frame_rate = int(video.frames_count / video.duration)
191
+ except:
192
+ frame_rate = "N/A"
193
+ rows.append(
194
+ [
195
+ video.id,
196
+ video.name,
197
+ size,
198
+ video.duration,
199
+ frame_rate,
200
+ video.frames_count,
201
+ dataset_info.name,
202
+ dataset_info.id,
203
+ ]
204
+ )
205
+
206
+ self.select_video.add_rows(rows)
207
+
208
+ def select_project(self, project_id: int, project_info: ProjectInfo = None):
209
+ if project_info is None:
210
+ project_info = self.api.project.get_info_by_id(project_id)
211
+ if project_info.type == ProjectType.IMAGES.value:
212
+ self.select_dataset_for_images.set_project_id(project_id)
213
+ self.select_dataset_for_images.select_all()
214
+ self.radio.set_value(ProjectType.IMAGES.value)
215
+ elif project_info.type == ProjectType.VIDEOS.value:
216
+ self.select_dataset_for_video.set_project_id(project_id)
217
+ self.select_dataset_for_video.select_all()
218
+ self._refresh_video_table()
219
+ self.select_video.select_rows(list(range(len(self.select_video._rows_total))))
220
+ self.radio.set_value(ProjectType.VIDEOS.value)
221
+ else:
222
+ raise ValueError(f"Project of type {project_info.type} is not supported.")
223
+
224
+ def select_datasets(self, dataset_ids: List[int], dataset_infos: List[DatasetInfo] = None):
225
+ if dataset_infos is None:
226
+ dataset_infos = [self.api.dataset.get_info_by_id(ds_id) for ds_id in dataset_ids]
227
+ project_ids = set(ds.project_id for ds in dataset_infos)
228
+ if len(project_ids) > 1:
229
+ raise ValueError("Cannot select datasets from different projects")
230
+ project_id = project_ids.pop()
231
+ project_info = self.api.project.get_info_by_id(project_id)
232
+ if project_info.type == ProjectType.IMAGES.value:
233
+ self.select_dataset_for_images.set_project_id(project_id)
234
+ self.select_dataset_for_images.set_dataset_ids(dataset_ids)
235
+ self.radio.set_value(ProjectType.IMAGES.value)
236
+ elif project_info.type == ProjectType.VIDEOS.value:
237
+ self.select_dataset_for_video.set_project_id(project_id)
238
+ self.select_dataset_for_video.set_dataset_ids(dataset_ids)
239
+ self._refresh_video_table()
240
+ self.select_video.select_rows(list(range(self.select_video._rows_total)))
241
+ self.radio.set_value(ProjectType.VIDEOS.value)
242
+ else:
243
+ raise ValueError(f"Project of type {project_info.type} is not supported.")
244
+
245
+ def select_videos(self, video_ids: List[int], video_infos: List[VideoInfo] = None):
246
+ if video_infos is None:
247
+ video_infos = self.api.video.get_info_by_id_batch(video_ids)
248
+ project_id = video_infos[0].project_id
249
+ self.select_dataset_for_video.set_project_id(project_id)
250
+ self.select_dataset_for_video.select_all()
251
+ self._refresh_video_table()
252
+ self.select_video.select_row_by_value("id", video_ids)
253
+ self.radio.set_value(ProjectType.VIDEOS.value)
254
+
255
+ def disable(self):
256
+ for widget in self.widgets_to_disable:
257
+ widget.disable()
258
+
259
+ def enable(self):
260
+ for widget in self.widgets_to_disable:
261
+ widget.enable()
262
+
263
+ @property
264
+ def widgets_to_disable(self) -> List[Widget]:
265
+ return [
266
+ # Images Selector
267
+ self.select_dataset_for_images,
268
+ self.select_dataset_for_images._select_project,
269
+ self.select_dataset_for_images._select_dataset,
270
+ # Videos Selector
271
+ self.select_dataset_for_video,
272
+ self.select_dataset_for_video._select_project,
273
+ self.select_dataset_for_video._select_dataset,
274
+ self.select_video,
275
+ # Controls
276
+ self.radio,
277
+ self.one_of,
278
+ ]
279
+
280
+ def get_settings(self) -> Dict[str, Any]:
281
+ if self.radio.get_value() == ProjectType.IMAGES.value:
282
+ return {
283
+ "project_id": self.select_dataset_for_images.get_selected_project_id(),
284
+ "dataset_ids": self.select_dataset_for_images.get_selected_ids(),
285
+ }
286
+ if self.radio.get_value() == ProjectType.VIDEOS.value:
287
+ rows = self.select_video.get_selected_rows()
288
+ if rows:
289
+ video_ids = [row.row[0] for row in rows]
290
+ else:
291
+ video_ids = None
292
+ return {"video_ids": video_ids}
293
+
294
+ def load_from_json(self, data):
295
+ if "video_ids" in data:
296
+ video_ids = data["video_ids"]
297
+ if not video_ids:
298
+ raise ValueError("Video ids cannot be empty")
299
+ video_infos = self.api.video.get_info_by_id_batch(video_ids)
300
+ if not video_infos:
301
+ raise ValueError(f"Videos with video ids {video_ids} are not found")
302
+ self.select_videos(video_ids, video_infos)
303
+ elif "dataset_ids" in data:
304
+ dataset_ids = data["dataset_ids"]
305
+ self.select_datasets(dataset_ids)
306
+ elif "project_id" in data:
307
+ project_id = data["project_id"]
308
+ self.select_project(project_id)
309
+
310
+ def get_project_id(self) -> int:
311
+ if self.radio.get_value() == ProjectType.IMAGES.value:
312
+ return self.select_dataset_for_images.project_id
313
+ if self.radio.get_value() == ProjectType.VIDEOS.value:
314
+ return self.select_dataset_for_video.project_id
315
+ return None
316
+
317
+ def validate_step(self) -> bool:
318
+ self.validator_text.hide()
319
+ if self.radio.get_value() == ProjectType.IMAGES.value:
320
+ selected_ids = self.select_dataset_for_images.get_selected_ids()
321
+ if selected_ids is None:
322
+ self.validator_text.set(text="Select a project", status="error")
323
+ self.validator_text.show()
324
+ return False
325
+ if len(selected_ids) == 0:
326
+ self.validator_text.set(text="Select at least one dataset", status="error")
327
+ self.validator_text.show()
328
+ return False
329
+ if self.radio.get_value() == ProjectType.VIDEOS.value:
330
+ if not self.select_dataset_for_video.get_selected_ids():
331
+ self.validator_text.set(text="Select a dataset", status="error")
332
+ self.validator_text.show()
333
+ return False
334
+ if self.select_video._rows_total == 0:
335
+ self.validator_text.set(
336
+ text="No videos found in the selected dataset", status="error"
337
+ )
338
+ self.validator_text.show()
339
+ return False
340
+ if self.select_video.get_selected_rows() == []:
341
+ self.validator_text.set(text="Select a video", status="error")
342
+ self.validator_text.show()
343
+ return False
344
+ return True
@@ -0,0 +1,77 @@
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 = "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
+ # Add widgets to display ------------ #
40
+ self.display_widgets.extend([self.validator_text])
41
+ # ----------------------------------- #
42
+
43
+ # Card Layout
44
+ self.container = Container(self.display_widgets)
45
+ self.card = Card(
46
+ title=self.title,
47
+ description=self.description,
48
+ content=self.container,
49
+ lock_message=self.lock_message,
50
+ )
51
+ # ----------------------------------- #
52
+
53
+ @property
54
+ def widgets_to_disable(self) -> list:
55
+ return [
56
+ self.model,
57
+ self.model.connect_button,
58
+ self.model.deploy_button,
59
+ self.model.stop_button,
60
+ self.model.disconnect_button,
61
+ ]
62
+
63
+ def get_settings(self) -> Dict[str, Any]:
64
+ return self.model.get_deploy_parameters()
65
+
66
+ def load_from_json(self, data):
67
+ self.model.load_from_json(data)
68
+
69
+ def validate_step(self) -> bool:
70
+ self.validator_text.hide()
71
+
72
+ if self.model.model_api is None:
73
+ self.validator_text.set(text="Please connect or deploy a model", status="error")
74
+ self.validator_text.show()
75
+ return False
76
+
77
+ return True
@@ -0,0 +1,179 @@
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
+ OneOf,
13
+ Progress,
14
+ ProjectThumbnail,
15
+ RadioGroup,
16
+ Text,
17
+ )
18
+ from supervisely.project.project_meta import ProjectType
19
+
20
+
21
+ class OutputSelector:
22
+ title = "Result"
23
+ description = "Select the output mode"
24
+ lock_message = "Select previous step to unlock"
25
+
26
+ def __init__(self, api: Api):
27
+ # Init Step
28
+ self.api = api
29
+ self.display_widgets: List[Any] = []
30
+ # -------------------------------- #
31
+
32
+ # Init Base Widgets
33
+ self.validator_text = None
34
+ self.start_button = None
35
+ self.stop_button = None
36
+ self.container = None
37
+ self.card = None
38
+ # -------------------------------- #
39
+
40
+ # Init Step Widgets
41
+ self.stop_serving_on_finish = None
42
+ self.stop_self_on_finish = None
43
+ self.project_name_input = None
44
+ self.project_name_field = None
45
+ self.progress = None
46
+ self.project_thumbnail = None
47
+ # -------------------------------- #
48
+
49
+ # TODO: Implement option later
50
+ # Stop Apps on Finish
51
+ # self.stop_serving_on_finish = Checkbox("Stop Serving App on prediction finish", False)
52
+ # self.stop_self_on_finish = Checkbox("Stop Predict App on prediction finish", True)
53
+ # Add widgets to display ------------ #
54
+ # self.display_widgets.extend([self.stop_serving_on_finish, self.stop_self_on_finish])
55
+ # ----------------------------------- #
56
+
57
+ # Project Name
58
+ self.project_name_input = Input(minlength=1, maxlength=255, placeholder="New Project Name")
59
+ self.project_name_field = Field(
60
+ content=self.project_name_input,
61
+ title="New Project Name",
62
+ description="Name of the new project to create for the results. The created project will have the same dataset structure as the input project.",
63
+ )
64
+ self.skip_annotated_checkbox = Checkbox("Skip annotated items", False)
65
+ self._tab_names = ["Create New Project", "Update source project"]
66
+ self._tab_contents = [self.project_name_field, self.skip_annotated_checkbox]
67
+ self.tabs = RadioGroup(
68
+ items=[
69
+ RadioGroup.Item(tab_name, content=tab_content)
70
+ for tab_name, tab_content in zip(self._tab_names, self._tab_contents)
71
+ ],
72
+ )
73
+ self.oneof = OneOf(self.tabs)
74
+ # Add widgets to display ------------ #
75
+ self.display_widgets.extend([self.tabs, self.oneof])
76
+ # ----------------------------------- #
77
+
78
+ # Base Widgets
79
+ self.validator_text = Text("", status="text")
80
+ self.validator_text.hide()
81
+ self.start_button = Button("Run", icon="zmdi zmdi-play")
82
+ self.stop_button = Button("Stop", icon="zmdi zmdi-stop")
83
+ # Add widgets to display ------------ #
84
+ self.display_widgets.extend([self.start_button, self.validator_text])
85
+ # ----------------------------------- #
86
+
87
+ # Progress
88
+ self.progress = Progress(hide_on_finish=False)
89
+ self.progress.hide()
90
+ self.secondary_progress = Progress(hide_on_finish=False)
91
+ self.secondary_progress.hide()
92
+ # Add widgets to display ------------ #
93
+ self.display_widgets.extend([self.progress, self.secondary_progress])
94
+ # ----------------------------------- #
95
+
96
+ # Result
97
+ self.project_thumbnail = ProjectThumbnail()
98
+ self.project_thumbnail.hide()
99
+ # Add widgets to display ------------ #
100
+ self.display_widgets.extend([self.project_thumbnail])
101
+ # ----------------------------------- #
102
+
103
+ # Card Layout
104
+ self.container = Container(self.display_widgets)
105
+ self.card = Card(
106
+ title=self.title,
107
+ description=self.description,
108
+ content=self.container,
109
+ lock_message=self.lock_message,
110
+ )
111
+ # ----------------------------------- #
112
+
113
+ def lock(self):
114
+ self.card.lock(self.lock_message)
115
+
116
+ def unlock(self):
117
+ self.card.unlock()
118
+
119
+ @property
120
+ def widgets_to_disable(self) -> list:
121
+ return [self.project_name_input]
122
+
123
+ def set_result_thumbnail(self, project_id: int):
124
+ try:
125
+ project_info = self.api.project.get_info_by_id(project_id)
126
+ self.project_thumbnail.set(project_info)
127
+ self.project_thumbnail.show()
128
+ except Exception as e:
129
+ logger.error(f"Failed to set result thumbnail: {str(e)}")
130
+ self.project_thumbnail.hide()
131
+
132
+ def get_settings(self) -> Dict[str, Any]:
133
+ settings = {}
134
+ if self.tabs.get_value() == self._tab_names[1]:
135
+ settings["upload_to_source_project"] = True
136
+ else:
137
+ settings["project_name"] = self.project_name_input.get_value()
138
+ settings["skip_annotated"] = self.skip_annotated_checkbox.is_checked()
139
+ return settings
140
+
141
+ def should_stop_serving_on_finish(self) -> bool:
142
+ if self.stop_serving_on_finish is not None:
143
+ return self.stop_serving_on_finish.is_checked()
144
+ return False
145
+
146
+ def should_stop_self_on_finish(self) -> bool:
147
+ if self.stop_self_on_finish is not None:
148
+ return self.stop_self_on_finish.is_checked()
149
+ return True
150
+
151
+ def load_from_json(self, data):
152
+ project_name = data.get("project_name", None)
153
+ if project_name:
154
+ self.project_name_input.set_value(project_name)
155
+ upload_to_source_project = data.get("upload_to_source_project", False)
156
+ if upload_to_source_project:
157
+ self.tabs.set_value(self._tab_names[1])
158
+ else:
159
+ self.tabs.set_value(self._tab_names[0])
160
+
161
+ def validate_step(self) -> bool:
162
+ self.validator_text.hide()
163
+ if (
164
+ self.tabs.get_value() == self._tab_names[0]
165
+ and self.project_name_input.get_value() == ""
166
+ ):
167
+ self.validator_text.set(text="Project name is required", status="error")
168
+ self.validator_text.show()
169
+ return False
170
+
171
+ return True
172
+
173
+ def update_item_type(self, item_type: str):
174
+ if item_type == ProjectType.IMAGES.value:
175
+ self.skip_annotated_checkbox.show()
176
+ elif item_type == ProjectType.VIDEOS.value:
177
+ self.skip_annotated_checkbox.hide()
178
+ else:
179
+ raise ValueError(f"Unsupported item type: {item_type}")
@@ -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
+ )