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.
- zea/__init__.py +3 -3
- zea/agent/masks.py +2 -2
- zea/agent/selection.py +3 -3
- zea/backend/__init__.py +1 -1
- zea/backend/tensorflow/dataloader.py +1 -5
- zea/beamform/beamformer.py +4 -2
- zea/beamform/pfield.py +2 -2
- zea/beamform/pixelgrid.py +1 -1
- zea/data/__init__.py +0 -9
- zea/data/augmentations.py +222 -29
- zea/data/convert/__init__.py +1 -6
- zea/data/convert/__main__.py +164 -0
- zea/data/convert/camus.py +106 -40
- zea/data/convert/echonet.py +184 -83
- zea/data/convert/echonetlvh/README.md +2 -3
- zea/data/convert/echonetlvh/{convert_raw_to_usbmd.py → __init__.py} +174 -103
- zea/data/convert/echonetlvh/manual_rejections.txt +73 -0
- zea/data/convert/echonetlvh/precompute_crop.py +43 -64
- zea/data/convert/picmus.py +37 -40
- zea/data/convert/utils.py +86 -0
- zea/data/convert/verasonics.py +1247 -0
- zea/data/data_format.py +124 -6
- zea/data/dataloader.py +12 -7
- zea/data/datasets.py +109 -70
- zea/data/file.py +119 -82
- zea/data/file_operations.py +496 -0
- zea/data/preset_utils.py +2 -2
- zea/display.py +8 -9
- zea/doppler.py +5 -5
- zea/func/__init__.py +109 -0
- zea/{tensor_ops.py → func/tensor.py} +113 -69
- zea/func/ultrasound.py +500 -0
- zea/internal/_generate_keras_ops.py +5 -5
- zea/internal/checks.py +6 -12
- zea/internal/operators.py +4 -0
- zea/io_lib.py +108 -160
- zea/metrics.py +6 -5
- zea/models/__init__.py +1 -1
- zea/models/diffusion.py +63 -12
- zea/models/echonetlvh.py +1 -1
- zea/models/gmm.py +1 -1
- zea/models/lv_segmentation.py +2 -0
- zea/ops/__init__.py +188 -0
- zea/ops/base.py +442 -0
- zea/{keras_ops.py → ops/keras_ops.py} +2 -2
- zea/ops/pipeline.py +1472 -0
- zea/ops/tensor.py +356 -0
- zea/ops/ultrasound.py +890 -0
- zea/probes.py +2 -10
- zea/scan.py +35 -28
- zea/tools/fit_scan_cone.py +90 -160
- zea/tools/selection_tool.py +1 -1
- zea/tracking/__init__.py +16 -0
- zea/tracking/base.py +94 -0
- zea/tracking/lucas_kanade.py +474 -0
- zea/tracking/segmentation.py +110 -0
- zea/utils.py +11 -2
- {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/METADATA +5 -1
- {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/RECORD +62 -48
- zea/data/convert/matlab.py +0 -1237
- zea/ops.py +0 -3294
- {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/WHEEL +0 -0
- {zea-0.0.7.dist-info → zea-0.0.9.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
262
|
-
which case all
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
514
|
-
which case all frames are loaded.
|
|
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
|
|