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/probes.py CHANGED
@@ -22,7 +22,7 @@ We can initialize a generic probe with the following code:
22
22
 
23
23
  >>> probe = Probe.from_name("generic")
24
24
  >>> print(probe.get_parameters())
25
- {'probe_geometry': None, 'center_frequency': None, 'sampling_frequency': None, 'xlims': None, 'zlims': None}
25
+ {'probe_geometry': None, 'center_frequency': None, 'sampling_frequency': None, 'xlims': None, 'zlims': None, 'n_el': None}
26
26
  """ # noqa: E501
27
27
 
28
28
  import numpy as np
@@ -114,6 +114,7 @@ class Probe(Object):
114
114
  "sampling_frequency": self.sampling_frequency,
115
115
  "xlims": self.xlims,
116
116
  "zlims": self.zlims,
117
+ "n_el": self.n_el,
117
118
  }
118
119
 
119
120
  @classmethod
@@ -231,12 +232,3 @@ class Esaote_sll1543(Probe):
231
232
  bandwidth_MHz=bandwidth_MHz,
232
233
  probe_type="linear",
233
234
  )
234
-
235
- def get_parameters(self):
236
- return {
237
- "probe_geometry": self.probe_geometry,
238
- "center_frequency": self.center_frequency,
239
- "sampling_frequency": self.sampling_frequency,
240
- "xlims": self.xlims,
241
- "zlims": self.zlims,
242
- }
zea/scan.py CHANGED
@@ -153,13 +153,11 @@ class Scan(Parameters):
153
153
  apply_lens_correction (bool, optional): Whether to apply lens correction to
154
154
  delays. Defaults to False.
155
155
  lens_thickness (float, optional): Thickness of the lens in meters.
156
- Defaults to None.
157
156
  f_number (float, optional): F-number of the transducer. Defaults to 1.0.
158
157
  theta_range (tuple, optional): Range of theta angles for 3D imaging.
159
158
  phi_range (tuple, optional): Range of phi angles for 3D imaging.
160
159
  rho_range (tuple, optional): Range of rho (radial) distances for 3D imaging.
161
160
  fill_value (float, optional): Value to use for out-of-bounds pixels.
162
- Defaults to 0.0.
163
161
  attenuation_coef (float, optional): Attenuation coefficient in dB/(MHz*cm).
164
162
  Defaults to 0.0.
165
163
  selected_transmits (None, str, int, list, slice, or np.ndarray, optional):
@@ -215,15 +213,15 @@ class Scan(Parameters):
215
213
  "initial_times": {"type": np.ndarray},
216
214
  "time_to_next_transmit": {"type": np.ndarray},
217
215
  "tgc_gain_curve": {"type": np.ndarray},
218
- "waveforms_one_way": {"type": np.ndarray},
219
- "waveforms_two_way": {"type": np.ndarray},
216
+ "waveforms_one_way": {"type": np.ndarray, "default": None},
217
+ "waveforms_two_way": {"type": np.ndarray, "default": None},
220
218
  "tx_waveform_indices": {"type": np.ndarray},
221
219
  "t_peak": {"type": np.ndarray},
222
220
  # scan conversion parameters
223
221
  "theta_range": {"type": (tuple, list)},
224
222
  "phi_range": {"type": (tuple, list)},
225
223
  "rho_range": {"type": (tuple, list)},
226
- "fill_value": {"type": float, "default": 0.0},
224
+ "fill_value": {"type": float},
227
225
  "resolution": {"type": float, "default": None},
228
226
  }
229
227
 
@@ -242,16 +240,7 @@ class Scan(Parameters):
242
240
  if selected_transmits_input is not None:
243
241
  self.set_transmits(selected_transmits_input)
244
242
 
245
- @cache_with_dependencies(
246
- "xlims",
247
- "zlims",
248
- "grid_size_x",
249
- "grid_size_z",
250
- "sound_speed",
251
- "center_frequency",
252
- "pixels_per_wavelength",
253
- "grid_type",
254
- )
243
+ @cache_with_dependencies("xlims", "zlims", "grid_size_x", "grid_size_z", "grid_type")
255
244
  def grid(self):
256
245
  """The beamforming grid of shape (grid_size_z, grid_size_x, 3)."""
257
246
  if self.grid_type == "polar":
@@ -271,12 +260,7 @@ class Scan(Parameters):
271
260
  "'cartesian' and 'polar'."
272
261
  )
273
262
 
274
- @cache_with_dependencies(
275
- "xlims",
276
- "wavelength",
277
- "pixels_per_wavelength",
278
- "grid_type",
279
- )
263
+ @cache_with_dependencies("xlims", "wavelength", "pixels_per_wavelength")
280
264
  def grid_size_x(self):
281
265
  """Grid width in pixels. For a cartesian grid, this is the lateral (x) pixels in the grid,
282
266
  set to prevent aliasing if not provided. For a polar grid, this can be thought of as
@@ -311,7 +295,7 @@ class Scan(Parameters):
311
295
  """Calculate the wavelength based on sound speed and center frequency."""
312
296
  return self.sound_speed / self.center_frequency
313
297
 
314
- @cache_with_dependencies("zlims", "grid_type", "polar_limits", "probe_geometry")
298
+ @cache_with_dependencies("zlims", "polar_limits", "probe_geometry")
315
299
  def xlims(self):
316
300
  """The x-limits of the beamforming grid [m]. If not explicitly set, it is computed based
317
301
  on the polar limits and probe geometry.
@@ -508,7 +492,7 @@ class Scan(Parameters):
508
492
  value = self._params.get("azimuth_angles")
509
493
  if value is None:
510
494
  log.warning("No azimuth angles provided, using zeros")
511
- value = np.zeros(self.n_tx_selected)
495
+ return np.zeros(self.n_tx_selected)
512
496
 
513
497
  return value[self.selected_transmits]
514
498
 
@@ -529,7 +513,7 @@ class Scan(Parameters):
529
513
  value = self._params.get("tx_apodizations")
530
514
  if value is None:
531
515
  log.warning("No transmit apodizations provided, using ones")
532
- value = np.ones((self.n_tx_selected, self.n_el))
516
+ return np.ones((self.n_tx_selected, self.n_el))
533
517
 
534
518
  return value[self.selected_transmits]
535
519
 
@@ -539,7 +523,7 @@ class Scan(Parameters):
539
523
  value = self._params.get("focus_distances")
540
524
  if value is None:
541
525
  log.warning("No focus distances provided, using zeros")
542
- value = np.zeros(self.n_tx_selected)
526
+ return np.zeros(self.n_tx_selected)
543
527
 
544
528
  return value[self.selected_transmits]
545
529
 
@@ -549,16 +533,28 @@ class Scan(Parameters):
549
533
  value = self._params.get("initial_times")
550
534
  if value is None:
551
535
  log.warning("No initial times provided, using zeros")
552
- value = np.zeros(self.n_tx_selected)
536
+ return np.zeros(self.n_tx_selected)
553
537
 
554
538
  return value[self.selected_transmits]
555
539
 
540
+ @property
541
+ def n_waveforms(self):
542
+ """The number of unique transmit waveforms."""
543
+
544
+ if self.waveforms_one_way is not None:
545
+ return self.waveforms_one_way.shape[0]
546
+
547
+ if self.waveforms_two_way is not None:
548
+ return self.waveforms_two_way.shape[0]
549
+
550
+ return 1
551
+
556
552
  @property
557
553
  def t_peak(self):
558
554
  """The time of the peak of the pulse in seconds of shape (n_waveforms,)."""
559
555
  t_peak = self._params.get("t_peak")
560
556
  if t_peak is None:
561
- t_peak = np.array([1 / self.center_frequency])
557
+ t_peak = np.array([1 / self.center_frequency] * self.n_waveforms)
562
558
 
563
559
  return t_peak
564
560
 
@@ -617,7 +613,7 @@ class Scan(Parameters):
617
613
 
618
614
  @cache_with_dependencies("pfield")
619
615
  def flat_pfield(self):
620
- """Flattened pfield for weighting."""
616
+ """Flattened pfield for weighting of shape (n_pix, n_tx)."""
621
617
  return self.pfield.reshape(self.n_tx, -1).swapaxes(0, 1)
622
618
 
623
619
  @cache_with_dependencies("zlims")
@@ -674,6 +670,17 @@ class Scan(Parameters):
674
670
  otherwise 2D."""
675
671
  return self.coordinates_3d if getattr(self, "phi_range", None) else self.coordinates_2d
676
672
 
673
+ @property
674
+ def pulse_repetition_frequency(self):
675
+ """The pulse repetition frequency (PRF) [Hz]. Assumes a constant PRF."""
676
+ if self.time_to_next_transmit is None:
677
+ log.warning("Time to next transmit is not set, cannot compute PRF")
678
+ return None
679
+
680
+ pulse_repetition_interval = np.mean(self.time_to_next_transmit)
681
+
682
+ return 1 / pulse_repetition_interval
683
+
677
684
  @cache_with_dependencies("time_to_next_transmit")
678
685
  def frames_per_second(self):
679
686
  """The number of frames per second [Hz]. Assumes a constant frame rate.