code-loader 1.0.210__tar.gz → 1.0.211__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.
Files changed (37) hide show
  1. {code_loader-1.0.210 → code_loader-1.0.211}/PKG-INFO +1 -1
  2. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/datasetclasses.py +2 -2
  3. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/enums.py +1 -1
  4. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/responsedataclasses.py +29 -0
  5. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/visualizer_classes.py +45 -44
  6. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/inner_leap_binder/leapbinder_decorators.py +3 -3
  7. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/plot_functions/plot_functions.py +22 -24
  8. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/visualizers/default_visualizers.py +1 -15
  9. {code_loader-1.0.210 → code_loader-1.0.211}/pyproject.toml +1 -1
  10. {code_loader-1.0.210 → code_loader-1.0.211}/LICENSE +0 -0
  11. {code_loader-1.0.210 → code_loader-1.0.211}/README.md +0 -0
  12. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/__init__.py +0 -0
  13. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/__init__.py +0 -0
  14. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/exceptions.py +0 -0
  15. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/mapping.py +0 -0
  16. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/contract/sim_config.py +0 -0
  17. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/default_losses.py +0 -0
  18. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/default_metrics.py +0 -0
  19. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/__init__.py +0 -0
  20. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/api.py +0 -0
  21. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/cli_config_utils.py +0 -0
  22. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/client.py +0 -0
  23. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/epoch.py +0 -0
  24. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/experiment.py +0 -0
  25. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/experiment_context.py +0 -0
  26. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/types.py +0 -0
  27. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/utils.py +0 -0
  28. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  29. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/inner_leap_binder/__init__.py +0 -0
  30. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/inner_leap_binder/leapbinder.py +0 -0
  31. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/leaploader.py +0 -0
  32. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/leaploaderbase.py +0 -0
  33. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/mixpanel_tracker.py +0 -0
  34. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/plot_functions/__init__.py +0 -0
  35. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/plot_functions/visualize.py +0 -0
  36. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/utils.py +0 -0
  37. {code_loader-1.0.210 → code_loader-1.0.211}/code_loader/visualizers/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.210
3
+ Version: 1.0.211
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -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, LeapVolume
11
+ LeapTextMask, LeapImageMask, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapPointCloud
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, LeapVolume]
216
+ LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapPointCloud]
217
217
 
218
218
  CustomCallableInterface = Callable[..., Any]
219
219
 
@@ -29,7 +29,7 @@ class LeapDataType(Enum):
29
29
  ImageWithHeatmap = 'ImageWithHeatmap'
30
30
  Video = 'Video'
31
31
  Audio = 'Audio'
32
- Volume = 'Volume'
32
+ PointCloud = 'PointCloud'
33
33
 
34
34
 
35
35
  class MetricDirection(Enum):
@@ -131,6 +131,35 @@ class BoundingBox:
131
131
  metadata: Optional[Dict[str, Union[str, int, float]]] = None
132
132
 
133
133
 
134
+ @dataclass
135
+ class BoundingBox3D:
136
+ """
137
+ Represents an oriented 3D bounding box in a point cloud's coordinate frame.
138
+
139
+ Attributes:
140
+ x, y, z (float): The center of the box.
141
+ width (float): Extent along the box's local y axis.
142
+ length (float): Extent along the box's local x axis (the heading axis).
143
+ height (float): Extent along the box's local z axis.
144
+ yaw (float): Rotation about +z in radians, counter-clockwise from +x. The +length/2 face is the front.
145
+ label (str): The class name of the box.
146
+ confidence (float): Prediction score, typically in [0, 1]; 1.0 for ground truth.
147
+ metadata (Optional[Dict[str, Union[str, int, float]]]): Optional extra fields shown with the box.
148
+
149
+ Corners are the local points (±length/2, ±width/2, ±height/2) rotated by yaw about z, then translated to (x, y, z).
150
+ """
151
+ x: float
152
+ y: float
153
+ z: float
154
+ width: float
155
+ length: float
156
+ height: float
157
+ yaw: float
158
+ label: str
159
+ confidence: float = 1.0
160
+ metadata: Optional[Dict[str, Union[str, int, float]]] = None
161
+
162
+
134
163
 
135
164
  @dataclass
136
165
  class LeapAnalysisConfiguration:
@@ -5,7 +5,7 @@ import numpy.typing as npt
5
5
  from dataclasses import dataclass
6
6
 
7
7
  from code_loader.contract.enums import LeapDataType
8
- from code_loader.contract.responsedataclasses import BoundingBox
8
+ from code_loader.contract.responsedataclasses import BoundingBox, BoundingBox3D
9
9
 
10
10
 
11
11
  class LeapValidationError(Exception):
@@ -378,57 +378,58 @@ class LeapAudio:
378
378
 
379
379
 
380
380
  @dataclass
381
- class LeapVolume:
381
+ class LeapPointCloud:
382
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.
383
+ Visualizer representing a set of 3D points for Tensorleap: a LiDAR sweep, a depth-camera frame, a mesh's
384
+ vertices, or any (N, 3) point set. The UI renders it with an orbit camera; the engine stores the points as a
385
+ raw float32 blob.
385
386
 
386
387
  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.
388
+ points (npt.NDArray[np.float32]): The points, shaped [N, 3] as x, y, z in any consistent frame and unit.
389
+ intensity (Optional[npt.NDArray[np.float32]]): Optional per-point scalar shaped [N]: the LiDAR return
390
+ intensity in most formats, but any per-point value (reflectance, depth, an error) fits. The UI offers it as a
391
+ color mode.
392
+ bounding_boxes (Optional[List[BoundingBox3D]]): Optional oriented 3D boxes in the same frame as `points`.
393
+ An empty list is valid.
394
+ type (LeapDataType): The data type, default is LeapDataType.PointCloud.
396
395
 
397
396
  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))
397
+ points = (np.random.rand(1000, 3) * 50).astype(np.float32)
398
+ box = BoundingBox3D(x=10, y=0, z=1, width=2, length=4.5, height=1.8, yaw=0.3, label="car", confidence=0.9)
399
+ leap_point_cloud = LeapPointCloud(points=points, intensity=points[:, 2].copy(), bounding_boxes=[box])
401
400
  """
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
401
+ points: npt.NDArray[np.float32]
402
+ intensity: Optional[npt.NDArray[np.float32]] = None
403
+ bounding_boxes: Optional[List[BoundingBox3D]] = None
404
+ type: LeapDataType = LeapDataType.PointCloud
407
405
 
408
406
  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):
407
+ validate_type(self.type, LeapDataType.PointCloud)
408
+ validate_type(type(self.points), np.ndarray)
409
+ validate_type(self.points.dtype, np.float32)
410
+ validate_type(len(self.points.shape), 2, 'Point cloud points must be of shape 2 [N, 3]')
411
+ if self.points.shape[1] != 3 or self.points.shape[0] < 1:
412
+ raise LeapValidationError(
413
+ f'Point cloud points must be of shape [N, 3] with N >= 1, got {self.points.shape}')
414
+ if not np.isfinite(self.points).all():
415
+ raise LeapValidationError('Point cloud points must be finite (no NaN/Inf)')
416
+ if self.intensity is not None:
417
+ validate_type(type(self.intensity), np.ndarray)
418
+ validate_type(self.intensity.dtype, np.float32)
419
+ if self.intensity.shape != (self.points.shape[0],):
427
420
  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}')
421
+ f'Point cloud intensity shape {self.intensity.shape} must be ({self.points.shape[0]},)')
422
+ if not np.isfinite(self.intensity).all():
423
+ raise LeapValidationError('Point cloud intensity must be finite (no NaN/Inf)')
424
+ if self.bounding_boxes is not None:
425
+ validate_type(type(self.bounding_boxes), list)
426
+ for box in self.bounding_boxes:
427
+ validate_type(type(box), BoundingBox3D)
428
+ numbers = (box.x, box.y, box.z, box.width, box.length, box.height, box.yaw, box.confidence)
429
+ if not all(np.isfinite(float(v)) for v in numbers):
430
+ raise LeapValidationError(f'BoundingBox3D "{box.label}" has a non-finite field')
431
+ if min(box.width, box.length, box.height) <= 0:
432
+ raise LeapValidationError(f'BoundingBox3D "{box.label}" extents must be positive')
432
433
 
433
434
 
434
435
  map_leap_data_type_to_visualizer_class = {
@@ -442,5 +443,5 @@ map_leap_data_type_to_visualizer_class = {
442
443
  LeapDataType.ImageWithBBox.value: LeapImageWithBBox,
443
444
  LeapDataType.ImageWithHeatmap.value: LeapImageWithHeatmap,
444
445
  LeapDataType.Audio.value: LeapAudio,
445
- LeapDataType.Volume.value: LeapVolume,
446
+ LeapDataType.PointCloud.value: LeapPointCloud,
446
447
  }
@@ -34,8 +34,8 @@ 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, LeapVolume, LeapValidationError, \
38
- map_leap_data_type_to_visualizer_class
37
+ LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapPointCloud, \
38
+ LeapValidationError, 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
41
41
  from code_loader.mixpanel_tracker import clear_integration_events, AnalyticsEvent, emit_integration_event_once
@@ -1594,7 +1594,7 @@ def tensorleap_custom_visualizer(name: str, visualizer_type: LeapDataType,
1594
1594
  LeapDataType.ImageWithHeatmap: LeapImageWithHeatmap,
1595
1595
  LeapDataType.Video: LeapVideo,
1596
1596
  LeapDataType.Audio: LeapAudio,
1597
- LeapDataType.Volume: LeapVolume,
1597
+ LeapDataType.PointCloud: LeapPointCloud
1598
1598
  }
1599
1599
  validate_output_structure(result, func_name=user_function.__name__,
1600
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, LeapVolume
23
+ LeapHorizontalBar, LeapImageMask, LeapTextMask, LeapImageWithHeatmap, LeapVideo, LeapPointCloud
24
24
  from code_loader.utils import rescale_min_max
25
25
 
26
26
 
@@ -420,28 +420,6 @@ 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
-
445
423
  @run_only_on_non_mapping_mode()
446
424
  def plot_image_with_heatmap(leap_data: LeapImageWithHeatmap, title: str) -> None:
447
425
  """
@@ -486,6 +464,26 @@ def plot_image_with_heatmap(leap_data: LeapImageWithHeatmap, title: str) -> None
486
464
 
487
465
 
488
466
 
467
+ @run_only_on_non_mapping_mode()
468
+ def plot_point_cloud(leap_data: LeapPointCloud, title: str) -> None:
469
+ """Bird's-eye view (x right, y up, equal aspect): points colored by height, box footprints as polylines."""
470
+ points = leap_data.points
471
+ fig, ax = plt.subplots(figsize=(7, 7))
472
+ fig.patch.set_facecolor('black')
473
+ ax.set_facecolor('black')
474
+ ax.scatter(points[:, 0], points[:, 1], s=1, c=points[:, 2], cmap='viridis')
475
+ for box in leap_data.bounding_boxes or []:
476
+ c, s = np.cos(box.yaw), np.sin(box.yaw)
477
+ local = np.array([[1, 1], [1, -1], [-1, -1], [-1, 1], [1, 1]], dtype=np.float64) * [box.length / 2, box.width / 2]
478
+ corners = local @ np.array([[c, -s], [s, c]]).T + [box.x, box.y]
479
+ ax.plot(corners[:, 0], corners[:, 1], linewidth=1.5, color='orange')
480
+ ax.text(box.x, box.y, f'{box.label} {box.confidence:.2f}', color='orange', fontsize=6)
481
+ ax.set_aspect('equal')
482
+ ax.set_title(f'{title} ({points.shape[0]} points)', color='white')
483
+ ax.tick_params(colors='white')
484
+ plt.show()
485
+
486
+
489
487
  plot_switch = {
490
488
  LeapDataType.Image: plot_image,
491
489
  LeapDataType.Text: plot_text_with_heatmap,
@@ -496,5 +494,5 @@ plot_switch = {
496
494
  LeapDataType.ImageWithHeatmap: plot_image_with_heatmap,
497
495
  LeapDataType.ImageWithBBox: plot_image_with_b_box,
498
496
  LeapDataType.Video: plot_video,
499
- LeapDataType.Volume: plot_volume,
497
+ LeapDataType.PointCloud: plot_point_cloud,
500
498
  }
@@ -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, LeapVolume
8
+ LeapImageMask, LeapTextMask, LeapVideo, LeapAudio
9
9
  from code_loader.utils import rescale_min_max
10
10
 
11
11
 
@@ -92,20 +92,6 @@ 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
-
109
95
  def default_graph_visualizer(data: npt.NDArray[np.float32]) -> LeapGraph:
110
96
  return LeapGraph(data[0])
111
97
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
- version = "1.0.210"
3
+ version = "1.0.211"
4
4
  description = ""
5
5
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
6
6
  license = "MIT"
File without changes
File without changes