Synthetic object quickstart¶
Preview the in-memory SyntheticObject constructors, render them as a gallery, then simulate and reconstruct one of them directly from Python. The two file-based loaders (from_amplitude_image and from_amplitude_phase_images) are noted separately so this notebook stays self-contained. Before running the code, install the released package in this notebook's environment with %pip install "fpm-rs[notebook]".
In [ ]:
Copied!
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import display
import fpm_rs as fpm
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import display
import fpm_rs as fpm
In [ ]:
Copied!
shape = (64, 64)
optics = fpm.Optics(532e-9, 0.10, 4.0, 6.5e-6)
illumination = fpm.LEDArray((3, 3), 4e-3, 90e-3, (1.0, 1.0))
model = fpm.compile_model(optics, illumination, (32, 32), shape)
row, column = np.indices(shape)
normalized_row = row / (shape[0] - 1)
normalized_column = column / (shape[1] - 1)
amplitude_map = 0.2 + 0.8 * normalized_column
phase_map = np.pi * (2.0 * normalized_row - 1.0)
custom_field = np.asarray(
amplitude_map * np.exp(1j * 0.5 * phase_map),
dtype=np.complex128,
)
shape = (64, 64)
optics = fpm.Optics(532e-9, 0.10, 4.0, 6.5e-6)
illumination = fpm.LEDArray((3, 3), 4e-3, 90e-3, (1.0, 1.0))
model = fpm.compile_model(optics, illumination, (32, 32), shape)
row, column = np.indices(shape)
normalized_row = row / (shape[0] - 1)
normalized_column = column / (shape[1] - 1)
amplitude_map = 0.2 + 0.8 * normalized_column
phase_map = np.pi * (2.0 * normalized_row - 1.0)
custom_field = np.asarray(
amplitude_map * np.exp(1j * 0.5 * phase_map),
dtype=np.complex128,
)
In [ ]:
Copied!
object_gallery = {
"custom_field": fpm.SyntheticObject(custom_field),
"constant": fpm.SyntheticObject.constant(shape, amplitude=0.8, phase=0.6),
"amplitude_only": fpm.SyntheticObject.amplitude_only(amplitude_map),
"phase_only": fpm.SyntheticObject.phase_only(phase_map),
"from_amplitude_phase": fpm.SyntheticObject.from_amplitude_phase(
amplitude_map,
0.5 * phase_map,
),
"phase_disk": fpm.SyntheticObject.phase_disk(shape, radius_pixels=18.0, phase_shift=1.2),
"siemens_star": fpm.SyntheticObject.siemens_star(shape, spokes=24),
"resolution_target": fpm.SyntheticObject.resolution_target(shape),
"random_phase": fpm.SyntheticObject.random_phase(shape, standard_deviation=0.6, seed=3),
"particle_field": fpm.SyntheticObject.particle_field(shape, particles=120, seed=5),
"mixed_test_pattern": fpm.SyntheticObject.mixed_test_pattern(shape),
"biological_like": fpm.SyntheticObject.biological_like(shape, features=18, seed=7),
}
list(object_gallery)
object_gallery = {
"custom_field": fpm.SyntheticObject(custom_field),
"constant": fpm.SyntheticObject.constant(shape, amplitude=0.8, phase=0.6),
"amplitude_only": fpm.SyntheticObject.amplitude_only(amplitude_map),
"phase_only": fpm.SyntheticObject.phase_only(phase_map),
"from_amplitude_phase": fpm.SyntheticObject.from_amplitude_phase(
amplitude_map,
0.5 * phase_map,
),
"phase_disk": fpm.SyntheticObject.phase_disk(shape, radius_pixels=18.0, phase_shift=1.2),
"siemens_star": fpm.SyntheticObject.siemens_star(shape, spokes=24),
"resolution_target": fpm.SyntheticObject.resolution_target(shape),
"random_phase": fpm.SyntheticObject.random_phase(shape, standard_deviation=0.6, seed=3),
"particle_field": fpm.SyntheticObject.particle_field(shape, particles=120, seed=5),
"mixed_test_pattern": fpm.SyntheticObject.mixed_test_pattern(shape),
"biological_like": fpm.SyntheticObject.biological_like(shape, features=18, seed=7),
}
list(object_gallery)
The API also exposes file-based grayscale loaders when you already have images on disk:
fpm.SyntheticObject.from_amplitude_image(...)fpm.SyntheticObject.from_amplitude_phase_images(...)
In [ ]:
Copied!
gallery_figure, gallery_axes = plt.subplots(6, 4, figsize=(14, 18), constrained_layout=True)
for index, (name, synthetic_object) in enumerate(object_gallery.items()):
row_index = index // 2
column_offset = (index % 2) * 2
amplitude_axis = gallery_axes[row_index, column_offset]
phase_axis = gallery_axes[row_index, column_offset + 1]
field = synthetic_object.field
amplitude_axis.imshow(np.abs(field), cmap="gray", vmin=0.0)
amplitude_axis.set_title(f"{name} | amplitude", fontsize=10)
phase_axis.imshow(np.angle(field), cmap="twilight", vmin=-np.pi, vmax=np.pi)
phase_axis.set_title("phase", fontsize=10)
for axis in (amplitude_axis, phase_axis):
axis.set_xticks([])
axis.set_yticks([])
display(gallery_figure)
# gallery_figure.savefig("synthetic_object_gallery.png", dpi=180, bbox_inches="tight")
plt.close(gallery_figure)
gallery_figure, gallery_axes = plt.subplots(6, 4, figsize=(14, 18), constrained_layout=True)
for index, (name, synthetic_object) in enumerate(object_gallery.items()):
row_index = index // 2
column_offset = (index % 2) * 2
amplitude_axis = gallery_axes[row_index, column_offset]
phase_axis = gallery_axes[row_index, column_offset + 1]
field = synthetic_object.field
amplitude_axis.imshow(np.abs(field), cmap="gray", vmin=0.0)
amplitude_axis.set_title(f"{name} | amplitude", fontsize=10)
phase_axis.imshow(np.angle(field), cmap="twilight", vmin=-np.pi, vmax=np.pi)
phase_axis.set_title("phase", fontsize=10)
for axis in (amplitude_axis, phase_axis):
axis.set_xticks([])
axis.set_yticks([])
display(gallery_figure)
# gallery_figure.savefig("synthetic_object_gallery.png", dpi=180, bbox_inches="tight")
plt.close(gallery_figure)
In [ ]:
Copied!
selected_name = "mixed_test_pattern"
true_object = object_gallery[selected_name]
simulation = fpm.simulate(model, true_object, seed=17)
problem = fpm.ReconstructionProblem(
simulation.measurements,
simulation.reconstruction_model,
name="synthetic-objects-quickstart",
)
result = fpm.AlternatingProjection(iterations=20).run(problem)
selected_name, result.runtime.completed_iterations, result.final_loss
selected_name = "mixed_test_pattern"
true_object = object_gallery[selected_name]
simulation = fpm.simulate(model, true_object, seed=17)
problem = fpm.ReconstructionProblem(
simulation.measurements,
simulation.reconstruction_model,
name="synthetic-objects-quickstart",
)
result = fpm.AlternatingProjection(iterations=20).run(problem)
selected_name, result.runtime.completed_iterations, result.final_loss
In [ ]:
Copied!
truth = simulation.ground_truth_object
figure, axes = fpm.plot.plot_reconstruction(truth, result)
display(figure)
# figure.savefig("synthetic_objects_reconstruction.png", dpi=180, bbox_inches="tight")
plt.close(figure)
truth = simulation.ground_truth_object
figure, axes = fpm.plot.plot_reconstruction(truth, result)
display(figure)
# figure.savefig("synthetic_objects_reconstruction.png", dpi=180, bbox_inches="tight")
plt.close(figure)