slmsuite.hardware.slms.screenmirrored.ScreenMirrored#
- class ScreenMirrored(display_number, bitdepth=8, wav_um=1, pitch_um=(8, 8), verbose=True, **kwargs)[source]#
Bases:
SLMWraps a
pygletwindow for displaying data to an SLM.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 thePySDL2package. 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 thePyOpenGL/PyOpenGL_acceleratepackage, a very light OpenGL wrapper.pyglet(readthedocs), a light OpenGL wrapper.
GUI Library wrappers:
gi(readthedocs), through thePyGObjectpackage wrappingGTKand other GUI libraries.pyqt6(link), through thePyQt6package wrapping the version 6QtGUI library.tkinter(link), included in standardpython, wrapping theTcl/TkGUI library.wx(link), through thewxPythonpackage wrapping thewxWidgetsGUI library.slmpy(GitHub) useswx.
slmsuiteusespygletas the default display package.pygletis generally more capable than the mentioned SDL wrappers while immediately supporting features such as detecting connected displays which low-level packages likeOpenGLandmoderngldo not have.pygletallows us to interact more directly with the display hardware without the additional overhead that is found in GUI libraries. Most importantly,pygletis 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). Another potential improvement could come from writing
cupydatastructures toOpenGLtextures directly, without using the CPU as an intermediary. There is some precedent for transferring data fromCUDA(on whichcupyis based) toOpenGL, thoughcupydoes not currently directly support this.Important
ScreenMirroreduses a double-buffered and vertically synchronized (vsync)OpenGLcontext. 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 theisImageLockflag inslmpy, but is implemented a bit closer to the hardware.- window#
Fullscreen window used to send information to the SLM.
- Type:
pyglet.window.Window
- tex_shape_ratio#
Ratio between the SLM shape and the (power-2-padded) texture stored in
OpenGL.- Type:
(int, int)
- buffer#
Memory used to load data to the
OpenGLmemory. Of typenp.uint8.- Type:
numpy.ndarray
- cbuffer#
Array of length
prod(shape) * 4(4 bytes per RGBA). Maps to the same memory asbuffer. Used to load data to the texture.- Type:
pyglet.gl.GLubyte
- texture#
Identifier for the texture loaded into
OpenGLmemory.- Type:
pyglet.gl.GLuint
Methods
Closes frame.
Extracts various
sourceparameters from the source for use in analytic functions.Fourier transforms the wavefront calibration's measured amplitude to find the expected diffraction-limited performance of the system in
"knm"space.Extracts the scaling for
zernike_aperture()from the scalars computed infit_source_amplitude().Extracts the source radius in normalized units for functions like
laguerre_gaussian()from the scalars computed infit_source_amplitude().Extracts the scaling for
zernike_aperture()from the scalars computed infit_source_amplitude().Approximates the expected standard deviation radius of farfield spots in the
"kxy"basis based on the near-field amplitude distribution stored insource.Get information about the available displays, their indexes, and their sizes.
Loads
displayfrom a file and writes to the SLM.Loads vendor-provided phase correction from file, setting
source["phase"].Returns a dictionary containing selected attributes of this class.
Plots the provided phase.
Plots measured or simulated amplitude and phase distribution of the SLM illumination.
Saves the dictionary returned from
pickle()to a file like"path/name_id.h5".Checks, cleans, and adds to data, then sends the data to the SLM and potentially waits for settle.
In the absence of a proper wavefront calibration, sets
sourceamplitude and phase using a fit_function fromfitfunctions.Backwards-compatibility alias for
set_phase().- __init__(display_number, bitdepth=8, wav_um=1, pitch_um=(8, 8), verbose=True, **kwargs)[source]#
Initializes a
pygletwindow for displaying data to an SLM.Caution
An SLM designed at 1064 nm can be used for an application at 780 nm by passing
wav_um=.780andwav_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 (seeset_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 um.
pitch_um ((float, float)) – Pixel pitch in microns. Defaults to 8 micron square pixels.
verbose (bool) – Whether or not to print extra information.
**kwargs – See
SLM.__init__()for permissible options.
- 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
sourceparameters from the source for use in analytic functions. This is done by analyzing thesource["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": floatThe 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": floatSmallest 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 forzernike_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
gridis recentered upon the detected center of the source. Thisgridis used to generated phase functions likelens()orlaguerre_gaussian(). Such generation works best when centered upon the source; alens()focuses coaxially and alaguerre_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.Trueforces recomputation.
- get_point_spread_function_knm(padded_shape=None)[source]#
Fourier transforms the wavefront calibration’s measured amplitude to find 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 infit_source_amplitude().
- get_source_radius()[source]#
Extracts the source radius in normalized units for functions like
laguerre_gaussian()from the scalars computed infit_source_amplitude().
- get_source_zernike_scaling()[source]#
Extracts the scaling for
zernike_aperture()from the scalars computed infit_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 insource. 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
displayfrom 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 likename+'-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
phasedoes 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.Santecloads 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. IfTrue, also pickles ‘heavy’ attributes such as large images and calibrations. Iflist 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 writtenphasefrom 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(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:
sim (bool) – Plots the simulated source distribution if
Trueor the measured source distribution ifFalse.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 usename+'-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
phaseanddisplayto 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 usename+'-phase'.
- Returns:
The file path that the phase was saved to.
- Return type:
str
- set_phase(phase, phase_correct=True, settle=False)[source]#
Checks, cleans, and adds to data, then sends the data to the SLM and potentially waits for settle. This method calls the SLM-specific private method
_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()instead.Caution
The sign on
phaseis flipped before converting to integer data. This is to convert between the ‘increasing value ==> increasing voltage (= decreasing phase delay)’ convention in most SLMs andslmsuite’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 onphase_scaling:phase_scalingis one.Fast bitwise integer modulo is used. Much faster than the other routines which depend on
numpy.mod().
phase_scalingis 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 slownumpy.mod(). Try to avoid this in applications where speed is important.
phase_scalingis more than one.In this case, the SLM has less phase tuning range than necessary. Processed the same way as the
phase_scalingis less than one case, with the important exception that phases (after wrapping) between2*pi/phase_scalingand2*piare set to zero. For instance, a sawtooth blaze would be truncated at the tips.
Caution
After scale conversion, data is
floor()ed to integers withnp.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 ofnp.copytoproducing undesired behavior, but will change this if such behavior is found.- Parameters:
phase (numpy.ndarray OR slmsuite.holography.algorithms.Hologram OR None) –
Phase data to display in units of \(2\pi\), unless the passed data is of integer type and the data is applied directly.
If
Noneis passed toset_phase(), data is zeroed.If a
Hologramis passed, the phase is grabbed fromget_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.uint8for <=8-bit SLMs,np.uint16otherwise), 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_correctis 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 fromdisplayleads to a TypeError.
Usually, an exact stored copy of the data passed by the user under
phaseis stored in the attributephase. However, in cases wherephase_scalingnot 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 attributephase.phase_correct (bool) – Whether or not to add
source```["phase"]`tophase.settle (bool) – Whether to sleep for
settle_time_s.
- 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
sourceamplitude and phase using a fit_function fromfitfunctions.Note
FourierSLMincludes capabilities for wavefront calibration viawavefront_calibrate(). This process also measures the amplitude of the source on the SLM and stores this insource.sourcekeywords are also used for better refinement of holograms during numerical optimization. If unable to runwavefront_calibrate(), this method allows the user to set an approximation of the complex source.- Parameters:
fit_function (str OR lambda) – Function name from
fitfunctionsused 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 thefit_function.sim (bool) – Sets the simulated source distribution if
Trueor the approximate experimental source distribution (in absence of wavefront calibration) ifFalse.phase_offset (float OR numpy.ndarray) – Additional phase (of shape
shape) added tosource.**kwargs – Arguments passed to
fit_functionin addition to the SLM grid in the requestedunits. If thefit_functionis"gaussian2d"and no keyword arguments have been passed, the radius defaults to 1/2 of the smaller of the two SLM dimensions.
- Returns:
- Return type:
dict
- write(phase, phase_correct=True, settle=False)[source]#
Backwards-compatibility alias for
set_phase().