supervisely 6.73.376__py3-none-any.whl → 6.73.378__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/entity_annotation/tag_api.py +223 -20
- supervisely/api/image_api.py +81 -1
- supervisely/app/fastapi/index.html +20 -0
- supervisely/app/widgets/__init__.py +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/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/convert/image/sly/sly_image_converter.py +10 -7
- supervisely/video/sampling.py +550 -0
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.dist-info}/METADATA +1 -1
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.dist-info}/RECORD +22 -19
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.dist-info}/LICENSE +0 -0
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.dist-info}/WHEEL +0 -0
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.376.dist-info → supervisely-6.73.378.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
|
|
@@ -17,7 +17,7 @@ from supervisely.api.api import Api
|
|
|
17
17
|
from supervisely.convert.base_converter import AvailableImageConverters
|
|
18
18
|
from supervisely.convert.image.image_converter import ImageConverter
|
|
19
19
|
from supervisely.convert.image.image_helper import validate_image_bounds
|
|
20
|
-
from supervisely.io.fs import dirs_filter, file_exists, get_file_ext
|
|
20
|
+
from supervisely.io.fs import dirs_filter, file_exists, get_file_ext, get_file_name
|
|
21
21
|
from supervisely.io.json import load_json_file
|
|
22
22
|
from supervisely.project.project import find_project_dirs
|
|
23
23
|
from supervisely.project.project import upload_project as upload_project_fs
|
|
@@ -122,17 +122,20 @@ class SLYImageConverter(ImageConverter):
|
|
|
122
122
|
self._items = []
|
|
123
123
|
for image_path in images_list:
|
|
124
124
|
item = self.Item(image_path)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
json_name, json_name_noext = f"{item.name}.json", f"{get_file_name(item.name)}.json"
|
|
126
|
+
ann_path = ann_dict.get(json_name, ann_dict.get(json_name_noext))
|
|
127
|
+
if ann_path:
|
|
128
128
|
if self._meta is None:
|
|
129
129
|
meta = self.generate_meta_from_annotation(ann_path, meta)
|
|
130
130
|
is_valid = self.validate_ann_file(ann_path, meta)
|
|
131
131
|
if is_valid:
|
|
132
132
|
item.ann_data = ann_path
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
|
|
134
|
+
meta_path = img_meta_dict.get(json_name, img_meta_dict.get(json_name_noext))
|
|
135
|
+
if meta_path:
|
|
136
|
+
item.set_meta_data(meta_path)
|
|
137
|
+
if item.ann_data is not None or item.meta is not None:
|
|
138
|
+
detected_ann_cnt += 1
|
|
136
139
|
self._items.append(item)
|
|
137
140
|
self._meta = meta
|
|
138
141
|
return detected_ann_cnt > 0
|