zea 0.0.6__py3-none-any.whl → 0.0.7__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.
- zea/__init__.py +54 -19
- zea/agent/__init__.py +12 -12
- zea/agent/masks.py +2 -1
- zea/backend/tensorflow/dataloader.py +2 -1
- zea/beamform/beamformer.py +100 -50
- zea/beamform/lens_correction.py +9 -2
- zea/beamform/pfield.py +9 -2
- zea/config.py +34 -25
- zea/data/__init__.py +22 -16
- zea/data/convert/camus.py +2 -1
- zea/data/convert/echonet.py +4 -4
- zea/data/convert/echonetlvh/convert_raw_to_usbmd.py +1 -1
- zea/data/convert/matlab.py +11 -4
- zea/data/data_format.py +31 -30
- zea/data/datasets.py +7 -5
- zea/data/file.py +104 -2
- zea/data/layers.py +3 -3
- zea/datapaths.py +16 -4
- zea/display.py +7 -5
- zea/interface.py +14 -16
- zea/internal/_generate_keras_ops.py +6 -7
- zea/internal/cache.py +2 -49
- zea/internal/config/validation.py +1 -2
- zea/internal/core.py +69 -6
- zea/internal/device.py +6 -2
- zea/internal/dummy_scan.py +330 -0
- zea/internal/operators.py +114 -2
- zea/internal/parameters.py +101 -70
- zea/internal/setup_zea.py +5 -6
- zea/internal/utils.py +282 -0
- zea/io_lib.py +247 -19
- zea/keras_ops.py +74 -4
- zea/log.py +9 -7
- zea/metrics.py +15 -7
- zea/models/__init__.py +30 -20
- zea/models/base.py +30 -14
- zea/models/carotid_segmenter.py +19 -4
- zea/models/diffusion.py +173 -12
- zea/models/echonet.py +22 -8
- zea/models/echonetlvh.py +31 -7
- zea/models/lpips.py +19 -2
- zea/models/lv_segmentation.py +28 -11
- zea/models/preset_utils.py +5 -5
- zea/models/regional_quality.py +30 -10
- zea/models/taesd.py +21 -5
- zea/models/unet.py +15 -1
- zea/ops.py +390 -196
- zea/probes.py +6 -6
- zea/scan.py +109 -49
- zea/simulator.py +24 -21
- zea/tensor_ops.py +406 -302
- zea/tools/hf.py +1 -1
- zea/tools/selection_tool.py +47 -86
- zea/utils.py +92 -480
- zea/visualize.py +177 -39
- {zea-0.0.6.dist-info → zea-0.0.7.dist-info}/METADATA +4 -2
- zea-0.0.7.dist-info/RECORD +114 -0
- zea-0.0.6.dist-info/RECORD +0 -112
- {zea-0.0.6.dist-info → zea-0.0.7.dist-info}/WHEEL +0 -0
- {zea-0.0.6.dist-info → zea-0.0.7.dist-info}/entry_points.txt +0 -0
- {zea-0.0.6.dist-info → zea-0.0.7.dist-info}/licenses/LICENSE +0 -0
zea/probes.py
CHANGED
|
@@ -16,14 +16,14 @@ Example usage
|
|
|
16
16
|
|
|
17
17
|
We can initialize a generic probe with the following code:
|
|
18
18
|
|
|
19
|
-
..
|
|
19
|
+
.. doctest::
|
|
20
20
|
|
|
21
|
-
import
|
|
21
|
+
>>> from zea import Probe
|
|
22
22
|
|
|
23
|
-
probe =
|
|
24
|
-
print(probe.get_parameters())
|
|
25
|
-
|
|
26
|
-
"""
|
|
23
|
+
>>> probe = Probe.from_name("generic")
|
|
24
|
+
>>> print(probe.get_parameters())
|
|
25
|
+
{'probe_geometry': None, 'center_frequency': None, 'sampling_frequency': None, 'xlims': None, 'zlims': None}
|
|
26
|
+
""" # noqa: E501
|
|
27
27
|
|
|
28
28
|
import numpy as np
|
|
29
29
|
|
zea/scan.py
CHANGED
|
@@ -43,38 +43,38 @@ Comparison to ``zea.Config`` and ``zea.Probe``
|
|
|
43
43
|
Example Usage
|
|
44
44
|
^^^^^^^^^^^^^
|
|
45
45
|
|
|
46
|
-
..
|
|
47
|
-
|
|
48
|
-
from zea import Config, Probe, Scan
|
|
49
|
-
|
|
50
|
-
# Initialize Scan from a Probe's parameters
|
|
51
|
-
probe = Probe.from_name("verasonics_l11_4v")
|
|
52
|
-
scan = Scan(**probe.get_parameters(), grid_size_z=256)
|
|
53
|
-
|
|
54
|
-
# Or initialize from a Config object
|
|
55
|
-
config = Config.from_hf("zeahub/configs", "config_picmus_rf.yaml", repo_type="dataset")
|
|
56
|
-
scan = Scan(**config.scan
|
|
57
|
-
|
|
58
|
-
# Or manually specify parameters
|
|
59
|
-
scan = Scan(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
# Access a derived property (computed lazily)
|
|
72
|
-
grid = scan.grid # shape: (grid_size_z, grid_size_x, 3)
|
|
73
|
-
|
|
74
|
-
# Select a subset of transmit events
|
|
75
|
-
scan.set_transmits(3) # Use 3 evenly spaced transmits
|
|
76
|
-
scan.set_transmits([0, 2, 4]) # Use specific transmit indices
|
|
77
|
-
scan.set_transmits("all") # Use all transmits
|
|
46
|
+
.. doctest::
|
|
47
|
+
|
|
48
|
+
>>> from zea import Config, Probe, Scan
|
|
49
|
+
|
|
50
|
+
>>> # Initialize Scan from a Probe's parameters
|
|
51
|
+
>>> probe = Probe.from_name("verasonics_l11_4v")
|
|
52
|
+
>>> scan = Scan(**probe.get_parameters(), grid_size_z=256, n_tx=11)
|
|
53
|
+
|
|
54
|
+
>>> # Or initialize from a Config object
|
|
55
|
+
>>> config = Config.from_hf("zeahub/configs", "config_picmus_rf.yaml", repo_type="dataset")
|
|
56
|
+
>>> scan = Scan(n_tx=11, **config.scan)
|
|
57
|
+
|
|
58
|
+
>>> # Or manually specify parameters
|
|
59
|
+
>>> scan = Scan(
|
|
60
|
+
... grid_size_x=128,
|
|
61
|
+
... grid_size_z=256,
|
|
62
|
+
... xlims=(-0.02, 0.02),
|
|
63
|
+
... zlims=(0.0, 0.06),
|
|
64
|
+
... center_frequency=6.25e6,
|
|
65
|
+
... sound_speed=1540.0,
|
|
66
|
+
... sampling_frequency=25e6,
|
|
67
|
+
... n_el=128,
|
|
68
|
+
... n_tx=11,
|
|
69
|
+
... )
|
|
70
|
+
|
|
71
|
+
>>> # Access a derived property (computed lazily)
|
|
72
|
+
>>> grid = scan.grid # shape: (grid_size_z, grid_size_x, 3)
|
|
73
|
+
|
|
74
|
+
>>> # Select a subset of transmit events
|
|
75
|
+
>>> _ = scan.set_transmits(3) # Use 3 evenly spaced transmits
|
|
76
|
+
>>> _ = scan.set_transmits([0, 2, 4]) # Use specific transmit indices
|
|
77
|
+
>>> _ = scan.set_transmits("all") # Use all transmits
|
|
78
78
|
|
|
79
79
|
"""
|
|
80
80
|
|
|
@@ -83,7 +83,11 @@ from keras import ops
|
|
|
83
83
|
|
|
84
84
|
from zea import log
|
|
85
85
|
from zea.beamform.pfield import compute_pfield
|
|
86
|
-
from zea.beamform.pixelgrid import
|
|
86
|
+
from zea.beamform.pixelgrid import (
|
|
87
|
+
cartesian_pixel_grid,
|
|
88
|
+
check_for_aliasing,
|
|
89
|
+
polar_pixel_grid,
|
|
90
|
+
)
|
|
87
91
|
from zea.display import (
|
|
88
92
|
compute_scan_convert_2d_coordinates,
|
|
89
93
|
compute_scan_convert_3d_coordinates,
|
|
@@ -130,6 +134,15 @@ class Scan(Parameters):
|
|
|
130
134
|
demodulation_frequency (float, optional): Demodulation frequency in Hz.
|
|
131
135
|
time_to_next_transmit (np.ndarray): The time between subsequent
|
|
132
136
|
transmit events of shape (n_frames, n_tx).
|
|
137
|
+
tgc_gain_curve (np.ndarray): Time gain compensation (TGC) curve of shape (n_ax,).
|
|
138
|
+
waveforms_one_way (np.ndarray): The one-way transmit waveforms of shape
|
|
139
|
+
(n_waveforms, n_samples).
|
|
140
|
+
waveforms_two_way (np.ndarray): The two-way transmit waveforms of shape
|
|
141
|
+
(n_waveforms, n_samples).
|
|
142
|
+
tx_waveform_indices (np.ndarray): Indices of the waveform used for each
|
|
143
|
+
transmit event of shape (n_tx,).
|
|
144
|
+
t_peak (np.ndarray, optional): The time of the peak of the pulse of every transmit waveform
|
|
145
|
+
of shape (n_waveforms,).
|
|
133
146
|
pixels_per_wavelength (int, optional): Number of pixels per wavelength.
|
|
134
147
|
Defaults to 4.
|
|
135
148
|
element_width (float, optional): Width of each transducer element in meters.
|
|
@@ -160,6 +173,7 @@ class Scan(Parameters):
|
|
|
160
173
|
Can be "cartesian" or "polar". Defaults to "cartesian".
|
|
161
174
|
dynamic_range (tuple, optional): Dynamic range for image display.
|
|
162
175
|
Defined in dB as (min_dB, max_dB). Defaults to (-60, 0).
|
|
176
|
+
|
|
163
177
|
"""
|
|
164
178
|
|
|
165
179
|
VALID_PARAMS = {
|
|
@@ -200,6 +214,11 @@ class Scan(Parameters):
|
|
|
200
214
|
"focus_distances": {"type": np.ndarray},
|
|
201
215
|
"initial_times": {"type": np.ndarray},
|
|
202
216
|
"time_to_next_transmit": {"type": np.ndarray},
|
|
217
|
+
"tgc_gain_curve": {"type": np.ndarray},
|
|
218
|
+
"waveforms_one_way": {"type": np.ndarray},
|
|
219
|
+
"waveforms_two_way": {"type": np.ndarray},
|
|
220
|
+
"tx_waveform_indices": {"type": np.ndarray},
|
|
221
|
+
"t_peak": {"type": np.ndarray},
|
|
203
222
|
# scan conversion parameters
|
|
204
223
|
"theta_range": {"type": (tuple, list)},
|
|
205
224
|
"phi_range": {"type": (tuple, list)},
|
|
@@ -209,8 +228,9 @@ class Scan(Parameters):
|
|
|
209
228
|
}
|
|
210
229
|
|
|
211
230
|
def __init__(self, **kwargs):
|
|
212
|
-
#
|
|
213
|
-
selected_transmits_input = kwargs.
|
|
231
|
+
# Ensure that selected_transmits is present and set to None by default
|
|
232
|
+
selected_transmits_input = kwargs.get("selected_transmits", None)
|
|
233
|
+
kwargs["selected_transmits"] = None
|
|
214
234
|
|
|
215
235
|
# Initialize parent class
|
|
216
236
|
super().__init__(**kwargs)
|
|
@@ -240,7 +260,10 @@ class Scan(Parameters):
|
|
|
240
260
|
)
|
|
241
261
|
elif self.grid_type == "cartesian":
|
|
242
262
|
return cartesian_pixel_grid(
|
|
243
|
-
self.xlims,
|
|
263
|
+
self.xlims,
|
|
264
|
+
self.zlims,
|
|
265
|
+
grid_size_z=self.grid_size_z,
|
|
266
|
+
grid_size_x=self.grid_size_x,
|
|
244
267
|
)
|
|
245
268
|
else:
|
|
246
269
|
raise ValueError(
|
|
@@ -301,7 +324,10 @@ class Scan(Parameters):
|
|
|
301
324
|
radius * np.cos(-np.pi / 2 + self.polar_limits[1]),
|
|
302
325
|
)
|
|
303
326
|
xlims_plane = (self.probe_geometry[0, 0], self.probe_geometry[-1, 0])
|
|
304
|
-
xlims =
|
|
327
|
+
xlims = (
|
|
328
|
+
min(xlims_polar[0], xlims_plane[0]),
|
|
329
|
+
max(xlims_polar[1], xlims_plane[1]),
|
|
330
|
+
)
|
|
305
331
|
return xlims
|
|
306
332
|
|
|
307
333
|
@cache_with_dependencies("sound_speed", "sampling_frequency", "n_ax")
|
|
@@ -344,6 +370,11 @@ class Scan(Parameters):
|
|
|
344
370
|
"""The total number of transmits in the full dataset."""
|
|
345
371
|
return self._params["n_tx"]
|
|
346
372
|
|
|
373
|
+
@property
|
|
374
|
+
def n_tx_selected(self):
|
|
375
|
+
"""The number of currently selected transmits."""
|
|
376
|
+
return len(self.selected_transmits)
|
|
377
|
+
|
|
347
378
|
@cache_with_dependencies("selected_transmits")
|
|
348
379
|
def n_tx(self):
|
|
349
380
|
"""The number of currently selected transmits."""
|
|
@@ -387,14 +418,12 @@ class Scan(Parameters):
|
|
|
387
418
|
if selection is None or selection == "all":
|
|
388
419
|
self._selected_transmits = None
|
|
389
420
|
self._invalidate("selected_transmits")
|
|
390
|
-
self._invalidate_dependents("selected_transmits")
|
|
391
421
|
return self
|
|
392
422
|
|
|
393
423
|
# Handle "center" - use center transmit
|
|
394
424
|
if selection == "center":
|
|
395
425
|
self._selected_transmits = [n_tx_total // 2]
|
|
396
426
|
self._invalidate("selected_transmits")
|
|
397
|
-
self._invalidate_dependents("selected_transmits")
|
|
398
427
|
return self
|
|
399
428
|
|
|
400
429
|
# Handle integer - select evenly spaced transmits
|
|
@@ -416,7 +445,6 @@ class Scan(Parameters):
|
|
|
416
445
|
self._selected_transmits = list(np.rint(tx_indices).astype(int))
|
|
417
446
|
|
|
418
447
|
self._invalidate("selected_transmits")
|
|
419
|
-
self._invalidate_dependents("selected_transmits")
|
|
420
448
|
return self
|
|
421
449
|
|
|
422
450
|
# Handle slice - convert to list of indices
|
|
@@ -436,7 +464,6 @@ class Scan(Parameters):
|
|
|
436
464
|
int(i) for i in selection
|
|
437
465
|
] # Convert numpy integers to Python ints
|
|
438
466
|
self._invalidate("selected_transmits")
|
|
439
|
-
self._invalidate_dependents("selected_transmits")
|
|
440
467
|
return self
|
|
441
468
|
|
|
442
469
|
# Aliasing check
|
|
@@ -481,7 +508,7 @@ class Scan(Parameters):
|
|
|
481
508
|
value = self._params.get("azimuth_angles")
|
|
482
509
|
if value is None:
|
|
483
510
|
log.warning("No azimuth angles provided, using zeros")
|
|
484
|
-
value = np.zeros(self.
|
|
511
|
+
value = np.zeros(self.n_tx_selected)
|
|
485
512
|
|
|
486
513
|
return value[self.selected_transmits]
|
|
487
514
|
|
|
@@ -492,7 +519,7 @@ class Scan(Parameters):
|
|
|
492
519
|
value = self._params.get("t0_delays")
|
|
493
520
|
if value is None:
|
|
494
521
|
log.warning("No transmit delays provided, using zeros")
|
|
495
|
-
return np.zeros((self.
|
|
522
|
+
return np.zeros((self.n_tx_selected, self.n_el))
|
|
496
523
|
|
|
497
524
|
return value[self.selected_transmits]
|
|
498
525
|
|
|
@@ -502,7 +529,7 @@ class Scan(Parameters):
|
|
|
502
529
|
value = self._params.get("tx_apodizations")
|
|
503
530
|
if value is None:
|
|
504
531
|
log.warning("No transmit apodizations provided, using ones")
|
|
505
|
-
value = np.ones((self.
|
|
532
|
+
value = np.ones((self.n_tx_selected, self.n_el))
|
|
506
533
|
|
|
507
534
|
return value[self.selected_transmits]
|
|
508
535
|
|
|
@@ -512,7 +539,7 @@ class Scan(Parameters):
|
|
|
512
539
|
value = self._params.get("focus_distances")
|
|
513
540
|
if value is None:
|
|
514
541
|
log.warning("No focus distances provided, using zeros")
|
|
515
|
-
value = np.zeros(self.
|
|
542
|
+
value = np.zeros(self.n_tx_selected)
|
|
516
543
|
|
|
517
544
|
return value[self.selected_transmits]
|
|
518
545
|
|
|
@@ -522,10 +549,19 @@ class Scan(Parameters):
|
|
|
522
549
|
value = self._params.get("initial_times")
|
|
523
550
|
if value is None:
|
|
524
551
|
log.warning("No initial times provided, using zeros")
|
|
525
|
-
value = np.zeros(self.
|
|
552
|
+
value = np.zeros(self.n_tx_selected)
|
|
526
553
|
|
|
527
554
|
return value[self.selected_transmits]
|
|
528
555
|
|
|
556
|
+
@property
|
|
557
|
+
def t_peak(self):
|
|
558
|
+
"""The time of the peak of the pulse in seconds of shape (n_waveforms,)."""
|
|
559
|
+
t_peak = self._params.get("t_peak")
|
|
560
|
+
if t_peak is None:
|
|
561
|
+
t_peak = np.array([1 / self.center_frequency])
|
|
562
|
+
|
|
563
|
+
return t_peak
|
|
564
|
+
|
|
529
565
|
@cache_with_dependencies("selected_transmits")
|
|
530
566
|
def time_to_next_transmit(self):
|
|
531
567
|
"""The time between subsequent transmit events of shape (n_frames, n_tx)."""
|
|
@@ -536,6 +572,23 @@ class Scan(Parameters):
|
|
|
536
572
|
selected = self.selected_transmits
|
|
537
573
|
return value[:, selected]
|
|
538
574
|
|
|
575
|
+
@cache_with_dependencies("n_ax")
|
|
576
|
+
def tgc_gain_curve(self):
|
|
577
|
+
"""Time gain compensation (TGC) curve of shape (n_ax,)."""
|
|
578
|
+
value = self._params.get("tgc_gain_curve")
|
|
579
|
+
if value is None:
|
|
580
|
+
return np.ones(self.n_ax)
|
|
581
|
+
return value[: self.n_ax]
|
|
582
|
+
|
|
583
|
+
@cache_with_dependencies("selected_transmits")
|
|
584
|
+
def tx_waveform_indices(self):
|
|
585
|
+
"""Indices of the waveform used for each transmit event of shape (n_tx,)."""
|
|
586
|
+
value = self._params.get("tx_waveform_indices")
|
|
587
|
+
if value is None:
|
|
588
|
+
return np.zeros(self.n_tx_selected, dtype=int)
|
|
589
|
+
|
|
590
|
+
return value[self.selected_transmits]
|
|
591
|
+
|
|
539
592
|
@cache_with_dependencies(
|
|
540
593
|
"sound_speed",
|
|
541
594
|
"center_frequency",
|
|
@@ -545,8 +598,9 @@ class Scan(Parameters):
|
|
|
545
598
|
"tx_apodizations",
|
|
546
599
|
"grid",
|
|
547
600
|
"t0_delays",
|
|
601
|
+
"pfield_kwargs",
|
|
548
602
|
)
|
|
549
|
-
def pfield(self):
|
|
603
|
+
def pfield(self) -> np.ndarray:
|
|
550
604
|
"""Compute or return the pressure field (pfield) for weighting."""
|
|
551
605
|
pfield = compute_pfield(
|
|
552
606
|
sound_speed=self.sound_speed,
|
|
@@ -596,7 +650,12 @@ class Scan(Parameters):
|
|
|
596
650
|
return coords
|
|
597
651
|
|
|
598
652
|
@cache_with_dependencies(
|
|
599
|
-
"rho_range",
|
|
653
|
+
"rho_range",
|
|
654
|
+
"theta_range",
|
|
655
|
+
"phi_range",
|
|
656
|
+
"resolution",
|
|
657
|
+
"grid_size_z",
|
|
658
|
+
"grid_size_x",
|
|
600
659
|
)
|
|
601
660
|
def coordinates_3d(self):
|
|
602
661
|
"""The coordinates for scan conversion."""
|
|
@@ -644,6 +703,7 @@ class Scan(Parameters):
|
|
|
644
703
|
"""The width of each transducer element in meters."""
|
|
645
704
|
value = self._params.get("element_width")
|
|
646
705
|
if value is None:
|
|
706
|
+
# assume uniform spacing
|
|
647
707
|
return np.linalg.norm(self.probe_geometry[1] - self.probe_geometry[0])
|
|
648
708
|
return value
|
|
649
709
|
|
|
@@ -651,5 +711,5 @@ class Scan(Parameters):
|
|
|
651
711
|
if key == "selected_transmits":
|
|
652
712
|
# If setting selected_transmits, call set_transmits to handle logic
|
|
653
713
|
self.set_transmits(value)
|
|
654
|
-
return
|
|
714
|
+
return super().__setattr__(key, self.selected_transmits)
|
|
655
715
|
return super().__setattr__(key, value)
|
zea/simulator.py
CHANGED
|
@@ -14,27 +14,30 @@ Example usage
|
|
|
14
14
|
A simple example of simulating RF data with a single scatterer at the center of the probe. For a
|
|
15
15
|
more in depth example see the notebook: :doc:`../notebooks/data/zea_simulation_example`.
|
|
16
16
|
|
|
17
|
-
..
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
.. doctest::
|
|
18
|
+
|
|
19
|
+
>>> from zea.simulator import simulate_rf
|
|
20
|
+
>>> import numpy as np
|
|
21
|
+
|
|
22
|
+
>>> raw_data = simulate_rf(
|
|
23
|
+
... scatterer_positions=np.array([[0, 0, 20e-3]]),
|
|
24
|
+
... scatterer_magnitudes=np.array([1.0]),
|
|
25
|
+
... probe_geometry=np.stack(
|
|
26
|
+
... [np.linspace(-20e-3, 20e-3, 64), np.zeros(64), np.zeros(64)], axis=-1
|
|
27
|
+
... ),
|
|
28
|
+
... apply_lens_correction=True,
|
|
29
|
+
... lens_thickness=1e-3,
|
|
30
|
+
... lens_sound_speed=1000,
|
|
31
|
+
... sound_speed=1540,
|
|
32
|
+
... n_ax=1024,
|
|
33
|
+
... center_frequency=5e6,
|
|
34
|
+
... sampling_frequency=20e6,
|
|
35
|
+
... t0_delays=np.zeros((1, 64)),
|
|
36
|
+
... initial_times=np.zeros(1),
|
|
37
|
+
... element_width=0.2e-3,
|
|
38
|
+
... attenuation_coef=0.5,
|
|
39
|
+
... tx_apodizations=np.ones((1, 64)),
|
|
40
|
+
... )
|
|
38
41
|
|
|
39
42
|
"""
|
|
40
43
|
|