zea 0.0.7__py3-none-any.whl → 0.0.9__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.
Files changed (64) hide show
  1. zea/__init__.py +3 -3
  2. zea/agent/masks.py +2 -2
  3. zea/agent/selection.py +3 -3
  4. zea/backend/__init__.py +1 -1
  5. zea/backend/tensorflow/dataloader.py +1 -5
  6. zea/beamform/beamformer.py +4 -2
  7. zea/beamform/pfield.py +2 -2
  8. zea/beamform/pixelgrid.py +1 -1
  9. zea/data/__init__.py +0 -9
  10. zea/data/augmentations.py +222 -29
  11. zea/data/convert/__init__.py +1 -6
  12. zea/data/convert/__main__.py +164 -0
  13. zea/data/convert/camus.py +106 -40
  14. zea/data/convert/echonet.py +184 -83
  15. zea/data/convert/echonetlvh/README.md +2 -3
  16. zea/data/convert/echonetlvh/{convert_raw_to_usbmd.py → __init__.py} +174 -103
  17. zea/data/convert/echonetlvh/manual_rejections.txt +73 -0
  18. zea/data/convert/echonetlvh/precompute_crop.py +43 -64
  19. zea/data/convert/picmus.py +37 -40
  20. zea/data/convert/utils.py +86 -0
  21. zea/data/convert/verasonics.py +1247 -0
  22. zea/data/data_format.py +124 -6
  23. zea/data/dataloader.py +12 -7
  24. zea/data/datasets.py +109 -70
  25. zea/data/file.py +119 -82
  26. zea/data/file_operations.py +496 -0
  27. zea/data/preset_utils.py +2 -2
  28. zea/display.py +8 -9
  29. zea/doppler.py +5 -5
  30. zea/func/__init__.py +109 -0
  31. zea/{tensor_ops.py → func/tensor.py} +113 -69
  32. zea/func/ultrasound.py +500 -0
  33. zea/internal/_generate_keras_ops.py +5 -5
  34. zea/internal/checks.py +6 -12
  35. zea/internal/operators.py +4 -0
  36. zea/io_lib.py +108 -160
  37. zea/metrics.py +6 -5
  38. zea/models/__init__.py +1 -1
  39. zea/models/diffusion.py +63 -12
  40. zea/models/echonetlvh.py +1 -1
  41. zea/models/gmm.py +1 -1
  42. zea/models/lv_segmentation.py +2 -0
  43. zea/ops/__init__.py +188 -0
  44. zea/ops/base.py +442 -0
  45. zea/{keras_ops.py → ops/keras_ops.py} +2 -2
  46. zea/ops/pipeline.py +1472 -0
  47. zea/ops/tensor.py +356 -0
  48. zea/ops/ultrasound.py +890 -0
  49. zea/probes.py +2 -10
  50. zea/scan.py +35 -28
  51. zea/tools/fit_scan_cone.py +90 -160
  52. zea/tools/selection_tool.py +1 -1
  53. zea/tracking/__init__.py +16 -0
  54. zea/tracking/base.py +94 -0
  55. zea/tracking/lucas_kanade.py +474 -0
  56. zea/tracking/segmentation.py +110 -0
  57. zea/utils.py +11 -2
  58. {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/METADATA +5 -1
  59. {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/RECORD +62 -48
  60. zea/data/convert/matlab.py +0 -1237
  61. zea/ops.py +0 -3294
  62. {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/WHEEL +0 -0
  63. {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/entry_points.txt +0 -0
  64. {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/licenses/LICENSE +0 -0
zea/data/file.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import enum
4
4
  from pathlib import Path
5
- from typing import List
5
+ from typing import List, Tuple, Union
6
6
 
7
7
  import h5py
8
8
  import numpy as np
@@ -16,6 +16,7 @@ from zea.internal.checks import (
16
16
  _REQUIRED_SCAN_KEYS,
17
17
  get_check,
18
18
  )
19
+ from zea.internal.core import DataTypes
19
20
  from zea.internal.utils import reduce_to_signature
20
21
  from zea.probes import Probe
21
22
  from zea.scan import Scan
@@ -42,13 +43,16 @@ class File(h5py.File):
42
43
  **kwargs: Additional keyword arguments to pass to h5py.File.
43
44
  """
44
45
 
46
+ # Resolve huggingface path
45
47
  if str(name).startswith(HF_PREFIX):
46
48
  name = _hf_resolve_path(str(name))
47
49
 
50
+ # Disable locking for read mode by default
48
51
  if "locking" not in kwargs and "mode" in kwargs and kwargs["mode"] == "r":
49
52
  # If the file is opened in read mode, disable locking
50
53
  kwargs["locking"] = False
51
54
 
55
+ # Initialize the h5py.File
52
56
  super().__init__(name, *args, **kwargs)
53
57
 
54
58
  @property
@@ -111,64 +115,10 @@ class File(h5py.File):
111
115
  else:
112
116
  raise NotImplementedError
113
117
 
114
- @staticmethod
115
- def _prepare_indices(indices):
116
- """Prepare the indices for loading data from hdf5 files.
117
- Options:
118
- - str("all")
119
- - int -> single frame
120
- - list of ints -> indexes first axis (frames)
121
- - list of list, ranges or slices -> indexes multiple axes
122
-
123
- Returns:
124
- indices (tuple): A tuple of indices / slices to use for indexing.
125
- """
126
- _value_error_msg = (
127
- f"Invalid value for indices: {indices}. "
128
- "Indices can be a 'all', int or a List[int, tuple, list, slice, range]."
129
- )
130
-
131
- # Check all options that only index the first axis
132
- if isinstance(indices, str):
133
- if indices == "all":
134
- return slice(None)
135
- else:
136
- raise ValueError(_value_error_msg)
137
-
138
- if isinstance(indices, range):
139
- return list(indices)
140
-
141
- if isinstance(indices, (int, slice, np.integer)):
142
- return indices
143
-
144
- # At this point, indices should be a list or tuple
145
- assert isinstance(indices, (list, tuple, np.ndarray)), _value_error_msg
146
-
147
- assert all(
148
- isinstance(idx, (list, tuple, int, slice, range, np.ndarray, np.integer))
149
- for idx in indices
150
- ), _value_error_msg
151
-
152
- # Convert ranges to lists
153
- processed_indices = [list(idx) if isinstance(idx, range) else idx for idx in indices]
154
-
155
- # Check if items are list-like and cast to tuple (needed for hdf5)
156
- if any(isinstance(idx, (list, tuple, slice)) for idx in processed_indices):
157
- processed_indices = tuple(processed_indices)
158
-
159
- return processed_indices
160
-
161
118
  def load_scan(self, event=None):
162
119
  """Alias for get_scan_parameters."""
163
120
  return self.get_scan_parameters(event)
164
121
 
165
- @staticmethod
166
- def check_data(data, key):
167
- """Check the data for a given key. For example, will check if the shape matches
168
- the data type (such as raw_data, ...)"""
169
- if key in _DATA_TYPES:
170
- get_check(key)(data, with_batch_dim=None)
171
-
172
122
  def format_key(self, key):
173
123
  """Format the key to match the data type."""
174
124
  # TODO: support events
@@ -206,7 +156,7 @@ class File(h5py.File):
206
156
  def load_transmits(self, key, selected_transmits):
207
157
  """Load raw_data or aligned_data for a given list of transmits.
208
158
  Args:
209
- data_type (str): The type of data to load. Options are 'raw_data' and 'aligned_data'.
159
+ key (str): The type of data to load. Options are 'raw_data' and 'aligned_data'.
210
160
  selected_transmits (list, np.ndarray): The transmits to load.
211
161
  """
212
162
  key = self.format_key(key)
@@ -214,23 +164,18 @@ class File(h5py.File):
214
164
  assert data_type in ["raw_data", "aligned_data"], (
215
165
  f"Cannot load transmits for {data_type}. Only raw_data and aligned_data are supported."
216
166
  )
217
- indices = [slice(None), np.array(selected_transmits)]
167
+ # First axis: all frames, second axis: selected transmits
168
+ indices = (slice(None), np.array(selected_transmits))
218
169
  return self.load_data(key, indices)
219
170
 
220
- def load_data(self, data_type, indices: str | int | List[int] = "all"):
171
+ def load_data(
172
+ self,
173
+ data_type,
174
+ indices: Tuple[Union[list, slice, int], ...] | List[int] | int | None = None,
175
+ ):
221
176
  """Load data from the file.
222
177
 
223
- The indices parameter can be used to load a subset of the data. This can be
224
-
225
- - 'all' to load all data
226
-
227
- - an int to load a single frame
228
-
229
- - a list of ints to load specific frames
230
-
231
- - a tuple of lists, ranges or slices to index frames and transmits. Note that
232
- indexing with lists of indices for both axes is not supported. In that case,
233
- try to define one of the axes with a slice.
178
+ .. include:: ../common/file_indexing.rst
234
179
 
235
180
  .. doctest::
236
181
 
@@ -258,13 +203,12 @@ class File(h5py.File):
258
203
  Args:
259
204
  data_type (str): The type of data to load. Options are 'raw_data', 'aligned_data',
260
205
  'beamformed_data', 'envelope_data', 'image' and 'image_sc'.
261
- indices (str, int, list, optional): The indices to load. Defaults to "all" in
262
- which case all frames are loaded. If an int is provided, it will be used
263
- as a single index. If a list is provided, it will be used as a list of
264
- indices.
206
+ indices (optional): The indices to load. Defaults to `None` in
207
+ which case all data is loaded.
265
208
  """
266
209
  key = self.format_key(data_type)
267
- indices = self._prepare_indices(indices)
210
+ if indices is None or (isinstance(indices, str) and indices == "all"):
211
+ indices = slice(None)
268
212
 
269
213
  if self._simple_index(key):
270
214
  data = self[key]
@@ -274,7 +218,6 @@ class File(h5py.File):
274
218
  raise ValueError(
275
219
  f"Invalid indices {indices} for key {key}. {key} has shape {data.shape}."
276
220
  ) from exc
277
- self.check_data(data, key)
278
221
  elif self.events_have_same_shape(key):
279
222
  raise NotImplementedError
280
223
  else:
@@ -345,6 +288,31 @@ class File(h5py.File):
345
288
  else:
346
289
  log.warning("Could not find scan parameters in file.")
347
290
 
291
+ scan_parameters = self._check_focus_distances(scan_parameters)
292
+
293
+ return scan_parameters
294
+
295
+ def _check_focus_distances(self, scan_parameters):
296
+ if "focus_distances" in scan_parameters:
297
+ focus_distances = scan_parameters["focus_distances"]
298
+ # check if focus distances are in wavelengths
299
+ if np.any(np.logical_and(focus_distances >= 1, focus_distances != np.inf)):
300
+ log.warning(
301
+ f"We have detected that focus distances in '{self.path}' are "
302
+ "(probably) stored wavelengths. Please update your file! "
303
+ "Converting to meters automatically for now."
304
+ )
305
+ assert "sound_speed" in scan_parameters, (
306
+ "Cannot convert focus distances from wavelengths to meters "
307
+ "because sound_speed is not defined in the scan parameters."
308
+ )
309
+ assert "center_frequency" in scan_parameters, (
310
+ "Cannot convert focus distances from wavelengths to meters "
311
+ "because center_frequency is not defined in the scan parameters."
312
+ )
313
+ wavelength = scan_parameters["sound_speed"] / scan_parameters["center_frequency"]
314
+ focus_distances = focus_distances * wavelength
315
+ scan_parameters["focus_distances"] = focus_distances
348
316
  return scan_parameters
349
317
 
350
318
  def get_scan_parameters(self, event=None) -> dict:
@@ -425,6 +393,21 @@ class File(h5py.File):
425
393
  ans[key] = self.recursively_load_dict_contents_from_group(path + "/" + key + "/")
426
394
  return ans
427
395
 
396
+ def has_key(self, key: str) -> bool:
397
+ """Check if the file has a specific key.
398
+
399
+ Args:
400
+ key (str): The key to check.
401
+
402
+ Returns:
403
+ bool: True if the key exists, False otherwise.
404
+ """
405
+ try:
406
+ key = self.format_key(key)
407
+ except AssertionError:
408
+ return False
409
+ return True
410
+
428
411
  @classmethod
429
412
  def get_shape(cls, path: str, key: str) -> tuple:
430
413
  """Get the shape of a key in a file.
@@ -490,10 +473,67 @@ class File(h5py.File):
490
473
  _print_hdf5_attrs(self)
491
474
 
492
475
 
476
+ def load_file_all_data_types(
477
+ path,
478
+ indices: Tuple[Union[list, slice, int], ...] | List[int] | int | None = None,
479
+ scan_kwargs: dict = None,
480
+ ):
481
+ """Loads a zea data files (h5py file).
482
+
483
+ Returns all data types together with a scan object containing the parameters
484
+ of the acquisition and a probe object containing the parameters of the probe.
485
+
486
+ Additionally, it can load a specific subset of frames / transmits.
487
+
488
+ .. include:: ../common/file_indexing.rst
489
+
490
+ Args:
491
+ path (str, pathlike): The path to the hdf5 file.
492
+ indices (optional): The indices to load. Defaults to None in
493
+ which case all frames are loaded.
494
+ scan_kwargs (Config, dict, optional): Additional keyword arguments
495
+ to pass to the Scan object. These will override the parameters from the file
496
+ if they are present in the file. Defaults to None.
497
+
498
+ Returns:
499
+ (dict): A dictionary with all data types as keys and the corresponding data as values.
500
+ (Scan): A scan object containing the parameters of the acquisition.
501
+ (Probe): A probe object containing the parameters of the probe.
502
+ """
503
+ # Define the additional keyword parameters from the scan object
504
+ if scan_kwargs is None:
505
+ scan_kwargs = {}
506
+
507
+ data_dict = {}
508
+
509
+ with File(path, mode="r") as file:
510
+ # Load the probe object from the file
511
+ probe = file.probe()
512
+
513
+ for data_type in DataTypes:
514
+ if not file.has_key(data_type.value):
515
+ data_dict[data_type.value] = None
516
+ continue
517
+
518
+ # Load the desired frames from the file
519
+ data_dict[data_type.value] = file.load_data(data_type.value, indices=indices)
520
+
521
+ # extract transmits from indices
522
+ # we only have to do this when the data has a n_tx dimension
523
+ # in that case we also have update scan parameters to match
524
+ # the number of selected transmits
525
+ if isinstance(indices, tuple) and len(indices) > 1:
526
+ scan_kwargs["selected_transmits"] = indices[1]
527
+
528
+ scan = file.scan(**scan_kwargs)
529
+
530
+ return data_dict, scan, probe
531
+
532
+
493
533
  def load_file(
494
534
  path,
495
535
  data_type="raw_data",
496
- indices: str | int | List[int] = "all",
536
+ indices: Tuple[Union[list, slice, int], ...] | List[int] | int | None = None,
497
537
  scan_kwargs: dict = None,
498
538
  ):
499
539
  """Loads a zea data files (h5py file).
@@ -503,17 +543,15 @@ def load_file(
503
543
 
504
544
  Additionally, it can load a specific subset of frames / transmits.
505
545
 
506
- # TODO: add support for event
546
+ .. include:: ../common/file_indexing.rst
507
547
 
508
548
  Args:
509
549
  path (str, pathlike): The path to the hdf5 file.
510
550
  data_type (str, optional): The type of data to load. Defaults to
511
551
  'raw_data'. Other options are 'aligned_data', 'beamformed_data',
512
552
  'envelope_data', 'image' and 'image_sc'.
513
- indices (str, int, list, optional): The indices to load. Defaults to "all" in
514
- which case all frames are loaded. If an int is provided, it will be used
515
- as a single index. If a list is provided, it will be used as a list of
516
- indices.
553
+ indices (optional): The indices to load. Defaults to None in
554
+ which case all frames are loaded.
517
555
  scan_kwargs (Config, dict, optional): Additional keyword arguments
518
556
  to pass to the Scan object. These will override the parameters from the file
519
557
  if they are present in the file. Defaults to None.
@@ -539,7 +577,6 @@ def load_file(
539
577
  # in that case we also have update scan parameters to match
540
578
  # the number of selected transmits
541
579
  if data_type in ["raw_data", "aligned_data"]:
542
- indices = File._prepare_indices(indices)
543
580
  if isinstance(indices, tuple) and len(indices) > 1:
544
581
  scan_kwargs["selected_transmits"] = indices[1]
545
582