Skip to content

Commit

Permalink
cleanup, fixed warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
IvoVellekoop committed Oct 4, 2024
1 parent e77b00d commit f8be408
Show file tree
Hide file tree
Showing 14 changed files with 51 additions and 79 deletions.
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@


# Hide some classes that are not production ready yet
def skip(app, what, name, obj, do_skip, options):
def skip(_app, _what, name, _obj, do_skip, _options):
if name in ("WFSController", "Gain"):
return True
return do_skip
Expand All @@ -142,7 +142,7 @@ def visit_citation(self, node):
self.add(f'<a name="{id}"></a>')


def visit_label(self, node):
def visit_label(_self, _node):
"""Patch-in function for markdown builder to support citations."""
pass

Expand Down
7 changes: 4 additions & 3 deletions examples/micro_manager_microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import astropy.units as u
import numpy as np

from openwfs.simulation import Microscope, StaticSource
from openwfs.simulation import Microscope, StaticSource, Camera

specimen_resolution = (1024, 1024) # height × width in pixels of the specimen image
specimen_pixel_size = 60 * u.nm # resolution (pixel size) of the specimen image
Expand All @@ -36,12 +36,13 @@
)

# simulate shot noise in an 8-bit camera with auto-exposure:
cam = mic.get_camera(
cam = Camera(
mic,
shot_noise=True,
digital_max=255,
data_shape=camera_resolution,
pixel_size=camera_pixel_size,
)

# construct dictionary of objects to expose to Micro-Manager
devices = {"camera": cam, "stage": mic.stage}
devices = {"camera": cam, "stage": mic.xy_stage}
5 changes: 3 additions & 2 deletions examples/sample_microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import set_path # noqa - needed for setting the module search path to find openwfs
from openwfs.plot_utilities import grab_and_show, imshow
from openwfs.simulation import Microscope, StaticSource
from openwfs.simulation import Microscope, StaticSource, Camera

specimen_resolution = (1024, 1024) # height × width in pixels of the specimen image
specimen_pixel_size = 60 * u.nm # resolution (pixel size) of the specimen image
Expand All @@ -36,7 +36,8 @@
)

# simulate shot noise in an 8-bit camera with auto-exposure:
cam = mic.get_camera(
cam = Camera(
mic,
shot_noise=True,
digital_max=255,
data_shape=camera_resolution,
Expand Down
4 changes: 2 additions & 2 deletions examples/troubleshooter_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from openwfs.algorithms import StepwiseSequential, troubleshoot
from openwfs.processors import SingleRoi
from openwfs.simulation import SLM, Microscope, Shutter
from openwfs.simulation import SLM, Microscope, Shutter, Camera
from openwfs.utilities import set_pixel_size

# === Define virtual devices for a WFS simulation ===
Expand Down Expand Up @@ -43,7 +43,7 @@
)

# Simulate a camera device with gaussian noise and shot noise
cam = sim.get_camera(analog_max=1e4, shot_noise=True, gaussian_noise_std=4.0)
cam = Camera(sim, analog_max=1e4, shot_noise=True, gaussian_noise_std=4.0)

# Define feedback as circular region of interest in the center of the frame
roi_detector = SingleRoi(cam, radius=0.1)
Expand Down
1 change: 1 addition & 0 deletions openwfs/algorithms/dual_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ def _compute_cobasis(self):
if self.phase_patterns is None:
raise "The phase_patterns must be set before computing the cobasis."

# TODO: simplify, integrate in calling function, fix warnings
cobasis = [None, None]
for side in range(2):
p = np.prod(self._shape) # Number of SLM pixels
Expand Down
5 changes: 3 additions & 2 deletions openwfs/algorithms/troubleshoot.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ def pearson_correlation(a: np.ndarray, b: np.ndarray, noise_var: np.ndarray = 0.
by subtracting the noise variance from the signal variance.
Args:
a, b: Real valued arrays.
a: real-valued input array.
b: real-valued input array.
noise_var: Variance of uncorrelated noise to compensate for.
"""
a_dev = a - a.mean() # Deviations from mean a
Expand Down Expand Up @@ -396,7 +397,7 @@ class WFSTroubleshootResult:
Attributes:
fidelity_non_modulated: The estimated fidelity reduction factor due to the presence of non-modulated light.
phase_calibration_ratio: A ratio indicating the correctness of the SLM phase response. An incorrect phase
fidelity_phase_calibration: A ratio indicating the correctness of the SLM phase response. An incorrect phase
response produces a value < 1.
wfs_result (WFSResult): Object containing the analyzed result of running the WFS algorithm.
feedback_before: Feedback from before running the WFS algorithm, with a flat wavefront.
Expand Down
2 changes: 1 addition & 1 deletion openwfs/devices/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def duration(self) -> Quantity[u.ms]:
"""Returns the exposure time in milliseconds if software triggering is used.
Returns ∞ if hardware triggering is used.
TODO: implement hardware triggering."""
return self.exposure_time.to(u.ms)
return self.exposure.to(u.ms)

@property
def exposure(self) -> u.Quantity[u.ms]:
Expand Down
42 changes: 2 additions & 40 deletions openwfs/simulation/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,8 @@

from ..core import Processor, Detector
from ..plot_utilities import imshow # noqa - for debugging
from ..processors import TransformProcessor
from ..simulation.mockdevices import XYStage, Camera, StaticSource
from ..utilities import (
project,
place,
Transform,
get_pixel_size,
patterns,
CoordinateType,
)
from ..simulation.mockdevices import XYStage, StaticSource
from ..utilities import project, place, Transform, get_pixel_size, patterns


class Microscope(Processor):
Expand Down Expand Up @@ -251,33 +243,3 @@ def pixel_size(self) -> Quantity:
def data_shape(self):
"""Returns the shape of the image in the image plane"""
return self._data_shape

def get_camera(
self,
*,
transform: Optional[Transform] = None,
data_shape: Optional[tuple[int, int]] = None,
pixel_size: Optional[CoordinateType] = None,
**kwargs
) -> Detector:
"""
Returns a simulated camera that observes the microscope image.
The camera is a MockCamera object that simulates an AD-converter with optional noise.
shot noise and readout noise (see MockCamera for options).
In addition to the inputs accepted by the MockCamera constructor (data_shape, analog_max, shot_noise, etc.),
it is also possible to specify a transform, to mimic the (mis)alignment of the camera.
Args:
transform ():
**kwargs ():
Returns:
"""
if transform is None and data_shape is None and pixel_size is None:
src = self
else:
src = TransformProcessor(self, data_shape=data_shape, pixel_size=pixel_size, transform=transform)

return Camera(src, **kwargs)
4 changes: 0 additions & 4 deletions openwfs/simulation/mockdevices.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,6 @@ def data_shape(self, value):
def exposure(self) -> Quantity[u.ms]:
return self.duration

@exposure.setter
def exposure(self, value: Quantity[u.ms]):
self.duration = value.to(u.ms)


class XYStage(Actuator):
"""
Expand Down
19 changes: 12 additions & 7 deletions openwfs/utilities/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,20 @@ def project(
The input image is scaled so that the pixel sizes match those of the output,
and cropped/zero-padded so that the data shape matches that of the output.
Optionally, an additional transformation can be specified, e.g., to scale or translate the source image.
This transformation is specified as a 2x3 transformation matrix in homogeneous coordinates.
Optionally, an additional :class:`~Transform` can be specified, e.g., to scale or translate the source image.
Args:
source (np.ndarray): input image.
Must have the pixel_size set (see set_pixel_size)
transform: transformation to appy to the source image before placing it in the output
out (np.ndarray): optional array where the output image is stored in.
If specified, `out_shape` is ignored.
source: input image.
source_extent: extent of the source image in some physical unit.
If not given (``None``), the extent metadata of the input image is used.
see :func:`~get_extent`.
transform: optional transformed (rotate, translate, etc.)
to appy to the source image before placing it in the output
out: optional array where the output image is stored in.
out_extent: extent of the output image in some physical unit.
If not given, the extent metadata of the out image is used.
out_shape: shape of the output image.
This value is ignored if `out` is specified.
Returns:
np.ndarray: the projected image (`out` if specified, otherwise a new array)
Expand Down
9 changes: 5 additions & 4 deletions tests/test_algorithms_troubleshoot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
measure_modulated_light_dual_phase_stepping,
)
from ..openwfs.processors import SingleRoi
from ..openwfs.simulation import Camera
from ..openwfs.simulation import SimulatedWFS, StaticSource, SLM, Microscope


Expand Down Expand Up @@ -214,7 +215,7 @@ def test_fidelity_phase_calibration_ssa_with_noise(n_y, n_x, phase_steps, gaussi
aberrations=aberration,
wavelength=800 * u.nm,
)
cam = sim.get_camera(analog_max=1e4, gaussian_noise_std=gaussian_noise_std)
cam = Camera(sim, analog_max=1e4, gaussian_noise_std=gaussian_noise_std)
roi_detector = SingleRoi(cam, radius=0) # Only measure that specific point

# Define and run WFS algorithm
Expand Down Expand Up @@ -253,7 +254,7 @@ def test_measure_modulated_light_dual_phase_stepping_with_noise(num_blocks, phas
# Aberration and image source
img = np.zeros((64, 64), dtype=np.int16)
img[32, 32] = 100
src = StaticSource(img, 200 * u.nm)
src = StaticSource(img, pixel_size=200 * u.nm)

# SLM, simulation, camera, ROI detector
slm = SLM(shape=(100, 100))
Expand All @@ -264,7 +265,7 @@ def test_measure_modulated_light_dual_phase_stepping_with_noise(num_blocks, phas
numerical_aperture=1.0,
wavelength=800 * u.nm,
)
cam = sim.get_camera(analog_max=1e4, gaussian_noise_std=gaussian_noise_std)
cam = Camera(sim, analog_max=1e4, gaussian_noise_std=gaussian_noise_std)
roi_detector = SingleRoi(cam, radius=0) # Only measure that specific point

# Measure the amount of modulated light (no non-modulated light present)
Expand Down Expand Up @@ -316,7 +317,7 @@ def test_measure_modulated_light_dual_phase_stepping_with_noise(
non_modulated_field_fraction=non_modulated_field,
)
sim = Microscope(source=src, incident_field=slm.field, wavelength=800 * u.nm)
cam = sim.get_camera(analog_max=1e3, gaussian_noise_std=gaussian_noise_std)
cam = Camera(sim, analog_max=1e3, gaussian_noise_std=gaussian_noise_std)
roi_detector = SingleRoi(cam, radius=0) # Only measure that specific point

# Measure the amount of modulated light (no non-modulated light present)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
)
def test_croppers():
img = sk.data.camera()
src = StaticSource(img, 50 * u.nm)
src = StaticSource(img, pixel_size=50 * u.nm)
roi = select_roi(src, "disk")
assert roi.mask_type == "disk"

Expand Down
7 changes: 3 additions & 4 deletions tests/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ def test_microscope_without_magnification(shape):

# construct microscope
sim = Microscope(source=src, magnification=1, numerical_aperture=1, wavelength=800 * u.nm)

cam = sim.get_camera()
cam = Camera(sim)
img = cam.read()
assert img[256, 256] == 2**16 - 1

Expand Down Expand Up @@ -138,7 +137,7 @@ def test_slm_tilt():

new_location = signal_location + shift

cam = sim.get_camera()
cam = Camera(sim)
img = cam.read(immediate=True)
max_pos = np.unravel_index(np.argmax(img), img.shape)
assert np.all(max_pos == new_location)
Expand Down Expand Up @@ -172,7 +171,7 @@ def test_microscope_wavefront_shaping(caplog):
wavelength=800 * u.nm,
)

cam = sim.get_camera(analog_max=100)
cam = Camera(sim, analog_max=100)
roi_detector = SingleRoi(cam, pos=signal_location, radius=0) # Only measure that specific point

alg = StepwiseSequential(feedback=roi_detector, slm=slm, phase_steps=3, n_x=3, n_y=3)
Expand Down
19 changes: 12 additions & 7 deletions tests/test_wfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
)
from ..openwfs.algorithms.troubleshoot import field_correlation
from ..openwfs.algorithms.utilities import WFSController
from ..openwfs.plot_utilities import plot_field
from ..openwfs.processors import SingleRoi
from ..openwfs.simulation.mockdevices import GaussianNoise
from ..openwfs.simulation import SimulatedWFS, StaticSource, SLM, Microscope
from ..openwfs.plot_utilities import plot_field
from ..openwfs.simulation.mockdevices import GaussianNoise, Camera


@pytest.mark.parametrize("shape", [(4, 7), (10, 7), (20, 31)])
@pytest.mark.parametrize("noise", [0.0, 0.1])
@pytest.mark.parametrize("algorithm", ["ssa", "fourier"])
def test_multi_target_algorithms(shape, noise: float, algorithm: str):
def test_multi_target_algorithms(shape: tuple[int, int], noise: float, algorithm: str):
"""
Test the multi-target capable algorithms (SSA and Fourier dual ref).
Expand Down Expand Up @@ -165,7 +165,7 @@ def test_fourier_microscope():
img[signal_location] = 100
slm_shape = (1000, 1000)

src = StaticSource(img, 400 * u.nm)
src = StaticSource(img, pixel_size=400 * u.nm)
slm = SLM(shape=(1000, 1000))
sim = Microscope(
source=src,
Expand All @@ -175,7 +175,7 @@ def test_fourier_microscope():
aberrations=aberration,
wavelength=800 * u.nm,
)
cam = sim.get_camera(analog_max=100)
cam = Camera(sim, analog_max=100)
roi_detector = SingleRoi(cam, pos=(250, 250)) # Only measure that specific point
alg = FourierDualReference(feedback=roi_detector, slm=slm, slm_shape=slm_shape, k_radius=1.5, phase_steps=3)
controller = WFSController(alg)
Expand Down Expand Up @@ -231,7 +231,6 @@ def test_phase_shift_correction():
# compute the phase pattern to optimize the intensity in target 0
optimised_wf = -np.angle(t)
sim.slm.set_phases(0)
before = sim.read()

optimised_wf -= 5
signals = []
Expand Down Expand Up @@ -376,7 +375,7 @@ def test_simple_genetic(population_size: int, elite_size: int):

@pytest.mark.parametrize("basis_str", ("plane_wave", "hadamard"))
@pytest.mark.parametrize("shape", ((8, 8), (16, 4)))
def test_dual_reference_ortho_split(basis_str: str, shape):
def test_dual_reference_ortho_split(basis_str: str, shape: tuple[int, int]):
"""Test dual reference with an orthonormal phase-only basis.
Two types of bases are tested: plane waves and Hadamard"""
do_debug = False
Expand Down Expand Up @@ -426,6 +425,9 @@ def test_dual_reference_ortho_split(basis_str: str, shape):
result = alg.execute()

if do_debug:
# Plot the modes
import matplotlib.pyplot as plt

plt.figure()
for m in range(N):
plt.subplot(*modes_shape[0:2], m + 1)
Expand Down Expand Up @@ -512,6 +514,9 @@ def test_dual_reference_non_ortho_split():
t_field = np.exp(1j * np.angle(result.t))

if do_debug:
# Plot the modes
import matplotlib.pyplot as plt

plt.figure()
for m in range(M):
plt.subplot(N2, N1, m + 1)
Expand Down

0 comments on commit f8be408

Please sign in to comment.