slmsuite.holography.toolbox.phase.zernike_sum#
- zernike_sum(grid, indices, weights, aperture=None, use_mask=True, derivative=(0, 0), out=None)[source]#
Returns a summation of Zernike polynomials in a computationally-efficient manner. These polynomials are commonly used as an orthonormal basis for optical aberration and are used in a number of places inside
slmsuitefor aberration compensation. This function returns a sum of polynomials:\[\phi(\vec{x}) = \sum_k w_k Z_{J_k}(\vec{x}).\]where \(J_k\) are the 1-dimensional ANSI
indicesof the polynomials and \(w_k\) are the floating pointweightsof each polynomial.Important
These polynomials \(Z_j\) are normalized within the edge of a standard Zernike pupil to a peak-to-valley amplitude of 2, corresponding to \(\pm 1\) for all polynomials except for the piston, which is identically 1. When
use_mask=False, the polynomial is not cropped outside the standard Zernike pupil. This should be used carefully, as polynomials outside the unit circle quickly explode with \(r^O\) for terms of order \(O\).Tip
See the below example to generate \(Z_1 - Z_2 + Z_4 = Z_1^{-1} - Z_1^1 + Z_2^0\), where the standard radial Zernike indexing \(Z_n^l\) is instead represented as \(Z_j\) by the 1-dimensional ANSI. index \(j\).
zernike_sum_phase = toolbox.phase.zernike_sum( grid=slm, indices=(1, 2, 4), # Define Z_1, Z_2, Z_4 weights=(1, -1, 1), # Request Z_1 - Z_2 + Z_4 aperture="circular" )
To improve performance, especially for higher order polynomials, we store a cache of Zernike coefficients to avoid regeneration.
Tip
slmsuite uses ANSI by default, but the user can convert between other common indexing conventions with
zernike_convert_index()- Parameters:
grid ((array_like, array_like) OR
SLM) – Meshgrids of normalized \(\frac{x}{\lambda}\) coordinates corresponding to SLM pixels, in(x_grid, y_grid)form. These are precalculated and stored in anySLM, so such a class can be passed instead of the grids directly.indices (array_like of int OR None) –
Which Zernike polynomials to sum, defined by ANSI indices. Of shape
(D,).Use
zernike_convert_index()to convert to ANSI from various other common indexing conventions.If
Noneis passed, the assumed Zernike basis depends on the dimensionality of the provided spots:If
D == 2, then the basis is assumed to be[2,1]corresponding to the \(x = Z_2 = Z_1^1\) and \(y = Z_1 = Z_1^{-1}\) tilt terms.If
D == 3, then the basis is assumed to be[2,1,4]corresponding to the previous, with the addition of the \(Z_4 = Z_2^0\) focus term.If
D > 3, then the basis is assumed to be[2,1,4,3,5,6...,D]. The piston term (Zernike index 0) is ignored as this constant phase is not relevant.
weights (array_like of float) – The weight for each given index. Of shape
(D,). If a stack of zernike sums is desired, then use shape(D, N).aperture ({"circular", "elliptical", "cropped"} OR (float, float) OR float OR None) –
Determines how the Zernike polynomials are laterally scaled. Parsed with
zernike_aperture().Important
Read the documentation and tips in
zernike_aperture()to avoid subtle issues with lateral scaling.use_mask (bool OR "return" OR np.nan) – If
True, sets the area where standard Zernike polynomials are undefined to zero. IfFalse, the polynomial is not cropped. This should be used carefully, as polynomials outside the unit circle quickly explode with \(r^O\) for terms of order \(O\). If"return", returns the 2D maskx_grid**2 + y_grid**2 <= 1. Ifnp.nan, the clipped area is set tonp.naninstead of zero; this is used for plotting transparency in this undefined region.derivative ((int, int)) – If non-negative, returns the Zernike derivative of the given order. For instance,
(1, 0)corresponds to the first derivative in the \(x\) direction. This is fast and accurate because the derivative is computed via power rule before generating Zernike images.out (array_like OR None) – Memory to be used for the phase output. Allocated separately if
None.
- Returns:
The phase for this function. Optionally returns the 2D Zernike mask.
- Return type:
numpy.ndarray