%%html
<style>
.container-fluid.main-container {
margin-left: 3em;
margin-right: 8em;
max-width: 100vw;
}
</style>
Introduction¶
This notebook showcases different functionalities of the GVS propagator, which is an efficient algorithm for simulating partially coherent light. The model uses the Van Cittert-Zernike theorem to find the complex coherence factor from a light source reaching the object plane, and it then uses the generalized Schell’s theorem to compute the diffraction pattern produced at a given propagation distance, not limited to the far field.
Eq. (1) gives the numerical propagation of GVS, with $\mu_{\mathcal{F}}$ given in Eq. (2). GVS also supports the use of any existing coherent propagator, as shown in Eq. (3), where $I_{\textrm{coherent}}$ is calculated with the object multiplied by a quadratic phase factor, $P(\xi, \eta) e^{i\frac{k}{2 z_{so}}(\xi^2 + \eta ^2)}$.
$$ I \left( \frac{x}{\lambda z_{oc}}, \frac{y}{\lambda z_{oc}} \right) = \frac{I_0}{(\lambda z_{oc})^2} \pmb{F} \left\{ \pmb{F^{-1}} \left\{ \left\| \pmb{F} \left\{ P(\xi, \eta) e^{i\frac{k}{2} \left ( \frac{1}{z_{so}} + \frac{1}{z_{oc}} \right ) (\xi^2 + \eta ^2)} \right\} \right\| ^ 2 \right\} \mu_{\mathcal{F}}(\Delta \xi, \Delta \eta) \right\} \qquad (1) $$$$ \mu_{\mathcal{F}} \left ( \frac{\Delta \xi}{\lambda z_{so}}, \frac{\Delta \eta}{\lambda z_{so}} \right ) = \frac{\pmb{F} \{I(u, v) \}}{\|\pmb{F} \{I(u, v) \}\|_\textrm{MAX}} \qquad (2) $$$$ I (x, y) = \frac{I_0}{(\lambda z_{oc})^2} \pmb{F} \left\{ \pmb{F^{-1}} \left\{ I_\textrm{coherent} \right\} \mu_{\mathcal{F}}(\Delta \xi, \Delta \eta) \right\} \qquad (3) $$import base64
import numpy as np
import pandas as pd
import plotly
import plotly.express as px
import imageio.v3 as imageio
from scipy.ndimage import zoom
from IPython.display import display, HTML, Image, Markdown
from gvs_propagator.constants import nm, um, mm, cm
from gvs_propagator.coherent import compute_angular_spectrum
from gvs_propagator.partially_coherent import (
compute_complex_coherence_factor,
compute_schells_theorem,
compute_gvs,
compute_angular_spectrum_gvs,
compute_fresnel_convolution_gvs,
compute_shifted_fresnel_gvs,
compute_mutual_intensity_integral_cross_section,
compute_intensity_point_sources_dask,
compute_intensity_cpr,
)
from gvs_propagator.utils import (
normalize,
crop_center,
mean_squared_error,
zoom_matrix
)
from gvs_propagator.notebooks.utils import display_image
from gvs_propagator.masks import (
create_circular_mask,
create_double_pinhole_mask,
)
plotly.offline.init_notebook_mode()
def add_margin(fig, margin=20, **kwargs):
fig.update_layout(margin_b=margin)
return Image(fig.to_image(**kwargs))
GVS Diffraction vs Far Field Schell Diffraction¶
def show_diffraction():
dx_object = (wavelength * z_object_camera) / (2 * dx_camera * num_pixels_x)
dx_source = (wavelength * z_source_object) / (2 * dx_object * num_pixels_x)
dy_object = (wavelength * z_object_camera) / (2 * dy_camera * num_pixels_y)
dy_source = (wavelength * z_source_object) / (2 * dy_object * num_pixels_y)
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius, num_pixels_y * 2, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
object = create_double_pinhole_mask(
num_pixels_x,
dx_object,
radius=pinhole_radius,
separation=pinhole_separation,
num_pixels_y=num_pixels_y,
dy=dy_object,
)
intensity_gvs = crop_center(
compute_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_object
),
)
intensity_far_field = crop_center(
compute_schells_theorem(object, complex_coherence_factor, z_object_camera, wavelength)
)
x = np.linspace(-num_pixels_x // 2, num_pixels_x // 2, num_pixels_x) * dx_camera
y = np.linspace(num_pixels_y // 2, -num_pixels_y // 2, num_pixels_y) * dy_camera
return add_margin(display_image(
field=[intensity_gvs, intensity_far_field],
x=x,
y=y,
show_ticks=True,
show_as_grid=True,
normalize_all_frames=True,
grid_titles=["GVS", "Far Field Schell"],
title=f"Propagation distance: {int(z_object_camera * 100)} cm",
return_figure=True
), width=512, height=300)
Source Radius: $100 \ \mu m$¶
num_pixels_x = num_pixels_y = 1024
dx_camera = dy_camera = 5 * um
wavelength = 633 * nm
z_object_camera = 10 * cm
z_source_object = 100 * cm
source_radius = 100 * um
pinhole_radius = 100 * um
pinhole_separation = 600 * um
for z_object_camera in np.array([10, 20, 50]) * cm:
display(show_diffraction())
Source Radius: $1 \ mm$¶
source_radius = 1 * mm
for z_object_camera in np.array([10, 20, 50]) * cm:
display(show_diffraction())
Source Radius: $4 \ mm$¶
source_radius = 4 * mm
for z_object_camera in np.array([10, 20, 50]) * cm:
display(show_diffraction())
Model Comparison - Double pinhole diffraction¶
def show_diffraction(integral_1d=False):
dx_object = (wavelength * z_object_camera) / (2 * dx_camera * num_pixels_x)
dx_source = (wavelength * z_source_object) / (2 * dx_object * num_pixels_x)
dy_object = (wavelength * z_object_camera) / (2 * dy_camera * num_pixels_y)
dy_source = (wavelength * z_source_object) / (2 * dy_object * num_pixels_y)
dx_source_alternative = (wavelength * z_source_object) / (2 * dx_camera * num_pixels_x)
dy_source_alternative = (wavelength * z_source_object) / (2 * dy_camera * num_pixels_y)
object = create_double_pinhole_mask(
num_pixels_x,
dx_object,
radius=pinhole_radius,
separation=pinhole_separation,
num_pixels_y=num_pixels_y,
dy=dy_object,
)
object_approximation = create_double_pinhole_mask(
num_pixels_x,
dx_camera,
radius=pinhole_radius,
separation=pinhole_separation,
num_pixels_y=num_pixels_y,
dy=dy_camera,
)
intensities = []
radii = []
names = ["Integral Computation 1", "Integral Computation 2"] * len(source_radii)
if integral_1d:
num_pixels_y_integral = 1
else:
num_pixels_y_integral = num_pixels_y
for source_radius in source_radii:
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius, num_pixels_y * 2, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensities.append(normalize(
compute_mutual_intensity_integral_cross_section(
crop_center(object, num_pixels_y_integral, num_pixels_x),
np.fft.ifftshift(crop_center(complex_coherence_factor, num_pixels_y_integral, num_pixels_x)),
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object,
chunk_size=300
)
))
radii.append(f"{round(source_radius * 1e6)} µm")
source_intensity = create_circular_mask(
num_pixels_x * 2, dx_source_alternative, source_radius, num_pixels_y * 2, dy_source_alternative
)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensities.append(normalize(
compute_mutual_intensity_integral_cross_section(
crop_center(object_approximation, num_pixels_y_integral, num_pixels_x),
np.fft.ifftshift(crop_center(complex_coherence_factor, num_pixels_y_integral, num_pixels_x)),
z_source_object,
z_object_camera,
wavelength,
dx_camera,
chunk_size=300
)
))
radii.append(f"{round(source_radius * 1e6)} µm")
intensities_gvs = []
intensities_far_field_schell = []
for i, source_radius in enumerate(source_radii):
radii += [f"{round(source_radius * 1e6)} µm"] * 7
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius, num_pixels_y * 2, dy_source)
cpr_filter = create_circular_mask(num_pixels_x * 2, dx_source, cpr_radii[i], num_pixels_y * 2, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensity = np.roll(normalize(crop_center(
compute_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_object
),
num_pixels_y, num_pixels_x
)), -1) # Roll to shift the array to the center
intensities_gvs.append(intensity)
intensities.append(intensity[num_pixels_y // 2, :])
names.append("GVS")
intensities.append(normalize(crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]))
names.append("Shifted Fresnel GVS")
intensities.append(normalize(crop_center(
compute_fresnel_convolution_gvs(
object_approximation,
complex_coherence_factor,
z_source_object,
z_object_camera,
dx_camera,
wavelength,
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]))
names.append("Fresnel Convolution GVS")
intensities.append(normalize(crop_center(
compute_angular_spectrum_gvs(
object_approximation,
complex_coherence_factor,
z_source_object,
z_object_camera,
dx_camera,
wavelength,
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]))
names.append("Angular Spectrum GVS")
intensities.append(np.roll(normalize(crop_center(
compute_intensity_point_sources_dask(
object_approximation,
source_intensity,
dx_source,
dx_camera,
z_source_object,
z_object_camera,
wavelength,
sample_size=10000
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]), 0))
names.append("Finite Point Sources")
intensities.append(np.roll(normalize(crop_center(
compute_intensity_cpr(
cpr_filter,
dx_camera,
object_approximation,
z_object_camera,
wavelength,
sample_size=30
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]), 0))
names.append("CP-R (M = 30)")
intensities.append(np.roll(normalize(crop_center(
compute_intensity_cpr(
cpr_filter,
dx_camera,
object_approximation,
z_object_camera,
wavelength,
sample_size=300
),
num_pixels_y, num_pixels_x
)[num_pixels_y // 2, :]), 0))
names.append("CP-R (M = 300)")
intensity = np.roll(normalize(crop_center(
compute_schells_theorem(object, complex_coherence_factor, z_object_camera, wavelength),
num_pixels_y, num_pixels_x
)), -1) # Roll to shift the array to the center
intensities_far_field_schell.append(intensity)
x = np.linspace(-num_pixels_x // 2, num_pixels_x // 2, num_pixels_x) * dx_camera
df = pd.DataFrame({
"Normalized Intensity": intensities,
"Source Radius": radii,
"Model": names,
})
results = []
for source_radius in source_radii:
df_mse = df[(df["Source Radius"] == f"{round(source_radius * 1e6)} µm")]
integral_1 = df_mse[df_mse["Model"] == "Integral Computation 1"].iloc[0]["Normalized Intensity"]
integral_2 = df_mse[df_mse["Model"] == "Integral Computation 2"].iloc[0]["Normalized Intensity"]
df_mse = df_mse.loc[~df["Model"].isin(["Integral Computation 1", "Integral Computation 2"])]
df_mse_1 = df_mse.loc[
(df["Model"].isin(["GVS", "Shifted Fresnel GVS"]))
]
df_mse_1 = df_mse_1.groupby("Model")["Normalized Intensity"].apply(
lambda s: mean_squared_error(s.iloc[0], integral_1)
)
df_mse_2 = df_mse.loc[
(df["Model"].isin([
"Fresnel Convolution GVS",
"Angular Spectrum GVS",
"Finite Point Sources",
"CP-R (M = 30)",
"CP-R (M = 300)",
]))
]
df_mse_2 = df_mse_2.groupby("Model")["Normalized Intensity"].apply(
lambda s: mean_squared_error(s.iloc[0], integral_2)
)
df_mse = pd.concat([df_mse_1, df_mse_2])
df_mse.name = f"Radius = {round(source_radius * 1e6)} µm"
results.append(df_mse.reset_index())
df_mse = results[0]
for result in results[1:]:
df_mse = df_mse.merge(result, on="Model", how="outer")
display(Markdown(f"## 2D diffraction"))
display(display_image(
intensities_gvs,
show_as_grid=True,
normalize_all_frames=True,
grid_titles=[f"{round(r * 1e6)} µm" for r in source_radii],
title="GVS",
width=1000,
height=300
))
display(display_image(
intensities_far_field_schell,
show_as_grid=True,
normalize_all_frames=True,
grid_titles=[f"{round(r * 1e6)} µm" for r in source_radii],
title="Schell (far field)",
width=1000,
height=300
))
display(Markdown(f"## Cross sections"))
display(Markdown(f"### Mean Squared Error:"))
display(df_mse)
df = df.explode("Normalized Intensity")
df["x (m)"] = x.tolist() * len(intensities)
fig = px.line(df, x="x (m)", y="Normalized Intensity", color="Model", facet_col="Source Radius")
fig.update_traces(selector=dict(name="Integral Computation 1"), line=dict(width=3))
fig.update_traces(selector=dict(name="Integral Computation 2"), line=dict(width=3))
fig.update_traces(selector=dict(name="GVS"), line=dict(dash="dot"))
fig.update_traces(selector=dict(name="Shifted Fresnel GVS"), line=dict(dash="dot"))
fig.update_traces(selector=dict(name="Fresnel Convolution GVS"), line=dict(dash="dot"))
fig.update_traces(selector=dict(name="Angular Spectrum GVS"), line=dict(dash="dot"))
fig.update_traces(selector=dict(name="Finite Point Sources"), line=dict(dash="longdashdot"))
fig.update_traces(selector=dict(name="CP-R (M = 30)"), line=dict(dash="longdashdot", color="#1f77b4"), opacity=0.5)
fig.update_traces(selector=dict(name="CP-R (M = 300)"), line=dict(dash="longdashdot"), opacity=0.5)
fig.update_layout(margin_t=20, margin_l=0, margin_r=0)
fig.update_xaxes(showticklabels=False, title=None)
fig.update_yaxes(showticklabels=False)
fig.update_legends(title_text=None)
fig.show()
num_pixels_x = num_pixels_y = 256
z_source_object = 10 * cm
source_radii = [100 * um, 250 * um, 500 * um, 1 * mm]
cpr_radii = [50 * um, 200 * um, 300 * um, 500 * um]
pinhole_radius = 25 * um
pinhole_separation = 200 * um
dx_camera = dy_camera = 5 * um
z_object_camera = 10 * mm
show_diffraction()
2D diffraction¶
Cross sections¶
Mean Squared Error:¶
Model | Radius = 100 µm | Radius = 250 µm | Radius = 500 µm | Radius = 1000 µm | |
---|---|---|---|---|---|
0 | Angular Spectrum GVS | 0.000122 | 0.000053 | 0.000045 | 0.000043 |
1 | CP-R (M = 30) | 0.004074 | 0.007874 | 0.027226 | 0.035960 |
2 | CP-R (M = 300) | 0.003913 | 0.007687 | 0.004589 | 0.031018 |
3 | Finite Point Sources | 0.000122 | 0.000053 | 0.000046 | 0.000051 |
4 | Fresnel Convolution GVS | 0.000121 | 0.000053 | 0.000045 | 0.000043 |
5 | GVS | 0.000035 | 0.000013 | 0.000013 | 0.000015 |
6 | Shifted Fresnel GVS | 0.000008 | 0.000002 | 0.000004 | 0.000013 |
Arbitrary shifts and pixel pitches¶
This section shows the off-axis propagation and arbitrary pixel pitch propagation by using the shifted fresnel GVS diffraction.
Pixel pitch variation¶
def display_three_images():
image_1_ = base64.b64encode(image_1.data).decode("utf-8")
image_2_ = base64.b64encode(image_2.data).decode("utf-8")
image_3_ = base64.b64encode(image_3.data).decode("utf-8")
html = f"""
<div style="display: flex; justify-content: space-around; align-items: center;">
<img src="data:image/png;base64,{image_1_}" style="width: 33%;">
<img src="data:image/png;base64,{image_2_}" style="width: 33%;">
<img src="data:image/png;base64,{image_3_}" style="width: 33%;">
</div>
"""
return HTML(html)
num_pixels_x = num_pixels_y = 1024
dx_camera = 10 * um
dx_object = 10 * um
shift_object_x = 0.0 * um
shift_object_y = 0.0 * um
shift_camera_x = 0.0 * um
shift_camera_y = 0.0 * um
z_object_camera = 40 * cm
z_source_object = 10 * cm
pinhole_radius = 50 * um
pinhole_separation = 300 * um
source_radius = 100 * um
dx_source = z_source_object / z_object_camera * dx_camera
object = create_double_pinhole_mask(
num_pixels_x,
dx_object,
radius=pinhole_radius,
separation=pinhole_separation,
num_pixels_y=num_pixels_y,
)
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensity_1 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object
),
num_pixels_y, num_pixels_x
)
x = np.linspace(-num_pixels_x // 2, num_pixels_x // 2, num_pixels_x) * dx_camera
image_1 = display_image(intensity_1, x=x, y=-x, title="$dx = 10 \ \mu m$")
dx_camera = 5 * um
dx_source = z_source_object / z_object_camera * dx_camera
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensity_2 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object
),
num_pixels_y, num_pixels_x
)
x = np.linspace(-num_pixels_x // 2, num_pixels_x // 2, num_pixels_x) * dx_camera
image_2 = display_image(intensity_2, x=x, y=-x, title="$dx = 5 \ \mu m$")
dx_camera = 2.5 * um
dx_source = z_source_object / z_object_camera * dx_camera
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensity_3 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object
),
num_pixels_y, num_pixels_x
)
x = np.linspace(-num_pixels_x // 2, num_pixels_x // 2, num_pixels_x) * dx_camera
image_3 = display_image(intensity_3, x=x, y=-x, title="$dx = 2.5 \ \mu m$")
display_three_images()
Shifted diffraction¶
Here we compute the diffraction with four different shifts, corresponding to the four quadrants of a cartesian plane.
num_pixels_x = 512
dx_camera = 5 * um
dx_source = z_source_object / z_object_camera * dx_camera
object = create_double_pinhole_mask(
num_pixels_x,
dx_object,
radius=pinhole_radius,
separation=pinhole_separation,
)
source_intensity = create_circular_mask(num_pixels_x * 2, dx_source, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
shift_object_y = -1.25 * mm
shift_object_x = 1.25 * mm
intensity_1 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object,
shift_object_x=shift_object_x,
shift_object_y=shift_object_y
),
num_pixels_x, num_pixels_x
)
shift_object_y = 1.25 * mm
intensity_2 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object,
shift_object_x=shift_object_x,
shift_object_y=shift_object_y
),
num_pixels_x, num_pixels_x
)
shift_object_x = -1.25 * mm
intensity_3 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object,
shift_object_x=shift_object_x,
shift_object_y=shift_object_y
),
num_pixels_x, num_pixels_x
)
shift_object_y = -1.25 * mm
intensity_4 = crop_center(
compute_shifted_fresnel_gvs(
object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object,
shift_object_x=shift_object_x,
shift_object_y=shift_object_y
),
num_pixels_x, num_pixels_x
)
display_image(
[intensity_2, intensity_3, intensity_1, intensity_4],
show_as_grid=True,
facet_col_wrap=2,
grid_titles=[""] * 4,
width=490,
height=512
)
Random objects¶
Here we compare the fourier-based models with the integral computation using small random objects. We separate the GVS and shifted fresnel models from the angular spectrum and fresnel convolution models for fair comparison.
Random Object¶
num_pixels = 40
dx_camera = 5 * um
z_object_camera = 2 * cm
z_source_object = 10 * cm
source_radius = 50 * um
dx_object = (wavelength * z_object_camera) / (2 * dx_camera * num_pixels)
dx_source = (wavelength * z_source_object) / (2 * dx_object * num_pixels)
random_object = np.pad(np.random.rand(num_pixels, num_pixels), num_pixels // 2)
display_image(random_object)
Models (1)¶
source_intensity = create_circular_mask(num_pixels * 2, dx_source, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensity_integral_1 = normalize(
compute_mutual_intensity_integral_cross_section(
random_object,
np.fft.ifftshift(complex_coherence_factor),
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object,
chunk_size=300
)
)[num_pixels - num_pixels // 2:num_pixels + num_pixels // 2]
intensity_gvs = np.roll(normalize(crop_center(
compute_gvs(
random_object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_object
),
num_pixels, num_pixels
)[num_pixels // 2, :]), -1) # Roll to shift the array to the center
intensity_shifted_fresnel = normalize(crop_center(
compute_shifted_fresnel_gvs(
random_object,
complex_coherence_factor,
z_source_object,
z_object_camera,
wavelength,
dx_camera,
dx_object=dx_object
),
num_pixels, num_pixels
)[num_pixels // 2, :])
fig = (
px.line(
y=[
intensity_integral_1,
intensity_gvs,
intensity_shifted_fresnel,
])
.update_yaxes(title="Normalized Intensity")
.update_xaxes(title="Pixel Index")
.update_legends(
title=None,
yanchor="top",
y=0.99,
xanchor="right",
x=0.99
)
)
fig.data[0].update(name="Integral Computation", line=dict(width=3))
fig.data[1].update(name="GVS", line=dict(dash="dot"))
fig.data[2].update(name="Shifted Fresnel GVS", line=dict(dash="dot"))
fig.show()
Models (2)¶
z_object_camera = 0.2 * cm
source_radius = 500 * um
dx_object = (wavelength * z_object_camera) / (2 * dx_camera * num_pixels)
dx_source = (wavelength * z_source_object) / (2 * dx_object * num_pixels)
random_object = np.pad(np.random.rand(num_pixels, num_pixels), num_pixels // 2)
source_intensity = create_circular_mask(num_pixels * 2, dx_source, source_radius)
source_intensity_point_sources = create_circular_mask(num_pixels * 2, dx_camera, source_radius)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
complex_coherence_factor_alternative = zoom_matrix(complex_coherence_factor, dx_object / dx_camera)
intensity_integral_2 = normalize(
compute_mutual_intensity_integral_cross_section(
random_object,
np.fft.ifftshift(complex_coherence_factor_alternative),
z_source_object,
z_object_camera,
wavelength,
dx_camera,
chunk_size=300
)
)[num_pixels - num_pixels // 2:num_pixels + num_pixels // 2]
intensity_fresnel_convolution = normalize(crop_center(
compute_fresnel_convolution_gvs(
random_object,
complex_coherence_factor,
z_source_object,
z_object_camera,
dx_camera,
wavelength
),
num_pixels, num_pixels
)[num_pixels // 2, :])
intensity_angular_spectrum_fresnel = normalize(crop_center(
compute_angular_spectrum_gvs(
random_object,
complex_coherence_factor,
z_source_object,
z_object_camera,
dx_camera,
wavelength
),
num_pixels, num_pixels
)[num_pixels // 2, :])
fig = (
px.line(
y=[
intensity_integral_2,
intensity_fresnel_convolution,
intensity_angular_spectrum_fresnel,
]
)
.update_yaxes(title="Normalized Intensity")
.update_xaxes(title="Pixel Index")
.update_legends(title_text="Model")
)
fig.data[0].update(name="Integral Computation", line=dict(width=3))
fig.data[1].update(name="Fresnel Convolution Schell", line=dict(dash="dot"))
fig.data[2].update(name="Angular Spectrum Schell", line=dict(dash="dot"))
fig.show()
USAF animation¶
def zoom_image(image):
return zoom(
image,
(
num_pixels_y / image.shape[0],
num_pixels_x / image.shape[1]
)
)
def show_diffraction():
object = normalize(zoom_image(imageio.imread("img/usaf.png", mode="L")))
object = np.pad(object, ((num_pixels_y // 2, num_pixels_y // 2), (num_pixels_x // 2, num_pixels_x // 2)))
ny_padded, nx_padded = object.shape
all_intensities = []
for z_object_camera in propagation_distances:
dx_object = (wavelength * z_object_camera) / (2 * dx_camera * num_pixels_x)
dx_source = (wavelength * z_source_object) / (2 * dx_object * num_pixels_x)
dy_object = (wavelength * z_object_camera) / (2 * dy_camera * num_pixels_y)
dy_source = (wavelength * z_source_object) / (2 * dy_object * num_pixels_y)
intensities = []
intensities.append(normalize(crop_center(abs(compute_angular_spectrum(
object, z_object_camera, dx_camera, wavelength, spherical=True, z_source_object=z_source_object
) ** 2))))
source_intensity = create_circular_mask(nx_padded, dx_source, source_radii[0], ny_padded, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensities.append(normalize(crop_center(compute_angular_spectrum_gvs(
object, complex_coherence_factor, z_source_object, z_object_camera, dx_camera, wavelength
))))
source_intensity = create_circular_mask(nx_padded, dx_source, source_radii[1], ny_padded, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensities.append(normalize(crop_center(compute_angular_spectrum_gvs(
object, complex_coherence_factor, z_source_object, z_object_camera, dx_camera, wavelength
))))
source_intensity = create_circular_mask(nx_padded, dx_source, source_radii[2], ny_padded, dy_source)
complex_coherence_factor = compute_complex_coherence_factor(source_intensity)
intensities.append(normalize(crop_center(compute_angular_spectrum_gvs(
object, complex_coherence_factor, z_source_object, z_object_camera, dx_camera, wavelength
))))
all_intensities.append(intensities)
fig = display_image(
all_intensities,
convert_to=None,
show_as_grid=True,
show_animation=True,
propagation_distances=propagation_distances.tolist(),
grid_titles=[
"Angular Spectrum (Coherent)",
"Angular Spectrum GVS (High Coherence)",
"Angular Spectrum GVS (Medium Coherence)",
"Angular Spectrum GVS (Low Coherence)",
],
return_figure=True,
width=None,
height=430
)
fig.update_layout(
sliders=[
{
"currentvalue": {
"font": {"size": 13},
"xanchor": "center",
"offset": 20,
},
"pad": {"t": 0}
}
],
)
for annotation in fig.layout.annotations:
annotation.font.size = 9
fig.frames = fig.frames[::2]
fig.show()
num_pixels_x = 1000
num_pixels_y = 750
z_source_object = 10 * cm
source_radii = [80 * um, 250 * um, 1000 * um]
dx_camera = dy_camera = 2 * um
propagation_distances = np.linspace(2 * mm, 20 * mm, 20).tolist()
propagation_distances = np.concat([propagation_distances, propagation_distances[::-1]])
show_diffraction()