zea 0.0.2__tar.gz → 0.0.4__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.
- {zea-0.0.2 → zea-0.0.4}/PKG-INFO +13 -7
- {zea-0.0.2 → zea-0.0.4}/README.md +8 -3
- {zea-0.0.2 → zea-0.0.4}/pyproject.toml +5 -4
- {zea-0.0.2 → zea-0.0.4}/zea/__init__.py +1 -1
- {zea-0.0.2 → zea-0.0.4}/zea/agent/selection.py +47 -30
- {zea-0.0.2 → zea-0.0.4}/zea/backend/__init__.py +5 -3
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/__init__.py +1 -1
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/beamformer.py +5 -4
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/pfield.py +6 -3
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/pixelgrid.py +29 -28
- {zea-0.0.2 → zea-0.0.4}/zea/config.py +25 -5
- {zea-0.0.2 → zea-0.0.4}/zea/data/data_format.py +44 -40
- {zea-0.0.2 → zea-0.0.4}/zea/data/file.py +3 -18
- zea-0.0.4/zea/data/preset_utils.py +123 -0
- {zea-0.0.2 → zea-0.0.4}/zea/datapaths.py +3 -3
- {zea-0.0.2 → zea-0.0.4}/zea/display.py +2 -2
- {zea-0.0.2 → zea-0.0.4}/zea/interface.py +1 -1
- {zea-0.0.2 → zea-0.0.4}/zea/internal/checks.py +17 -8
- {zea-0.0.2 → zea-0.0.4}/zea/internal/config/parameters.py +2 -2
- {zea-0.0.2 → zea-0.0.4}/zea/internal/config/validation.py +2 -2
- {zea-0.0.2 → zea-0.0.4}/zea/internal/core.py +11 -42
- {zea-0.0.2 → zea-0.0.4}/zea/internal/parameters.py +204 -163
- {zea-0.0.2 → zea-0.0.4}/zea/internal/registry.py +16 -8
- {zea-0.0.2 → zea-0.0.4}/zea/internal/viewer.py +1 -1
- {zea-0.0.2 → zea-0.0.4}/zea/io_lib.py +2 -7
- {zea-0.0.2 → zea-0.0.4}/zea/log.py +8 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/base.py +1 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/diffusion.py +43 -17
- {zea-0.0.2 → zea-0.0.4}/zea/ops.py +195 -117
- {zea-0.0.2 → zea-0.0.4}/zea/probes.py +23 -5
- {zea-0.0.2 → zea-0.0.4}/zea/scan.py +97 -68
- {zea-0.0.2 → zea-0.0.4}/zea/tensor_ops.py +3 -3
- {zea-0.0.2 → zea-0.0.4}/zea/tools/selection_tool.py +1 -1
- {zea-0.0.2 → zea-0.0.4}/zea/utils.py +31 -0
- {zea-0.0.2 → zea-0.0.4}/zea/visualize.py +15 -7
- zea-0.0.2/zea/data/preset_utils.py +0 -109
- {zea-0.0.2 → zea-0.0.4}/LICENSE +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/__main__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/agent/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/agent/gumbel.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/agent/masks.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/autograd.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/jax/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/dataloader.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/layers/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/layers/apodization.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/layers/utils.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/losses.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/models/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/models/lista.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/scripts/convert-echonet-dynamic.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/scripts/convert-taesd.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/utils/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/utils/callbacks.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tensorflow/utils/utils.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/tf2jax.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/torch/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/backend/torch/losses.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/delays.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/lens_correction.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/beamform/phantoms.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/__main__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/augmentations.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/camus.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/echonet.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/echonetlvh/README.md +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/echonetlvh/convert_raw_to_usbmd.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/echonetlvh/precompute_crop.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/images.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/matlab.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/convert/picmus.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/dataloader.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/datasets.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/layers.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/data/utils.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/cache.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/config/create.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/convert.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/device.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/git_info.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/operators.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/internal/setup_zea.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/metrics.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/carotid_segmenter.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/dense.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/echonet.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/generative.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/gmm.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/layers.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/lpips.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/preset_utils.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/presets.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/taesd.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/unet.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/models/utils.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/simulator.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/tools/__init__.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/tools/fit_scan_cone.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/tools/hf.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/tools/wndb.py +0 -0
- {zea-0.0.2 → zea-0.0.4}/zea/zea_darkmode.mplstyle +0 -0
{zea-0.0.2 → zea-0.0.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: zea
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A Toolbox for Cognitive Ultrasound Imaging. Provides a set of tools for processing of ultrasound data, all built in your favorite machine learning framework.
|
|
5
5
|
Keywords: ultrasound,machine learning,beamforming
|
|
6
6
|
Author: Tristan Stevens
|
|
@@ -22,6 +22,8 @@ Provides-Extra: display-headless
|
|
|
22
22
|
Provides-Extra: docs
|
|
23
23
|
Provides-Extra: jax
|
|
24
24
|
Provides-Extra: tests
|
|
25
|
+
Requires-Dist: IPython ; extra == "dev"
|
|
26
|
+
Requires-Dist: IPython ; extra == "docs"
|
|
25
27
|
Requires-Dist: PyStemmer ; extra == "dev"
|
|
26
28
|
Requires-Dist: PyStemmer ; extra == "docs"
|
|
27
29
|
Requires-Dist: cloudpickle (>=3.1.1) ; extra == "dev"
|
|
@@ -54,7 +56,6 @@ Requires-Dist: papermill (>=2.4) ; extra == "tests"
|
|
|
54
56
|
Requires-Dist: pillow (>=10)
|
|
55
57
|
Requires-Dist: pre-commit ; extra == "dev"
|
|
56
58
|
Requires-Dist: pre-commit ; extra == "tests"
|
|
57
|
-
Requires-Dist: pydicom (>=2.4)
|
|
58
59
|
Requires-Dist: pytest (>=8.1) ; extra == "dev"
|
|
59
60
|
Requires-Dist: pytest (>=8.1) ; extra == "tests"
|
|
60
61
|
Requires-Dist: pytest-cov ; extra == "dev"
|
|
@@ -83,8 +84,8 @@ Requires-Dist: torch ; extra == "backends"
|
|
|
83
84
|
Requires-Dist: tqdm (>=4)
|
|
84
85
|
Requires-Dist: wandb (>=0.18)
|
|
85
86
|
Requires-Dist: wget (>=3.2)
|
|
86
|
-
Project-URL: Homepage, https://github.com/tue-bmd/
|
|
87
|
-
Project-URL: Repository, https://github.com/tue-bmd/
|
|
87
|
+
Project-URL: Homepage, https://github.com/tue-bmd/zea/
|
|
88
|
+
Project-URL: Repository, https://github.com/tue-bmd/zea/
|
|
88
89
|
Description-Content-Type: text/markdown
|
|
89
90
|
|
|
90
91
|
# zea <img src="https://raw.githubusercontent.com/tue-bmd/zea/main/docs/_static/zea-logo.png" width="120" height="120" align="right" alt="zea Logo" />
|
|
@@ -95,8 +96,14 @@ Description-Content-Type: text/markdown
|
|
|
95
96
|
[](https://zea.readthedocs.io/en/latest/?badge=latest)
|
|
96
97
|
[](https://github.com/tue-bmd/zea/blob/main/LICENSE)
|
|
97
98
|
[](https://codecov.io/gh/tue-bmd/zea)
|
|
99
|
+
[](https://joss.theoj.org/papers/fa923917ca41761fe0623ca6c350017d)
|
|
100
|
+
[](https://github.com/tue-bmd/zea/stargazers)
|
|
98
101
|
|
|
99
|
-
Welcome to the
|
|
102
|
+
Welcome to the `zea` package: *A Toolbox for Cognitive Ultrasound Imaging.*
|
|
103
|
+
|
|
104
|
+
- 📚 Full documentation: [zea.readthedocs.io](https://zea.readthedocs.io)
|
|
105
|
+
- 🔬 Try hands-on examples (with Colab): [Examples & Tutorials](https://zea.readthedocs.io/en/latest/examples.html)
|
|
106
|
+
- ⚙️ Installation guide: [Installation](https://zea.readthedocs.io/en/latest/installation.html)
|
|
100
107
|
|
|
101
108
|
`zea` is a Python library that offers ultrasound signal processing, image reconstruction, and deep learning. Currently, `zea` offers:
|
|
102
109
|
|
|
@@ -110,6 +117,5 @@ Welcome to the documentation for the `zea` package: *A Toolbox for Cognitive Ult
|
|
|
110
117
|
> This package is highly experimental and under active development. It is mainly used to support [our research](https://www.tue.nl/en/research/research-groups/signal-processing-systems/biomedical-diagnostics-lab) and as a basis for our publications. That being said, we are happy to share it with the ultrasound community and hope it will be useful for your research as well.
|
|
111
118
|
|
|
112
119
|
> [!NOTE]
|
|
113
|
-
> 📖
|
|
114
|
-
> You can find the citation information by clicking the **"Cite this repository"** button on the top right of this page.
|
|
120
|
+
> 📖 Please cite `zea` in your publications if it helps your research. You can find citation info [here](https://zea.readthedocs.io/en/latest/getting-started.html#citation).
|
|
115
121
|
|
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
[](https://zea.readthedocs.io/en/latest/?badge=latest)
|
|
7
7
|
[](https://github.com/tue-bmd/zea/blob/main/LICENSE)
|
|
8
8
|
[](https://codecov.io/gh/tue-bmd/zea)
|
|
9
|
+
[](https://joss.theoj.org/papers/fa923917ca41761fe0623ca6c350017d)
|
|
10
|
+
[](https://github.com/tue-bmd/zea/stargazers)
|
|
9
11
|
|
|
10
|
-
Welcome to the
|
|
12
|
+
Welcome to the `zea` package: *A Toolbox for Cognitive Ultrasound Imaging.*
|
|
13
|
+
|
|
14
|
+
- 📚 Full documentation: [zea.readthedocs.io](https://zea.readthedocs.io)
|
|
15
|
+
- 🔬 Try hands-on examples (with Colab): [Examples & Tutorials](https://zea.readthedocs.io/en/latest/examples.html)
|
|
16
|
+
- ⚙️ Installation guide: [Installation](https://zea.readthedocs.io/en/latest/installation.html)
|
|
11
17
|
|
|
12
18
|
`zea` is a Python library that offers ultrasound signal processing, image reconstruction, and deep learning. Currently, `zea` offers:
|
|
13
19
|
|
|
@@ -21,5 +27,4 @@ Welcome to the documentation for the `zea` package: *A Toolbox for Cognitive Ult
|
|
|
21
27
|
> This package is highly experimental and under active development. It is mainly used to support [our research](https://www.tue.nl/en/research/research-groups/signal-processing-systems/biomedical-diagnostics-lab) and as a basis for our publications. That being said, we are happy to share it with the ultrasound community and hope it will be useful for your research as well.
|
|
22
28
|
|
|
23
29
|
> [!NOTE]
|
|
24
|
-
> 📖
|
|
25
|
-
> You can find the citation information by clicking the **"Cite this repository"** button on the top right of this page.
|
|
30
|
+
> 📖 Please cite `zea` in your publications if it helps your research. You can find citation info [here](https://zea.readthedocs.io/en/latest/getting-started.html#citation).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "zea"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4"
|
|
4
4
|
description = "A Toolbox for Cognitive Ultrasound Imaging. Provides a set of tools for processing of ultrasound data, all built in your favorite machine learning framework."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Tristan Stevens", email = "t.s.w.stevens@tue.nl" },
|
|
@@ -43,7 +43,6 @@ dependencies = [
|
|
|
43
43
|
"wandb >=0.18",
|
|
44
44
|
"wget >=3.2",
|
|
45
45
|
# can we make these optional or remove?
|
|
46
|
-
"pydicom >=2.4",
|
|
47
46
|
"scikit-image >=0.23",
|
|
48
47
|
"scikit-learn >=1.4",
|
|
49
48
|
]
|
|
@@ -70,6 +69,7 @@ dev = [
|
|
|
70
69
|
"nbsphinx",
|
|
71
70
|
"furo",
|
|
72
71
|
"PyStemmer",
|
|
72
|
+
"IPython",
|
|
73
73
|
# display
|
|
74
74
|
"opencv-python-headless>=4",
|
|
75
75
|
]
|
|
@@ -96,6 +96,7 @@ docs = [
|
|
|
96
96
|
"nbsphinx",
|
|
97
97
|
"furo",
|
|
98
98
|
"PyStemmer",
|
|
99
|
+
"IPython",
|
|
99
100
|
]
|
|
100
101
|
jax = [
|
|
101
102
|
"jax[cuda12_pip]>=0.4.26"
|
|
@@ -112,8 +113,8 @@ display-headless = [
|
|
|
112
113
|
backends = ["jax", "tensorflow", "torch"]
|
|
113
114
|
|
|
114
115
|
[project.urls]
|
|
115
|
-
Homepage = "https://github.com/tue-bmd/
|
|
116
|
-
Repository = "https://github.com/tue-bmd/
|
|
116
|
+
Homepage = "https://github.com/tue-bmd/zea/"
|
|
117
|
+
Repository = "https://github.com/tue-bmd/zea/"
|
|
117
118
|
|
|
118
119
|
[project.scripts]
|
|
119
120
|
zea = "zea.__main__:main"
|
|
@@ -92,6 +92,7 @@ class GreedyEntropy(LinesActionModel):
|
|
|
92
92
|
mean: float = 0,
|
|
93
93
|
std_dev: float = 1,
|
|
94
94
|
num_lines_to_update: int = 5,
|
|
95
|
+
entropy_sigma: float = 1.0,
|
|
95
96
|
):
|
|
96
97
|
"""Initialize the GreedyEntropy action selection model.
|
|
97
98
|
|
|
@@ -104,6 +105,8 @@ class GreedyEntropy(LinesActionModel):
|
|
|
104
105
|
std_dev (float, optional): The standard deviation of the RBF. Defaults to 1.
|
|
105
106
|
num_lines_to_update (int, optional): The number of lines around the selected line
|
|
106
107
|
to update. Must be odd.
|
|
108
|
+
entropy_sigma (float, optional): The standard deviation of the Gaussian
|
|
109
|
+
Mixture components used to approximate the posterior.
|
|
107
110
|
"""
|
|
108
111
|
super().__init__(n_actions, n_possible_actions, img_width, img_height)
|
|
109
112
|
|
|
@@ -124,7 +127,7 @@ class GreedyEntropy(LinesActionModel):
|
|
|
124
127
|
self.num_lines_to_update,
|
|
125
128
|
)
|
|
126
129
|
self.upside_down_gaussian = upside_down_gaussian(points_to_evaluate)
|
|
127
|
-
self.entropy_sigma =
|
|
130
|
+
self.entropy_sigma = entropy_sigma
|
|
128
131
|
|
|
129
132
|
@staticmethod
|
|
130
133
|
def compute_pairwise_pixel_gaussian_error(
|
|
@@ -152,51 +155,58 @@ class GreedyEntropy(LinesActionModel):
|
|
|
152
155
|
# TODO: I think we only need to compute the lower triangular
|
|
153
156
|
# of this matrix, since it's symmetric
|
|
154
157
|
squared_l2_error_matrices = (particles[:, :, None, ...] - particles[:, None, :, ...]) ** 2
|
|
155
|
-
gaussian_error_per_pixel_i_j = ops.exp(
|
|
158
|
+
gaussian_error_per_pixel_i_j = ops.exp(
|
|
159
|
+
-(squared_l2_error_matrices) / (2 * entropy_sigma**2)
|
|
160
|
+
)
|
|
156
161
|
# Vertically stack all columns corresponding with the same line
|
|
157
162
|
# This way we can just sum across the height axis and get the entropy
|
|
158
163
|
# for each pixel in a given line
|
|
159
164
|
batch_size, n_particles, _, height, _ = gaussian_error_per_pixel_i_j.shape
|
|
160
|
-
gaussian_error_per_pixel_stacked = ops.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
gaussian_error_per_pixel_stacked = ops.transpose(
|
|
166
|
+
ops.reshape(
|
|
167
|
+
ops.transpose(gaussian_error_per_pixel_i_j, (0, 1, 2, 4, 3)),
|
|
168
|
+
[
|
|
169
|
+
batch_size,
|
|
170
|
+
n_particles,
|
|
171
|
+
n_particles,
|
|
172
|
+
n_possible_actions,
|
|
173
|
+
height * stack_n_cols,
|
|
174
|
+
],
|
|
175
|
+
),
|
|
176
|
+
(0, 1, 2, 4, 3),
|
|
169
177
|
)
|
|
170
178
|
# [n_particles, n_particles, batch, height, width]
|
|
171
179
|
return gaussian_error_per_pixel_stacked
|
|
172
180
|
|
|
173
|
-
def
|
|
174
|
-
"""
|
|
175
|
-
|
|
181
|
+
def compute_pixelwise_entropy(self, particles):
|
|
182
|
+
"""
|
|
176
183
|
This function computes the entropy for each line using a Gaussian Mixture Model
|
|
177
184
|
approximation of the posterior distribution.
|
|
178
|
-
For more details see Section
|
|
185
|
+
For more details see Section VI. B here: https://arxiv.org/pdf/2410.13310
|
|
179
186
|
|
|
180
187
|
Args:
|
|
181
188
|
particles (Tensor): Particles of shape (batch_size, n_particles, height, width)
|
|
182
189
|
|
|
183
190
|
Returns:
|
|
184
|
-
Tensor: batch of entropies per
|
|
191
|
+
Tensor: batch of entropies per pixel, of shape (batch, height, width)
|
|
185
192
|
"""
|
|
186
|
-
|
|
193
|
+
n_particles = ops.shape(particles)[1]
|
|
194
|
+
gaussian_error_per_pixel_stacked = self.compute_pairwise_pixel_gaussian_error(
|
|
187
195
|
particles,
|
|
188
196
|
self.stack_n_cols,
|
|
189
197
|
self.n_possible_actions,
|
|
190
198
|
self.entropy_sigma,
|
|
191
199
|
)
|
|
192
|
-
gaussian_error_per_line = ops.sum(gaussian_error_per_pixel_stacked, axis=3)
|
|
193
200
|
# sum out first dimension of (n_particles x n_particles) error matrix
|
|
194
|
-
# [n_particles, batch,
|
|
195
|
-
|
|
201
|
+
# [n_particles, batch, height, width]
|
|
202
|
+
pixelwise_entropy_sum_j = ops.sum(
|
|
203
|
+
(1 / n_particles) * gaussian_error_per_pixel_stacked, axis=1
|
|
204
|
+
)
|
|
205
|
+
log_pixelwise_entropy_sum_j = ops.log(pixelwise_entropy_sum_j)
|
|
196
206
|
# sum out second dimension of (n_particles x n_particles) error matrix
|
|
197
|
-
# [batch,
|
|
198
|
-
|
|
199
|
-
return
|
|
207
|
+
# [batch, height, width]
|
|
208
|
+
pixelwise_entropy = -ops.sum((1 / n_particles) * log_pixelwise_entropy_sum_j, axis=1)
|
|
209
|
+
return pixelwise_entropy
|
|
200
210
|
|
|
201
211
|
def select_line_and_reweight_entropy(self, entropy_per_line):
|
|
202
212
|
"""Select the line with maximum entropy and reweight the entropies.
|
|
@@ -254,17 +264,19 @@ class GreedyEntropy(LinesActionModel):
|
|
|
254
264
|
particles (Tensor): Particles of shape (batch_size, n_particles, height, width)
|
|
255
265
|
|
|
256
266
|
Returns:
|
|
257
|
-
|
|
267
|
+
Tuple[Tensor, Tensor]:
|
|
258
268
|
- Newly selected lines as k-hot vectors, shaped (batch_size, n_possible_actions)
|
|
259
|
-
|
|
269
|
+
- Masks of shape (batch_size, img_height, img_width)
|
|
260
270
|
"""
|
|
261
|
-
|
|
271
|
+
|
|
272
|
+
pixelwise_entropy = self.compute_pixelwise_entropy(particles)
|
|
273
|
+
linewise_entropy = ops.sum(pixelwise_entropy, axis=1)
|
|
262
274
|
|
|
263
275
|
# Greedily select best line, reweight entropies, and repeat
|
|
264
276
|
all_selected_lines = []
|
|
265
277
|
for _ in range(self.n_actions):
|
|
266
|
-
max_entropy_line,
|
|
267
|
-
self.select_line_and_reweight_entropy,
|
|
278
|
+
max_entropy_line, linewise_entropy = ops.vectorized_map(
|
|
279
|
+
self.select_line_and_reweight_entropy, linewise_entropy
|
|
268
280
|
)
|
|
269
281
|
all_selected_lines.append(max_entropy_line)
|
|
270
282
|
|
|
@@ -428,10 +440,15 @@ class CovarianceSamplingLines(LinesActionModel):
|
|
|
428
440
|
generation. Defaults to None.
|
|
429
441
|
|
|
430
442
|
Returns:
|
|
431
|
-
Tensor
|
|
443
|
+
Tuple[Tensor, Tensor]:
|
|
444
|
+
- Newly selected lines as k-hot vectors, shaped (batch_size, n_possible_actions)
|
|
445
|
+
- Masks of shape (batch_size, img_height, img_width)
|
|
432
446
|
"""
|
|
433
447
|
batch_size, n_particles, rows, _ = ops.shape(particles)
|
|
434
448
|
|
|
449
|
+
# [batch_size, rows, cols, n_particles]
|
|
450
|
+
particles = ops.transpose(particles, (0, 2, 3, 1))
|
|
451
|
+
|
|
435
452
|
# [batch_size, rows * stack_n_cols, n_possible_actions, n_particles]
|
|
436
453
|
shape = [
|
|
437
454
|
batch_size,
|
|
@@ -441,7 +458,7 @@ class CovarianceSamplingLines(LinesActionModel):
|
|
|
441
458
|
]
|
|
442
459
|
particles = ops.reshape(particles, shape)
|
|
443
460
|
|
|
444
|
-
# [batch_size, rows, n_possible_actions, n_possible_actions]
|
|
461
|
+
# [batch_size, rows * stack_n_cols, n_possible_actions, n_possible_actions]
|
|
445
462
|
cov_matrix = tensor_ops.batch_cov(particles)
|
|
446
463
|
|
|
447
464
|
# Sum over the row dimension [batch_size, n_possible_actions, n_possible_actions]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Backend
|
|
1
|
+
"""Backend-specific utilities.
|
|
2
2
|
|
|
3
3
|
This subpackage provides backend-specific utilities for the ``zea`` library. Most backend logic is handled by Keras 3, but a few features require custom wrappers to ensure compatibility and performance across JAX, TensorFlow, and PyTorch.
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ Key Features
|
|
|
9
9
|
------------
|
|
10
10
|
|
|
11
11
|
- **JIT Compilation** (:func:`zea.backend.jit`):
|
|
12
|
-
Provides a unified interface for just-in-time (JIT) compilation of functions, dispatching to the appropriate backend (JAX or TensorFlow) as needed. This enables accelerated execution of computationally intensive routines.
|
|
12
|
+
Provides a unified interface for just-in-time (JIT) compilation of functions, dispatching to the appropriate backend (JAX or TensorFlow) as needed. This enables accelerated execution of computationally intensive routines. Note that jit compilation is not yet supported when using the `torch` backend.
|
|
13
13
|
|
|
14
14
|
- **Automatic Differentiation** (:class:`zea.backend.AutoGrad`):
|
|
15
15
|
Offers a backend-agnostic wrapper for automatic differentiation, allowing gradient computation regardless of the underlying ML library.
|
|
@@ -108,7 +108,9 @@ def _jit_compile(func, jax=True, tensorflow=True, **kwargs):
|
|
|
108
108
|
return func
|
|
109
109
|
else:
|
|
110
110
|
log.warning(
|
|
111
|
-
f"
|
|
111
|
+
f"JIT compilation not currently supported for backend {backend}. "
|
|
112
|
+
"Supported backends are 'tensorflow' and 'jax'."
|
|
112
113
|
)
|
|
114
|
+
log.warning("Initialize zea.Pipeline with jit_options=None to suppress this warning.")
|
|
113
115
|
log.warning("Falling back to non-compiled mode.")
|
|
114
116
|
return func
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Main beamforming functions for ultrasound imaging."""
|
|
2
2
|
|
|
3
|
+
import keras
|
|
3
4
|
import numpy as np
|
|
4
5
|
from keras import ops
|
|
5
6
|
|
|
@@ -58,7 +59,7 @@ def tof_correction(
|
|
|
58
59
|
demodulation_frequency,
|
|
59
60
|
fnum,
|
|
60
61
|
angles,
|
|
61
|
-
|
|
62
|
+
focus_distances,
|
|
62
63
|
apply_phase_rotation=False,
|
|
63
64
|
apply_lens_correction=False,
|
|
64
65
|
lens_thickness=1e-3,
|
|
@@ -84,7 +85,7 @@ def tof_correction(
|
|
|
84
85
|
fnum (int, optional): Focus number. Defaults to 1.
|
|
85
86
|
angles (ops.Tensor): The angles of the plane waves in radians of shape
|
|
86
87
|
`(n_tx,)`
|
|
87
|
-
|
|
88
|
+
focus_distances (ops.Tensor): The focus distance of shape `(n_tx,)`
|
|
88
89
|
apply_phase_rotation (bool, optional): Whether to apply phase rotation to
|
|
89
90
|
time-of-flights. Defaults to False.
|
|
90
91
|
apply_lens_correction (bool, optional): Whether to apply lens correction to
|
|
@@ -133,7 +134,7 @@ def tof_correction(
|
|
|
133
134
|
sound_speed,
|
|
134
135
|
n_tx,
|
|
135
136
|
n_el,
|
|
136
|
-
|
|
137
|
+
focus_distances,
|
|
137
138
|
angles,
|
|
138
139
|
lens_thickness=lens_thickness,
|
|
139
140
|
lens_sound_speed=lens_sound_speed,
|
|
@@ -487,7 +488,7 @@ def fnumber_mask(flatgrid, probe_geometry, f_number, fnum_window_fn):
|
|
|
487
488
|
# The f-number is fnum = z/aperture = 1/(2 * tan(alpha))
|
|
488
489
|
# Rearranging gives us alpha = arctan(1/(2 * fnum))
|
|
489
490
|
# We can use this to compute the maximum angle alpha that is allowed
|
|
490
|
-
max_alpha = ops.arctan(1 / (2 * f_number))
|
|
491
|
+
max_alpha = ops.arctan(1 / (2 * f_number + keras.backend.epsilon()))
|
|
491
492
|
|
|
492
493
|
normalized_angle = alpha / max_alpha
|
|
493
494
|
mask = fnum_window_fn(normalized_angle)
|
|
@@ -60,7 +60,8 @@ def compute_pfield(
|
|
|
60
60
|
n_el (int): Number of elements in the probe.
|
|
61
61
|
probe_geometry (array): Geometry of the probe elements.
|
|
62
62
|
tx_apodizations (array): Transmit apodization values.
|
|
63
|
-
grid (array): Grid points where the pressure field is computed
|
|
63
|
+
grid (array): Grid points where the pressure field is computed
|
|
64
|
+
of shape (grid_size_z, grid_size_x, 3).
|
|
64
65
|
t0_delays (array): Transmit delays for each transmit event.
|
|
65
66
|
frequency_step (int, optional): Frequency step. Default is 4.
|
|
66
67
|
Higher is faster but less accurate.
|
|
@@ -78,7 +79,8 @@ def compute_pfield(
|
|
|
78
79
|
verbose (bool, optional): Whether to print progress.
|
|
79
80
|
|
|
80
81
|
Returns:
|
|
81
|
-
ops.array: The (normalized) pressure field (across tx events)
|
|
82
|
+
ops.array: The (normalized) pressure field (across tx events)
|
|
83
|
+
of shape (n_tx, grid_size_z, grid_size_x).
|
|
82
84
|
"""
|
|
83
85
|
# medium params
|
|
84
86
|
alpha_db = 0 # currently we ignore attenuation in the compounding
|
|
@@ -293,7 +295,8 @@ def normalize_pressure_field(pfield, alpha: float = 1.0, percentile: float = 10.
|
|
|
293
295
|
Normalize the input array of intensities by zeroing out values below a given percentile.
|
|
294
296
|
|
|
295
297
|
Args:
|
|
296
|
-
pfield (array): The unnormalized pressure field array
|
|
298
|
+
pfield (array): The unnormalized pressure field array
|
|
299
|
+
of shape (n_tx, grid_size_z, grid_size_x).
|
|
297
300
|
alpha (float, optional): Exponent to 'sharpen or smooth' the weighting.
|
|
298
301
|
Higher values result in sharper weighting. Default is 1.0.
|
|
299
302
|
percentile (int, optional): minimum percentile threshold to keep in the weighting.
|
|
@@ -16,52 +16,52 @@ def check_for_aliasing(scan):
|
|
|
16
16
|
depth = scan.zlims[1] - scan.zlims[0]
|
|
17
17
|
wvln = scan.wavelength
|
|
18
18
|
|
|
19
|
-
if width / scan.
|
|
19
|
+
if width / scan.grid_size_x > wvln / 2:
|
|
20
20
|
log.warning(
|
|
21
|
-
f"width/
|
|
22
|
-
f"Consider either increasing scan.
|
|
23
|
-
"increasing scan.pixels_per_wavelength to 2 or more."
|
|
21
|
+
f"width/grid_size_x = {width / scan.grid_size_x:.7f} < wavelength/2 = {wvln / 2}. "
|
|
22
|
+
f"Consider either increasing scan.grid_size_x to {int(np.ceil(width / (wvln / 2)))} "
|
|
23
|
+
"or more, or increasing scan.pixels_per_wavelength to 2 or more."
|
|
24
24
|
)
|
|
25
|
-
if depth / scan.
|
|
25
|
+
if depth / scan.grid_size_z > wvln / 2:
|
|
26
26
|
log.warning(
|
|
27
|
-
f"depth/
|
|
28
|
-
f"Consider either increasing scan.
|
|
29
|
-
"increasing scan.pixels_per_wavelength to 2 or more."
|
|
27
|
+
f"depth/grid_size_z = {depth / scan.grid_size_z:.7f} < wavelength/2 = {wvln / 2:.7f}. "
|
|
28
|
+
f"Consider either increasing scan.grid_size_z to {int(np.ceil(depth / (wvln / 2)))} "
|
|
29
|
+
"or more, or increasing scan.pixels_per_wavelength to 2 or more."
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def cartesian_pixel_grid(xlims, zlims,
|
|
33
|
+
def cartesian_pixel_grid(xlims, zlims, grid_size_x=None, grid_size_z=None, dx=None, dz=None):
|
|
34
34
|
"""Generate a Cartesian pixel grid based on input parameters.
|
|
35
35
|
|
|
36
36
|
Args:
|
|
37
37
|
xlims (tuple): Azimuthal limits of pixel grid ([xmin, xmax])
|
|
38
38
|
zlims (tuple): Depth limits of pixel grid ([zmin, zmax])
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
grid_size_x (int): Number of azimuthal pixels, overrides dx and dz parameters
|
|
40
|
+
grid_size_z (int): Number of depth pixels, overrides dx and dz parameters
|
|
41
41
|
dx (float): Pixel spacing in azimuth
|
|
42
42
|
dz (float): Pixel spacing in depth
|
|
43
43
|
|
|
44
44
|
Raises:
|
|
45
|
-
ValueError: Either
|
|
45
|
+
ValueError: Either grid_size_x and grid_size_z or dx and dz must be defined.
|
|
46
46
|
|
|
47
47
|
Returns:
|
|
48
|
-
grid (np.ndarray): Pixel grid of size (
|
|
48
|
+
grid (np.ndarray): Pixel grid of size (grid_size_z, nx, 3) in
|
|
49
49
|
Cartesian coordinates (x, y, z)
|
|
50
50
|
"""
|
|
51
|
-
assert (bool(
|
|
52
|
-
"Either
|
|
51
|
+
assert (bool(grid_size_x) and bool(grid_size_z)) ^ (bool(dx) and bool(dz)), (
|
|
52
|
+
"Either grid_size_x and grid_size_z or dx and dz must be defined."
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
# Determine the grid spacing
|
|
56
|
-
if
|
|
57
|
-
x = np.linspace(xlims[0], xlims[1] + eps,
|
|
58
|
-
z = np.linspace(zlims[0], zlims[1] + eps,
|
|
56
|
+
if grid_size_x is not None and grid_size_z is not None:
|
|
57
|
+
x = np.linspace(xlims[0], xlims[1] + eps, grid_size_x)
|
|
58
|
+
z = np.linspace(zlims[0], zlims[1] + eps, grid_size_z)
|
|
59
59
|
elif dx is not None and dz is not None:
|
|
60
60
|
sign = np.sign(xlims[1] - xlims[0])
|
|
61
61
|
x = np.arange(xlims[0], xlims[1] + eps, sign * dx)
|
|
62
62
|
z = np.arange(zlims[0], zlims[1] + eps, sign * dz)
|
|
63
63
|
else:
|
|
64
|
-
raise ValueError("Either
|
|
64
|
+
raise ValueError("Either grid_size_x and grid_size_z or dx and dz must be defined.")
|
|
65
65
|
|
|
66
66
|
# Create the pixel grid
|
|
67
67
|
z_grid, x_grid = np.meshgrid(z, x, indexing="ij")
|
|
@@ -102,29 +102,30 @@ def radial_pixel_grid(rlims, dr, oris, dirs):
|
|
|
102
102
|
return grid
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def polar_pixel_grid(polar_limits, zlims,
|
|
105
|
+
def polar_pixel_grid(polar_limits, zlims, num_radial_pixels: int, num_polar_pixels: int):
|
|
106
106
|
"""Generate a polar grid.
|
|
107
107
|
|
|
108
108
|
Uses radial_pixel_grid but based on parameters that are present in the scan class.
|
|
109
109
|
|
|
110
110
|
Args:
|
|
111
|
-
polar_limits (tuple):
|
|
111
|
+
polar_limits (tuple): Polar limits of pixel grid ([polar_min, polar_max])
|
|
112
112
|
zlims (tuple): Depth limits of pixel grid ([zmin, zmax])
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
num_radial_pixels (int, optional): Number of depth pixels.
|
|
114
|
+
num_polar_pixels (int, optional): Number of polar pixels.
|
|
115
115
|
|
|
116
116
|
Returns:
|
|
117
|
-
grid (np.ndarray): Pixel grid of size (
|
|
117
|
+
grid (np.ndarray): Pixel grid of size (num_radial_pixels, num_polar_pixels, 3)
|
|
118
|
+
in Cartesian coordinates (x, y, z)
|
|
118
119
|
"""
|
|
119
120
|
assert len(polar_limits) == 2, "polar_limits must be a tuple of length 2."
|
|
120
121
|
assert len(zlims) == 2, "zlims must be a tuple of length 2."
|
|
121
122
|
|
|
122
|
-
dr = (zlims[1] - zlims[0]) /
|
|
123
|
+
dr = (zlims[1] - zlims[0]) / num_radial_pixels
|
|
123
124
|
|
|
124
125
|
oris = np.array([0, 0, 0])
|
|
125
|
-
oris = np.tile(oris, (
|
|
126
|
-
dirs_az = np.linspace(*polar_limits,
|
|
126
|
+
oris = np.tile(oris, (num_polar_pixels, 1))
|
|
127
|
+
dirs_az = np.linspace(*polar_limits, num_polar_pixels)
|
|
127
128
|
|
|
128
|
-
dirs_el = np.zeros(
|
|
129
|
+
dirs_el = np.zeros(num_polar_pixels)
|
|
129
130
|
dirs = np.vstack((dirs_az, dirs_el)).T
|
|
130
131
|
return radial_pixel_grid(zlims, dr, oris, dirs).transpose(1, 0, 2)
|
|
@@ -47,8 +47,9 @@ import yaml
|
|
|
47
47
|
from huggingface_hub import hf_hub_download
|
|
48
48
|
|
|
49
49
|
from zea import log
|
|
50
|
+
from zea.data.preset_utils import HF_PREFIX, _hf_resolve_path
|
|
50
51
|
from zea.internal.config.validation import config_schema
|
|
51
|
-
from zea.internal.core import
|
|
52
|
+
from zea.internal.core import dict_to_tensor
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
class Config(dict):
|
|
@@ -430,8 +431,22 @@ class Config(dict):
|
|
|
430
431
|
v._recursive_setattr(set_key, set_value)
|
|
431
432
|
|
|
432
433
|
@classmethod
|
|
433
|
-
def
|
|
434
|
-
"""Load config object from
|
|
434
|
+
def from_path(cls, path, **kwargs):
|
|
435
|
+
"""Load config object from a file path.
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
path (str or Path): The path to the config file.
|
|
439
|
+
Can be a string or a Path object. Additionally can be a string with
|
|
440
|
+
the prefix 'hf://', in which case it will be resolved to a
|
|
441
|
+
huggingface path.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
Config: config object.
|
|
445
|
+
"""
|
|
446
|
+
if str(path).startswith(HF_PREFIX):
|
|
447
|
+
path = _hf_resolve_path(str(path))
|
|
448
|
+
if isinstance(path, str):
|
|
449
|
+
path = Path(path)
|
|
435
450
|
return _load_config_from_yaml(path, config_class=cls, **kwargs)
|
|
436
451
|
|
|
437
452
|
@classmethod
|
|
@@ -460,9 +475,14 @@ class Config(dict):
|
|
|
460
475
|
local_path = hf_hub_download(repo_id, path, **kwargs)
|
|
461
476
|
return _load_config_from_yaml(local_path, config_class=cls)
|
|
462
477
|
|
|
463
|
-
|
|
478
|
+
@classmethod
|
|
479
|
+
def from_yaml(cls, path, **kwargs):
|
|
480
|
+
"""Load config object from yaml file."""
|
|
481
|
+
return cls.from_path(path, **kwargs)
|
|
482
|
+
|
|
483
|
+
def to_tensor(self, keep_as_is=None):
|
|
464
484
|
"""Convert the attributes in the object to keras tensors"""
|
|
465
|
-
return
|
|
485
|
+
return dict_to_tensor(self.serialize(), keep_as_is=keep_as_is)
|
|
466
486
|
|
|
467
487
|
|
|
468
488
|
def check_config(config: Union[dict, Config], verbose: bool = False):
|