supervisely 6.73.367__py3-none-any.whl → 6.73.369__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supervisely/api/volume/volume_figure_api.py +1 -15
- supervisely/convert/volume/nii/nii_volume_helper.py +4 -4
- supervisely/geometry/constants.py +2 -0
- supervisely/geometry/mask_3d.py +219 -45
- supervisely/nn/model/prediction_session.py +48 -17
- supervisely/volume/volume.py +59 -32
- supervisely/volume_annotation/volume_figure.py +3 -3
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/METADATA +1 -1
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/RECORD +13 -13
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/LICENSE +0 -0
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/WHEEL +0 -0
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.367.dist-info → supervisely-6.73.369.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
import tempfile
|
|
5
|
-
from collections import OrderedDict
|
|
6
5
|
from typing import Dict, List
|
|
7
6
|
from uuid import UUID
|
|
8
7
|
|
|
@@ -470,7 +469,7 @@ class VolumeFigureApi(FigureApi):
|
|
|
470
469
|
for figure in figures:
|
|
471
470
|
if figure.key() == key:
|
|
472
471
|
geometry_data = figure.geometry.data
|
|
473
|
-
header =
|
|
472
|
+
header = figure.geometry.create_header()
|
|
474
473
|
geometry_bytes = encode(geometry_data.astype(uint8), header)
|
|
475
474
|
self.upload_sf_geometries([key], {key: geometry_bytes}, key_id_map)
|
|
476
475
|
|
|
@@ -625,19 +624,6 @@ class VolumeFigureApi(FigureApi):
|
|
|
625
624
|
geometry = Mask3D.create_from_file(figure_path)
|
|
626
625
|
spatial_figure._set_3d_geometry(geometry)
|
|
627
626
|
|
|
628
|
-
def _create_header_for_geometry(self, geometry: Mask3D) -> OrderedDict:
|
|
629
|
-
"""
|
|
630
|
-
Create header for encoding Mask3D to NRRD bytes
|
|
631
|
-
"""
|
|
632
|
-
header = OrderedDict()
|
|
633
|
-
if geometry._space is not None:
|
|
634
|
-
header["space"] = geometry._space
|
|
635
|
-
if geometry._space_directions is not None:
|
|
636
|
-
header["space directions"] = geometry._space_directions
|
|
637
|
-
if geometry._space_origin is not None:
|
|
638
|
-
header["space origin"] = geometry._space_origin.to_json()["space_origin"]
|
|
639
|
-
return header
|
|
640
|
-
|
|
641
627
|
def download(
|
|
642
628
|
self, dataset_id: int, volume_ids: List[int] = None, skip_geometry: bool = False, **kwargs
|
|
643
629
|
) -> Dict[int, List[FigureInfo]]:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections import defaultdict, namedtuple
|
|
3
|
+
from pathlib import Path
|
|
2
4
|
from typing import Generator
|
|
3
5
|
|
|
4
6
|
import nrrd
|
|
5
7
|
import numpy as np
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from collections import defaultdict, namedtuple
|
|
8
8
|
|
|
9
9
|
from supervisely import Api
|
|
10
10
|
from supervisely.collection.str_enum import StrEnum
|
|
@@ -107,13 +107,13 @@ def nifti_to_nrrd(nii_file_path: str, converted_dir: str) -> str:
|
|
|
107
107
|
def get_annotation_from_nii(path: str) -> Generator[Mask3D, None, None]:
|
|
108
108
|
"""Get annotation from NIfTI 3D volume file."""
|
|
109
109
|
|
|
110
|
-
data,
|
|
110
|
+
data, header = convert_3d_nifti_to_nrrd(path)
|
|
111
111
|
unique_classes = np.unique(data)
|
|
112
112
|
|
|
113
113
|
for class_id in unique_classes:
|
|
114
114
|
if class_id == 0:
|
|
115
115
|
continue
|
|
116
|
-
mask = Mask3D(data == class_id)
|
|
116
|
+
mask = Mask3D(data == class_id, volume_header=header)
|
|
117
117
|
yield mask, class_id
|
|
118
118
|
|
|
119
119
|
|
supervisely/geometry/mask_3d.py
CHANGED
|
@@ -6,9 +6,9 @@ from __future__ import annotations
|
|
|
6
6
|
import base64
|
|
7
7
|
import gzip
|
|
8
8
|
import tempfile
|
|
9
|
+
from collections import OrderedDict
|
|
9
10
|
from typing import Dict, List, Literal, Optional, Tuple, Union
|
|
10
11
|
|
|
11
|
-
import nrrd
|
|
12
12
|
import numpy as np
|
|
13
13
|
|
|
14
14
|
from supervisely import logger
|
|
@@ -22,6 +22,8 @@ from supervisely.geometry.constants import (
|
|
|
22
22
|
ID,
|
|
23
23
|
LABELER_LOGIN,
|
|
24
24
|
MASK_3D,
|
|
25
|
+
SPACE,
|
|
26
|
+
SPACE_DIRECTIONS,
|
|
25
27
|
SPACE_ORIGIN,
|
|
26
28
|
UPDATED_AT,
|
|
27
29
|
)
|
|
@@ -183,6 +185,10 @@ class Mask3D(Geometry):
|
|
|
183
185
|
:type updated_at: str, optional
|
|
184
186
|
:param created_at: Date and Time when Mask 3D was created. Date Format is the same as in "updated_at" parameter.
|
|
185
187
|
:type created_at: str, optional
|
|
188
|
+
:param volume_header: NRRD header dictionary. Optional.
|
|
189
|
+
:type volume_header: dict, optional
|
|
190
|
+
:param convert_to_ras: If True, converts the mask to RAS orientation. Default is True.
|
|
191
|
+
:type convert_to_ras: bool, optional
|
|
186
192
|
:raises: :class:`ValueError`, if data is not bool or no pixels set to True in data
|
|
187
193
|
:Usage example:
|
|
188
194
|
|
|
@@ -219,6 +225,8 @@ class Mask3D(Geometry):
|
|
|
219
225
|
labeler_login: Optional[str] = None,
|
|
220
226
|
updated_at: Optional[str] = None,
|
|
221
227
|
created_at: Optional[str] = None,
|
|
228
|
+
volume_header: Optional[Dict] = None,
|
|
229
|
+
convert_to_ras: bool = True,
|
|
222
230
|
):
|
|
223
231
|
super().__init__(
|
|
224
232
|
sly_id=sly_id,
|
|
@@ -253,6 +261,98 @@ class Mask3D(Geometry):
|
|
|
253
261
|
self._space = None
|
|
254
262
|
self._space_directions = None
|
|
255
263
|
|
|
264
|
+
if volume_header is not None:
|
|
265
|
+
self.set_volume_space_meta(volume_header)
|
|
266
|
+
if self.space is not None and self.space != "right-anterior-superior":
|
|
267
|
+
if convert_to_ras:
|
|
268
|
+
self.orient_ras()
|
|
269
|
+
else:
|
|
270
|
+
logger.debug(
|
|
271
|
+
"Mask3D is not in RAS orientation. It is recommended to use RAS orientation for 3D masks."
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def space_origin(self) -> Optional[List[float]]:
|
|
276
|
+
"""
|
|
277
|
+
Get the space origin of the Mask3D as a list of floats.
|
|
278
|
+
|
|
279
|
+
:return: Space origin of the Mask3D.
|
|
280
|
+
:rtype: List[float] or None
|
|
281
|
+
"""
|
|
282
|
+
if self._space_origin is not None:
|
|
283
|
+
return [self._space_origin.x, self._space_origin.y, self._space_origin.z]
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
@space_origin.setter
|
|
287
|
+
def space_origin(self, value: Union[PointVolume, List[float], np.array]):
|
|
288
|
+
"""
|
|
289
|
+
Set the space origin of the Mask3D.
|
|
290
|
+
|
|
291
|
+
:param value: Space origin of the Mask3D. If provided as a list or array, it should contain 3 floats in the order [x, y, z].
|
|
292
|
+
:type value: :class:`PointVolume<PointVolume>` or List[float]
|
|
293
|
+
"""
|
|
294
|
+
if isinstance(value, PointVolume):
|
|
295
|
+
self._space_origin = value
|
|
296
|
+
elif isinstance(value, list) and len(value) == 3:
|
|
297
|
+
self._space_origin = PointVolume(x=value[0], y=value[1], z=value[2])
|
|
298
|
+
elif isinstance(value, np.ndarray) and value.shape == (3,):
|
|
299
|
+
self._space_origin = PointVolume(x=value[0], y=value[1], z=value[2])
|
|
300
|
+
else:
|
|
301
|
+
raise ValueError("Space origin must be a PointVolume or a list of 3 floats.")
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def space(self) -> Optional[str]:
|
|
305
|
+
"""
|
|
306
|
+
Get the space of the Mask3D.
|
|
307
|
+
|
|
308
|
+
:return: Space of the Mask3D.
|
|
309
|
+
:rtype: :class:`str`
|
|
310
|
+
"""
|
|
311
|
+
return self._space
|
|
312
|
+
|
|
313
|
+
@space.setter
|
|
314
|
+
def space(self, value: str):
|
|
315
|
+
"""
|
|
316
|
+
Set the space of the Mask3D.
|
|
317
|
+
|
|
318
|
+
:param value: Space of the Mask3D.
|
|
319
|
+
:type value: str
|
|
320
|
+
"""
|
|
321
|
+
if not isinstance(value, str):
|
|
322
|
+
raise ValueError("Space must be a string.")
|
|
323
|
+
self._space = value
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def space_directions(self) -> Optional[List[List[float]]]:
|
|
327
|
+
"""
|
|
328
|
+
Get the space directions of the Mask3D.
|
|
329
|
+
|
|
330
|
+
:return: Space directions of the Mask3D.
|
|
331
|
+
:rtype: :class:`List[List[float]]`
|
|
332
|
+
"""
|
|
333
|
+
return self._space_directions
|
|
334
|
+
|
|
335
|
+
@space_directions.setter
|
|
336
|
+
def space_directions(self, value: Union[List[List[float]], np.ndarray]):
|
|
337
|
+
"""
|
|
338
|
+
Set the space directions of the Mask3D.
|
|
339
|
+
|
|
340
|
+
:param value: Space directions of the Mask3D. Should be a 3x3 array-like structure.
|
|
341
|
+
:type value: List[List[float]] or np.ndarray
|
|
342
|
+
"""
|
|
343
|
+
if isinstance(value, np.ndarray):
|
|
344
|
+
if value.shape != (3, 3):
|
|
345
|
+
raise ValueError("Space directions must be a 3x3 array.")
|
|
346
|
+
self._space_directions = value.tolist()
|
|
347
|
+
elif (
|
|
348
|
+
isinstance(value, list)
|
|
349
|
+
and len(value) == 3
|
|
350
|
+
and all(isinstance(row, (list, np.ndarray)) and len(row) == 3 for row in value)
|
|
351
|
+
):
|
|
352
|
+
self._space_directions = [list(row) for row in value]
|
|
353
|
+
else:
|
|
354
|
+
raise ValueError("Space directions must be a 3x3 array or list of lists.")
|
|
355
|
+
|
|
256
356
|
@staticmethod
|
|
257
357
|
def geometry_name():
|
|
258
358
|
"""Return geometry name"""
|
|
@@ -268,22 +368,8 @@ class Mask3D(Geometry):
|
|
|
268
368
|
:param file_path: Path to nrrd file with data
|
|
269
369
|
:type file_path: str
|
|
270
370
|
"""
|
|
271
|
-
|
|
272
|
-
figure.
|
|
273
|
-
try:
|
|
274
|
-
figure.geometry._space_origin = PointVolume(
|
|
275
|
-
x=mask3d_header["space origin"][0],
|
|
276
|
-
y=mask3d_header["space origin"][1],
|
|
277
|
-
z=mask3d_header["space origin"][2],
|
|
278
|
-
)
|
|
279
|
-
figure.geometry._space = mask3d_header["space"]
|
|
280
|
-
figure.geometry._space_directions = mask3d_header["space directions"]
|
|
281
|
-
except KeyError as e:
|
|
282
|
-
header_keys = ["'space'", "'space directions'", "'space origin'"]
|
|
283
|
-
if str(e) in header_keys:
|
|
284
|
-
logger.warning(
|
|
285
|
-
f"The Mask3D geometry for figure ID '{get_file_name(file_path)}' doesn't contain optional space attributes that have similar names to {', '.join(header_keys)}. To set the values for these attributes, you can use information from the Volume associated with this figure object."
|
|
286
|
-
)
|
|
371
|
+
mask3d = Mask3D.create_from_file(file_path)
|
|
372
|
+
figure._set_3d_geometry(mask3d)
|
|
287
373
|
path_without_filename = "/".join(file_path.split("/")[:-1])
|
|
288
374
|
remove_dir(path_without_filename)
|
|
289
375
|
|
|
@@ -295,22 +381,26 @@ class Mask3D(Geometry):
|
|
|
295
381
|
:param file_path: Path to nrrd file with data
|
|
296
382
|
:type file_path: str
|
|
297
383
|
"""
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
384
|
+
from supervisely.volume.volume import read_nrrd_serie_volume_np
|
|
385
|
+
|
|
386
|
+
mask3d_data, meta = read_nrrd_serie_volume_np(file_path)
|
|
387
|
+
direction = np.array(meta["directions"]).reshape(3, 3)
|
|
388
|
+
spacing = np.array(meta["spacing"])
|
|
389
|
+
space_directions = (direction.T * spacing[:, None]).tolist()
|
|
390
|
+
mask3d_header = {
|
|
391
|
+
"space": "right-anterior-superior",
|
|
392
|
+
"space directions": space_directions,
|
|
393
|
+
"space origin": meta.get("origin", None),
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
geometry = cls(data=mask3d_data, volume_header=mask3d_header)
|
|
397
|
+
|
|
398
|
+
fields_to_check = ["space", "space_directions", "space_origin"]
|
|
399
|
+
if any([getattr(geometry, value) is None for value in fields_to_check]):
|
|
309
400
|
header_keys = ["'space'", "'space directions'", "'space origin'"]
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
401
|
+
logger.debug(
|
|
402
|
+
f"The Mask3D geometry created from the file '{file_path}' doesn't contain optional space attributes that have similar names to {', '.join(header_keys)}. To set the values for these attributes, you can use information from the Volume associated with this figure object."
|
|
403
|
+
)
|
|
314
404
|
return geometry
|
|
315
405
|
|
|
316
406
|
@classmethod
|
|
@@ -323,7 +413,7 @@ class Mask3D(Geometry):
|
|
|
323
413
|
:return: A Mask3D geometry object.
|
|
324
414
|
:rtype: Mask3D
|
|
325
415
|
"""
|
|
326
|
-
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
|
|
416
|
+
with tempfile.NamedTemporaryFile(delete=True, suffix=".nrrd") as temp_file:
|
|
327
417
|
temp_file.write(geometry_bytes)
|
|
328
418
|
return cls.create_from_file(temp_file.name)
|
|
329
419
|
|
|
@@ -368,12 +458,14 @@ class Mask3D(Geometry):
|
|
|
368
458
|
GEOMETRY_TYPE: self.name(),
|
|
369
459
|
}
|
|
370
460
|
|
|
371
|
-
if self.
|
|
372
|
-
res[f"{self._impl_json_class_name()}"][f"{SPACE_ORIGIN}"] =
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
461
|
+
if self.space_origin:
|
|
462
|
+
res[f"{self._impl_json_class_name()}"][f"{SPACE_ORIGIN}"] = self.space_origin
|
|
463
|
+
|
|
464
|
+
if self.space:
|
|
465
|
+
res[f"{self._impl_json_class_name()}"][f"{SPACE}"] = self.space
|
|
466
|
+
|
|
467
|
+
if self.space_directions:
|
|
468
|
+
res[f"{self._impl_json_class_name()}"][f"{SPACE_DIRECTIONS}"] = self.space_directions
|
|
377
469
|
|
|
378
470
|
self._add_creation_info(res)
|
|
379
471
|
return res
|
|
@@ -426,18 +518,30 @@ class Mask3D(Geometry):
|
|
|
426
518
|
created_at = json_data.get(CREATED_AT, None)
|
|
427
519
|
sly_id = json_data.get(ID, None)
|
|
428
520
|
class_id = json_data.get(CLASS_ID, None)
|
|
429
|
-
|
|
521
|
+
|
|
522
|
+
header = {}
|
|
523
|
+
|
|
524
|
+
space_origin = json_data[json_root_key].get(SPACE_ORIGIN, None)
|
|
525
|
+
if space_origin is not None:
|
|
526
|
+
header["space origin"] = space_origin
|
|
527
|
+
|
|
528
|
+
space = json_data[json_root_key].get(SPACE, None)
|
|
529
|
+
if space is not None:
|
|
530
|
+
header["space"] = space
|
|
531
|
+
|
|
532
|
+
space_directions = json_data[json_root_key].get(SPACE_DIRECTIONS, None)
|
|
533
|
+
if space_directions is not None:
|
|
534
|
+
header["space directions"] = space_directions
|
|
535
|
+
|
|
536
|
+
return cls(
|
|
430
537
|
data=data.astype(np.bool_),
|
|
431
538
|
sly_id=sly_id,
|
|
432
539
|
class_id=class_id,
|
|
433
540
|
labeler_login=labeler_login,
|
|
434
541
|
updated_at=updated_at,
|
|
435
542
|
created_at=created_at,
|
|
543
|
+
volume_header=header,
|
|
436
544
|
)
|
|
437
|
-
if SPACE_ORIGIN in json_data[json_root_key]:
|
|
438
|
-
x, y, z = json_data[json_root_key][SPACE_ORIGIN]
|
|
439
|
-
instance._space_origin = PointVolume(x=x, y=y, z=z)
|
|
440
|
-
return instance
|
|
441
545
|
|
|
442
546
|
@classmethod
|
|
443
547
|
def _impl_json_class_name(cls):
|
|
@@ -474,7 +578,7 @@ class Mask3D(Geometry):
|
|
|
474
578
|
path_for_mesh = f"meshes/{figure_id}.nrrd"
|
|
475
579
|
api.volume.figure.download_stl_meshes([figure_id], [path_for_mesh])
|
|
476
580
|
|
|
477
|
-
mask3d_data, _ =
|
|
581
|
+
mask3d_data, _ = sly.volume.volume.read_nrrd_serie_volume_np(path_for_mesh)
|
|
478
582
|
encoded_string = sly.Mask3D.data_2_base64(mask3d_data)
|
|
479
583
|
|
|
480
584
|
print(encoded_string)
|
|
@@ -619,3 +723,73 @@ class Mask3D(Geometry):
|
|
|
619
723
|
continue
|
|
620
724
|
geometries_dict[key] = geometry_bytes
|
|
621
725
|
return geometries_dict
|
|
726
|
+
|
|
727
|
+
def set_volume_space_meta(self, header: Dict):
|
|
728
|
+
"""
|
|
729
|
+
Set space, space directions, and space origin attributes from a NRRD header dictionary.
|
|
730
|
+
|
|
731
|
+
:param header: NRRD header dictionary.
|
|
732
|
+
:type header: dict
|
|
733
|
+
"""
|
|
734
|
+
if "space" in header:
|
|
735
|
+
self.space = header["space"]
|
|
736
|
+
if "space directions" in header:
|
|
737
|
+
self.space_directions = header["space directions"]
|
|
738
|
+
if "space origin" in header:
|
|
739
|
+
self.space_origin = PointVolume(
|
|
740
|
+
x=header["space origin"][0],
|
|
741
|
+
y=header["space origin"][1],
|
|
742
|
+
z=header["space origin"][2],
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
def create_header(self) -> OrderedDict:
|
|
746
|
+
"""
|
|
747
|
+
Create header for encoding Mask3D to NRRD bytes
|
|
748
|
+
|
|
749
|
+
:return: Header for NRRD file
|
|
750
|
+
:rtype: OrderedDict
|
|
751
|
+
"""
|
|
752
|
+
header = OrderedDict()
|
|
753
|
+
if self.space is not None:
|
|
754
|
+
header["space"] = self.space
|
|
755
|
+
if self.space_directions is not None:
|
|
756
|
+
header["space directions"] = self.space_directions
|
|
757
|
+
if self.space_origin is not None:
|
|
758
|
+
header["space origin"] = self.space_origin
|
|
759
|
+
return header
|
|
760
|
+
|
|
761
|
+
def orient_ras(self) -> None:
|
|
762
|
+
"""
|
|
763
|
+
Transforms the mask data and updates spatial metadata (origin, directions, spacing)
|
|
764
|
+
to align with the RAS coordinate system using SimpleITK.
|
|
765
|
+
|
|
766
|
+
:rtype: None
|
|
767
|
+
"""
|
|
768
|
+
import SimpleITK as sitk
|
|
769
|
+
|
|
770
|
+
from supervisely.volume.volume import _sitk_image_orient_ras
|
|
771
|
+
|
|
772
|
+
sitk_volume = sitk.GetImageFromArray(self.data)
|
|
773
|
+
if self.space_origin is not None:
|
|
774
|
+
sitk_volume.SetOrigin(self.space_origin)
|
|
775
|
+
if self.space_directions is not None:
|
|
776
|
+
# Convert space directions to spacing and direction
|
|
777
|
+
space_directions = np.array(self.space_directions)
|
|
778
|
+
spacing = np.linalg.norm(space_directions, axis=1)
|
|
779
|
+
direction = space_directions / spacing[:, np.newaxis]
|
|
780
|
+
sitk_volume.SetSpacing(spacing)
|
|
781
|
+
sitk_volume.SetDirection(direction.flatten())
|
|
782
|
+
|
|
783
|
+
sitk_volume = _sitk_image_orient_ras(sitk_volume)
|
|
784
|
+
|
|
785
|
+
# Extract transformed data and update object
|
|
786
|
+
self.data = sitk.GetArrayFromImage(sitk_volume)
|
|
787
|
+
new_direction = np.array(sitk_volume.GetDirection()).reshape(3, 3)
|
|
788
|
+
new_spacing = np.array(sitk_volume.GetSpacing())
|
|
789
|
+
new_space_directions = (new_direction.T * new_spacing[:, None]).tolist()
|
|
790
|
+
new_header = {
|
|
791
|
+
"space": "right-anterior-superior",
|
|
792
|
+
"space directions": new_space_directions,
|
|
793
|
+
"space origin": sitk_volume.GetOrigin(),
|
|
794
|
+
}
|
|
795
|
+
self.set_volume_space_meta(new_header)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import time
|
|
3
3
|
from os import PathLike
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from typing import Any, Dict, Iterator, List, Literal, Tuple, Union
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import requests
|
|
8
9
|
from requests import Timeout
|
|
10
|
+
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
|
9
11
|
from tqdm.auto import tqdm
|
|
10
12
|
|
|
11
13
|
import supervisely.io.env as env
|
|
@@ -17,6 +19,7 @@ from supervisely.io.fs import (
|
|
|
17
19
|
dir_exists,
|
|
18
20
|
file_exists,
|
|
19
21
|
get_file_ext,
|
|
22
|
+
get_file_size,
|
|
20
23
|
list_files,
|
|
21
24
|
list_files_recursively,
|
|
22
25
|
)
|
|
@@ -374,28 +377,26 @@ class PredictionSession:
|
|
|
374
377
|
json_body = self._get_json_body()
|
|
375
378
|
return self._post(method, json=json_body).json()
|
|
376
379
|
|
|
377
|
-
def _update_progress(
|
|
380
|
+
def _update_progress(
|
|
381
|
+
self,
|
|
382
|
+
tqdm: tqdm,
|
|
383
|
+
message: str = None,
|
|
384
|
+
current: int = None,
|
|
385
|
+
total: int = None,
|
|
386
|
+
is_size: bool = False,
|
|
387
|
+
):
|
|
378
388
|
if tqdm is None:
|
|
379
389
|
return
|
|
380
|
-
json_progress = response.get("progress", None)
|
|
381
|
-
if json_progress is None or json_progress.get("message") is None:
|
|
382
|
-
json_progress = response.get("preparing_progress", None)
|
|
383
|
-
if json_progress is None:
|
|
384
|
-
return
|
|
385
390
|
refresh = False
|
|
386
|
-
message
|
|
387
|
-
if message is not None and tqdm.desc not in [message, f"{message}:"]:
|
|
391
|
+
if message is not None and tqdm.desc != message:
|
|
388
392
|
tqdm.set_description(message, refresh=False)
|
|
389
393
|
refresh = True
|
|
390
|
-
current = json_progress.get("current", None)
|
|
391
394
|
if current is not None and tqdm.n != current:
|
|
392
395
|
tqdm.n = current
|
|
393
396
|
refresh = True
|
|
394
|
-
total = json_progress.get("total", None)
|
|
395
397
|
if total is not None and tqdm.total != total:
|
|
396
398
|
tqdm.total = total
|
|
397
399
|
refresh = True
|
|
398
|
-
is_size = json_progress.get("is_size", False)
|
|
399
400
|
if is_size and tqdm.unit == "it":
|
|
400
401
|
tqdm.unit = "iB"
|
|
401
402
|
tqdm.unit_scale = True
|
|
@@ -409,6 +410,20 @@ class PredictionSession:
|
|
|
409
410
|
if refresh:
|
|
410
411
|
tqdm.refresh()
|
|
411
412
|
|
|
413
|
+
def _update_progress_from_response(self, tqdm: tqdm, response: Dict[str, Any]):
|
|
414
|
+
if tqdm is None:
|
|
415
|
+
return
|
|
416
|
+
json_progress = response.get("progress", None)
|
|
417
|
+
if json_progress is None or json_progress.get("message") is None:
|
|
418
|
+
json_progress = response.get("preparing_progress", None)
|
|
419
|
+
if json_progress is None:
|
|
420
|
+
return
|
|
421
|
+
message = json_progress.get("message", json_progress.get("status", None))
|
|
422
|
+
current = json_progress.get("current", None)
|
|
423
|
+
total = json_progress.get("total", None)
|
|
424
|
+
is_size = json_progress.get("is_size", False)
|
|
425
|
+
self._update_progress(tqdm, message, current, total, is_size)
|
|
426
|
+
|
|
412
427
|
def _wait_for_inference_start(
|
|
413
428
|
self, delay=1, timeout=None, tqdm: tqdm = None
|
|
414
429
|
) -> Tuple[dict, bool]:
|
|
@@ -424,7 +439,7 @@ class PredictionSession:
|
|
|
424
439
|
last_stage = stage
|
|
425
440
|
has_started = stage not in ["preparing", "preprocess", None]
|
|
426
441
|
has_started = has_started or bool(resp.get("result")) or resp["progress"]["total"] != 1
|
|
427
|
-
self.
|
|
442
|
+
self._update_progress_from_response(tqdm, resp)
|
|
428
443
|
if not has_started:
|
|
429
444
|
time.sleep(delay)
|
|
430
445
|
timeout_exceeded = timeout and time.time() - t0 > timeout
|
|
@@ -440,7 +455,7 @@ class PredictionSession:
|
|
|
440
455
|
t0 = time.monotonic()
|
|
441
456
|
while not has_results and not timeout_exceeded:
|
|
442
457
|
resp = self._pop_pending_results()
|
|
443
|
-
self.
|
|
458
|
+
self._update_progress_from_response(tqdm, resp)
|
|
444
459
|
pending_results = resp["pending_results"]
|
|
445
460
|
exception_json = resp["exception"]
|
|
446
461
|
if exception_json:
|
|
@@ -567,14 +582,30 @@ class PredictionSession:
|
|
|
567
582
|
state["video_id"] = videos[0]
|
|
568
583
|
return self._start_inference(method, json=json_body)
|
|
569
584
|
elif isinstance(videos[0], (str, PathLike)):
|
|
585
|
+
video_path = videos[0]
|
|
570
586
|
files = []
|
|
571
587
|
try:
|
|
572
588
|
method = "inference_video_async"
|
|
573
|
-
files
|
|
574
|
-
|
|
575
|
-
|
|
589
|
+
files.append((Path(video_path).name, open(video_path, "rb"), "video/*"))
|
|
590
|
+
fields = {
|
|
591
|
+
"files": files[-1],
|
|
592
|
+
"state": json.dumps(state),
|
|
593
|
+
}
|
|
594
|
+
encoder = MultipartEncoder(fields)
|
|
595
|
+
if self.tqdm is not None:
|
|
596
|
+
|
|
597
|
+
def _callback(monitor):
|
|
598
|
+
self.tqdm.update(monitor.bytes_read)
|
|
599
|
+
|
|
600
|
+
video_size = get_file_size(video_path)
|
|
601
|
+
self._update_progress(self.tqdm, "Uploading video", 0, video_size, is_size=True)
|
|
602
|
+
encoder = MultipartEncoderMonitor(encoder, _callback)
|
|
603
|
+
|
|
604
|
+
return self._start_inference(
|
|
605
|
+
method, data=encoder, headers={"Content-Type": encoder.content_type}
|
|
606
|
+
)
|
|
576
607
|
finally:
|
|
577
|
-
for _, f in files:
|
|
608
|
+
for _, f, _ in files:
|
|
578
609
|
f.close()
|
|
579
610
|
else:
|
|
580
611
|
raise ValueError(
|
supervisely/volume/volume.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
from typing import List, Tuple, Union
|
|
6
|
+
from typing import List, Optional, Tuple, Union
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import pydicom
|
|
@@ -15,6 +15,7 @@ import supervisely.volume.nrrd_encoder as nrrd_encoder
|
|
|
15
15
|
from supervisely import logger
|
|
16
16
|
from supervisely.geometry.mask_3d import Mask3D
|
|
17
17
|
from supervisely.io.fs import get_file_ext, get_file_name, list_files_recursively
|
|
18
|
+
from supervisely.volume.stl_converter import matrix_from_nrrd_header
|
|
18
19
|
|
|
19
20
|
# Do NOT use directly for extension validation. Use is_valid_ext() / has_valid_ext() below instead.
|
|
20
21
|
ALLOWED_VOLUME_EXTENSIONS = [".nrrd", ".dcm"]
|
|
@@ -455,7 +456,7 @@ def inspect_dicom_series(root_dir: str, logging: bool = True) -> dict:
|
|
|
455
456
|
return found_series
|
|
456
457
|
|
|
457
458
|
|
|
458
|
-
def _sitk_image_orient_ras(sitk_volume):
|
|
459
|
+
def _sitk_image_orient_ras(sitk_volume: sitk.Image) -> sitk.Image:
|
|
459
460
|
import SimpleITK as sitk
|
|
460
461
|
|
|
461
462
|
if sitk_volume.GetDimension() == 4 and sitk_volume.GetSize()[3] == 1:
|
|
@@ -704,7 +705,7 @@ def read_nrrd_serie_volume(path: str) -> Tuple[sitk.Image, dict]:
|
|
|
704
705
|
"""
|
|
705
706
|
Read NRRD volume with given path.
|
|
706
707
|
|
|
707
|
-
:param path:
|
|
708
|
+
:param path: Path to NRRD volume files.
|
|
708
709
|
:type path: List[str]
|
|
709
710
|
:return: Volume data in SimpleITK.Image format and dictionary with metadata.
|
|
710
711
|
:rtype: Tuple[SimpleITK.Image, dict]
|
|
@@ -741,12 +742,12 @@ def read_nrrd_serie_volume(path: str) -> Tuple[sitk.Image, dict]:
|
|
|
741
742
|
return sitk_volume, meta
|
|
742
743
|
|
|
743
744
|
|
|
744
|
-
def read_nrrd_serie_volume_np(paths:
|
|
745
|
+
def read_nrrd_serie_volume_np(paths: str) -> Tuple[np.ndarray, dict]:
|
|
745
746
|
"""
|
|
746
747
|
Read NRRD volume with given path.
|
|
747
748
|
|
|
748
|
-
:param
|
|
749
|
-
:type
|
|
749
|
+
:param paths: Path to NRRD volume file.
|
|
750
|
+
:type paths: str
|
|
750
751
|
:return: Volume data in NumPy array format and dictionary with metadata.
|
|
751
752
|
:rtype: Tuple[np.ndarray, dict]
|
|
752
753
|
:Usage example:
|
|
@@ -868,17 +869,18 @@ def is_nifti_file(path: str) -> bool:
|
|
|
868
869
|
|
|
869
870
|
def convert_3d_geometry_to_mesh(
|
|
870
871
|
geometry: Mask3D,
|
|
871
|
-
spacing: tuple =
|
|
872
|
+
spacing: tuple = (1.0, 1.0, 1.0),
|
|
872
873
|
level: float = 0.5,
|
|
873
874
|
apply_decimation: bool = False,
|
|
874
875
|
decimation_fraction: float = 0.5,
|
|
876
|
+
volume_meta: Optional[dict] = None,
|
|
875
877
|
) -> Trimesh:
|
|
876
878
|
"""
|
|
877
879
|
Converts a 3D geometry (Mask3D) to a Trimesh mesh.
|
|
878
880
|
|
|
879
881
|
:param geometry: The 3D geometry to convert.
|
|
880
882
|
:type geometry: supervisely.geometry.mask_3d.Mask3D
|
|
881
|
-
:param spacing: Voxel spacing in (x, y, z).
|
|
883
|
+
:param spacing: Voxel spacing in (x, y, z).
|
|
882
884
|
:type spacing: tuple
|
|
883
885
|
:param level: Isosurface value for marching cubes. Default is 0.5.
|
|
884
886
|
:type level: float
|
|
@@ -886,6 +888,8 @@ def convert_3d_geometry_to_mesh(
|
|
|
886
888
|
:type apply_decimation: bool
|
|
887
889
|
:param decimation_fraction: Fraction of faces to keep if decimation is applied. Default is 0.5.
|
|
888
890
|
:type decimation_fraction: float
|
|
891
|
+
:param volume_meta: Metadata of the volume. Used for mesh alignment if geometry lacks specific fields. Default is None.
|
|
892
|
+
:type volume_meta: dict, optional
|
|
889
893
|
:return: The resulting Trimesh mesh.
|
|
890
894
|
:rtype: trimesh.Trimesh
|
|
891
895
|
|
|
@@ -893,37 +897,40 @@ def convert_3d_geometry_to_mesh(
|
|
|
893
897
|
|
|
894
898
|
.. code-block:: python
|
|
895
899
|
|
|
900
|
+
volume_header = nrrd.read_header("path/to/volume.nrrd")
|
|
896
901
|
mask3d = Mask3D.create_from_file("path/to/mask3d")
|
|
897
|
-
mesh = convert_3d_geometry_to_mesh(mask3d, spacing=(1.0, 1.0, 1.0), level=0.7, apply_decimation=True)
|
|
902
|
+
mesh = convert_3d_geometry_to_mesh(mask3d, spacing=(1.0, 1.0, 1.0), level=0.7, apply_decimation=True, volume_meta=volume_header)
|
|
898
903
|
"""
|
|
899
904
|
from skimage import measure
|
|
900
905
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
except Exception as e:
|
|
909
|
-
logger.warning(
|
|
910
|
-
"Failed to get spacing from geometry meta. Using (1.0, 1.0, 1.0).", exc_info=1
|
|
911
|
-
)
|
|
912
|
-
spacing = (1.0, 1.0, 1.0)
|
|
913
|
-
|
|
914
|
-
# marching_cubes expects (z, y, x) order
|
|
915
|
-
verts, faces, normals, _ = measure.marching_cubes(
|
|
916
|
-
mask.astype(np.float32), level=level, spacing=spacing
|
|
917
|
-
)
|
|
906
|
+
if volume_meta is None:
|
|
907
|
+
volume_meta = {}
|
|
908
|
+
|
|
909
|
+
space_directions = geometry.space_directions or volume_meta.get("space directions")
|
|
910
|
+
space_origin = geometry.space_origin or volume_meta.get("space origin")
|
|
911
|
+
|
|
912
|
+
verts, faces, normals, _ = measure.marching_cubes(geometry.data, level=level, spacing=spacing)
|
|
918
913
|
mesh = Trimesh(vertices=verts, faces=faces, vertex_normals=normals, process=False)
|
|
919
914
|
|
|
920
915
|
if apply_decimation and 0 < decimation_fraction < 1:
|
|
921
916
|
mesh = mesh.simplify_quadric_decimation(int(len(mesh.faces) * decimation_fraction))
|
|
922
917
|
|
|
918
|
+
if space_directions is not None and space_origin is not None:
|
|
919
|
+
header = {
|
|
920
|
+
"space directions": space_directions,
|
|
921
|
+
"space origin": space_origin,
|
|
922
|
+
}
|
|
923
|
+
align_mesh_to_volume(mesh, header)
|
|
924
|
+
|
|
925
|
+
# flip x and y axes to match initial mask orientation
|
|
926
|
+
mesh.apply_transform(np.diag([-1, -1, 1, 1]))
|
|
927
|
+
|
|
928
|
+
mesh.fix_normals()
|
|
929
|
+
|
|
923
930
|
return mesh
|
|
924
931
|
|
|
925
932
|
|
|
926
|
-
def export_3d_as_mesh(geometry: Mask3D, output_path: str, kwargs
|
|
933
|
+
def export_3d_as_mesh(geometry: Mask3D, output_path: str, **kwargs):
|
|
927
934
|
"""
|
|
928
935
|
Exports the 3D mesh representation of the object to a file in either STL or OBJ format.
|
|
929
936
|
|
|
@@ -936,7 +943,7 @@ def export_3d_as_mesh(geometry: Mask3D, output_path: str, kwargs=None):
|
|
|
936
943
|
- level (float): Isosurface value for marching cubes. Default is 0.5.
|
|
937
944
|
- apply_decimation (bool): Whether to simplify the mesh. Default is False.
|
|
938
945
|
- decimation_fraction (float): Fraction of faces to keep if decimation is applied. Default is 0.5.
|
|
939
|
-
|
|
946
|
+
- volume_meta (dict): Metadata of the volume. Used for mesh alignment if geometry lacks specific fields. Default is None.
|
|
940
947
|
:return: None
|
|
941
948
|
|
|
942
949
|
:Usage example:
|
|
@@ -946,14 +953,34 @@ def export_3d_as_mesh(geometry: Mask3D, output_path: str, kwargs=None):
|
|
|
946
953
|
mask3d_path = "path/to/mask3d"
|
|
947
954
|
mask3d = Mask3D.create_from_file(mask3d_path)
|
|
948
955
|
|
|
949
|
-
mask3d.export_3d_as_mesh(mask3d, "output.stl",
|
|
956
|
+
mask3d.export_3d_as_mesh(mask3d, "output.stl", spacing=(1.0, 1.0, 1.0), level=0.7, apply_decimation=True)
|
|
950
957
|
"""
|
|
951
958
|
|
|
952
|
-
if kwargs is None:
|
|
953
|
-
kwargs = {}
|
|
954
|
-
|
|
955
959
|
if get_file_ext(output_path).lower() not in [".stl", ".obj"]:
|
|
956
960
|
raise ValueError('File extension must be either ".stl" or ".obj"')
|
|
957
961
|
|
|
958
962
|
mesh = convert_3d_geometry_to_mesh(geometry, **kwargs)
|
|
959
963
|
mesh.export(output_path)
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def align_mesh_to_volume(mesh: Trimesh, volume_header: dict) -> None:
|
|
967
|
+
"""
|
|
968
|
+
Transforms the given mesh in-place using spatial information from an NRRD header.
|
|
969
|
+
The mesh will be tranformed to match the coordinate system defined in the header.
|
|
970
|
+
|
|
971
|
+
:param mesh: The mesh object to be transformed. The transformation is applied in-place.
|
|
972
|
+
:type mesh: Trimesh
|
|
973
|
+
:param volume_header: The NRRD header containing spatial metadata, including "space directions",
|
|
974
|
+
"space origin", and "space". Field "space" should be in the format of
|
|
975
|
+
"right-anterior-superior", "left-anterior-superior", etc.
|
|
976
|
+
:type volume_header: dict
|
|
977
|
+
:returns: None
|
|
978
|
+
:rtype: None
|
|
979
|
+
"""
|
|
980
|
+
from supervisely.geometry.constants import SPACE_ORIGIN
|
|
981
|
+
from supervisely.geometry.mask_3d import PointVolume
|
|
982
|
+
|
|
983
|
+
if isinstance(volume_header["space origin"], PointVolume):
|
|
984
|
+
volume_header["space origin"] = volume_header["space origin"].to_json()[SPACE_ORIGIN]
|
|
985
|
+
transform_mat = matrix_from_nrrd_header(volume_header)
|
|
986
|
+
mesh.apply_transform(transform_mat)
|
|
@@ -675,6 +675,6 @@ class VolumeFigure(VideoFigure):
|
|
|
675
675
|
)
|
|
676
676
|
|
|
677
677
|
self.geometry.data = new_geometry.data
|
|
678
|
-
self.geometry.
|
|
679
|
-
self.geometry.
|
|
680
|
-
self.geometry.
|
|
678
|
+
self.geometry.space = new_geometry.space
|
|
679
|
+
self.geometry.space_origin = new_geometry.space_origin
|
|
680
|
+
self.geometry.space_directions = new_geometry.space_directions
|
|
@@ -77,7 +77,7 @@ supervisely/api/video/video_tag_api.py,sha256=wPe1HeJyg9kV1z2UJq6BEte5sKBoPJ2UGA
|
|
|
77
77
|
supervisely/api/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
78
|
supervisely/api/volume/volume_annotation_api.py,sha256=NOHpLeqHLCeRs1KlXWoG91vtIXdUVTO69wh1ws0VmOQ,22246
|
|
79
79
|
supervisely/api/volume/volume_api.py,sha256=rz_yaBbbTkVeAHmF449zPI8Va_YpDHfHYjXgjGAjMJg,55390
|
|
80
|
-
supervisely/api/volume/volume_figure_api.py,sha256=
|
|
80
|
+
supervisely/api/volume/volume_figure_api.py,sha256=upjIdiiQgOJ6och0KUg0rQo-q-PIipL5RX2V3fOBPvI,25437
|
|
81
81
|
supervisely/api/volume/volume_object_api.py,sha256=F7pLV2MTlBlyN6fEKdxBSUatIMGWSuu8bWj3Hvcageo,2139
|
|
82
82
|
supervisely/api/volume/volume_tag_api.py,sha256=yNGgXz44QBSW2VGlNDOVLqLXnH8Q2fFrxDFb_girYXA,3639
|
|
83
83
|
supervisely/app/__init__.py,sha256=4yW79U_xvo7vjg6-vRhjtt0bO8MxMSx2PD8dMamS9Q8,633
|
|
@@ -671,7 +671,7 @@ supervisely/convert/volume/dicom/dicom_helper.py,sha256=OrKlyt1hA5BOXKhE1LF1WxBI
|
|
|
671
671
|
supervisely/convert/volume/nii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
672
672
|
supervisely/convert/volume/nii/nii_planes_volume_converter.py,sha256=dXoBA8AYUOEjLpV2cZJ5n1HDq4_gNhnD__NVsgfc_Qc,14551
|
|
673
673
|
supervisely/convert/volume/nii/nii_volume_converter.py,sha256=BAOKX96-bp6WfTFLrCQNrXk2YhKqIFSU5LJ-auKiAfc,8514
|
|
674
|
-
supervisely/convert/volume/nii/nii_volume_helper.py,sha256=
|
|
674
|
+
supervisely/convert/volume/nii/nii_volume_helper.py,sha256=_FepXNu1RIFuzBOv0aAtrG-J2xN3uyyEMcq6pUD9fsk,14159
|
|
675
675
|
supervisely/convert/volume/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
676
676
|
supervisely/convert/volume/sly/sly_volume_converter.py,sha256=XmSuxnRqxchG87b244f3h0UHvOt6IkajMquL1drWlCM,5595
|
|
677
677
|
supervisely/convert/volume/sly/sly_volume_helper.py,sha256=gUY0GW3zDMlO2y-zQQG36uoXMrKkKz4-ErM1CDxFCxE,5620
|
|
@@ -687,7 +687,7 @@ supervisely/geometry/any_geometry.py,sha256=BOZBsuMYgtkA7cOKp-URjzV9zQkpHuvfp2QP
|
|
|
687
687
|
supervisely/geometry/bitmap.py,sha256=-tyIXCfgvNn3c7jHs18aq693JR5xdvhfNf6Vmf4952g,21869
|
|
688
688
|
supervisely/geometry/bitmap_base.py,sha256=lNamVL3gZ355oYlIEPc0yC84k1bxuCbUVI0ouaZ_Q4k,13814
|
|
689
689
|
supervisely/geometry/closed_surface_mesh.py,sha256=3ZplCm3Q2bhPcxNmtv2U1UfdezRkC3_BxjwH4yl7wrs,1558
|
|
690
|
-
supervisely/geometry/constants.py,sha256=
|
|
690
|
+
supervisely/geometry/constants.py,sha256=6lXpwTTFuswuH9WXMy4akHKshQ5C6fgQhdY-XCdVIMA,842
|
|
691
691
|
supervisely/geometry/conversions.py,sha256=ZY6xWYFWaDA5KDJkcIBBP8LAmMfZwxMeVFfYUYEM6fw,1170
|
|
692
692
|
supervisely/geometry/cuboid.py,sha256=GVHeUrVgfjUjE3PorV_vtge6_thDvvUYI5-9_HZjfWs,21077
|
|
693
693
|
supervisely/geometry/cuboid_2d.py,sha256=enQ-7ZVix5SqC7ZEwxgC0Kvmz9J_wXL7NH3m02snNvc,13444
|
|
@@ -697,7 +697,7 @@ supervisely/geometry/graph.py,sha256=kSShcGU4kZgwAbvTrqGzC55qha0nI7M5luiMZSbNx_4
|
|
|
697
697
|
supervisely/geometry/helpers.py,sha256=2gdYMFWTAr836gVXcp-lkDQs9tdaV0ou33kj3mzJBQA,5132
|
|
698
698
|
supervisely/geometry/image_rotator.py,sha256=wrU8cXEUfuNcmPms2myUV4BpZqz_2oDArsEUFeiTpxs,6888
|
|
699
699
|
supervisely/geometry/main_tests.py,sha256=K3Olsz9igHDW2IfIA5JOpjoE8bZ3ex2PXvVR2ZCDrHU,27199
|
|
700
|
-
supervisely/geometry/mask_3d.py,sha256=
|
|
700
|
+
supervisely/geometry/mask_3d.py,sha256=gaac4wUoG-qmpVcttgfAh2WhS3VUWjcdNqw5V-Aa5GA,26720
|
|
701
701
|
supervisely/geometry/multichannel_bitmap.py,sha256=dL0igkOCVZiIZ9LDU7srFLA50XGo4doE-B5_E1uboXM,4968
|
|
702
702
|
supervisely/geometry/point.py,sha256=7ed_Ipd-Ab8ZeqJF5ft0kP9pKVb2iWXCxPuRhMuweMc,13228
|
|
703
703
|
supervisely/geometry/point_3d.py,sha256=0ico0aV4fuKNBVrysDjUy1Cx1S9CEzBlEVE3AsbVd0E,1669
|
|
@@ -965,7 +965,7 @@ supervisely/nn/legacy/training/eval_planner.py,sha256=zN9b0_CX7sWGdC8e6riTvD-NOU
|
|
|
965
965
|
supervisely/nn/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
966
966
|
supervisely/nn/model/model_api.py,sha256=rIne6ymvL2aVwzSTJMGxAbevE0U-f-WKhDciqaneqRc,9666
|
|
967
967
|
supervisely/nn/model/prediction.py,sha256=N3oO9s3NDiC5CFvW8utfU8rz3bfpCl37Sk4VEBH94Bc,11307
|
|
968
|
-
supervisely/nn/model/prediction_session.py,sha256=
|
|
968
|
+
supervisely/nn/model/prediction_session.py,sha256=sy0FSQaWSmT8i0RkR4J8oIn3Ek4IDVJNBR1Tg4mulkM,25523
|
|
969
969
|
supervisely/nn/tracker/__init__.py,sha256=LiojByb5kGsTQ49lWuboEh7B4JUwM1vfz81J8kJlLYo,337
|
|
970
970
|
supervisely/nn/tracker/tracker.py,sha256=Hs0c07l9IVF86jvjloNAGJsSZAHuNQZf0kVaUHfw3Fs,9694
|
|
971
971
|
supervisely/nn/tracker/bot_sort/__init__.py,sha256=zEs5eT8NQMrM5dtSn7kbI2aWpgS2rvO7aNmrAB8v0VU,632
|
|
@@ -1075,13 +1075,13 @@ supervisely/volume/__init__.py,sha256=EBZBY_5mzabXzMUQh5akusIGd16XnX9n8J0jIi_JmW
|
|
|
1075
1075
|
supervisely/volume/nrrd_encoder.py,sha256=1lqwwyqxEvctw1ysQ70x4xPSV1uy1g5YcH5CURwL7-c,4084
|
|
1076
1076
|
supervisely/volume/nrrd_loader.py,sha256=_yqahKcqSRxunHZ5LtnUWIRA7UvIhPKOhAUwYijSGY4,9065
|
|
1077
1077
|
supervisely/volume/stl_converter.py,sha256=WIMQgHO_u4JT58QdcMXcb_euF1BFhM7D52IVX_0QTxE,6285
|
|
1078
|
-
supervisely/volume/volume.py,sha256=
|
|
1078
|
+
supervisely/volume/volume.py,sha256=jDu_p1zPQxCojjtdJlVVTxfuKgVCYmMSY13Xz99k7pA,30765
|
|
1079
1079
|
supervisely/volume_annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1080
1080
|
supervisely/volume_annotation/constants.py,sha256=BdFIh56fy7vzLIjt0gH8xP01EIU-qgQIwbSHVUcABCU,569
|
|
1081
1081
|
supervisely/volume_annotation/plane.py,sha256=wyezAcc8tLp38O44CwWY0wjdQxf3VjRdFLWooCrk-Nw,16301
|
|
1082
1082
|
supervisely/volume_annotation/slice.py,sha256=9m3jtUYz4PYKV3rgbeh2ofDebkyg4TomNbkC6BwZ0lA,4635
|
|
1083
1083
|
supervisely/volume_annotation/volume_annotation.py,sha256=pGu6n8_5JkFpir4HTVRf302gGD2EqJ96Gh4M0_236Qg,32047
|
|
1084
|
-
supervisely/volume_annotation/volume_figure.py,sha256=
|
|
1084
|
+
supervisely/volume_annotation/volume_figure.py,sha256=3iFyknF8TlLCMBwgg8gJ26wnNTDTRaw8uEJFVkJGh78,25331
|
|
1085
1085
|
supervisely/volume_annotation/volume_object.py,sha256=rWzOnycoSJ4-CvFgDOP_rPortU4CdcYR26txe5wJHNo,3577
|
|
1086
1086
|
supervisely/volume_annotation/volume_object_collection.py,sha256=Tc4AovntgoFj5hpTLBv7pCQ3eL0BjorOVpOh2nAE_tA,5706
|
|
1087
1087
|
supervisely/volume_annotation/volume_tag.py,sha256=MEk1ky7X8zWe2JgV-j8jXt14e8yu2g1kScU26n9lOMk,9494
|
|
@@ -1097,9 +1097,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1097
1097
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1098
1098
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1099
1099
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1100
|
-
supervisely-6.73.
|
|
1101
|
-
supervisely-6.73.
|
|
1102
|
-
supervisely-6.73.
|
|
1103
|
-
supervisely-6.73.
|
|
1104
|
-
supervisely-6.73.
|
|
1105
|
-
supervisely-6.73.
|
|
1100
|
+
supervisely-6.73.369.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1101
|
+
supervisely-6.73.369.dist-info/METADATA,sha256=IkZVEQIZSwqfmNiQuR6FoA7bYffMBsM4Ld9bJBWaOSA,35154
|
|
1102
|
+
supervisely-6.73.369.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
1103
|
+
supervisely-6.73.369.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1104
|
+
supervisely-6.73.369.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1105
|
+
supervisely-6.73.369.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|