code-loader 1.0.209__tar.gz → 1.0.210__tar.gz
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.
- {code_loader-1.0.209 → code_loader-1.0.210}/PKG-INFO +3 -4
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/datasetclasses.py +2 -2
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/enums.py +1 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/visualizer_classes.py +56 -1
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/inner_leap_binder/leapbinder_decorators.py +3 -2
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/plot_functions/plot_functions.py +24 -1
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/visualizers/default_visualizers.py +15 -1
- {code_loader-1.0.209 → code_loader-1.0.210}/pyproject.toml +1 -1
- {code_loader-1.0.209 → code_loader-1.0.210}/LICENSE +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/README.md +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/__init__.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/__init__.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/exceptions.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/mapping.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/responsedataclasses.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/contract/sim_config.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/default_losses.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/default_metrics.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/__init__.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/api.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/cli_config_utils.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/client.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/epoch.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/experiment.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/experiment_context.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/types.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/utils.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/inner_leap_binder/__init__.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/inner_leap_binder/leapbinder.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/leaploader.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/leaploaderbase.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/mixpanel_tracker.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/plot_functions/__init__.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/plot_functions/visualize.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/utils.py +0 -0
- {code_loader-1.0.209 → code_loader-1.0.210}/code_loader/visualizers/__init__.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: code-loader
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.210
|
|
4
4
|
Summary:
|
|
5
|
+
Home-page: https://github.com/tensorleap/code-loader
|
|
5
6
|
License: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
7
|
Author: dorhar
|
|
8
8
|
Author-email: doron.harnoy@tensorleap.ai
|
|
9
9
|
Requires-Python: >=3.8,<3.13
|
|
@@ -20,7 +20,6 @@ Requires-Dist: numpy (>=2.3.2,<3.0.0) ; python_version >= "3.11" and python_vers
|
|
|
20
20
|
Requires-Dist: psutil (>=5.9.5,<6.0.0)
|
|
21
21
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
22
22
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
23
|
-
Project-URL: Homepage, https://github.com/tensorleap/code-loader
|
|
24
23
|
Project-URL: Repository, https://github.com/tensorleap/code-loader
|
|
25
24
|
Description-Content-Type: text/markdown
|
|
26
25
|
|
|
@@ -8,7 +8,7 @@ import numpy.typing as npt
|
|
|
8
8
|
from code_loader.contract.enums import DataStateType, DataStateEnum, LeapDataType, ConfusionMatrixValue, \
|
|
9
9
|
MetricDirection, DatasetMetadataType, LatentSpaceReduction, CustomLatentSpaceComputedAt
|
|
10
10
|
from code_loader.contract.visualizer_classes import LeapImage, LeapText, LeapGraph, LeapHorizontalBar, \
|
|
11
|
-
LeapTextMask, LeapImageMask, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio
|
|
11
|
+
LeapTextMask, LeapImageMask, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapVolume
|
|
12
12
|
from code_loader.contract.sim_config import SimConfig
|
|
13
13
|
|
|
14
14
|
custom_latent_space_attribute = "custom_latent_space"
|
|
@@ -213,7 +213,7 @@ VisualizerCallableInterface = Union[
|
|
|
213
213
|
]
|
|
214
214
|
|
|
215
215
|
LeapData = Union[LeapImage, LeapText, LeapGraph, LeapHorizontalBar, LeapImageMask, LeapTextMask, LeapImageWithBBox,
|
|
216
|
-
LeapImageWithHeatmap, LeapVideo, LeapAudio]
|
|
216
|
+
LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapVolume]
|
|
217
217
|
|
|
218
218
|
CustomCallableInterface = Callable[..., Any]
|
|
219
219
|
|
|
@@ -377,6 +377,60 @@ class LeapAudio:
|
|
|
377
377
|
raise LeapValidationError(f'sample_rate must be a positive int, got {self.sample_rate}')
|
|
378
378
|
|
|
379
379
|
|
|
380
|
+
@dataclass
|
|
381
|
+
class LeapVolume:
|
|
382
|
+
"""
|
|
383
|
+
Visualizer representing a volume (3-D scalar field) for Tensorleap: a CT/MRI scan, a microscopy
|
|
384
|
+
z-stack, or any (D, H, W) grid. The UI slices it along any axis; the engine stores it as uint8.
|
|
385
|
+
|
|
386
|
+
Attributes:
|
|
387
|
+
data (npt.NDArray[np.float32] | npt.NDArray[np.uint8]): The volume, shaped [D, H, W]. Any finite value
|
|
388
|
+
range is accepted: the engine min-max quantizes it to uint8 for display and keeps the original
|
|
389
|
+
[min, max] so the UI can show true values.
|
|
390
|
+
mask (Optional[npt.NDArray[np.uint8]]): Optional label volume shaped like `data`; voxel value v is the
|
|
391
|
+
index into `labels`.
|
|
392
|
+
labels (Optional[List[str]]): Names for mask values; required with `mask`, len(labels) > mask.max().
|
|
393
|
+
spacing (Optional[Tuple[float, float, float]]): Physical voxel size per axis (D, H, W), e.g. mm, so the
|
|
394
|
+
UI renders anisotropic voxels with the right aspect. None means (1, 1, 1).
|
|
395
|
+
type (LeapDataType): The data type, default is LeapDataType.Volume.
|
|
396
|
+
|
|
397
|
+
Example:
|
|
398
|
+
volume = np.random.rand(64, 128, 128).astype(np.float32)
|
|
399
|
+
mask = (volume > 0.8).astype(np.uint8)
|
|
400
|
+
leap_volume = LeapVolume(data=volume, mask=mask, labels=["background", "lesion"], spacing=(2.0, 0.7, 0.7))
|
|
401
|
+
"""
|
|
402
|
+
data: Union[npt.NDArray[np.float32], npt.NDArray[np.uint8]]
|
|
403
|
+
mask: Optional[npt.NDArray[np.uint8]] = None
|
|
404
|
+
labels: Optional[List[str]] = None
|
|
405
|
+
spacing: Optional[Tuple[float, float, float]] = None
|
|
406
|
+
type: LeapDataType = LeapDataType.Volume
|
|
407
|
+
|
|
408
|
+
def __post_init__(self) -> None:
|
|
409
|
+
validate_type(self.type, LeapDataType.Volume)
|
|
410
|
+
validate_type(type(self.data), np.ndarray)
|
|
411
|
+
validate_type(self.data.dtype, [np.uint8, np.float32])
|
|
412
|
+
validate_type(len(self.data.shape), 3, 'Volume data must be of shape 3 [D, H, W]')
|
|
413
|
+
if not np.isfinite(self.data).all():
|
|
414
|
+
raise LeapValidationError('Volume data must be finite (no NaN/Inf)')
|
|
415
|
+
if (self.mask is None) != (self.labels is None):
|
|
416
|
+
raise LeapValidationError('Volume mask and labels must be given together')
|
|
417
|
+
if self.mask is not None:
|
|
418
|
+
validate_type(type(self.mask), np.ndarray)
|
|
419
|
+
validate_type(self.mask.dtype, np.uint8)
|
|
420
|
+
if self.mask.shape != self.data.shape:
|
|
421
|
+
raise LeapValidationError(
|
|
422
|
+
f'Volume mask shape {self.mask.shape} must equal data shape {self.data.shape}')
|
|
423
|
+
validate_type(type(self.labels), list)
|
|
424
|
+
for label in self.labels:
|
|
425
|
+
validate_type(type(label), str)
|
|
426
|
+
if self.mask.size and int(self.mask.max()) >= len(self.labels):
|
|
427
|
+
raise LeapValidationError(
|
|
428
|
+
f'Volume mask has value {int(self.mask.max())} but only {len(self.labels)} labels')
|
|
429
|
+
if self.spacing is not None:
|
|
430
|
+
if len(self.spacing) != 3 or any(float(s) <= 0 for s in self.spacing):
|
|
431
|
+
raise LeapValidationError(f'Volume spacing must be 3 positive numbers, got {self.spacing}')
|
|
432
|
+
|
|
433
|
+
|
|
380
434
|
map_leap_data_type_to_visualizer_class = {
|
|
381
435
|
LeapDataType.Image.value: LeapImage,
|
|
382
436
|
LeapDataType.Graph.value: LeapGraph,
|
|
@@ -387,5 +441,6 @@ map_leap_data_type_to_visualizer_class = {
|
|
|
387
441
|
LeapDataType.TextMask.value: LeapTextMask,
|
|
388
442
|
LeapDataType.ImageWithBBox.value: LeapImageWithBBox,
|
|
389
443
|
LeapDataType.ImageWithHeatmap.value: LeapImageWithHeatmap,
|
|
390
|
-
LeapDataType.Audio.value: LeapAudio
|
|
444
|
+
LeapDataType.Audio.value: LeapAudio,
|
|
445
|
+
LeapDataType.Volume.value: LeapVolume,
|
|
391
446
|
}
|
{code_loader-1.0.209 → code_loader-1.0.210}/code_loader/inner_leap_binder/leapbinder_decorators.py
RENAMED
|
@@ -34,7 +34,7 @@ from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMet
|
|
|
34
34
|
from code_loader import leap_binder, LeapLoader
|
|
35
35
|
from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
|
|
36
36
|
from code_loader.contract.visualizer_classes import LeapImage, LeapImageMask, LeapTextMask, LeapText, LeapGraph, \
|
|
37
|
-
LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapValidationError, \
|
|
37
|
+
LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapVolume, LeapValidationError, \
|
|
38
38
|
map_leap_data_type_to_visualizer_class
|
|
39
39
|
from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame, \
|
|
40
40
|
_reject_stringized_sample_preprocess_response
|
|
@@ -1593,7 +1593,8 @@ def tensorleap_custom_visualizer(name: str, visualizer_type: LeapDataType,
|
|
|
1593
1593
|
LeapDataType.ImageWithBBox: LeapImageWithBBox,
|
|
1594
1594
|
LeapDataType.ImageWithHeatmap: LeapImageWithHeatmap,
|
|
1595
1595
|
LeapDataType.Video: LeapVideo,
|
|
1596
|
-
LeapDataType.Audio: LeapAudio
|
|
1596
|
+
LeapDataType.Audio: LeapAudio,
|
|
1597
|
+
LeapDataType.Volume: LeapVolume,
|
|
1597
1598
|
}
|
|
1598
1599
|
validate_output_structure(result, func_name=user_function.__name__,
|
|
1599
1600
|
expected_type_name=result_type_map[visualizer_type])
|
|
@@ -20,7 +20,7 @@ from textwrap import wrap
|
|
|
20
20
|
import math
|
|
21
21
|
|
|
22
22
|
from code_loader.contract.visualizer_classes import LeapImage, LeapImageWithBBox, LeapGraph, LeapText, \
|
|
23
|
-
LeapHorizontalBar, LeapImageMask, LeapTextMask, LeapImageWithHeatmap, LeapVideo
|
|
23
|
+
LeapHorizontalBar, LeapImageMask, LeapTextMask, LeapImageWithHeatmap, LeapVideo, LeapVolume
|
|
24
24
|
from code_loader.utils import rescale_min_max
|
|
25
25
|
|
|
26
26
|
|
|
@@ -420,6 +420,28 @@ def plot_video(leap_data: LeapVideo, title: str) -> None:
|
|
|
420
420
|
plt.pause(0.1) # Adjust the pause duration as needed
|
|
421
421
|
plt.show()
|
|
422
422
|
|
|
423
|
+
@run_only_on_non_mapping_mode()
|
|
424
|
+
def plot_volume(leap_data: LeapVolume, title: str) -> None:
|
|
425
|
+
"""Middle slice along each axis, grayscale as the engine stores it (uint8), mask overlaid."""
|
|
426
|
+
volume = rescale_min_max(leap_data.data.astype(np.float32))
|
|
427
|
+
spacing = leap_data.spacing or (1.0, 1.0, 1.0)
|
|
428
|
+
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
|
|
429
|
+
fig.patch.set_facecolor('black')
|
|
430
|
+
fig.suptitle(title, color='white')
|
|
431
|
+
for axis, ax in enumerate(axes):
|
|
432
|
+
index = volume.shape[axis] // 2
|
|
433
|
+
ax.imshow(np.take(volume, index, axis=axis), cmap='gray', vmin=0, vmax=255)
|
|
434
|
+
if leap_data.mask is not None:
|
|
435
|
+
mask = np.take(leap_data.mask, index, axis=axis)
|
|
436
|
+
ax.imshow(np.ma.masked_where(mask == 0, mask), cmap='tab10', alpha=0.4,
|
|
437
|
+
vmin=0, vmax=max(9, len(leap_data.labels) - 1))
|
|
438
|
+
rows, cols = [i for i in range(3) if i != axis]
|
|
439
|
+
ax.set_aspect(spacing[rows] / spacing[cols])
|
|
440
|
+
ax.set_title(f'axis {axis} / slice {index}', color='white')
|
|
441
|
+
ax.axis('off')
|
|
442
|
+
plt.show()
|
|
443
|
+
|
|
444
|
+
|
|
423
445
|
@run_only_on_non_mapping_mode()
|
|
424
446
|
def plot_image_with_heatmap(leap_data: LeapImageWithHeatmap, title: str) -> None:
|
|
425
447
|
"""
|
|
@@ -474,4 +496,5 @@ plot_switch = {
|
|
|
474
496
|
LeapDataType.ImageWithHeatmap: plot_image_with_heatmap,
|
|
475
497
|
LeapDataType.ImageWithBBox: plot_image_with_b_box,
|
|
476
498
|
LeapDataType.Video: plot_video,
|
|
499
|
+
LeapDataType.Volume: plot_volume,
|
|
477
500
|
}
|
|
@@ -5,7 +5,7 @@ import numpy as np
|
|
|
5
5
|
import numpy.typing as npt
|
|
6
6
|
|
|
7
7
|
from code_loader.contract.visualizer_classes import LeapImage, LeapGraph, LeapHorizontalBar, LeapText, \
|
|
8
|
-
LeapImageMask, LeapTextMask, LeapVideo, LeapAudio
|
|
8
|
+
LeapImageMask, LeapTextMask, LeapVideo, LeapAudio, LeapVolume
|
|
9
9
|
from code_loader.utils import rescale_min_max
|
|
10
10
|
|
|
11
11
|
|
|
@@ -92,6 +92,20 @@ def default_audio_visualizer(audio: npt.NDArray[np.float32],
|
|
|
92
92
|
raise ValueError(f'visual must be 1-D, 2-D, or 3-D, got shape {v.shape}')
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
def default_volume_visualizer(data: npt.NDArray[np.float32]) -> LeapVolume:
|
|
96
|
+
"""Strip the batch axis and a single channel axis (first or last) to get a [D, H, W] volume."""
|
|
97
|
+
volume = data[0]
|
|
98
|
+
if volume.ndim == 4:
|
|
99
|
+
if volume.shape[0] == 1:
|
|
100
|
+
volume = volume[0]
|
|
101
|
+
elif volume.shape[-1] == 1:
|
|
102
|
+
volume = volume[..., 0]
|
|
103
|
+
if volume.ndim != 3:
|
|
104
|
+
raise ValueError('default_volume_visualizer expects [1, D, H, W], [1, 1, D, H, W] or [1, D, H, W, 1], '
|
|
105
|
+
f'got {data.shape}')
|
|
106
|
+
return LeapVolume(volume)
|
|
107
|
+
|
|
108
|
+
|
|
95
109
|
def default_graph_visualizer(data: npt.NDArray[np.float32]) -> LeapGraph:
|
|
96
110
|
return LeapGraph(data[0])
|
|
97
111
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/experiment_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.209 → code_loader-1.0.210}/code_loader/experiment_api/workingspace_config_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|