hotopy 0.21__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.
- hotopy/__init__.py +42 -0
- hotopy/datasets/__init__.py +76 -0
- hotopy/datasets/_fetcher.py +37 -0
- hotopy/datasets/_holograms.py +54 -0
- hotopy/datasets/_phantoms.py +120 -0
- hotopy/datasets/registry.txt +9 -0
- hotopy/holo/__init__.py +124 -0
- hotopy/holo/_ap.py +103 -0
- hotopy/holo/_ctf.py +455 -0
- hotopy/holo/_pbi.py +94 -0
- hotopy/holo/_pca.py +92 -0
- hotopy/holo/_tieregime.py +344 -0
- hotopy/holo/_tikhonov.py +424 -0
- hotopy/holo/_util.py +496 -0
- hotopy/holo/constraints.py +239 -0
- hotopy/holo/propagation.py +387 -0
- hotopy/holo/regularization.py +202 -0
- hotopy/image/__init__.py +114 -0
- hotopy/image/_filter.py +607 -0
- hotopy/image/_generators.py +100 -0
- hotopy/image/_inpainting.py +103 -0
- hotopy/image/_registration.py +118 -0
- hotopy/image/_stats.py +223 -0
- hotopy/image/_transforms.py +328 -0
- hotopy/optimize.py +649 -0
- hotopy/tomo/__init__.py +184 -0
- hotopy/tomo/_alignment.py +150 -0
- hotopy/tomo/_astra.py +763 -0
- hotopy/tomo/_operators.py +104 -0
- hotopy/tomo/_reprojection_alignment.py +345 -0
- hotopy/tomo/_ringremove.py +164 -0
- hotopy/utils/__init__.py +72 -0
- hotopy/utils/_io.py +110 -0
- hotopy/utils/_misc.py +90 -0
- hotopy/utils/_padding.py +206 -0
- hotopy/utils/_xray.py +59 -0
- hotopy/utils/fourier.py +342 -0
- hotopy-0.21.dist-info/METADATA +89 -0
- hotopy-0.21.dist-info/RECORD +41 -0
- hotopy-0.21.dist-info/WHEEL +4 -0
- hotopy-0.21.dist-info/licenses/LICENSE +674 -0
hotopy/__init__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
=============================================
|
|
3
|
+
HoToPy: A Holo- and Tomography Python Toolbox
|
|
4
|
+
=============================================
|
|
5
|
+
|
|
6
|
+
To use any submodule an explicit import is required either via
|
|
7
|
+
- ``import hotopy.holo`` or
|
|
8
|
+
- ``from hotopy import holo`` or
|
|
9
|
+
- ``from hotopy.holo import *``
|
|
10
|
+
|
|
11
|
+
or the used functions are imported explicitly ``from hotopy.holo import CTF``.
|
|
12
|
+
|
|
13
|
+
Submodules
|
|
14
|
+
----------
|
|
15
|
+
|
|
16
|
+
.. autosummary::
|
|
17
|
+
:toctree: generated/
|
|
18
|
+
|
|
19
|
+
holo
|
|
20
|
+
tomo
|
|
21
|
+
image
|
|
22
|
+
datasets
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Optimization
|
|
26
|
+
------------
|
|
27
|
+
|
|
28
|
+
.. autosummary::
|
|
29
|
+
:toctree: generated/
|
|
30
|
+
|
|
31
|
+
optimize
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Utilities and helpers
|
|
35
|
+
----------------------
|
|
36
|
+
|
|
37
|
+
.. autosummary::
|
|
38
|
+
:toctree: generated/
|
|
39
|
+
|
|
40
|
+
utils
|
|
41
|
+
|
|
42
|
+
"""
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
=========================================
|
|
3
|
+
Example datasets (:mod:`hotopy.datasets`)
|
|
4
|
+
=========================================
|
|
5
|
+
|
|
6
|
+
Experimental holographic datasets
|
|
7
|
+
---------------------------------
|
|
8
|
+
.. autosummary::
|
|
9
|
+
:toctree: generated/
|
|
10
|
+
|
|
11
|
+
beads
|
|
12
|
+
radiodurans
|
|
13
|
+
macrophage
|
|
14
|
+
world_holograms
|
|
15
|
+
spider
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
|
|
19
|
+
>>> from hotopy.datasets import beads
|
|
20
|
+
>>> data = beads()
|
|
21
|
+
>>> # list content
|
|
22
|
+
>>> print(list(data.keys())) # ['holograms', 'fresnelNumbers']
|
|
23
|
+
|
|
24
|
+
Some dataset also have a ``'support'`` field, which can be used for constrained phase retrieval.
|
|
25
|
+
|
|
26
|
+
>>> holos, fresnel_nums = data["holograms"], data["fresnelNumbers"]
|
|
27
|
+
>>> print(holos.shape, fresnel_nums.shape)
|
|
28
|
+
|
|
29
|
+
This datasets can be used for phase retrieval, e.g. with CTF or Tikhonov.
|
|
30
|
+
|
|
31
|
+
>>> from hotopy.holo import Tikhonov
|
|
32
|
+
>>> imshape = holos.shape[-2:]
|
|
33
|
+
>>> alpha = [0, 5e-2]
|
|
34
|
+
>>> betadelta = 0.01 # 1% effective absorption
|
|
35
|
+
>>> device = "cpu" # if CUDA cards are available set ``device="cuda"``.
|
|
36
|
+
>>> tik = Tikhonov(imshape, fresnel_nums, betadelta=betadelta, alpha=alpha, device=device)
|
|
37
|
+
>>> rec_tik = tik(holos).cpu().numpy()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Simulation phantoms
|
|
41
|
+
-------------------
|
|
42
|
+
.. autosummary::
|
|
43
|
+
:toctree: generated/
|
|
44
|
+
|
|
45
|
+
dicty
|
|
46
|
+
dicty_multi
|
|
47
|
+
world
|
|
48
|
+
|
|
49
|
+
.. author: Jens Lucht, 2023-2024
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
from ._holograms import (
|
|
53
|
+
beads,
|
|
54
|
+
radiodurans,
|
|
55
|
+
macrophage,
|
|
56
|
+
world_holograms,
|
|
57
|
+
spider,
|
|
58
|
+
logo_holograms,
|
|
59
|
+
catparticle,
|
|
60
|
+
)
|
|
61
|
+
from ._phantoms import dicty, dicty_multi, world, balls, checkerboard
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
"beads",
|
|
65
|
+
"radiodurans",
|
|
66
|
+
"macrophage",
|
|
67
|
+
"world_holograms",
|
|
68
|
+
"logo_holograms",
|
|
69
|
+
"catparticle",
|
|
70
|
+
"spider",
|
|
71
|
+
"dicty",
|
|
72
|
+
"dicty_multi",
|
|
73
|
+
"world",
|
|
74
|
+
"balls",
|
|
75
|
+
"checkerboard",
|
|
76
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pooch
|
|
3
|
+
import importlib.resources # requires python>=3.7
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _DataFetcher:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.storage = pooch.create(
|
|
9
|
+
path=pooch.os_cache("hotopy/datasets"),
|
|
10
|
+
base_url="https://gitlab.gwdg.de/irp/",
|
|
11
|
+
registry=None,
|
|
12
|
+
env="HOTOPY_DATA_DIR",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# find registry
|
|
16
|
+
self._registry_fp = registry_fp = importlib.resources.files(__package__).joinpath(
|
|
17
|
+
"registry.txt"
|
|
18
|
+
)
|
|
19
|
+
self.storage.load_registry(registry_fp)
|
|
20
|
+
|
|
21
|
+
def __call__(self, *args, **kwargs):
|
|
22
|
+
defaults = {"processor": _process_npz}
|
|
23
|
+
kwargs = {**defaults, **kwargs}
|
|
24
|
+
return self.storage.fetch(*args, **kwargs)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _process_npz(fname, *args):
|
|
28
|
+
return np.load(fname)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _decompress_npz(fname, *args):
|
|
32
|
+
decompressor = pooch.processors.Decompress()
|
|
33
|
+
fd_inflated = decompressor(fname, *args)
|
|
34
|
+
return np.load(fd_inflated)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
fetcher = _DataFetcher()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. autosummary::
|
|
3
|
+
:toctree: generated/
|
|
4
|
+
|
|
5
|
+
.. author: Jens Lucht, 2024
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ._fetcher import fetcher
|
|
9
|
+
from ._fetcher import _decompress_npz
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _simple_holograms(name):
|
|
13
|
+
return fetcher(f"holograms_{name!s}.npz")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def radiodurans():
|
|
17
|
+
return _simple_holograms("radiodurans")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def beads():
|
|
21
|
+
return _simple_holograms("beads")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def macrophage():
|
|
25
|
+
return _simple_holograms("macrophage")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def world_holograms():
|
|
29
|
+
return _simple_holograms("world")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def spider():
|
|
33
|
+
"""
|
|
34
|
+
Single-distance dataset for direct contrast aka TIE regime phase retrieval.
|
|
35
|
+
"""
|
|
36
|
+
return _simple_holograms("spider")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def logo_holograms():
|
|
40
|
+
"""
|
|
41
|
+
deep-holographic dataset
|
|
42
|
+
"""
|
|
43
|
+
return _simple_holograms("logo")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def catparticle():
|
|
47
|
+
"""
|
|
48
|
+
Normalized holographic projections (holograms) at two defocus positions of a
|
|
49
|
+
catalytic nano-particle in the deep-holographic regime.
|
|
50
|
+
This dataset is at a single tomographic angle.
|
|
51
|
+
|
|
52
|
+
For the full tomographic dataset see https://doi.org/10.25625/CQ1EKY
|
|
53
|
+
"""
|
|
54
|
+
return fetcher("holograms_catparticle.npz.bz2", processor=_decompress_npz)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. autosummary::
|
|
3
|
+
:toctree: generated/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
.. author: Jens Lucht, 2023
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ._fetcher import fetcher
|
|
10
|
+
from ..image import dissect_levels, ball
|
|
11
|
+
import numpy as np
|
|
12
|
+
from functools import reduce
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _simple_phantom(name):
|
|
16
|
+
cnt = fetcher(f"phantom_{name!s}.npz")
|
|
17
|
+
return cnt["phantom"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def dicty():
|
|
21
|
+
return _simple_phantom("dicty")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# not an actual dataset, but simple wrapper to get a multi-component/material phantom
|
|
25
|
+
def dicty_multi(*lims):
|
|
26
|
+
"""
|
|
27
|
+
Multi-component/material ``dicty`` phantom.
|
|
28
|
+
|
|
29
|
+
See dissect_levels, dicty
|
|
30
|
+
"""
|
|
31
|
+
im = dicty()
|
|
32
|
+
return dissect_levels(im, *lims)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def world():
|
|
36
|
+
return _simple_phantom("world").astype(float)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def world_uint():
|
|
40
|
+
# dataset in original datatype
|
|
41
|
+
return _simple_phantom("world")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_default_cen_rad_dens(shape=(120, 128, 128)):
|
|
45
|
+
ref_length_radii = min(shape)
|
|
46
|
+
height = shape[0]
|
|
47
|
+
dist_center = 0.7 * min(shape[1:]) / 2
|
|
48
|
+
|
|
49
|
+
centers, radii, densities = [], [], []
|
|
50
|
+
for z, phi in zip(
|
|
51
|
+
((0.3 * height,) * 3 + (0.7 * height,)),
|
|
52
|
+
2 * np.pi * np.array((0, 0.25, 0.5, 0.25)),
|
|
53
|
+
strict=True,
|
|
54
|
+
):
|
|
55
|
+
radii.append(ref_length_radii / 15)
|
|
56
|
+
centers.append((z, dist_center, phi)) # z, r, phi
|
|
57
|
+
densities.append(1)
|
|
58
|
+
|
|
59
|
+
for i, phi in enumerate(2 * np.pi * np.arange(0, 1, 1 / 12)[:-1]):
|
|
60
|
+
radii.append(ref_length_radii * np.sqrt(i + 1) / 60)
|
|
61
|
+
centers.append((0.5 * height, dist_center, phi)) # z, r, phi
|
|
62
|
+
densities.append(2 / np.sqrt(i + 1))
|
|
63
|
+
|
|
64
|
+
centers = np.array(centers)
|
|
65
|
+
# centers: r, phi -> x, y
|
|
66
|
+
r, phi = centers[:, 1:].T
|
|
67
|
+
centers[:, 1:] = np.stack(
|
|
68
|
+
(shape[1] / 2 + r * np.cos(phi), shape[2] / 2 + r * np.sin(phi)), axis=-1
|
|
69
|
+
)
|
|
70
|
+
return centers, radii, densities
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def balls(shape=(120, 128, 128), centers=None, radii=None, densities=1, dtype=np.float32):
|
|
74
|
+
"""generate a volume containing balls of uniform density.
|
|
75
|
+
The default values yield an example volume for tomography.
|
|
76
|
+
"""
|
|
77
|
+
# preprocess parameters
|
|
78
|
+
if centers is None:
|
|
79
|
+
if len(shape) == 3:
|
|
80
|
+
centers, radii, densities = _get_default_cen_rad_dens(shape)
|
|
81
|
+
else:
|
|
82
|
+
rng = np.random.default_rng(seed=0)
|
|
83
|
+
centers = shape * rng.random((10, len(shape)))
|
|
84
|
+
else:
|
|
85
|
+
centers = np.asarray(centers)
|
|
86
|
+
if radii is None:
|
|
87
|
+
radii = min((min(shape) / 2, sum(shape) / len(shape) / 10))
|
|
88
|
+
radii = np.broadcast_to(radii, len(centers))
|
|
89
|
+
densities = np.broadcast_to(densities, len(centers))
|
|
90
|
+
|
|
91
|
+
# generate phantom
|
|
92
|
+
phantom = np.zeros(shape, dtype=dtype)
|
|
93
|
+
for r, cen, d in zip(radii, centers, densities, strict=True):
|
|
94
|
+
r_px = int(r)
|
|
95
|
+
cen_px = cen.astype(int)
|
|
96
|
+
slc = tuple( # roi in phantom
|
|
97
|
+
slice(
|
|
98
|
+
max(0, c_px - r_px),
|
|
99
|
+
c_px + r_px + 2,
|
|
100
|
+
)
|
|
101
|
+
for c_px in cen_px
|
|
102
|
+
)
|
|
103
|
+
slc_ball = tuple( # crop ball at borders of phantom
|
|
104
|
+
slice(
|
|
105
|
+
max(0, r_px - c_px),
|
|
106
|
+
r_px + s - c_px,
|
|
107
|
+
)
|
|
108
|
+
for s, c_px in zip(shape, cen_px, strict=True)
|
|
109
|
+
)
|
|
110
|
+
phantom[slc] += d * ball(len(shape) * (2 * r_px + 2,), r, center=r_px + cen % 1)[slc_ball]
|
|
111
|
+
return phantom
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def checkerboard(shape, boxsize=1):
|
|
115
|
+
ndim = len(shape)
|
|
116
|
+
boxsize = np.broadcast_to(boxsize, ndim)
|
|
117
|
+
coordinates = np.indices(shape)
|
|
118
|
+
stripes = (coordinates / (2 * np.expand_dims(boxsize, tuple(range(1, ndim + 1))))) % 1
|
|
119
|
+
stripes = (coordinates / boxsize[(slice(None),) + ndim * (None,)]) % 2 >= 1
|
|
120
|
+
return reduce(np.logical_xor, stripes)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
phantom_dicty.npz d80a8ce6b4faef56057e79bee18b2303780f340c462132b398e3b61ad045738d https://gitlab.gwdg.de/irp/datasets/dataset-phantom-dicty/-/raw/master/phantom_dicty.npz
|
|
2
|
+
phantom_world.npz 41ad9834121af5e94200927a0013cb4d9807a384790f1b157b76f87bac569374 https://gitlab.gwdg.de/irp/datasets/dataset-phantom-world/-/raw/master/phantom_world.npz
|
|
3
|
+
holograms_radiodurans.npz 30d1b234124a6f4dd1d0c86bf24429c7ba509cf0e4b2cbdd71c8300f03e76873 https://gitlab.gwdg.de/irp/datasets/dataset-holograms-radiodurans/-/raw/master/holograms_radiodurans.npz
|
|
4
|
+
holograms_beads.npz 27b6648c526f9272e6d492aa7f1de3a35172351fb0ecb84abc68e6711b6b9495 https://gitlab.gwdg.de/irp/datasets/dataset-holograms-beads/-/raw/master/holograms_beads.npz
|
|
5
|
+
holograms_macrophage.npz 2f50a1a06b09cc8cf2246742daec4412fd638f005da5e84892fe3f33a9195c95 https://gitlab.gwdg.de/irp/datasets/dataset-holograms-macrophage/-/raw/master/holograms_macrophage.npz
|
|
6
|
+
holograms_world.npz e9856299ad9d392b5253b3cdfe10edcb08f2e4aca3d67c48d645f74d11a4c78e https://gitlab.gwdg.de/irp/datasets/dataset-holograms-world/-/raw/master/holograms_world.npz
|
|
7
|
+
holograms_spider.npz ea226167db6668b564698d7ce1c2f5c4c61da0ff3b9568ab6e08be1d71ce0dc1 https://gitlab.gwdg.de/irp/datasets/dataset-holograms-spider/-/raw/master/holograms_spider.npz
|
|
8
|
+
holograms_logo.npz 45ae0903c4e79d3b1d56ab880bc290382bfec66577401ab9d01002aace3aad75 https://gitlab.gwdg.de/irp/datasets/dataset-holograms-logo/-/raw/main/holograms_logo.npz
|
|
9
|
+
holograms_catparticle.npz.bz2 d058890d4985706124286cfa608d2a2b879814a1b2683b9272ed8b822e88515f https://gitlab.gwdg.de/irp/datasets/dataset-holograms-catparticle/-/raw/master/holograms_catparticle.npz.bz2
|
hotopy/holo/__init__.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
=========================================================
|
|
3
|
+
Holographic phase retrieval methods (:mod:`hotopy.holo`)
|
|
4
|
+
=========================================================
|
|
5
|
+
|
|
6
|
+
.. currentmodule:: hotopy.holo
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Submodules
|
|
10
|
+
==========
|
|
11
|
+
|
|
12
|
+
.. autosummary::
|
|
13
|
+
:toctree: generated/
|
|
14
|
+
|
|
15
|
+
propagation
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
(Deep) Holographic regime phase retrieval
|
|
19
|
+
-----------------------------------------
|
|
20
|
+
|
|
21
|
+
For Fresnel numbers << 1 and monochromatic sources, e.g. at synchrotrons.
|
|
22
|
+
|
|
23
|
+
.. autosummary::
|
|
24
|
+
:toctree: generated/
|
|
25
|
+
|
|
26
|
+
CTF
|
|
27
|
+
Tikhonov
|
|
28
|
+
TikhonovTV
|
|
29
|
+
AP
|
|
30
|
+
ICT
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Transport of Intensity Equation based methods (TIE)
|
|
34
|
+
---------------------------------------------------
|
|
35
|
+
|
|
36
|
+
Transport of Intensity (TIE) used for laboratory X-ray sources.
|
|
37
|
+
|
|
38
|
+
.. autosummary::
|
|
39
|
+
:toctree: generated/
|
|
40
|
+
|
|
41
|
+
BronnikovAidedCorrection
|
|
42
|
+
ModifiedBronnikov
|
|
43
|
+
Paganin
|
|
44
|
+
GeneralizedPaganin
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Propagation methods
|
|
48
|
+
-------------------
|
|
49
|
+
|
|
50
|
+
Convenience imports from ``propagation`` submodule.
|
|
51
|
+
|
|
52
|
+
.. autosummary::
|
|
53
|
+
:toctree: generated/
|
|
54
|
+
|
|
55
|
+
simulate_hologram
|
|
56
|
+
|
|
57
|
+
Helpers
|
|
58
|
+
-------
|
|
59
|
+
|
|
60
|
+
.. autosummary::
|
|
61
|
+
:toctree: generated/
|
|
62
|
+
|
|
63
|
+
Constraints
|
|
64
|
+
rescale_defocus_series
|
|
65
|
+
rescale_defocus_fresnel_numbers
|
|
66
|
+
twolevel_regularization
|
|
67
|
+
ctf_erf_filter
|
|
68
|
+
erf_filter
|
|
69
|
+
find_fresnel_number
|
|
70
|
+
pca_decompose_flats
|
|
71
|
+
pca_synthesize_flat
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
from .propagation import (
|
|
75
|
+
FresnelTFPropagator,
|
|
76
|
+
simulate_hologram,
|
|
77
|
+
expand_fresnel_numbers,
|
|
78
|
+
)
|
|
79
|
+
from .constraints import Constraints, WaveConstraints
|
|
80
|
+
from ._tieregime import BronnikovAidedCorrection, ModifiedBronnikov, Paganin, GeneralizedPaganin
|
|
81
|
+
from ._ctf import CTF
|
|
82
|
+
from ._tikhonov import Tikhonov, TikhonovTV, nonlinearity_low_freq_correction
|
|
83
|
+
from ._ap import AP
|
|
84
|
+
from ._pbi import ICT
|
|
85
|
+
from .regularization import erf_filter, ctf_erf_filter, twolevel_regularization
|
|
86
|
+
from ._util import (
|
|
87
|
+
check_fresnel_number,
|
|
88
|
+
rescale_defocus_series,
|
|
89
|
+
rescale_defocus_fresnel_numbers,
|
|
90
|
+
find_fresnel_number,
|
|
91
|
+
difference_fresnel_numbers,
|
|
92
|
+
flatfield_inpainting_correction,
|
|
93
|
+
)
|
|
94
|
+
from ._pca import pca_decompose_flats, pca_synthesize_flat, pca_decompose_arpack
|
|
95
|
+
|
|
96
|
+
__all__ = [
|
|
97
|
+
"FresnelTFPropagator",
|
|
98
|
+
"simulate_hologram",
|
|
99
|
+
"expand_fresnel_numbers",
|
|
100
|
+
"BronnikovAidedCorrection",
|
|
101
|
+
"ModifiedBronnikov",
|
|
102
|
+
"Paganin",
|
|
103
|
+
"GeneralizedPaganin",
|
|
104
|
+
"CTF",
|
|
105
|
+
"Tikhonov",
|
|
106
|
+
"TikhonovTV",
|
|
107
|
+
"ICT",
|
|
108
|
+
"AP",
|
|
109
|
+
"Constraints",
|
|
110
|
+
"WaveConstraints",
|
|
111
|
+
"nonlinearity_low_freq_correction",
|
|
112
|
+
"erf_filter",
|
|
113
|
+
"ctf_erf_filter",
|
|
114
|
+
"twolevel_regularization",
|
|
115
|
+
"check_fresnel_number",
|
|
116
|
+
"rescale_defocus_series",
|
|
117
|
+
"rescale_defocus_fresnel_numbers",
|
|
118
|
+
"find_fresnel_number",
|
|
119
|
+
"difference_fresnel_numbers",
|
|
120
|
+
"flatfield_inpainting_correction",
|
|
121
|
+
"pca_decompose_flats",
|
|
122
|
+
"pca_synthesize_flat",
|
|
123
|
+
"pca_decompose_arpack",
|
|
124
|
+
]
|
hotopy/holo/_ap.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Jens Lucht
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from numpy import ndarray, newaxis
|
|
6
|
+
from torch import ones, as_tensor
|
|
7
|
+
|
|
8
|
+
from ..optimize import AlternatingProjections
|
|
9
|
+
from .propagation import FresnelTFPropagator
|
|
10
|
+
from .constraints import AmplitudeProjector, ConstraintOperator, WaveConstraints
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AP:
|
|
14
|
+
"""Alternating projections for holographic phase retrieval."""
|
|
15
|
+
|
|
16
|
+
algorithm = AlternatingProjections
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
shape,
|
|
21
|
+
fresnel_nums,
|
|
22
|
+
ndim=2,
|
|
23
|
+
dtype=None,
|
|
24
|
+
device=None,
|
|
25
|
+
):
|
|
26
|
+
self.dtype = dtype
|
|
27
|
+
self.device = device
|
|
28
|
+
self.ndim = ndim
|
|
29
|
+
|
|
30
|
+
self.propagator = FresnelTFPropagator(
|
|
31
|
+
shape,
|
|
32
|
+
fresnel_nums,
|
|
33
|
+
dtype=dtype,
|
|
34
|
+
device=device,
|
|
35
|
+
keep_type=False,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def __call__(
|
|
39
|
+
self,
|
|
40
|
+
holograms,
|
|
41
|
+
constraints=None,
|
|
42
|
+
initial_guess=None,
|
|
43
|
+
max_iter: int = 100,
|
|
44
|
+
keep_type=False,
|
|
45
|
+
):
|
|
46
|
+
"""Reconstruct holograms.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
holograms :
|
|
51
|
+
Intensities measured at detector.
|
|
52
|
+
constraints :
|
|
53
|
+
Constraints projectors for object/sample. Default Amplitude = 1 constrain for pure-phase object.
|
|
54
|
+
max_iter : int, Optional
|
|
55
|
+
Maximal number of iterations.
|
|
56
|
+
"""
|
|
57
|
+
holograms_t = type(holograms)
|
|
58
|
+
holograms = as_tensor(holograms, device=self.device)
|
|
59
|
+
shape = holograms.shape[-self.ndim :]
|
|
60
|
+
|
|
61
|
+
# if single image is entered expand to stack of one image
|
|
62
|
+
single_image = holograms.ndim == self.ndim
|
|
63
|
+
if single_image:
|
|
64
|
+
holograms = holograms[newaxis]
|
|
65
|
+
|
|
66
|
+
projector_holos = AmplitudeProjector(holograms.sqrt())
|
|
67
|
+
projector_object = (
|
|
68
|
+
constraints if constraints is not None else WaveConstraints(betadelta=0.0)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# ensure correct device placement
|
|
72
|
+
if isinstance(projector_object, ConstraintOperator):
|
|
73
|
+
projector_object.to_device(self.device)
|
|
74
|
+
|
|
75
|
+
# initialize with plane wave (ones) or initial guess if given
|
|
76
|
+
if initial_guess is None:
|
|
77
|
+
x = ones(shape, device=self.device, dtype=holograms.dtype)
|
|
78
|
+
else:
|
|
79
|
+
x = as_tensor(initial_guess, device=self.device)
|
|
80
|
+
|
|
81
|
+
# AP
|
|
82
|
+
ap = self.algorithm(
|
|
83
|
+
self.propagator,
|
|
84
|
+
(projector_object, projector_holos),
|
|
85
|
+
x,
|
|
86
|
+
max_iter=max_iter,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# iterate util stopping condition is met
|
|
90
|
+
while not ap.done():
|
|
91
|
+
ap.step()
|
|
92
|
+
|
|
93
|
+
# recast into numpy if requested
|
|
94
|
+
if keep_type and holograms_t is ndarray:
|
|
95
|
+
out = ap.x.cpu().numpy()
|
|
96
|
+
else:
|
|
97
|
+
out = ap.x
|
|
98
|
+
|
|
99
|
+
# remove stack axis if single image
|
|
100
|
+
if single_image:
|
|
101
|
+
out = out[0]
|
|
102
|
+
|
|
103
|
+
return out
|