zernpy.zernikepol
Main script with the class definition for accessing Zernike polynomial initialization, calculation and plotting.
Also, provides a few functions useful for fitting set of Zernike polynomials to an image with phases.
@author: Sergei Klykov, @year: 2024, @licence: MIT
Define the Zernike polynomial class and associated calculation methods.
Initialize of the class for Zernike polynomial definition as the object.
Parameters
**kwargs (orders or index for Zernike polynomial initialization expected as key=value pairs): Acceptable variants for key=value pairs arguments:
1) n=int, m=int with alternatives: "radial_order" for n; "l", "azimuthal_order", "angular_frequency" for m;
2) osa_index=int with alternatives: "osa", "ansi_index", "ansi";
3) noll_index=int with alternative "noll";
4) fringe_index=int with alternative "fringe"
Raises
- ValueError: Raised if the specified orders (m, n) or index (OSA, Noll...) have inconsistencies like:
for specified orders: 1) m < 0 or n < 0; 2) m or n is not int; 3) (self.__n - abs(self.__m)) % 2 == 0;
4) if n > 54 - because the polynomials with higher orders are meaningless due to very slow calculations;
for indices - see the raised error message.
References
[1] Wiki article: https://en.wikipedia.org/wiki/Zernike_polynomials
[2] Shakibaei B.H., Paramesran R. "Recursive formula to compute Zernike radial polynomials" (2013)
[3] Lakshminarayanan V., Fleck A. "Zernike polynomials: a guide" (2011)
Returns
- ZernPol class instance.
Return the tuple with following orders: ((m, n), OSA index, Noll index, Fringe index).
Returns
- tuple: with elements: (tuple (azimuthal (m), radial (n)) orders, OSA index, Noll index, Fringe index)
All indices are integers.
Return tuple with the (azimuthal, radial) orders, i.e. return (m, n).
Returns
- tuple: with the (azimuthal, radial) orders for the initialized Zernike polynomial.
Return string with the conventional name (see references for details) of polynomial up to 7th order.
Parameters
- short (bool, optional): If True, this method returns shortened name. The default is False.
References
[1] Up to 4th order: Wiki article: https://en.wikipedia.org/wiki/Zernike_polynomials
[2] 5th order names: from the website https://www.telescope-optics.net/monochromatic_eye_aberrations.htm
6th order - 7th order: my guess about the naming
Returns
- str: Name of the initialized polynomial.
Calculate Zernike polynomial value(-s) within the unit circle.
Calculation up to 10th order of Zernike function performed by exact equations from Ref.[2], after - using the recurrence equations taken from the Ref.[1], using shortcut of storing coefficients for each power of radius (coefficient*R^n)
Surprisingly, the exact equation for polynomials are just pretty fast to use them directly, without any need to use the recurrence equations. It can be used by providing the flag as the input parameter.
However, after ~ the 46th radial order due to the high integer values involved in the exact equation (factorials) producing ambiguous results (failed simple check that radial polynomial <= 1.0), only iterative equations (which along with increasing order become time-consuming and slow) could be used. The 40th radial order as the limit for usage of the exact equation is selected due to found increasing after this order discrepancy between results of recursive and factorial formulas.
References
[1] Shakibaei B.H., Paramesran R. "Recursive formula to compute Zernike radial polynomials" (2013)
[2] Lakshminarayanan V., Fleck A. "Zernike polynomials: a guide" (2011)
[3] Andersen T. B. "Efficient and robust recurrence relations for the Zernike circle polynomials and their derivatives in Cartesian coordinates" (2018)
Parameters
- r (float or numpy.ndarray): Radius (radii) from unit circle or the range [0.0, 1.0], float / array, for which the polynomial is calculated.
- theta (float or numpy.ndarray): Theta - angle in radians from the range [0, 2*pi], float or array, for which the polynomial is calculated. Note that the theta counting is counterclockwise, as it is default for the matplotlib library.
- use_exact_eq (bool, optional): Flag for using the exact equation with factorials. The default is False. Note about the limit for usage of the exact equation - up to 40th radial order (n).
Raises
- ValueError: Check the Error stack trace for reasons of raising, most probably input parameters aren't acceptable.
- Warning: If the theta angles lie outside the range [0, 2*pi] (entire period).
Returns
- float or numpy.ndarray: Calculated polynomial values on provided float values / arrays.
Calculate R(m, n) - radial Zernike function value(-s) within the unit circle.
Calculation up to 10th order of Zernike function performed by exact equations from Ref.[2], after - using the recurrence equations taken from the Ref.[1], using shortcut of storing coefficients for each power of radius (coefficient*R^n)
Surprisingly, the exact equation for polynomials are just pretty fast to use them directly, without any need to use the recurrence equations. It can be used by providing the flag as the input parameter.
However, after ~ the 46th radial order due to the high integer values involved in the exact equation (factorials) producing ambiguous results (failed simple check that radial polynomial <= 1.0), only iterative equations (which along with increasing order become time-consuming and slow) could be used. The 40th radial order as the limit for usage of the exact equation is selected due to found increasing after this order discrepancy between results of recursive and factorial formulas.
References
[1] Shakibaei B.H., Paramesran R. "Recursive formula to compute Zernike radial polynomials" (2013)
[2] Lakshminarayanan V., Fleck A. "Zernike polynomials: a guide" (2011)
[3] Andersen T. B. "Efficient and robust recurrence relations for the Zernike circle polynomials and their derivatives in Cartesian coordinates" (2018)
Parameters
- r (float or numpy.ndarray): Radius (radii) from unit circle or the range [0.0, 1.0], float / array, for which the function is calculated.
- use_exact_eq (bool, optional): Flag for using the exact equation with factorials. The default is False. Note about the limit for usage of the exact equation - up to 40th radial order (n).
Raises
- ValueError: The probable reasons: radius (radii) doesn't belong to a unit circle, input type isn't acceptable.
Returns
- float or numpy.ndarray: Calculated Zernike radial function value(-s) on provided float values / arrays of radiuses.
Calculate triangular Zernike function value(-s) within the unit circle.
Parameters
- theta (float or numpy.ndarray): Theta - angle in radians from the range [0, 2*pi], float or array, for which the polynomial is calculated. Note that the theta counting is counterclockwise, as it is default for the matplotlib library.
Raises
- ValueError: Most probably, raised if the conversion to float number is failed.
It happens when input parameter is not float, numpy.ndarray, list or tuple.
- Warning: If the theta angles lie outside the range [0, 2*pi] (entire period).
Returns
- float or numpy.ndarray: Calculated value(-s) of Zernike triangular function on provided angle.
Calculate derivative of radial Zernike polynomial value(-s) within the unit circle.
Calculation up to 10th order of Zernike polynomials performed by exact equations, after - using the recurrence equations, using shortcut of storing coefficients for each power of radius (coefficient*R^n)
The input flag use_exact_eq allows using the exact equation with factorials. But note that after 38th radial order the usage of the exact equation is forbidden, because after ~ the 44th radial order due to the high integer values associated with factorials and power values produced by derivatives leading to ambiguous results, only iterative equations (which along with increasing order become time-consuming and slow) could be used. The 38th radial order as the limit for usage of the exact equation is selected due to found increasing after this order discrepancy between results of recursive and factorial formulas.
References
Same as for the method "radial" or "polynomial value"
Parameters
- r (float or numpy.ndarray): Radius (radii) from unit circle or the range [0.0, 1.0], float / array, for which the polynomial is calculated.
- use_exact_eq (bool, optional): Flag for using the exact equation with factorials. The default is False. Note about the limit for usage of the exact equation - up to 38th radial order (n).
Raises
- ValueError: The probable reasons: radius (radii) doesn't belong to a unit circle, input type isn't acceptable.
Returns
- float or numpy.ndarray: Calculated derivative of Zernike radial function value(-s) on provided float values / arrays of radiuses.
Calculate derivative from triangular function on angle theta.
Parameters
- theta (float or numpy.ndarray): Theta - angle in radians from the range [0, 2*pi], float or array, for which the function is calculated. Note that the theta counting is counterclockwise, as it is default for the matplotlib library.
Raises
- ValueError: Most probably, raised if the conversion to float number is failed.
It happens when input parameter is not float, numpy.ndarray, list or tuple.
- Warning: If the theta angles lie outside the range [0, 2*pi] (entire period).
Returns
- float or numpy.ndarray: Calculated derivative value(-s) of Zernike triangular function on provided angle.
Calculate normalization factor for the Zernike polynomial calculated according to the References below.
References
[1] Shakibaei B.H., Paramesran R. "Recursive formula to compute Zernike radial polynomials" (2013) [2] Check also preceding coefficients in the table Zj column: https://en.wikipedia.org/wiki/Zernike_polynomials#Zernike_polynomials
Returns
- float: Normalization factor calculated according to the References.
Calculate OSA / ANSI index from the 2 orders of Zernike polynomials.
Parameters
- m (int): Azimuthal order (angular frequency).
- n (int): Radial order.
References
[1] Wiki article: https://en.wikipedia.org/wiki/Zernike_polynomials
Returns
- int: OSA index according to [1].
Calculate Noll index from the 2 orders of Zernike polynomials.
Parameters
- m (int): Azimuthal order (angular frequency).
- n (int): Radial order.
References
[1] Wiki article: https://en.wikipedia.org/wiki/Zernike_polynomials
Returns
- int: Noll index.
Calculate Fringe / University of Arizona index from the 2 orders of Zernike polynomials.
Parameters
- m (int): Azimuthal order (angular frequency).
- n (int): Radial order.
References
[1] Wiki article: https://en.wikipedia.org/wiki/Zernike_polynomials
Returns
- int: Fringe index.
Return tuple as (azimuthal, radial) orders for the specified by osa_, noll_ or fringe_index input parameter.
Parameters
- **kwargs (dict): Recognizable values: osa_index, noll_index, fringe_index.
Returns
- tuple: (m, n) - contains azimuthal and radial orders as integers.
Convert the OSA / ANSI index of a Zernike polynomial to the Noll index.
Parameters
- osa_index (int): OSA / ANSI index with int type, it must be not less than 0.
Raises
- ValueError: If the index provided with not int type or index < 0.
Returns
- int: Converted Noll index.
Convert the Noll index of a Zernike polynomial to the OSA / ANSI index.
Parameters
- noll_index (int): The Noll index with int type, it must be not less than 1.
Raises
- ValueError: If the index provided with not int type or index < 1.
Returns
- int: Converted OSA / ANSI index.
Convert the OSA / ANSI index of a Zernike polynomial to the Fringe index.
Parameters
- osa_index (int): OSA / ANSI index with int type, it must be not less than 0.
Raises
- ValueError: If the index provided with not int type or index < 0.
Returns
- int: Converted Fringe index.
Convert the Fringe / Univ. of Arizona index of a Zernike polynomial to the OSA / ANSI index.
Parameters
- fringe_index (int): The noll index with int type, it must be not less than 1.
Raises
- ValueError: If the index provided with not int type or index < 1.
Returns
- int: Converted OSA / ANSI index.
Calculate sum of Zernike polynomials with their amplitude coefficients (e.g., for plotting over a unit circle).
Parameters
- coefficients (Sequence[float]): Coefficients of Zernike polynomials for calculation of their sum.
- polynomials (Sequence[ZernPol]): Initialized polynomials as class instances of ZernPol class specified in this module.
- r (float or numpy.ndarray): Radius(Radii) from a unit circle.
- theta (float or numpy.ndarray): Polar angle(-s) from a unit circle.
get_surface (bool, optional): If True, it forces to calculate 2D sum of polynomials based on r and theta (as a mesh of polar coordinates). The default is False.
Note that if r and theta provided as the numpy ndarrays with different shapes and this flag is False, then the result of this method will raise ValueError (because r and theta shapes will be checked for equality).
Raises
- TypeError: If the input parameters aren't iterable (doesn't support len() function), this error will be raised.
- ValueError: If the lengths of lists (tuples, numpy.ndarrays) aren't equal for coefficients and polynomials.
Or if the list (tuple, numpy.ndarray vector, ...) with Zernike polynomials instances (ZernPol()).
Returns
- Sum of Zernike polynomials (float or numpy.ndarray): Depending on the input values and parameter get_surface - can be: float, 1D or 2D numpy.ndarrays.
Generate the named tuple "PolarVectors" with R and Theta - vectors with polar coordinates for an entire unit circle.
Note that R and Theta are generated as the numpy.ndarrays vectors (shape like (n elements, )). Their shapes are defined by the specification of r_step and theta_rad_step parameters.
Parameters
- r_step (float, optional): Step for generation the vector with radiuses for an entire unit circle. The default is 0.01.
- theta_rad_step (float, optional): Step for generation the vector with theta angles for an entire unit circle. The default is (np.pi/240).
Raises
- ValueError: If the r_step or theta_rad_step provided in the way, that vectors R and Theta cannot be generated.
Returns
- polar_vectors: namedtuple("PolarVectors", "R Theta"), where R - vector with radiuses values [0.0, r_step, ... 1.0], Theta - vector with theta angles values [0.0, theta_rad_step, ... 2*pi].
Generate the named tuple "PolarVectors" with R and Theta - vectors with polar coordinates for an entire unit circle.
Note that R and Theta are generated as the numpy.ndarrays vectors (shape like (n elements, )). Their shapes are equal and defined by the parameter n_points.
Parameters
- n_points (int, optional): Number of points between 0.0 ... 1.0 for radii and 0.0 ... 2pi for thetas. The default is 250.
Returns
- polar_vectors: namedtuple("PolarVectors", "R Theta"), where R - vector with radiuses values [0.0, r_step, ... 1.0], Theta - vector with theta angles values [0.0, theta_rad_step, ... 2*pi].
Plot the provided Zernike polynomial (instance of ZernPol class) on the matplotlib figure.
Note that the plotting function plt.show() creates the plot in non-interactive mode.
Parameters
- polynomial (ZernPol): Instance of ZernPol class.
- color_map (str, optional): Color map of the polar plot, common values for representation: coolwarm, jet, turbo, rainbow. As alternative - perceptually equal color maps: viridis, plasma. The default is "coolwarm". Note that rainbow, jet, turbo - not perceptually equal color maps.
- show_title (bool, optional): Toggle for showing the name of polynomial on the plot or not (only works for 2D case).
- use_defaults (bool, optional): Use default parameters for polar coordinates generation. The default is True.
- projection (str, optional): Either "2d" ("2D") - for 2D profile plot, or "3d" ("3D") - for 3D surface plot. The default is "2d".
- polar_coordinates (polar_vectors, optional): If the flag 'use_defaults' is False, this named tuple is used for accessing polar coordinates for plotting. The default is ().
Raises
- ValueError: If the flag use_defaults is False and polar_coordinates are not provided.
Returns
- None.
Generate surface of provided Zernike polynomials on the generated polar coordinates used steps.
Parameters
- coefficients (Sequence[float]): Coefficients of Zernike polynomials for calculation of their sum.
- polynomials (Sequence[ZernPol]): Initialized polynomials as class instances of ZernPol class specified in this module.
r_step (float, optional): Step for generation the vector with radiuses for an entire unit circle. The default is 0.01.
See also the documentation for the method gen_polar_coordinates().
theta_rad_step (float, optional): Step for generation the vector with theta angles for an entire unit circle. The default is (np.pi/180).
See also the documentation for the method gen_polar_coordinates().
- equal_n_coordinates (bool, optional): Switch between generation polar coordinates based on individual steps or on equal number of points. The default is False.
- n_points (int, optional): Number of points used for generation of equal sized polar coordinates r, theta.
Returns
- zernikes_surface: namedtuple("ZernikesSurface", "ZernSurf R Theta") - tuple for storing mesh values for polar coordinates. ZernSurf variable is 2D matrix with the sum of the input polynomials on generated polar coordinates (R, Theta).
Plot a sum of the specified Zernike polynomials by input lists (see function parameters) on the provided figure.
Note that for showing the plotted figure, one needs to call appropriate functions (e.g., matplotlib.pyplot.show() or figure.show()) as the method of the input parameter figure.
Parameters
- figure (plt.Figure): 'Figure' class there the plotting will be done, previous plot will be cleared out.
- use_defaults (bool, optional): Use for plotting default values for generation of a mesh of polar coordinates and calculation of Zernike polynomials sum or Use for plotting provided calculated beforehand surface. The default is True.
- coefficients (Sequence[float], optional): Coefficients of Zernike polynomials for calculation of their sum. The default is ().
- polynomials (Sequence[ZernPol], optional): Initialized polynomials as class instances of ZernPol class specified in this module. The default is ().
- zernikes_sum_surface (namedtuple("ZernikesSurface", "ZernSurf R Theta") , optional): This tuple should contain the ZernSurf calculated on a mesh of polar coordinates R, Theta. This tuple could be generated by the call of the static method gen_zernikes_surface(). Check the method signature for details. The default is ().
- show_range (bool, optional): Flag for showing range of provided values as the colorbar on the figure. The default is True.
- color_map (str, optional): Color map of the polar plot, common values for representation: coolwarm, jet, turbo, rainbow. As alternative - perceptually equal color maps: viridis, plasma. The default is "coolwarm". Note that rainbow, jet, turbo - not perceptually equal color maps.
- projection (str, optional): Either "2d" ("2D") - for 2D profile plot, or "3d" ("3D") - for 3D surface plot. The default is "2d".
Raises
- ValueError: Check the signature for details. In general, it will be raised if some input parameters are inconsistent.
Returns
- figure (plt.Figure): Matplotlib.pyplot Figure class there the Zernike polynomials sum plotted.
Fit provided Zernike polynomials (instances of ZernPol class) as the input tuple to the provided 1D vectors.
1D vectors should contain: phases recorded in the related radii and thetas (polar coordinates). For the example of phases and coordinates composing, see the module test_fitting in 'tests' sub-folder of the repository.
Parameters
- polynomials (tuple): Initialized tuple with instances of the ZernPol class that effectively represents target set of Zernike polynomials for fitting.
- phases_vector (numpy.ndarray): Recorded phases.
- radii_vector (numpy.ndarray): Related radii - 1st polar coordinates for the recorded phases.
- thetas_vector (numpy.ndarray): Related angles (thetas) - 2nd polar coordinates for the recorded phases.
- round_digits (int, optional): Round digits for returned polynomials amplitudes (numpy.round(...) function call). The default is 4.
Raises
- AttributeError: Any of provided vectors (phases_vector, etc.) isn't proper 1D numpy.ndarray (len(array.shape > 1)).
Returns
- zernike_coefficients (numpy.ndarray): 1D numpy.ndarray with the fitted coefficients of Zernike polynomials.
Fit provided Zernike polynomials (instances of ZernPol class) as the input tuple to the 2D phase image.
2D phase image implies that phases recorded depending on cartesian coordinates and circle aperture is cropped out from this image for fitting procedure. One can check the result of cropping by plotting return_cropped_image as True and plotting the second item from the returned tuple.
Parameters
- phases_image (numpy.ndarray): 2D image with recorded phases which should be approximated by the sum of Zernike polynomials. Note that image is assumed as the recorded phases on the cartesian coordinates. The circle (aperture) containing phases is cropped from the input image during the fitting procedure.
- polynomials (tuple): Initialized tuple with instances of the ZernPol class that effectively represents target set of Zernike polynomials.
- crop_radius (float, optional): Allow cropping pixel from range [0.5, 1.0], where 1.0 corresponds to radius of the cropped circle = min image size. The default is 1.0.
- suppress_warnings (bool, optional): Flag for suppress warnings about the provided 2D image sizes. The default is False.
- strict_circle_border (bool, optional): Flag for controlling how the border pixels (on the circle radius) are treated: strictly or less strict for allowing more pixels to be treated as belonged to a cropped circle. The default is False.
- round_digits (int, optional): Round digits for returned polynomials amplitudes (numpy.round(...) function call). The default is 4.
- return_cropped_image (bool, optional): Flag for calculating and returning cropped image, used for fitting procedure. The default is False.
Returns
- tuple: Depending on the input parameter (flag) "return_cropped_image": if it is True, then tuple returned with following variables: zernike_coefficients - 1D numpy.ndarray with the fitted coefficients of Zernike polynomials, and cropped_image - the cropped part from the input image with phases that is used for fitting procedure (useful for debugging purposes); if it is False, the following tuple will be returned: zernike_coefficients, None - 1st with the same meaning and type as explained before.
Generate phases image (profile) for provided set of polynomials with provided coefficients (amplitudes).
Parameters
- polynomials (tuple, optional): Initialized ZernPol instances for generation of phases as the sum of all stored in this tuple polynomials. The default is ().
- polynomials_amplitudes (tuple, optional): Amplitudes of polynomials provided in the tuple 'polynomials'. The default is ().
- img_width (int, optional): Width of generated image. The default is 513.
- img_height (int, optional): Height of generated image. The default is 513.
Returns
- phases_image (np.ndarray): 2D image with phases calculated as the sum of provided polynomials.
Generate phases image (profile) for random set of polynomials with randomly selected amplitudes.
Parameters
- max_order (int, optional): Maximum radial order of generated Zernike polynomials. The default is 4.
- img_width (int, optional): Width of generated image. The default is 513.
- img_height (int, optional): Height of generated image. The default is 513.
- round_digits (int, optional): Round digits for polynomials amplitudes generation (numpy.round(...) function call). The default is 4.
Returns
- tuple: Consisting of: 2D phase profile image with provided width and height; 1D numpy.ndarray containing randomly selected polynomials amplitudes; tuple with generated Zernike polynomials.
Generate tuple with ZernPol instances (ultimately, representing Zernike polynomials) indexed using OSA scheme, starting with Piston(m=0,n=0).
Parameters
- max_order (int, optional): Maximum overall radial order (n) for generated Zernike list (pyramid). The default is 10.
Raises
- ValueError: Raised if max_order < 1, because it should be not less than 0.
Returns
- tuple: It composes generated ZernPol instances (Zernike polynomials) ordered using OSA indexing scheme.