structured-optics 0.6__tar.gz → 0.6.2__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.
- {structured_optics-0.6/structured_optics.egg-info → structured_optics-0.6.2}/PKG-INFO +8 -4
- {structured_optics-0.6 → structured_optics-0.6.2}/README.md +7 -3
- {structured_optics-0.6 → structured_optics-0.6.2}/pyproject.toml +4 -1
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics/__init__.py +1 -1
- structured_optics-0.6.2/structured_optics/bluestein.py +229 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics/diagnostics.py +0 -17
- structured_optics-0.6.2/structured_optics/hologram.py +133 -0
- structured_optics-0.6.2/structured_optics/masks.py +410 -0
- structured_optics-0.6.2/structured_optics/modes.py +751 -0
- structured_optics-0.6.2/structured_optics/polarization.py +261 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics/prop_methods.py +117 -10
- structured_optics-0.6.2/structured_optics/slmdisplay.py +72 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics/struct_opt.py +92 -227
- structured_optics-0.6.2/structured_optics/tools.py +301 -0
- structured_optics-0.6.2/structured_optics/utils.py +883 -0
- {structured_optics-0.6 → structured_optics-0.6.2/structured_optics.egg-info}/PKG-INFO +8 -4
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics.egg-info/SOURCES.txt +1 -1
- structured_optics-0.6/structured_optics/algebra_utils.py +0 -76
- structured_optics-0.6/structured_optics/bluestein.py +0 -118
- structured_optics-0.6/structured_optics/hologram.py +0 -63
- structured_optics-0.6/structured_optics/masks.py +0 -206
- structured_optics-0.6/structured_optics/modes.py +0 -309
- structured_optics-0.6/structured_optics/polarization.py +0 -76
- structured_optics-0.6/structured_optics/slmdisplay.py +0 -160
- structured_optics-0.6/structured_optics/utils.py +0 -471
- {structured_optics-0.6 → structured_optics-0.6.2}/LICENSE +0 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/setup.cfg +0 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics.egg-info/dependency_links.txt +0 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics.egg-info/requires.txt +0 -0
- {structured_optics-0.6 → structured_optics-0.6.2}/structured_optics.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structured_optics
|
|
3
|
-
Version: 0.6
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Package for Structured Light simulation
|
|
5
5
|
Author: Altilano C. Barbosa
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -59,18 +59,22 @@ beam.propagate(z=0.1, method='fres_c')
|
|
|
59
59
|
# Look at the intensity profile
|
|
60
60
|
import matplotlib.pyplot as plt
|
|
61
61
|
plt.pcolormesh(beam.x, beam.y, beam.int_profile())
|
|
62
|
+
plt.colorbar()
|
|
62
63
|
plt.show()
|
|
63
64
|
```
|
|
64
65
|
|
|
65
66
|
Expected output:
|
|
66
|
-
|
|
67
|
+
<p align="center">
|
|
68
|
+
<img src="https://github.com/Altilano/structured-optics/blob/main/examples/image_examples/LG_20%20prop.png" width="400" alt="LG20">
|
|
69
|
+
</p>
|
|
70
|
+
|
|
67
71
|
|
|
68
72
|
## Documentation
|
|
69
73
|
|
|
70
74
|
Full API documentation, including all mode types, propagation methods, and
|
|
71
75
|
optical elements, is available at:
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
(https://altilano.github.io/structured-optics/)
|
|
74
78
|
|
|
75
79
|
## Requirements
|
|
76
80
|
|
|
@@ -96,7 +100,7 @@ This project is licensed under the BSD-3-Clause License. See `LICENSE` for detai
|
|
|
96
100
|
|
|
97
101
|
- Documentation.
|
|
98
102
|
- Benchmark and optimization.
|
|
99
|
-
- Support for high NA focalization. (Non paraxial propagation)
|
|
103
|
+
- Support for high NA focalization. (Non paraxial propagation, no scalar field)
|
|
100
104
|
- Cuda and ROCm speed up.
|
|
101
105
|
- Support for propagation inside linear medium.
|
|
102
106
|
|
|
@@ -45,18 +45,22 @@ beam.propagate(z=0.1, method='fres_c')
|
|
|
45
45
|
# Look at the intensity profile
|
|
46
46
|
import matplotlib.pyplot as plt
|
|
47
47
|
plt.pcolormesh(beam.x, beam.y, beam.int_profile())
|
|
48
|
+
plt.colorbar()
|
|
48
49
|
plt.show()
|
|
49
50
|
```
|
|
50
51
|
|
|
51
52
|
Expected output:
|
|
52
|
-
|
|
53
|
+
<p align="center">
|
|
54
|
+
<img src="https://github.com/Altilano/structured-optics/blob/main/examples/image_examples/LG_20%20prop.png" width="400" alt="LG20">
|
|
55
|
+
</p>
|
|
56
|
+
|
|
53
57
|
|
|
54
58
|
## Documentation
|
|
55
59
|
|
|
56
60
|
Full API documentation, including all mode types, propagation methods, and
|
|
57
61
|
optical elements, is available at:
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
(https://altilano.github.io/structured-optics/)
|
|
60
64
|
|
|
61
65
|
## Requirements
|
|
62
66
|
|
|
@@ -82,7 +86,7 @@ This project is licensed under the BSD-3-Clause License. See `LICENSE` for detai
|
|
|
82
86
|
|
|
83
87
|
- Documentation.
|
|
84
88
|
- Benchmark and optimization.
|
|
85
|
-
- Support for high NA focalization. (Non paraxial propagation)
|
|
89
|
+
- Support for high NA focalization. (Non paraxial propagation, no scalar field)
|
|
86
90
|
- Cuda and ROCm speed up.
|
|
87
91
|
- Support for propagation inside linear medium.
|
|
88
92
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "structured_optics"
|
|
7
|
-
version = "0.6"
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
description = "Package for Structured Light simulation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -18,3 +18,6 @@ dependencies = [
|
|
|
18
18
|
"scipy",
|
|
19
19
|
"screeninfo"
|
|
20
20
|
]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools]
|
|
23
|
+
packages = ["structured_optics"]
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def fft_bluestein(f, dx, dk, D_out=None, x0=0.0, k0=0.0, inverse=False):
|
|
5
|
+
"""
|
|
6
|
+
Generalized 1D Fourier transform using Bluestein's algorithm
|
|
7
|
+
(chirp-z transform).
|
|
8
|
+
|
|
9
|
+
Unlike a standard FFT, this evaluates the continuous Fourier
|
|
10
|
+
integral on arbitrary, independently chosen input/output sample
|
|
11
|
+
spacings (`dx`, `dk`) and offsets (`x0`, `k0`), and can change the
|
|
12
|
+
number of samples from `D` (input) to `D_out` (output). It works by
|
|
13
|
+
rewriting the transform as a linear convolution (via the standard
|
|
14
|
+
Bluestein/chirp-z trick) and evaluating that convolution with zero-
|
|
15
|
+
padded FFTs, so it costs O((D + D_out) log(D + D_out)) rather than
|
|
16
|
+
the O(D * D_out) of a direct sum.
|
|
17
|
+
|
|
18
|
+
Forward:
|
|
19
|
+
F(k) = integral of f(x) * exp(-i k x) dx
|
|
20
|
+
|
|
21
|
+
Inverse:
|
|
22
|
+
f(x) = 1/(2*pi) * integral of F(k) * exp(+i k x) dk
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
f : array_like
|
|
27
|
+
Input samples. The transform is taken along the last axis, so
|
|
28
|
+
`f` may have leading batch dimensions (shape `(..., D)`).
|
|
29
|
+
dx : float
|
|
30
|
+
Input sample spacing. Must be positive. In inverse mode, this
|
|
31
|
+
is the desired *output* spacing (of `x`).
|
|
32
|
+
dk : float
|
|
33
|
+
Output sample spacing. Must be nonzero. In inverse mode, this
|
|
34
|
+
is the *input* spacing (of `k`/`F`).
|
|
35
|
+
D_out : int, optional
|
|
36
|
+
Number of output samples. Defaults to `D` (the input length,
|
|
37
|
+
i.e. `f.shape[-1]`), giving a same-length transform.
|
|
38
|
+
x0 : float, optional
|
|
39
|
+
Origin of the `x` grid (forward: input origin; inverse: output
|
|
40
|
+
origin). Defaults to 0.0.
|
|
41
|
+
k0 : float, optional
|
|
42
|
+
Origin of the `k` grid (forward: output origin; inverse: input
|
|
43
|
+
origin). Defaults to 0.0.
|
|
44
|
+
inverse : bool, optional
|
|
45
|
+
If False (default), compute the forward transform
|
|
46
|
+
`f(x) -> F(k)`. If True, compute the inverse transform
|
|
47
|
+
`F(k) -> f(x)` (with `f` here holding the input spectrum
|
|
48
|
+
samples).
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
result : np.ndarray
|
|
53
|
+
Forward mode: the transformed array `F`, of shape
|
|
54
|
+
`(..., D_out)`.
|
|
55
|
+
Inverse mode: the reconstructed array `f`, of shape
|
|
56
|
+
`(..., D_out)`.
|
|
57
|
+
coords : np.ndarray
|
|
58
|
+
Forward mode: the output `k` grid, `k0 + m*dk` for
|
|
59
|
+
`m = 0, ..., D_out-1`.
|
|
60
|
+
Inverse mode: the output `x` grid, `x0 + m*dx` for
|
|
61
|
+
`m = 0, ..., D_out-1`.
|
|
62
|
+
|
|
63
|
+
Raises
|
|
64
|
+
------
|
|
65
|
+
ValueError
|
|
66
|
+
If the input is empty, `D_out` is not positive, `dx` is not
|
|
67
|
+
positive, or `dk` is zero.
|
|
68
|
+
|
|
69
|
+
Reference
|
|
70
|
+
---------
|
|
71
|
+
[1] Hu, Yanlei, et al. "Efficient full-path optical calculation of scalar and vector diffraction using the Bluestein method."
|
|
72
|
+
Light: Science & Applications 9.1 (2020): 119.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
f = np.asarray(f)
|
|
77
|
+
D = f.shape[-1]
|
|
78
|
+
|
|
79
|
+
if D_out is None:
|
|
80
|
+
D_out = D
|
|
81
|
+
if D < 1:
|
|
82
|
+
raise ValueError("Input cannot be empty.")
|
|
83
|
+
if D_out < 1:
|
|
84
|
+
raise ValueError("D_out must be positive.")
|
|
85
|
+
if dx <= 0:
|
|
86
|
+
raise ValueError("dx must be positive.")
|
|
87
|
+
if dk == 0:
|
|
88
|
+
raise ValueError("dk cannot be zero.")
|
|
89
|
+
|
|
90
|
+
n = np.arange(D)
|
|
91
|
+
m = np.arange(D_out)
|
|
92
|
+
a = dx * dk
|
|
93
|
+
|
|
94
|
+
# FORWARD
|
|
95
|
+
if not inverse:
|
|
96
|
+
# Chirps
|
|
97
|
+
chirp_n = np.exp(-0.5j * a * n**2)
|
|
98
|
+
chirp_m = np.exp(-0.5j * a * m**2)
|
|
99
|
+
|
|
100
|
+
# Coordinate-origin phases
|
|
101
|
+
input_phase = np.exp(-1j * k0 * dx * n)
|
|
102
|
+
output_phase = np.exp(-1j * x0 * dk * m)
|
|
103
|
+
global_phase = np.exp(-1j * k0 * x0)
|
|
104
|
+
|
|
105
|
+
# Input chirp
|
|
106
|
+
a_input = (f * input_phase * chirp_n)
|
|
107
|
+
|
|
108
|
+
# Convolution kernel
|
|
109
|
+
q = np.arange(-(D - 1),D_out)
|
|
110
|
+
kernel = np.exp(+0.5j * a * q**2)
|
|
111
|
+
|
|
112
|
+
# Linear convolution length
|
|
113
|
+
L = D + D_out - 1
|
|
114
|
+
|
|
115
|
+
# FFT padding
|
|
116
|
+
P = 1 << (L - 1).bit_length()
|
|
117
|
+
|
|
118
|
+
A = np.fft.fft(a_input, P, axis=-1)
|
|
119
|
+
B = np.fft.fft(kernel, P)
|
|
120
|
+
B = B.reshape((1,) * (f.ndim - 1) + (P,))
|
|
121
|
+
convolution = np.fft.ifft(A * B, axis=-1)
|
|
122
|
+
|
|
123
|
+
# Extract m = 0,...,D_out-1
|
|
124
|
+
convolution = np.take(convolution, np.arange(D - 1, D - 1 + D_out), axis=-1)
|
|
125
|
+
|
|
126
|
+
# Final result
|
|
127
|
+
F = (dx * global_phase * output_phase * chirp_m * convolution)
|
|
128
|
+
k = k0 + m * dk
|
|
129
|
+
|
|
130
|
+
return F, k
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# INVERSE
|
|
134
|
+
else:
|
|
135
|
+
# Positive exponential
|
|
136
|
+
chirp_n = np.exp(+0.5j * a * n**2)
|
|
137
|
+
chirp_m = np.exp(+0.5j * a * m**2)
|
|
138
|
+
input_phase = np.exp(+1j * x0 * dk * n)
|
|
139
|
+
output_phase = np.exp(+1j * k0 * dx * m)
|
|
140
|
+
global_phase = np.exp(+1j * k0 * x0)
|
|
141
|
+
a_input = (f * input_phase * chirp_n)
|
|
142
|
+
|
|
143
|
+
# Correct convolution kernel
|
|
144
|
+
q = np.arange(-(D - 1), D_out)
|
|
145
|
+
kernel = np.exp(-0.5j * a * q**2)
|
|
146
|
+
L = D + D_out - 1
|
|
147
|
+
P = 1 << (L - 1).bit_length()
|
|
148
|
+
A = np.fft.fft(a_input, P, axis=-1)
|
|
149
|
+
B = np.fft.fft(kernel, P)
|
|
150
|
+
B = B.reshape((1,) * (f.ndim - 1) + (P,))
|
|
151
|
+
convolution = np.fft.ifft(A * B, axis=-1)
|
|
152
|
+
convolution = np.take(convolution, np.arange(D - 1, D - 1 + D_out), axis=-1)
|
|
153
|
+
result = (dk/(2*np.pi) * global_phase * output_phase * chirp_m * convolution)
|
|
154
|
+
x = x0 + m * dx
|
|
155
|
+
return result, x
|
|
156
|
+
|
|
157
|
+
def fft2_bluestein(field, dx, dy, dkx, dky, Dx_out=None, Dy_out=None, x0=0.0, y0=0.0, kx0=0.0, ky0=0.0):
|
|
158
|
+
"""
|
|
159
|
+
Generalized 2D (forward) Fourier transform using Bluestein's
|
|
160
|
+
algorithm, applied separably along each axis.
|
|
161
|
+
|
|
162
|
+
Performs `fft_bluestein` along the last axis (x), then along the
|
|
163
|
+
remaining axis (y), i.e. a separable 2D chirp-z transform with
|
|
164
|
+
independently chosen sample spacing, sample count, and origin for
|
|
165
|
+
each axis.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
field : array_like
|
|
170
|
+
2D input array of shape `(Dy, Dx)`.
|
|
171
|
+
dx : float
|
|
172
|
+
Input sample spacing along x.
|
|
173
|
+
dy : float
|
|
174
|
+
Input sample spacing along y.
|
|
175
|
+
dkx : float
|
|
176
|
+
Output sample spacing along kx. Must be nonzero.
|
|
177
|
+
dky : float
|
|
178
|
+
Output sample spacing along ky. Must be nonzero.
|
|
179
|
+
Dx_out : int, optional
|
|
180
|
+
Number of output samples along x. Defaults to `Dx`
|
|
181
|
+
(`field.shape[1]`).
|
|
182
|
+
Dy_out : int, optional
|
|
183
|
+
Number of output samples along y. Defaults to `Dy`
|
|
184
|
+
(`field.shape[0]`).
|
|
185
|
+
x0 : float, optional
|
|
186
|
+
Origin of the input x grid. Defaults to 0.0.
|
|
187
|
+
y0 : float, optional
|
|
188
|
+
Origin of the input y grid. Defaults to 0.0.
|
|
189
|
+
kx0 : float, optional
|
|
190
|
+
Origin of the output kx grid. Defaults to 0.0.
|
|
191
|
+
ky0 : float, optional
|
|
192
|
+
Origin of the output ky grid. Defaults to 0.0.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
F : np.ndarray
|
|
197
|
+
Transformed array, of shape `(Dy_out, Dx_out)`.
|
|
198
|
+
kx : np.ndarray
|
|
199
|
+
Output kx grid, `kx0 + m*dkx` for `m = 0, ..., Dx_out-1`.
|
|
200
|
+
ky : np.ndarray
|
|
201
|
+
Output ky grid, `ky0 + m*dky` for `m = 0, ..., Dy_out-1`.
|
|
202
|
+
|
|
203
|
+
Raises
|
|
204
|
+
------
|
|
205
|
+
ValueError
|
|
206
|
+
If `field` is not 2-dimensional, or if any of the underlying
|
|
207
|
+
`fft_bluestein` calls raise (e.g. non-positive spacing, zero
|
|
208
|
+
`dk`).
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
field = np.asarray(field)
|
|
212
|
+
if field.ndim != 2:
|
|
213
|
+
raise ValueError("bluestein_fft2 expects a 2D array.")
|
|
214
|
+
|
|
215
|
+
Dy, Dx = field.shape
|
|
216
|
+
if Dx_out is None:
|
|
217
|
+
Dx_out = Dx
|
|
218
|
+
if Dy_out is None:
|
|
219
|
+
Dy_out = Dy
|
|
220
|
+
|
|
221
|
+
# x transform
|
|
222
|
+
F, kx = fft_bluestein(field, dx=dx, dk=dkx, D_out=Dx_out, x0=x0, k0=kx0)
|
|
223
|
+
|
|
224
|
+
# y transform
|
|
225
|
+
F = np.swapaxes(F, -1, -2)
|
|
226
|
+
F, ky = fft_bluestein(F, dx=dy, dk=dky, D_out=Dy_out, x0=y0, k0=ky0)
|
|
227
|
+
F = np.swapaxes(F, -1, -2)
|
|
228
|
+
return F, kx, ky
|
|
229
|
+
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
|
|
3
2
|
from .utils import get_section
|
|
4
3
|
|
|
5
4
|
|
|
@@ -260,21 +259,5 @@ class BeamDiagnostics:
|
|
|
260
259
|
return self
|
|
261
260
|
|
|
262
261
|
|
|
263
|
-
"""
|
|
264
|
-
Parameters
|
|
265
|
-
----------
|
|
266
|
-
center : tuple of float, optional
|
|
267
|
-
(x, y) center of the crop window. Defaults to the beam's own centroid.
|
|
268
|
-
std : float, optional
|
|
269
|
-
Standard deviation used to size the crop window. Defaults to the
|
|
270
|
-
beam's own computed std.
|
|
271
|
-
window : float, optional
|
|
272
|
-
Multiplier on `std` defining the half-width of the crop region.
|
|
273
262
|
|
|
274
|
-
Returns
|
|
275
|
-
-------
|
|
276
|
-
Beam
|
|
277
|
-
A new, spatially cropped Beam.
|
|
278
|
-
"""
|
|
279
|
-
return get_crop(self, center, std, window)
|
|
280
263
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from .utils import inv_J0, inv_J1, inv_sinc
|
|
3
|
+
|
|
4
|
+
__all__ =["slm_hologram", "dmd_hologram"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def slm_hologram(beam, x_grating:int, y_grating:int, method:str = 'bessel1', input_beam:object= None,
|
|
9
|
+
eps:float = np.finfo(float).eps, max_range:int = 255)-> np.ndarray:
|
|
10
|
+
"""
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
x_grating : int
|
|
14
|
+
Carrier grating spatial frequency along x (grid pixels).
|
|
15
|
+
y_grating : int
|
|
16
|
+
Carrier grating spatial frequency along y (grid pixels).
|
|
17
|
+
method : str, optional
|
|
18
|
+
Amplitude/phase encoding scheme used to build the hologram.
|
|
19
|
+
input_beam : Beam, optional
|
|
20
|
+
Reference illumination beam; defaults to self if None.
|
|
21
|
+
eps : float, optional
|
|
22
|
+
Small value added to avoid division by zero during encoding.
|
|
23
|
+
max_range : int, optional
|
|
24
|
+
Output gray-level range (e.g. 255 for an 8-bit SLM lookup table).
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
ndarray
|
|
29
|
+
Encoded hologram pattern, shape (Dy, Dx), ready for SLM display.
|
|
30
|
+
|
|
31
|
+
Reference
|
|
32
|
+
---------
|
|
33
|
+
[1] T. Clark, R. Offer, S. Franke-Arnold, A. Arnold, and N. Radwell,
|
|
34
|
+
"Comparison of beam generation techniques using a phase only spatial light modulator,"
|
|
35
|
+
Opt. Express 24, 6249-6264 (2016).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
dx = (beam.x[0,1] - beam.x[0,0])
|
|
39
|
+
dy = (beam.y[1,0] - beam.y[0,0])
|
|
40
|
+
if input_beam is None:
|
|
41
|
+
input_field = np.exp(-((beam.x**2 + beam.y**2)/(2*np.max(beam.x))**2))
|
|
42
|
+
else:
|
|
43
|
+
input_field = input_beam.Ex
|
|
44
|
+
desired = beam.Ex/np.max(np.abs(beam.Ex))
|
|
45
|
+
lamb_x = dx*x_grating
|
|
46
|
+
lamb_y = dy*y_grating
|
|
47
|
+
Ain = np.abs(input_field)
|
|
48
|
+
Ain = Ain / Ain.max()
|
|
49
|
+
Ades = np.abs(desired)
|
|
50
|
+
s = np.min(Ain / (Ades + eps))
|
|
51
|
+
Ades = s * Ades
|
|
52
|
+
A_rel = Ades / Ain
|
|
53
|
+
phi_g = np.mod(2*np.pi*(beam.x/lamb_x + beam.y/lamb_y), 2*np.pi)
|
|
54
|
+
phi_relg = np.angle(desired) - np.angle(input_field) + phi_g
|
|
55
|
+
if method == 'simple':
|
|
56
|
+
H = A_rel*phi_relg
|
|
57
|
+
elif method == 'g_holo':
|
|
58
|
+
edg = desired*np.exp(1j*phi_g)
|
|
59
|
+
N = np.min(np.abs(input_field)/(np.abs(desired) + 1e-9))
|
|
60
|
+
H = np.angle(N*edg + input_field)
|
|
61
|
+
elif method == 'davis':
|
|
62
|
+
H = (1-inv_sinc(A_rel)/np.pi)*phi_relg
|
|
63
|
+
elif method == 'bolduc':
|
|
64
|
+
M = 1+inv_sinc(A_rel)/np.pi
|
|
65
|
+
H = M*(phi_relg - np.pi*M)
|
|
66
|
+
elif method == 'bessel0':
|
|
67
|
+
H = phi_relg + inv_J0(A_rel)*np.sin(phi_relg)
|
|
68
|
+
elif method == 'bessel1':
|
|
69
|
+
H = inv_J1(A_rel)*np.sin(phi_relg)
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError('Error! method must be simple, g_holo, davis, bolduc, bessel0 or bessel1.')
|
|
72
|
+
H = max_range * (H - H.min()) / (H.max() - H.min())
|
|
73
|
+
return H.astype(np.uint8)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def dmd_hologram(beam, cx:float, cy:float, sign:int=1, input:object=None)-> np.ndarray:
|
|
77
|
+
"""
|
|
78
|
+
Generate a binary-amplitude DMD hologram.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
beam : Beam
|
|
83
|
+
Desired output beam. Its Ex component defines the target amplitude and phase.
|
|
84
|
+
cx, cy : float
|
|
85
|
+
Carrier frequencies expressed in DMD pixel units.
|
|
86
|
+
sign : int
|
|
87
|
+
Controls the binary hologram orientation. Usually +1 or -1.
|
|
88
|
+
input : Beam
|
|
89
|
+
Optional Incident beam illuminating the DMD. If None, the incident beam is assumed
|
|
90
|
+
to have uniform amplitude and zero phase, reproducing the original behavior.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
H : ndarray
|
|
95
|
+
Binary DMD hologram.
|
|
96
|
+
|
|
97
|
+
Reference
|
|
98
|
+
---------
|
|
99
|
+
[1] Scholes, Stirling, et al. "Structured light with digital micromirror devices: a guide to best practice."
|
|
100
|
+
Optical Engineering 59.4 (2020): 041202-041202."""
|
|
101
|
+
|
|
102
|
+
# Target field
|
|
103
|
+
target = beam.Ex
|
|
104
|
+
# Target amplitude and phase
|
|
105
|
+
target_amp = np.abs(target)
|
|
106
|
+
target_phase = np.angle(target)
|
|
107
|
+
|
|
108
|
+
if input is None:
|
|
109
|
+
U = target_amp / np.amax(target_amp)
|
|
110
|
+
phi = target_phase
|
|
111
|
+
else:
|
|
112
|
+
input_field = input.Ex
|
|
113
|
+
input_amp = np.abs(input_field)
|
|
114
|
+
input_phase = np.angle(input_field)
|
|
115
|
+
|
|
116
|
+
eps = np.finfo(float).eps
|
|
117
|
+
U = target_amp / np.maximum(input_amp, eps)
|
|
118
|
+
phi = target_phase - input_phase
|
|
119
|
+
max_U = np.amax(U)
|
|
120
|
+
if max_U > 0:
|
|
121
|
+
U = U / max_U
|
|
122
|
+
|
|
123
|
+
A = np.arcsin(U)
|
|
124
|
+
fx = cx * beam.Dx / (2 * beam.nix)
|
|
125
|
+
fy = cy * beam.Dy / (2 * beam.niy)
|
|
126
|
+
G = fx * beam.x + fy * beam.y
|
|
127
|
+
|
|
128
|
+
H = 0.5 + sign * 0.5 * np.sign( np.cos(phi + 2 * np.pi * G) - np.cos(A) )
|
|
129
|
+
return H
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|