slmsuite.hardware.slms.screenmirrored.ScreenMirrored#

class ScreenMirrored(display_number, bitdepth=8, wav_um=1, pitch_um=(8, 8), verbose=True, slm_shape=None, **kwargs)[source]#

Bases: SLM

Wraps a pyglet window for displaying data to an SLM.

Warning

Version 2.1.9 of pyglet introduced a bug that leaves the SLM display zeroed even after phase data has been applied. Please use version 2.1.8 or earlier until this is resolved in a future release.

Important

Many SLM manufacturers provide an SDK for interfacing with their devices. Using a python wrapper for these SDKs is recommended, instead of or in supplement to this class, as there often is functionality additional to a mirrored screen (e.g. USB for changing settings) along with device-specific optimizations.

Note

There are a variety of python packages that support blitting images onto a fullscreen display.

  • Simple DirectMedia Layer (SDL) wrappers:

    • pygame (link), which also supports OpenGL. Only supports one screen.

    • sdl2 (readthedocs) through the PySDL2 package. Requires additional libraries.

  • Open Graphics Library (OpenGL) wrappers:

    • moderngl (readthedocs), an OpenGL wrapper focusing on a pythonic interface for core OpenGL functions.

    • OpenGL (link) through the PyOpenGL/PyOpenGL_accelerate package, a very light OpenGL wrapper.

    • pyglet (readthedocs), a light OpenGL wrapper.

  • GUI Library wrappers:

    • gi (readthedocs), through the PyGObject package wrapping GTK and other GUI libraries.

    • pyqt6 (link), through the PyQt6 package wrapping the version 6 Qt GUI library.

    • tkinter (link), included in standard python, wrapping the Tcl/Tk GUI library.

    • wx (link), through the wxPython package wrapping the wxWidgets GUI library. slmpy (GitHub) uses wx.

slmsuite uses pyglet as the default display package. pyglet is generally more capable than the mentioned SDL wrappers while immediately supporting features such as detecting connected displays which low-level packages like OpenGL and moderngl do not have. pyglet allows us to interact more directly with the display hardware without the additional overhead that is found in GUI libraries. Most importantly, pyglet is well documented.

However, it might be worthwhile in the future to look back into SDL options, as SDL surfaces are closer to the pixels than OpenGL textures, so greater speed might be achievable (even without loading data to the GPU as a texture).

This class supports GPU arrays when CuPy is available. Phase data can stay on the GPU throughout the processing pipeline, with only the final display step transferring to CPU. When pinned memory is available, direct CUDA memcpy to pinned host memory is used for faster DMA transfers compared to standard cp.asnumpy().

Important

ScreenMirrored uses a double-buffered and vertically synchronized (vsync) OpenGL context. This is to prevent “tearing” resulting from data being modified during a display write: rather, all monitor writes are synchronized such that clean frames are always displayed. This feature is similar to the isImageLock flag in slmpy, but is implemented a bit closer to the hardware.

Each ScreenMirrored window is created on its own dedicated background thread via _WindowThread. This allows the background threads to handle OS events and independent event dispatch/vsync timing for multi-SLM support.

This main thread communicates with those window threads via submit(), which blocks until the command completes on the window thread.

Note

Windows are created in fullscreen mode by default and are not intended for user interaction - they exist solely to display phase patterns to the SLM hardware. Event handling is implemented purely to prevent freezing, not to enable interactivity.

window#

Fullscreen window used to send information to the SLM.

Type:

_Window

display_resolution#

Resolution of the mirrored display in pixels, as (width, height).

Type:

(int, int)

Methods

close

Closes the SLM window and stops its background thread.

fit_source_amplitude

Extracts various source parameters from the source for use in analytic functions.

get_point_spread_function_knm

Fourier transforms the wavefront calibration's measured amplitude to directly compute the expected diffraction-limited performance of the system in "knm" space.

get_source_center

Extracts the scaling for zernike_aperture() from the scalars computed in fit_source_amplitude().

get_source_radius

Extracts the source radius in normalized units for functions like laguerre_gaussian() from the scalars computed in fit_source_amplitude().

get_source_zernike_scaling

Extracts the scaling for zernike_aperture() from the scalars computed in fit_source_amplitude().

get_spot_radius_kxy

Approximates the expected standard deviation radius of farfield spots in the "kxy" basis based on the near-field amplitude distribution stored in source.

info

Get information about the available displays, their indexes, and their sizes.

load_phase

Loads display from a file and writes to the SLM.

load_vendor_phase_correction

Loads vendor-provided phase correction from file, setting source["phase"].

pickle

Returns a dictionary containing selected attributes of this class.

plot

Plots the provided phase.

plot_source

Plots measured or simulated amplitude and phase distribution of the SLM illumination.

save

Saves the dictionary returned from pickle() to a file like "path/name_id.h5".

save_phase

Saves phase and display to a file like "path/name_id.h5".

set_input_trigger

(Not supported by this SLM.) Configures the input trigger of the SLM, where an external electronic signal can synchronize the time at which the SLM updates its display.

set_output_trigger

(Not supported by this SLM.) Configures the output trigger of the SLM, where the SLM can send an electronic signal upon updating its display.

set_phase

Checks, cleans, and adds to data, then sends the data to the SLM and potentially waits for settle_time_s seconds.

set_source_analytic

In the absence of a proper wavefront calibration, sets source amplitude and phase using a fit_function from fitfunctions.

set_source_aperture

Sets source aperture parameters measured by fit_source_amplitude() and takes appropriate follow-on actions like shifting the grid.

test

Tests the core hardware methods of SLM.

write

Backwards-compatibility alias for set_phase().

Attributes

bitresolution

__init__(display_number, bitdepth=8, wav_um=1, pitch_um=(8, 8), verbose=True, slm_shape=None, **kwargs)[source]#

Initializes a pyglet window for displaying data to an SLM.

The window is created on a dedicated background thread to ensure continuous event dispatch and prevent freezing.

Caution

An SLM designed at 1064 nm can be used for an application at 780 nm by passing wav_um=.780 and wav_design_um=1.064, thus causing the SLM to use only a fraction (780/1064) of the full dynamic range. Be sure these values are correct. Note that there are some performance losses from using this modality (see set_phase()).

Caution

There is some subtlety to complex display setups with Linux. Working outside the default display is currently not implemented.

Parameters:
  • display_number (int) – Monitor number for frame to be instantiated upon.

  • bitdepth (int) –

    Bitdepth of the SLM. Defaults to 8.

    Caution

    This class currently supports SLMs with 8-bit precision or less. In the future, this class will also support 16-bit SLMs using RG color.

  • wav_um (float) – Wavelength of operation in microns. Defaults to 1 μm.

  • pitch_um ((float, float)) – Pixel pitch in microns. Defaults to 8 micron square pixels.

  • verbose (bool) – Whether or not to print extra information.

  • slm_shape (tuple of int or None) –

    SLM resolution as (width, height), for when the SLM’s active area differs from the display resolution (e.g. PLM). Defaults to None, which uses the display’s native resolution.

    Caution

    This should normally be left as None unless the SLM has a different shape than the display. Note that different SLM and screen resolutions are not generally supported unless explicitly implemented in the associated SLM class.

  • **kwargs – See SLM.__init__() for permissible options.

close()[source]#

Closes the SLM window and stops its background thread.

See SLM.

static info(verbose=True)[source]#

Get information about the available displays, their indexes, and their sizes.

Parameters:

verbose (bool) – Whether or not to print display information.

Returns:

The number, geometry of each display.

Return type:

list of (int, (int, int, int, int), bool, bool) tuples

fit_source_amplitude(method='moments', extent_threshold=0.1, force=True)[source]#

Extracts various source parameters from the source for use in analytic functions. This is done by analyzing the source ["amplitude"] distribution with "moments" or least squares "fit". These parameters include the following keys:

  • "amplitude_center_pix" : (float, float)

    Pixel corresponding to the center of the source. The grid is also changed to be centered on this pixel.

  • "amplitude_radius" : float

    The radial standard deviation of the amplitude distribution in normalized units. For a Gaussian source, this is the \(1/e\) amplitude radius (\(1/e^2\) power radius). This is scalar and averages the \(x\) and \(y\) distributions. This is used to set the source radius for laguerre_gaussian() and similar.

  • "amplitude_extent" : (float, float)

    The box radii of the smallest rectangle which covers all amplitude larger than extent_threshold, where the maximum of the distribution is normalized to one.

  • "amplitude_extent_radius" : float

    Smallest scalar radius about the center of the that covers all amplitude larger than extent_threshold, where the maximum of the distribution is normalized to one. This is used to determine the scaling for zernike_aperture(): Too small of a scaling is not good because amplitude would overlap outside where Zernike is defined, with divergent phase for higher order Zernike polynomials. Too large of a scaling is not good because one needs to use high order Zernike to attain sufficient spatial resolution at the center of the distribution.

Important

If source ["amplitude"] is not set, then the parameters are guessed as fractions of the grid:

  • "amplitude_center_pix" Unchanged from current center.

  • "amplitude_radius" Guessed as 1/4 of the smallest extent.

  • "amplitude_extent" Guessed as the the rectangle that circumscribes the SLM field.

  • "amplitude_extent_radius" Guessed as the the radius that circumscribes the SLM field.

Important

The grid is recentered upon the detected center of the source. This grid is used to generated phase functions like lens() or laguerre_gaussian(). Such generation works best when centered upon the source; a lens() focuses coaxially and a laguerre_gaussian() appears symmetric.

Parameters:
  • method (str {"fit", "moments"}) – Whether to use moment calculations "moments" or a least squares "fit" to determine "amplitude_center_pix" and "amplitude_radius". "moments" is faster but "fit" is more accurate.

  • extent_threshold (float) – Fraction of the maximal amplitude to use as the full extent of the amplitude distribution.

  • force (bool) – If False, does not calculate if these quantities already exist. True forces recomputation.

get_point_spread_function_knm(padded_shape=None)[source]#

Fourier transforms the wavefront calibration’s measured amplitude to directly compute the expected diffraction-limited performance of the system in "knm" space.

Parameters:

padded_shape ((int, int) OR None) – The point spread function changes in resolution depending on the padding. Use this variable to provide this padding. If None, do not pad.

Returns:

The point spread function of shape padded_shape.

Return type:

numpy.ndarray

get_source_center()[source]#

Extracts the scaling for zernike_aperture() from the scalars computed in fit_source_amplitude().

get_source_radius()[source]#

Extracts the source radius in normalized units for functions like laguerre_gaussian() from the scalars computed in fit_source_amplitude().

get_source_zernike_scaling()[source]#

Extracts the scaling for zernike_aperture() from the scalars computed in fit_source_amplitude().

get_spot_radius_kxy()[source]#

Approximates the expected standard deviation radius of farfield spots in the "kxy" basis based on the near-field amplitude distribution stored in source. For a Gaussian source, this is the \(1/e\) amplitude radius (\(1/e^2\) power radius).

Returns:

Radius of the farfield spot.

Return type:

float

load_phase(file_path=None, settle=False)[source]#

Loads display from a file and writes to the SLM.

Parameters:
  • file_path (str OR None) – Full path to the phase file. If None, will search the current directory for a file with a name like name + '-phase'.

  • settle (bool) – Whether to sleep for settle_time_s.

Returns:

The file path that the phase was loaded from.

Return type:

str

Raises:
  • FileNotFoundError – If a file is not found.

  • Warning – Warns the user if the stored phase does not agree with the displayed value.

load_vendor_phase_correction(file_path)[source]#

Loads vendor-provided phase correction from file, setting source["phase"]. By default, this is interpreted as an image file and is padded or unpadded to the shape of the SLM. Subclasses should implement vendor-specific routines for loading and interpreting the file (e.g. Santec loads a .csv).

Parameters:

file_path (str) – File path for the vendor-provided phase correction.

Returns:

source["phase"], the vendor-provided phase correction.

Return type:

numpy.ndarray

pickle(attributes=True, metadata=True)[source]#

Returns a dictionary containing selected attributes of this class.

Parameters:
  • attributes (bool OR list of str) – If False, pickles only baseline attributes, usually single floats. If True, also pickles ‘heavy’ attributes such as large images and calibrations. If list of str, pickles the keys in the given list. By default, the chosen attributes should be things that can be written to .h5 files: scalars and lists of scalars.

  • metadata (bool) – If True, package the dictionary as the "__meta__" value of a superdictionary which also contains: "__version__", the current slmsuite version, "__time__", the time formatted as a date string, and "__timestamp__", the time formatting as a floating point timestamp. This information is used as standard metadata for calibrations and saving.

plot(phase=None, limits=None, title='Phase', ax=None, cbar=True)[source]#

Plots the provided phase.

Parameters:
  • phase (ndarray OR None) – Phase to be plotted. If None, grabs the last written phase from the SLM.

  • limits (None OR float OR [[float, float], [float, float]]) – Scales the limits by a given factor or uses the passed limits directly.

  • title (str) – Title the axis.

  • ax (matplotlib.pyplot.axis OR None) – Axis to plot upon.

  • cbar (bool) – Also plot a colorbar.

Returns:

Axis of the plotted phase.

Return type:

matplotlib.pyplot.axis

plot_source(source=None, sim=False, power=False)[source]#

Plots measured or simulated amplitude and phase distribution of the SLM illumination. Also plots the rsquared goodness of fit value if available.

Parameters:
  • source (dict OR None) – The data to plot. If None, uses source.

  • sim (bool) – Plots the simulated source distribution if True or the measured source distribution if False.

  • power (bool) – If True, plot the power (amplitude squared) instead of the amplitude.

Returns:

Axis handles for the generated plot.

Return type:

matplotlib.pyplot.axis

save(path='.', name=None, **kwargs)[source]#

Saves the dictionary returned from pickle() to a file like "path/name_id.h5".

Parameters:
  • path (str) – Path to directory to save in. Default is current directory.

  • name (str OR None) – Name of the save file. If None, will use name + '-pickle'.

  • **kwargs – Passed to pickle() to customize how and what data is saved.

Returns:

The file path that the pickled data was saved to.

Return type:

str

save_phase(path='.', name=None)[source]#

Saves phase and display to a file like "path/name_id.h5".

Parameters:
  • path (str) – Path to directory to save in. Default is current directory.

  • name (str OR None) – Name of the save file. If None, will use name + '-phase'.

Returns:

The file path that the phase was saved to.

Return type:

str

set_input_trigger(on=False)[source]#

(Not supported by this SLM.) Configures the input trigger of the SLM, where an external electronic signal can synchronize the time at which the SLM updates its display.

Parameters:

on (bool) – Subclasses must support a boolean configuration argument, but can also accept other datatypes or parameters as needed.

set_output_trigger(on=False)[source]#

(Not supported by this SLM.) Configures the output trigger of the SLM, where the SLM can send an electronic signal upon updating its display.

Parameters:

on (bool) – Subclasses must support a boolean configuration argument, but can also accept other datatypes or parameters as needed.

set_phase(phase, phase_correct=None, settle=None, execute=None, block=None, **kwargs)[source]#

Checks, cleans, and adds to data, then sends the data to the SLM and potentially waits for settle_time_s seconds. This method calls the SLM-specific private method _format_phase_hw() (if implemented) to format the phase data before calling _set_phase_hw() which transfers the data to the SLM.

Warning

Subclasses implementing vendor-specific software should not overwrite this method. Subclasses should overwrite _set_phase_hw() (and _format_phase_hw() if required) instead.

Caution

The sign on phase is flipped before converting to integer data. This is to convert between the ‘increasing value ==> increasing voltage (= decreasing phase delay)’ convention in most SLMs and slmsuite’s ‘increasing value ==> increasing phase delay’ convention. As a result, zero phase will appear entirely white (255 for an 8-bit SLM), and increasing phase will darken the displayed pattern. If integer data is passed, this data is displayed directly and the sign is not flipped.

Important

The user does not need to wrap (e.g. numpy.mod(data, 2*numpy.pi)) the passed phase data, unless they are pre-caching data for speed (see below). set_phase() uses optimized routines to wrap the phase (see the private method _phase2gray()). Which routine is used depends on phase_scaling:

  • phase_scaling is one.

    Fast bitwise integer modulo is used. Much faster than the other routines which depend on numpy.mod().

  • phase_scaling is less than one.

    In this case, the SLM has more phase tuning range than necessary. If the data is within the SLM range [0, 2*pi/phase_scaling], then the data is passed directly. Otherwise, the data is wrapped by \(2\pi\) using the very slow numpy.mod(). Try to avoid this in applications where speed is important.

  • phase_scaling is more than one.

    In this case, the SLM has less phase tuning range than necessary. Processed the same way as the phase_scaling is less than one case, with the important exception that phases (after wrapping) between 2*pi/phase_scaling and 2*pi are set to zero. For instance, a sawtooth blaze would be truncated at the tips.

Caution

After scale conversion, data is floor() ed to integers with np.copyto, rather than rounded to the nearest integer (np.rint() equivalent). While this is irrelevant for the average user, it may be significant in some cases. If this behavior is undesired consider either: set_phase() integer data directly or modifying the behavior of the private method _phase2gray() in a pull request. We have not been able to find an example of np.copyto producing undesired behavior, but will change this if such behavior is found.

Parameters:

phase (numpy.ndarray OR cupy.cparray OR) – slmsuite.holography.algorithms.Hologram OR None

:param Phase data to display in units of \(2\pi\): :param unless the passed: :param data is of integer type and the data is applied directly.:

  • If None is passed to set_phase(), data is zeroed.

  • If a Hologram is passed, the phase is grabbed from get_phase().

  • If the array has a larger shape than the SLM shape, then the data is cropped to size in a centered manner (unpad).

  • If integer data is passed with the same type as display (np.uint8 for <=8-bit SLMs, np.uint16 otherwise), then this data is directly passed to the SLM, without going through the “phase delay to grayscale” conversion defined in the private method _phase2gray(). In this situation, phase_correct is ignored. This is error-checked such that bits with greater significance than the bitdepth of the SLM are zero (e.g. the final 6 bits of 16 bit data for a 10-bit SLM). Integer data with type different from display leads to a TypeError.

Usually, an exact stored copy of the data passed by the user under phase is stored in the attribute phase. However, in cases where phase_scaling not one, this copy is modified to include how the data was wrapped. If the data was cropped, then the cropped data is stored, etc. If integer data was passed, the equivalent floating point phase is computed and stored in the attribute phase.

Parameters:
  • phase_correct (bool OR None) – Whether to add wavefront correction to the pattern. This correction is stored in source```["phase"]`. If None, defaults to phase_correct (which defaults to True).

  • settle (bool OR None) – Whether to sleep for settle_time_s. If None, defaults to settle (which defaults to False).

  • execute (bool OR None) –

    Whether to actually send the image to the SLM. Most SLMs do not support this feature, and will error if execute is not None. Otherwise, None must default to True. Use case: if execute=False and block=True, only the block is enforced and no new data is written.

    Important

    New phase/display data is always calculated regardless of the value of execute.

  • block (bool OR None) – Some SLM subclasses support non-blocking writes that are triggered externally. This parameter will determine whether to block the thread until the image is fully written. Most SLMs do not support this feature, and will error if block is not None. Otherwise, None must default to True. Use case: if execute=True and block=False, the write is non-blocking.

  • **kwargs – Passed to the SLM in case the subclass needs to do something special. For instance, some SLMs support a timeout parameter that determines how long to wait for the SLM commands to execute before raising an error.

Returns:

display, the integer data sent to the SLM.

Return type:

numpy.ndarray

Raises:

TypeError – If integer data is incompatible with the bitdepth or if the passed phase is otherwise incompatible (not a 2D array or smaller than the SLM shape, etc).

set_source_analytic(fit_function='gaussian2d', units='norm', phase_offset=0, sim=False, **kwargs)[source]#

In the absence of a proper wavefront calibration, sets source amplitude and phase using a fit_function from fitfunctions.

Note

FourierSLM includes capabilities for wavefront calibration via wavefront_calibrate(). This process also measures the amplitude of the source on the SLM and stores this in source. source keywords are also used for better refinement of holograms during numerical optimization. If unable to run wavefront_calibrate(), this method allows the user to set an approximation of the complex source.

Parameters:
  • fit_function (str OR lambda) – Function name from fitfunctions used to set the source profile. The function can also be passed directly. Defaults to "gaussian2d".

  • units (str in {"norm", "frac", "nm", "um", "mm", "m"}) – Units for the \((x,y)\) grid passed to fit_function. This essentially determines the scaling on the normalized grid stored in the SLM which is passed to the fit_function.

  • sim (bool) – Sets the simulated source distribution if True or the approximate experimental source distribution (in absence of wavefront calibration) if False.

  • phase_offset (float OR numpy.ndarray) – Additional phase (of shape shape) added to source.

  • **kwargs – Arguments passed to fit_function in addition to the SLM grid in the requested units. If the fit_function is "gaussian2d" and no keyword arguments have been passed, the radius defaults to 1/2 of the smaller of the two SLM dimensions.

Returns:

source.

Return type:

dict

set_source_aperture(amplitude_center_pix=None, amplitude_radius=None, amplitude_extent=None, amplitude_extent_radius=None)[source]#

Sets source aperture parameters measured by fit_source_amplitude() and takes appropriate follow-on actions like shifting the grid.

Parameters:
  • amplitude_center_pix ((float, float) OR None) – Pixel corresponding to the center of the source. If provided, the grid is recentered on this pixel.

  • amplitude_radius (float OR None) – The radial standard deviation of the amplitude distribution in normalized units. For a Gaussian source, this is the \(1/e\) amplitude radius (\(1/e^2\) power radius).

  • amplitude_extent ((float, float) OR None) – The box radii of the smallest rectangle which covers the desired amplitude extent in normalized units.

  • amplitude_extent_radius (float OR None) – Scalar radius about the center that covers the desired amplitude extent in normalized units.

Returns:

source.

Return type:

dict

test()[source]#

Tests the core hardware methods of SLM. Validates that the SLM is connected correctly and all hardware features are supported.

write(phase, phase_correct=True, settle=False, **kwargs)[source]#

Backwards-compatibility alias for set_phase().