supervisely 6.73.375__py3-none-any.whl → 6.73.377__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/__init__.py +1 -0
- supervisely/_utils.py +11 -0
- supervisely/api/nn/neural_network_api.py +1 -1
- supervisely/app/fastapi/index.html +20 -0
- supervisely/app/widgets/__init__.py +1 -0
- supervisely/app/widgets/agent_selector/agent_selector.py +6 -0
- supervisely/app/widgets/agent_selector/template.html +2 -0
- supervisely/app/widgets/button/button.py +28 -1
- supervisely/app/widgets/button/template.html +1 -1
- supervisely/app/widgets/card/card.py +4 -0
- supervisely/app/widgets/card/template.html +1 -1
- supervisely/app/widgets/classes_table/classes_table.py +3 -1
- supervisely/app/widgets/fast_table/fast_table.py +16 -0
- supervisely/app/widgets/fast_table/script.js +6 -2
- supervisely/app/widgets/fast_table/template.html +1 -0
- supervisely/app/widgets/flexbox/flexbox.py +4 -2
- supervisely/app/widgets/flexbox/template.html +5 -9
- supervisely/app/widgets/input_number/input_number.py +2 -0
- supervisely/app/widgets/input_number/template.html +3 -0
- supervisely/app/widgets/random_splits_table/random_splits_table.py +2 -0
- supervisely/app/widgets/sampling/__init__.py +0 -0
- supervisely/app/widgets/sampling/sampling.py +550 -0
- supervisely/app/widgets/select_dataset/select_dataset.py +15 -5
- supervisely/app/widgets/sly_tqdm/sly_tqdm.py +9 -0
- supervisely/nn/training/train_app.py +10 -14
- supervisely/video/sampling.py +550 -0
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/METADATA +1 -1
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/RECORD +32 -29
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/LICENSE +0 -0
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/WHEEL +0 -0
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
from typing import List, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from supervisely.api.api import Api
|
|
4
|
+
from supervisely.api.dataset_api import DatasetInfo
|
|
5
|
+
from supervisely.app.widgets import Progress
|
|
6
|
+
from supervisely.app.widgets.button.button import Button
|
|
7
|
+
from supervisely.app.widgets.checkbox.checkbox import Checkbox
|
|
8
|
+
from supervisely.app.widgets.container.container import Container
|
|
9
|
+
from supervisely.app.widgets.empty.empty import Empty
|
|
10
|
+
from supervisely.app.widgets.flexbox.flexbox import Flexbox
|
|
11
|
+
from supervisely.app.widgets.input_number.input_number import InputNumber
|
|
12
|
+
from supervisely.app.widgets.notification_box.notification_box import NotificationBox
|
|
13
|
+
from supervisely.app.widgets.one_of.one_of import OneOf
|
|
14
|
+
from supervisely.app.widgets.project_thumbnail.project_thumbnail import ProjectThumbnail
|
|
15
|
+
from supervisely.app.widgets.radio_group.radio_group import RadioGroup
|
|
16
|
+
from supervisely.app.widgets.select_dataset.select_dataset import SelectDataset
|
|
17
|
+
from supervisely.app.widgets.select_project.select_project import SelectProject
|
|
18
|
+
from supervisely.app.widgets.text.text import Text
|
|
19
|
+
from supervisely.app.widgets.widget import Widget
|
|
20
|
+
from supervisely.project.project import ProjectType
|
|
21
|
+
from supervisely.video.sampling import SamplingSettings, sample_video_project
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Sampling(Widget):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
project_id: int = None,
|
|
28
|
+
input_selectable: bool = True,
|
|
29
|
+
datasets_ids: List[int] = None,
|
|
30
|
+
output_project_id: int = None,
|
|
31
|
+
output_project_selectable: bool = True,
|
|
32
|
+
widgth: int = 370,
|
|
33
|
+
widget_id: str = None,
|
|
34
|
+
file_path: str = __file__,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(widget_id, file_path)
|
|
37
|
+
if not input_selectable and project_id is None:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"Either 'project_id' must be provided or 'project_selectable' must be True."
|
|
40
|
+
)
|
|
41
|
+
if project_id is None and not input_selectable:
|
|
42
|
+
input_selectable = True
|
|
43
|
+
if not output_project_selectable and output_project_id is None:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"Either 'output_project_id' must be provided or 'output_project_selectable' must be True."
|
|
46
|
+
)
|
|
47
|
+
if output_project_id is None and not output_project_selectable:
|
|
48
|
+
output_project_selectable = True
|
|
49
|
+
self._api = Api()
|
|
50
|
+
self.project_id = project_id
|
|
51
|
+
self.project_selectable = input_selectable
|
|
52
|
+
self.datasets_ids = datasets_ids
|
|
53
|
+
self.output_project_id = output_project_id
|
|
54
|
+
self.output_project_selectable = output_project_selectable
|
|
55
|
+
self.widgth = widgth
|
|
56
|
+
self.project_info = (
|
|
57
|
+
self._api.project.get_info_by_id(self.project_id) if self.project_id else None
|
|
58
|
+
)
|
|
59
|
+
self.all_datasets = (
|
|
60
|
+
self._api.dataset.get_list(self.project_id, recursive=True) if self.project_id else []
|
|
61
|
+
)
|
|
62
|
+
self.items_count = self._count_items(self.all_datasets, datasets_ids, with_children=True)
|
|
63
|
+
self._init_gui()
|
|
64
|
+
|
|
65
|
+
def _init_input_gui(self):
|
|
66
|
+
self.input_datasets_select = SelectDataset(
|
|
67
|
+
default_id=self.datasets_ids,
|
|
68
|
+
project_id=self.project_id,
|
|
69
|
+
allowed_project_types=[ProjectType.VIDEOS],
|
|
70
|
+
size="mini",
|
|
71
|
+
multiselect=True,
|
|
72
|
+
select_all_datasets=self.datasets_ids is None,
|
|
73
|
+
)
|
|
74
|
+
self.project_preview = ProjectThumbnail()
|
|
75
|
+
self.project_preview.hide()
|
|
76
|
+
if not self.project_selectable:
|
|
77
|
+
self.input_datasets_select.hide()
|
|
78
|
+
project_info = self._api.project.get_info_by_id(self.project_id)
|
|
79
|
+
self.project_preview.set(project_info)
|
|
80
|
+
self.project_preview.show()
|
|
81
|
+
self.nested_datasets_checkbox = Checkbox(
|
|
82
|
+
"Include nested datasets",
|
|
83
|
+
checked=True,
|
|
84
|
+
)
|
|
85
|
+
self.input_project_container = Container(
|
|
86
|
+
widgets=[
|
|
87
|
+
self.input_datasets_select,
|
|
88
|
+
self.nested_datasets_checkbox,
|
|
89
|
+
self.project_preview,
|
|
90
|
+
],
|
|
91
|
+
style="padding-left: 21px; padding-top: 10px;",
|
|
92
|
+
)
|
|
93
|
+
self.input_field = Container(
|
|
94
|
+
widgets=[
|
|
95
|
+
Text(
|
|
96
|
+
'<i class="zmdi zmdi-collection-video" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Input project</b>'
|
|
97
|
+
),
|
|
98
|
+
self.input_project_container,
|
|
99
|
+
],
|
|
100
|
+
gap=0,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def _init_settings_gui(self):
|
|
104
|
+
self.only_annotated_checkbox = Checkbox(Text("Only annotated frames", font_size=13))
|
|
105
|
+
self.only_annotated_row = Flexbox(widgets=[self.only_annotated_checkbox])
|
|
106
|
+
self.step_label = Text("Step:", font_size=13)
|
|
107
|
+
self.step_input = InputNumber(value=1, min=1, step=1, size="mini", width=160)
|
|
108
|
+
self.step_row = Container(
|
|
109
|
+
widgets=[self.step_label, self.step_input],
|
|
110
|
+
direction="horizontal",
|
|
111
|
+
style="width: 202px; align-items: center;",
|
|
112
|
+
widgets_style="flex: 1; display: flex;",
|
|
113
|
+
)
|
|
114
|
+
self.resize_checkbox = Checkbox(Text("Resize frames", font_size=13))
|
|
115
|
+
self.resize_input_h_label = Text("Height:", font_size=13)
|
|
116
|
+
self.resize_input_h = InputNumber(value=224, min=1, step=1, size="mini", controls=False)
|
|
117
|
+
self.resize_input_w_label = Text("Width:", font_size=13)
|
|
118
|
+
self.resize_input_w = InputNumber(value=224, min=1, step=1, size="mini", controls=False)
|
|
119
|
+
self.resize_h_row = Flexbox(widgets=[self.resize_input_h_label, self.resize_input_h])
|
|
120
|
+
self.resize_w_row = Flexbox(
|
|
121
|
+
widgets=[self.resize_input_w_label, self.resize_input_w], gap=15
|
|
122
|
+
)
|
|
123
|
+
self.resize_row = Flexbox(widgets=[self.resize_checkbox])
|
|
124
|
+
self.resize_hw_container = Container(
|
|
125
|
+
widgets=[Empty(), self.resize_h_row, self.resize_w_row]
|
|
126
|
+
)
|
|
127
|
+
self.resize_hw_container.hide()
|
|
128
|
+
self.resize_container = Container(
|
|
129
|
+
widgets=[self.resize_row, self.resize_hw_container], gap=0
|
|
130
|
+
)
|
|
131
|
+
self.copy_annotations_checkbox = Checkbox(
|
|
132
|
+
Text("Copy annotations from source project", font_size=13)
|
|
133
|
+
)
|
|
134
|
+
self.copy_annotations_row = Flexbox(widgets=[self.copy_annotations_checkbox])
|
|
135
|
+
|
|
136
|
+
@self.resize_checkbox.value_changed
|
|
137
|
+
def on_resize_checkbox_change(checked: bool):
|
|
138
|
+
if checked:
|
|
139
|
+
self.resize_hw_container.show()
|
|
140
|
+
else:
|
|
141
|
+
self.resize_hw_container.hide()
|
|
142
|
+
|
|
143
|
+
self.settings_container = Container(
|
|
144
|
+
widgets=[
|
|
145
|
+
Empty(),
|
|
146
|
+
self.step_row,
|
|
147
|
+
self.only_annotated_row,
|
|
148
|
+
self.resize_container,
|
|
149
|
+
self.copy_annotations_row,
|
|
150
|
+
Empty(),
|
|
151
|
+
],
|
|
152
|
+
style="padding-left: 21px; padding-top: 10px;",
|
|
153
|
+
)
|
|
154
|
+
self.settings_field = Container(
|
|
155
|
+
widgets=[
|
|
156
|
+
Text(
|
|
157
|
+
'<i class="zmdi zmdi-settings" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Sampling settings</b>'
|
|
158
|
+
),
|
|
159
|
+
self.settings_container,
|
|
160
|
+
],
|
|
161
|
+
gap=0,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def _datasets_to_process(
|
|
165
|
+
self, all_datasets: List[DatasetInfo], datasets_ids: List[int], with_children: bool
|
|
166
|
+
) -> List[DatasetInfo]:
|
|
167
|
+
if datasets_ids is None:
|
|
168
|
+
return all_datasets.copy()
|
|
169
|
+
datasets = []
|
|
170
|
+
for ds in [ds for ds in all_datasets if ds.id in datasets_ids]:
|
|
171
|
+
datasets.append(ds)
|
|
172
|
+
if with_children:
|
|
173
|
+
children = [child for child in all_datasets if child.parent_id == ds.id]
|
|
174
|
+
if children:
|
|
175
|
+
datasets.extend(
|
|
176
|
+
self._datasets_to_process(
|
|
177
|
+
all_datasets, [child.id for child in children], True
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
return datasets
|
|
181
|
+
|
|
182
|
+
def _count_items(
|
|
183
|
+
self, all_datasets: List[DatasetInfo], datasets_ids: List[int], with_children: bool
|
|
184
|
+
) -> int:
|
|
185
|
+
return sum(
|
|
186
|
+
ds.items_count
|
|
187
|
+
for ds in self._datasets_to_process(
|
|
188
|
+
all_datasets, datasets_ids, with_children=with_children
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _selected_text(self, datasets_ids: List[int] = None, with_children: bool = True) -> str:
|
|
193
|
+
red = "#FF6458;"
|
|
194
|
+
blue = "rgb(0, 154, 255);"
|
|
195
|
+
datasets = self._datasets_to_process(
|
|
196
|
+
self.all_datasets, datasets_ids, with_children=with_children
|
|
197
|
+
)
|
|
198
|
+
color = blue if self.items_count > 0 else red
|
|
199
|
+
ds_num = len(datasets)
|
|
200
|
+
return f'Selected <b style="color: {color};">{ds_num}</b> dataset{"s" if ds_num % 10 != 1 else ""} with <b style="color: {color};">{self.items_count}</b> videos'
|
|
201
|
+
|
|
202
|
+
def _update_preview(self):
|
|
203
|
+
with_children = self.nested_datasets_checkbox.is_checked()
|
|
204
|
+
self.selected_items_text.text = self._selected_text(
|
|
205
|
+
self.datasets_ids, with_children=with_children
|
|
206
|
+
)
|
|
207
|
+
if self.items_count > 0:
|
|
208
|
+
self.run_button.enable()
|
|
209
|
+
else:
|
|
210
|
+
self.run_button.disable()
|
|
211
|
+
|
|
212
|
+
def _datasets_changed(self, datasets_ids: List[int]):
|
|
213
|
+
self.preview_container.loading = True
|
|
214
|
+
self.datasets_ids = datasets_ids
|
|
215
|
+
project_id = self.input_datasets_select.get_selected_project_id()
|
|
216
|
+
with_children = self.nested_datasets_checkbox.is_checked()
|
|
217
|
+
if self.project_id != project_id:
|
|
218
|
+
if project_id is None:
|
|
219
|
+
self.project_id = None
|
|
220
|
+
self.project_info = None
|
|
221
|
+
self.all_datasets = []
|
|
222
|
+
else:
|
|
223
|
+
self.project_id = project_id
|
|
224
|
+
self._api.project.get_info_by_id(project_id)
|
|
225
|
+
self.all_datasets = self._api.dataset.get_list(project_id, recursive=True)
|
|
226
|
+
self.items_count = self._count_items(
|
|
227
|
+
self.all_datasets, datasets_ids, with_children=with_children
|
|
228
|
+
)
|
|
229
|
+
self._update_preview()
|
|
230
|
+
self.preview_container.loading = False
|
|
231
|
+
|
|
232
|
+
def _init_peview_gui(self):
|
|
233
|
+
self.selected_items_text = Text("", font_size=13)
|
|
234
|
+
self.run_button = Button("Run", icon="zmdi zmdi-play", button_size="mini")
|
|
235
|
+
self.run_button.disable()
|
|
236
|
+
|
|
237
|
+
self.preview_text = Text(
|
|
238
|
+
'<i class="zmdi zmdi-eye" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b style="font-size: 14px">Preview</b>',
|
|
239
|
+
font_size=13,
|
|
240
|
+
)
|
|
241
|
+
self.preview_field = Container(
|
|
242
|
+
widgets=[
|
|
243
|
+
self.preview_text,
|
|
244
|
+
Container(widgets=[self.selected_items_text], style="padding-left: 21px;"),
|
|
245
|
+
],
|
|
246
|
+
style="padding-top: 10px;",
|
|
247
|
+
)
|
|
248
|
+
self.run_button_container = Container(
|
|
249
|
+
widgets=[self.run_button],
|
|
250
|
+
direction="horizontal",
|
|
251
|
+
overflow="wrap",
|
|
252
|
+
style="display: flex; justify-content: flex-end;",
|
|
253
|
+
widgets_style="display: flex; flex: none;",
|
|
254
|
+
)
|
|
255
|
+
self.preview_container = Container(
|
|
256
|
+
widgets=[self.preview_field, self.run_button_container],
|
|
257
|
+
)
|
|
258
|
+
self._update_preview()
|
|
259
|
+
|
|
260
|
+
@self.input_datasets_select.value_changed
|
|
261
|
+
def on_input_datasets_select_change(datasets_ids: List[int]):
|
|
262
|
+
self._datasets_changed(datasets_ids)
|
|
263
|
+
|
|
264
|
+
@self.nested_datasets_checkbox.value_changed
|
|
265
|
+
def on_nested_datasets_checkbox_change(is_checked: bool):
|
|
266
|
+
self._datasets_changed(self.input_datasets_select.get_selected_ids())
|
|
267
|
+
|
|
268
|
+
@self.run_button.click
|
|
269
|
+
def on_run_button_click():
|
|
270
|
+
self.run()
|
|
271
|
+
|
|
272
|
+
def _init_output_gui(self):
|
|
273
|
+
self.output_project_select = SelectProject(
|
|
274
|
+
default_id=self.output_project_id, allowed_types=[ProjectType.IMAGES], size="mini"
|
|
275
|
+
)
|
|
276
|
+
self.output_project_preview = ProjectThumbnail()
|
|
277
|
+
self.output_project_preview.hide()
|
|
278
|
+
self.output_mode_radio = RadioGroup(
|
|
279
|
+
items=[
|
|
280
|
+
RadioGroup.Item("create", "Create new project", content=Empty()),
|
|
281
|
+
RadioGroup.Item(
|
|
282
|
+
"merge",
|
|
283
|
+
"Merge with existing project",
|
|
284
|
+
content=self.output_project_select,
|
|
285
|
+
),
|
|
286
|
+
]
|
|
287
|
+
)
|
|
288
|
+
self.output_merge_project_oneof = OneOf(self.output_mode_radio)
|
|
289
|
+
self.output_project_container = Container(
|
|
290
|
+
widgets=[self.output_mode_radio, self.output_merge_project_oneof],
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
self.output_container = Container(
|
|
294
|
+
widgets=[self.output_project_preview, self.output_project_container],
|
|
295
|
+
gap=0,
|
|
296
|
+
style="padding-left: 21px; padding-top: 10px;",
|
|
297
|
+
)
|
|
298
|
+
if not self.output_project_selectable:
|
|
299
|
+
self.output_project_container.hide()
|
|
300
|
+
project_info = self._api.project.get_info_by_id(self.output_project_id)
|
|
301
|
+
self.output_project_preview.set(project_info)
|
|
302
|
+
self.output_project_preview.show()
|
|
303
|
+
if self.output_project_id is not None:
|
|
304
|
+
self.output_mode_radio.set_value("merge")
|
|
305
|
+
|
|
306
|
+
self.output_field = Container(
|
|
307
|
+
widgets=[
|
|
308
|
+
Text(
|
|
309
|
+
'<i class="zmdi zmdi-collection-folder-image" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Output project</b>'
|
|
310
|
+
),
|
|
311
|
+
self.output_container,
|
|
312
|
+
],
|
|
313
|
+
gap=0,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def _init_progress_gui(self):
|
|
317
|
+
self.items_progress = Progress(hide_on_finish=False)
|
|
318
|
+
self.frames_progress = Progress(hide_on_finish=False)
|
|
319
|
+
self.error_notification = NotificationBox(title="Error", description="", box_type="error")
|
|
320
|
+
self.error_notification.hide()
|
|
321
|
+
self.progress_container = Container(widgets=[self.items_progress, self.frames_progress])
|
|
322
|
+
self.progress_container.hide()
|
|
323
|
+
self.result_project_preview = ProjectThumbnail()
|
|
324
|
+
self.result_project_preview_field = Container(
|
|
325
|
+
widgets=[
|
|
326
|
+
Text(
|
|
327
|
+
'<i class="zmdi zmdi-check-square" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Result project</b>'
|
|
328
|
+
),
|
|
329
|
+
Container(
|
|
330
|
+
widgets=[self.result_project_preview],
|
|
331
|
+
style="padding-left: 21px;",
|
|
332
|
+
),
|
|
333
|
+
]
|
|
334
|
+
)
|
|
335
|
+
self.result_project_preview_field.hide()
|
|
336
|
+
self.result_container = Container(
|
|
337
|
+
widgets=[
|
|
338
|
+
self.progress_container,
|
|
339
|
+
self.error_notification,
|
|
340
|
+
self.result_project_preview_field,
|
|
341
|
+
],
|
|
342
|
+
gap=0,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
def _init_gui(self):
|
|
346
|
+
self._init_input_gui()
|
|
347
|
+
self._init_settings_gui()
|
|
348
|
+
self._init_output_gui()
|
|
349
|
+
self._init_peview_gui()
|
|
350
|
+
self._init_progress_gui()
|
|
351
|
+
|
|
352
|
+
self.content = Container(
|
|
353
|
+
widgets=[
|
|
354
|
+
self.input_field,
|
|
355
|
+
self.settings_field,
|
|
356
|
+
self.output_field,
|
|
357
|
+
self.preview_container,
|
|
358
|
+
self.result_container,
|
|
359
|
+
],
|
|
360
|
+
style=f"width: {self.widgth}px;",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def _get_settings(self) -> dict:
|
|
364
|
+
settings = {
|
|
365
|
+
SamplingSettings.ONLY_ANNOTATED: self.only_annotated_checkbox.is_checked(),
|
|
366
|
+
SamplingSettings.STEP: self.step_input.get_value(),
|
|
367
|
+
SamplingSettings.RESIZE: None,
|
|
368
|
+
SamplingSettings.COPY_ANNOTATIONS: self.copy_annotations_checkbox.is_checked(),
|
|
369
|
+
}
|
|
370
|
+
if self.resize_checkbox.is_checked():
|
|
371
|
+
settings[SamplingSettings.RESIZE] = [
|
|
372
|
+
self.resize_input_h.get_value(),
|
|
373
|
+
self.resize_input_w.get_value(),
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
return settings
|
|
377
|
+
|
|
378
|
+
def _get_dst_project_id(self) -> int:
|
|
379
|
+
if self.output_mode_radio.get_value() == "create":
|
|
380
|
+
return None
|
|
381
|
+
else:
|
|
382
|
+
return self.output_project_select.get_selected_id()
|
|
383
|
+
|
|
384
|
+
def run(self):
|
|
385
|
+
self.progress_container.show()
|
|
386
|
+
self.error_notification.hide()
|
|
387
|
+
self.result_project_preview_field.hide()
|
|
388
|
+
try:
|
|
389
|
+
project_id = self.input_datasets_select.get_selected_project_id()
|
|
390
|
+
datasets_ids = self.input_datasets_select.get_selected_ids()
|
|
391
|
+
with_children = self.nested_datasets_checkbox.is_checked()
|
|
392
|
+
selected_datasets_with_children = self._datasets_to_process(
|
|
393
|
+
all_datasets=self.all_datasets,
|
|
394
|
+
datasets_ids=datasets_ids,
|
|
395
|
+
with_children=with_children,
|
|
396
|
+
)
|
|
397
|
+
total_items = sum(ds.items_count for ds in selected_datasets_with_children)
|
|
398
|
+
datasets_ids = [ds.id for ds in selected_datasets_with_children]
|
|
399
|
+
project_info = self.project_info
|
|
400
|
+
|
|
401
|
+
if project_info.type != str(ProjectType.VIDEOS):
|
|
402
|
+
raise ValueError(
|
|
403
|
+
f"Project with ID {self.input_datasets_select.get_selected_id()} is not a video project."
|
|
404
|
+
)
|
|
405
|
+
frames_pbar = self.frames_progress()
|
|
406
|
+
with self.items_progress(
|
|
407
|
+
message=f"Videos progress...",
|
|
408
|
+
total=total_items,
|
|
409
|
+
) as pbar:
|
|
410
|
+
self.progress_container.show()
|
|
411
|
+
dst_project_info = sample_video_project(
|
|
412
|
+
api=self._api,
|
|
413
|
+
project_id=project_id,
|
|
414
|
+
settings=self._get_settings(),
|
|
415
|
+
dst_project_id=self._get_dst_project_id(),
|
|
416
|
+
datasets_ids=datasets_ids,
|
|
417
|
+
items_progress_cb=pbar.update,
|
|
418
|
+
video_progress=frames_pbar,
|
|
419
|
+
)
|
|
420
|
+
except Exception as e:
|
|
421
|
+
self.error_notification.set(title="Error", description=str(e))
|
|
422
|
+
self.error_notification.show()
|
|
423
|
+
raise e
|
|
424
|
+
else:
|
|
425
|
+
if self.output_project_selectable:
|
|
426
|
+
dst_project_info = self._api.project.get_info_by_id(dst_project_info.id)
|
|
427
|
+
self.result_project_preview.set(dst_project_info)
|
|
428
|
+
self.result_project_preview_field.show()
|
|
429
|
+
else:
|
|
430
|
+
dst_project_info = self._api.project.get_info_by_id(self.output_project_id)
|
|
431
|
+
self.output_project_preview.set(dst_project_info)
|
|
432
|
+
finally:
|
|
433
|
+
self.progress_container.hide()
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def selected_project_id(self) -> int:
|
|
437
|
+
if not self.project_selectable:
|
|
438
|
+
return self.project_id
|
|
439
|
+
return self.input_datasets_select.get_selected_project_id()
|
|
440
|
+
|
|
441
|
+
@selected_project_id.setter
|
|
442
|
+
def selected_project_id(self, value: int):
|
|
443
|
+
if not self.project_selectable:
|
|
444
|
+
raise ValueError("Project is not selectable.")
|
|
445
|
+
self.input_datasets_select.set_project_id(value)
|
|
446
|
+
self._datasets_changed(self.input_datasets_select.get_selected_ids())
|
|
447
|
+
|
|
448
|
+
@property
|
|
449
|
+
def selected_all_datasets(self) -> bool:
|
|
450
|
+
return self.input_datasets_select._all_datasets_checkbox.is_checked()
|
|
451
|
+
|
|
452
|
+
@selected_all_datasets.setter
|
|
453
|
+
def selected_all_datasets(self, value: bool):
|
|
454
|
+
if value:
|
|
455
|
+
self.input_datasets_select._all_datasets_checkbox.check()
|
|
456
|
+
else:
|
|
457
|
+
self.input_datasets_select._all_datasets_checkbox.uncheck()
|
|
458
|
+
self._datasets_changed(self.input_datasets_select.get_selected_ids())
|
|
459
|
+
|
|
460
|
+
@property
|
|
461
|
+
def selected_datasets_ids(self) -> List[int]:
|
|
462
|
+
return self.input_datasets_select.get_selected_ids()
|
|
463
|
+
|
|
464
|
+
@selected_datasets_ids.setter
|
|
465
|
+
def selected_datasets_ids(self, value: List[int]):
|
|
466
|
+
self.input_datasets_select.set_dataset_ids(value)
|
|
467
|
+
|
|
468
|
+
@property
|
|
469
|
+
def include_nested_datasets(self) -> bool:
|
|
470
|
+
return self.nested_datasets_checkbox.is_checked()
|
|
471
|
+
|
|
472
|
+
@include_nested_datasets.setter
|
|
473
|
+
def include_nested_datasets(self, value: bool):
|
|
474
|
+
if value:
|
|
475
|
+
self.nested_datasets_checkbox.check()
|
|
476
|
+
else:
|
|
477
|
+
self.nested_datasets_checkbox.uncheck()
|
|
478
|
+
self._datasets_changed(self.input_datasets_select.get_selected_ids())
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def step(self) -> int:
|
|
482
|
+
return self.step_input.get_value()
|
|
483
|
+
|
|
484
|
+
@step.setter
|
|
485
|
+
def step(self, value: int):
|
|
486
|
+
self.step_input.value = value
|
|
487
|
+
|
|
488
|
+
@property
|
|
489
|
+
def only_annotated(self) -> bool:
|
|
490
|
+
return self.only_annotated_checkbox.is_checked()
|
|
491
|
+
|
|
492
|
+
@only_annotated.setter
|
|
493
|
+
def only_annotated(self, value: bool):
|
|
494
|
+
if value:
|
|
495
|
+
self.only_annotated_checkbox.check()
|
|
496
|
+
else:
|
|
497
|
+
self.only_annotated_checkbox.uncheck()
|
|
498
|
+
|
|
499
|
+
@property
|
|
500
|
+
def resize(self) -> Union[Tuple[int, int], None]:
|
|
501
|
+
if self.resize_checkbox.is_checked():
|
|
502
|
+
return (self.resize_input_h.get_value(), self.resize_input_w.get_value())
|
|
503
|
+
return None
|
|
504
|
+
|
|
505
|
+
@resize.setter
|
|
506
|
+
def resize(self, value: Union[Tuple[int, int], None]):
|
|
507
|
+
if value is None:
|
|
508
|
+
self.resize_checkbox.uncheck()
|
|
509
|
+
else:
|
|
510
|
+
self.resize_checkbox.check()
|
|
511
|
+
self.resize_input_h.value = value[0]
|
|
512
|
+
self.resize_input_w.value = value[1]
|
|
513
|
+
|
|
514
|
+
@property
|
|
515
|
+
def copy_annotations(self) -> bool:
|
|
516
|
+
return self.copy_annotations_checkbox.is_checked()
|
|
517
|
+
|
|
518
|
+
@copy_annotations.setter
|
|
519
|
+
def copy_annotations(self, value: bool):
|
|
520
|
+
if value:
|
|
521
|
+
self.copy_annotations_checkbox.check()
|
|
522
|
+
else:
|
|
523
|
+
self.copy_annotations_checkbox.uncheck()
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def selected_output_project_id(self) -> int:
|
|
527
|
+
if not self.output_project_selectable:
|
|
528
|
+
return self.output_project_id
|
|
529
|
+
if self.output_mode_radio.get_value() == "create":
|
|
530
|
+
return None
|
|
531
|
+
return self.output_project_select.get_selected_id()
|
|
532
|
+
|
|
533
|
+
@selected_output_project_id.setter
|
|
534
|
+
def selected_output_project_id(self, value: int):
|
|
535
|
+
if not self.output_project_selectable:
|
|
536
|
+
raise ValueError("Output project is not selectable.")
|
|
537
|
+
self.output_project_select.set_project_id(value)
|
|
538
|
+
if value is not None:
|
|
539
|
+
self.output_mode_radio.set_value("merge")
|
|
540
|
+
else:
|
|
541
|
+
self.output_mode_radio.set_value("create")
|
|
542
|
+
|
|
543
|
+
def get_json_data(self) -> dict:
|
|
544
|
+
return {}
|
|
545
|
+
|
|
546
|
+
def get_json_state(self) -> dict:
|
|
547
|
+
return {}
|
|
548
|
+
|
|
549
|
+
def to_html(self):
|
|
550
|
+
return self.content.to_html()
|
|
@@ -138,11 +138,18 @@ class SelectDataset(Widget):
|
|
|
138
138
|
|
|
139
139
|
def set_project_id(self, id: int):
|
|
140
140
|
self._project_id = id
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
self._project_selector.set_project_id(self._project_id)
|
|
142
|
+
|
|
143
|
+
def set_select_all_datasets(self, is_checked: bool):
|
|
144
|
+
if self._multiselect is False:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
"Multiselect is disabled. Use another method 'set_dataset_id' instead of 'set_select_all_datasets'"
|
|
147
|
+
)
|
|
148
|
+
if is_checked:
|
|
149
|
+
self._all_datasets_checkbox.check()
|
|
144
150
|
else:
|
|
145
|
-
|
|
151
|
+
self._all_datasets_checkbox.uncheck()
|
|
152
|
+
StateJson()[self.widget_id]["datasets"] = []
|
|
146
153
|
StateJson().send_changes()
|
|
147
154
|
|
|
148
155
|
def set_dataset_id(self, id: int):
|
|
@@ -202,6 +209,7 @@ class SelectDataset(Widget):
|
|
|
202
209
|
_process()
|
|
203
210
|
|
|
204
211
|
if self._compact is False:
|
|
212
|
+
|
|
205
213
|
@self._project_selector.value_changed
|
|
206
214
|
def _update_datasets(project_id):
|
|
207
215
|
if self._multiselect is True:
|
|
@@ -217,7 +225,7 @@ class SelectDataset(Widget):
|
|
|
217
225
|
self._cb_called = True
|
|
218
226
|
_process()
|
|
219
227
|
|
|
220
|
-
@server.post(route_path)
|
|
228
|
+
@server.post(route_path)
|
|
221
229
|
def _click():
|
|
222
230
|
if self._cb_called is False or self._multiselect is False:
|
|
223
231
|
_process()
|
|
@@ -256,4 +264,6 @@ class SelectDataset(Widget):
|
|
|
256
264
|
info = self._api.dataset.get_info_by_id(self._default_id[0], raise_error=True)
|
|
257
265
|
elif isinstance(self._default_id, int):
|
|
258
266
|
info = self._api.dataset.get_info_by_id(self._default_id, raise_error=True)
|
|
267
|
+
else:
|
|
268
|
+
return
|
|
259
269
|
self._project_id = info.project_id
|
|
@@ -171,6 +171,15 @@ class CustomTqdm(tqdm):
|
|
|
171
171
|
**kwargs,
|
|
172
172
|
)
|
|
173
173
|
|
|
174
|
+
def __setattr__(self, name, value):
|
|
175
|
+
if name == "total":
|
|
176
|
+
if hasattr(self, "fp"):
|
|
177
|
+
self.fp.total = value
|
|
178
|
+
if name == "message":
|
|
179
|
+
if hasattr(self, "fp"):
|
|
180
|
+
self.fp.progress["message"] = value
|
|
181
|
+
return super().__setattr__(name, value)
|
|
182
|
+
|
|
174
183
|
def refresh(self, *args, **kwargs):
|
|
175
184
|
if self.fp is not None:
|
|
176
185
|
self.fp._n = self.n
|
|
@@ -44,7 +44,7 @@ from supervisely import (
|
|
|
44
44
|
)
|
|
45
45
|
from supervisely._utils import abs_url, get_filename_from_headers
|
|
46
46
|
from supervisely.api.file_api import FileInfo
|
|
47
|
-
from supervisely.app import get_synced_data_dir
|
|
47
|
+
from supervisely.app import get_synced_data_dir, show_dialog
|
|
48
48
|
from supervisely.app.widgets import Progress
|
|
49
49
|
from supervisely.nn.benchmark import (
|
|
50
50
|
InstanceSegmentationBenchmark,
|
|
@@ -2602,9 +2602,8 @@ class TrainApp:
|
|
|
2602
2602
|
except Exception as e:
|
|
2603
2603
|
message = f"Error occurred during training initialization. {check_logs_text}"
|
|
2604
2604
|
self._show_error(message, e)
|
|
2605
|
-
self._restore_train_widgets_state_on_error()
|
|
2606
2605
|
self._set_ws_progress_status("reset")
|
|
2607
|
-
|
|
2606
|
+
raise e
|
|
2608
2607
|
|
|
2609
2608
|
try:
|
|
2610
2609
|
self._set_text_status("preparing")
|
|
@@ -2613,9 +2612,8 @@ class TrainApp:
|
|
|
2613
2612
|
except Exception as e:
|
|
2614
2613
|
message = f"Error occurred during data preparation. {check_logs_text}"
|
|
2615
2614
|
self._show_error(message, e)
|
|
2616
|
-
self._restore_train_widgets_state_on_error()
|
|
2617
2615
|
self._set_ws_progress_status("reset")
|
|
2618
|
-
|
|
2616
|
+
raise e
|
|
2619
2617
|
|
|
2620
2618
|
try:
|
|
2621
2619
|
self._set_text_status("training")
|
|
@@ -2629,15 +2627,13 @@ class TrainApp:
|
|
|
2629
2627
|
"Please check input data and hyperparameters."
|
|
2630
2628
|
)
|
|
2631
2629
|
self._show_error(message, e)
|
|
2632
|
-
self._restore_train_widgets_state_on_error()
|
|
2633
2630
|
self._set_ws_progress_status("reset")
|
|
2634
2631
|
return
|
|
2635
2632
|
except Exception as e:
|
|
2636
2633
|
message = f"Error occurred during training. {check_logs_text}"
|
|
2637
2634
|
self._show_error(message, e)
|
|
2638
|
-
self._restore_train_widgets_state_on_error()
|
|
2639
2635
|
self._set_ws_progress_status("reset")
|
|
2640
|
-
|
|
2636
|
+
raise e
|
|
2641
2637
|
|
|
2642
2638
|
try:
|
|
2643
2639
|
self._set_text_status("finalizing")
|
|
@@ -2645,18 +2641,17 @@ class TrainApp:
|
|
|
2645
2641
|
self._finalize(experiment_info)
|
|
2646
2642
|
self.gui.training_process.start_button.loading = False
|
|
2647
2643
|
|
|
2648
|
-
|
|
2644
|
+
if is_production() and self.gui.training_logs.tensorboard_offline_button is not None:
|
|
2645
|
+
self.gui.training_logs.tensorboard_button.hide()
|
|
2646
|
+
self.gui.training_logs.tensorboard_offline_button.show()
|
|
2649
2647
|
|
|
2650
|
-
|
|
2651
|
-
self.gui.training_logs.tensorboard_offline_button.show()
|
|
2652
|
-
sleep(1) # wait for the button to be shown
|
|
2648
|
+
sleep(1)
|
|
2653
2649
|
self.app.shutdown()
|
|
2654
2650
|
except Exception as e:
|
|
2655
2651
|
message = f"Error occurred during finalizing and uploading training artifacts. {check_logs_text}"
|
|
2656
2652
|
self._show_error(message, e)
|
|
2657
|
-
self._restore_train_widgets_state_on_error()
|
|
2658
2653
|
self._set_ws_progress_status("reset")
|
|
2659
|
-
|
|
2654
|
+
raise e
|
|
2660
2655
|
|
|
2661
2656
|
def _show_error(self, message: str, e=None):
|
|
2662
2657
|
if e is not None:
|
|
@@ -2667,6 +2662,7 @@ class TrainApp:
|
|
|
2667
2662
|
self.gui.training_process.validator_text.show()
|
|
2668
2663
|
self.gui.training_process.start_button.loading = False
|
|
2669
2664
|
self._restore_train_widgets_state_on_error()
|
|
2665
|
+
show_dialog(title="Error", description=message, status="error")
|
|
2670
2666
|
|
|
2671
2667
|
def _set_train_widgets_state_on_start(self):
|
|
2672
2668
|
self.gui.disable_select_buttons()
|