pycvcam.ZernikeDistortion#
ZernikeDistortion Class#
- class ZernikeDistortion(parameters=None, constants=None, n_params=None, n_zer=None)[source]#
Subclass of the
pycvcam.core.Distortionclass that represents a Zernike decomposition distortion model.Note
This class represents the distortion transformation, which is the middle step of the process from the
world_pointsto theimage_points.The
ZernikeDistortionmodel consists of a Zernike polynomial decomposition of the distortion along the x and y axes. The distortion is represented as a sum of Zernike polynomials, which are orthogonal polynomials defined on the unit disk.Lets consider
normalized_pointsin the camera normalized coordinate system \(\vec{x}_n = (x_n, y_n)\), the correspondingdistorted_pointsin the camera normalized coordinate system are given \(\vec{x}_d\) can be obtained by :\[x_{d} = x_{n} + \sum_{n=0}^{N_{zer}} \sum_{m=-n}^{n} C^{x}_{n,m} Z_{nm}(\rho, \theta)\]\[y_{d} = y_{n} + \sum_{n=0}^{N_{zer}} \sum_{m=-n}^{n} C^{y}_{n,m} Z_{nm}(\rho, \theta)\]where \(Z_{nm}(\rho, \theta)\) are the Zernike polynomials, \(C^{x}_{n,m}\) and \(C^{y}_{n,m}\) are the Zernike coefficients for the x and y coordinates, respectively, and \(\rho\) and \(\theta\) are the polar coordinates of the normalized points in the defined unit ellipse with radius \(R_x, R_y\) and center \((x_0, y_0)\):
\[\rho = \sqrt{\left(\frac{x_{n} - x_{0}}{R_x}\right)^2 + \left(\frac{y_{n} - y_{0}}{R_y}\right)^2}\]\[\theta = \arctan2(y_{n} - y_{0}, x_{n} - x_{0})\]Note
For more informations about Zernike polynomials, see the package pyzernike (Artezaru/pyzernike).
This transformation is caracterized by n_params parameters and 4 constants:
n_params parameters \(C^{x}_{n,m}\) and \(C^{y}_{n,m}\) for the Zernike coefficients.
4 constants \((R_x, R_y, x_0, y_0)\) for the radius and center of the ellipse of definition of the Zernike polynomials.
Only coefficients for \(n \leq N_{zer}\) and \(m \in [-n, n]\) and \(n-m \equiv 0 \mod 2\) are stored.
The coefficients are storred in a
parameters1D-array with the OSA/ANSI standard indices but with a x/y separation:C^{x}_{0,0}, parameters[0] for the x coordinate \(n=0, m=0\)
C^{y}_{0,0}, parameters[1] for the y coordinate \(n=0, m=0\)
C^{x}_{1,-1}, parameters[2] for the x coordinate \(n=1, m=-1\)
C^{y}_{1,-1}, parameters[3] for the y coordinate \(n=1, m=-1\)
C^{x}_{1,1}, parameters[4] for the x coordinate \(n=1, m=1\)
C^{y}_{1,1}, parameters[5] for the y coordinate \(n=1, m=1\)
C^{x}_{2,-2}, parameters[6] for the x coordinate \(n=2, m=-2\)
C^{y}_{2,-2}, parameters[7] for the y coordinate \(n=2, m=-2\)
C^{x}_{2,0}, parameters[8] for the x coordinate \(n=2, m=0\)
C^{y}_{2,0}, parameters[9] for the y coordinate \(n=2, m=0\)
C^{x}_{2,2}, parameters[10] for the x coordinate \(n=2, m=2\)
C^{y}_{2,2}, parameters[11] for the y coordinate \(n=2, m=2\)
…
If the number of input parameters is not equal to the number of parameters required by the model, the other parameters are set to 0.
The number of parameters is given by the formula:
\[N_{params} = (N_{zer}+1)(N_{zer}+2)\]Ordre of Zernike
n_zerNparameters for X or Y
Nparameters in model
n_paramsNone
0
0
0
1
2
1
3
6
2
6
12
3
10
20
4
15
30
Warning
If the ordre of the zernike polynomials
n_zeris given during instantiation, the given parameters are truncated or extended to the given number of parameters. Same for the number of parametersn_params.To compute the Distortion, the user must define the unit circle in which the normalized points are defined. The unit circle is defined by the radius \(R\) and the center \((x_{0}, y_{0})\).
- Parameters:
parameters (Optional[numpy.ndarray], optional) – The parameters of the distortion transformation. It should be a numpy array of shape (n_params,) containing the distortion coefficients ordered as described above. Default is None, which means no distortion is setted.
constants (numpy.ndarray, optional) – The constants of the distortion transformation. It should be a numpy array of shape (4,) containing the radius and center of the ellipse of definition of the Zernike polynomials in the order: (R_x, R_y, x_0, y_0). Default is [1.0, 1.0, 0.0, 0.0], which means the unit circle is defined with radius 1 and center (0, 0).
n_params (Optional[Integral], optional) – The number of parameters for the distortion model. If not specified, it will be inferred from the shape of the parameters array.
n_zer (int, optional) – The order of the Zernike polynomials. If None, the order is set according to the number of parameters. The default is None. Only use
n_zerorn_params, not both.
Accessing the parameters of ZernikeDistortion objects#
The parameters and constants properties can be accessing using pycvcam.core.Transform methods.
Some additional convenience methods are provided to access commonly used parameters of the Cv2Distortion model:
Get or set the center of the unit circle in which the normalized points are defined. |
|
Get or set the x coordinate of the center of the unit circle in which the normalized points are defined. |
|
Get or set the y coordinate of the center of the unit circle in which the normalized points are defined. |
|
Get or set the order of the Zernike polynomials. |
|
Get or set the Zernike coefficients for the x coordinate. |
|
["Cx(0, 0), Cx(1, -1), Cx(1, 1), ...] |
|
Get or set the Zernike coefficients for the y coordinate. |
|
["Cy(0, 0), Cy(1, -1), Cy(1, 1), ...] |
|
Get or set the radius of the unit circle in which the normalized points are defined. |
|
Get or set the radius of the unit circle in the x direction. |
|
Get or set the radius of the unit circle in the y direction. |
Optionnaly, the parameters of the ZernikeDistortion can be accessed using set using the convenience methods based on Zernike polynomials indexing:
|
Get the index of the Zernike coefficient for the given order and azimuthal frequency. |
|
Get the Zernike coefficient for the x coordinate. |
|
Get the Zernike coefficient for the y coordinate. |
|
Set the Zernike coefficient for the x coordinate. |
|
Set the Zernike coefficient for the y coordinate. |
Performing distortion with ZernikeDistortion objects#
The transform and inverse_transform methods can be used to perform distortion and undistortion using the Cv2Distortion model (as described in the pycvcam.core.Transform documentation).
The implementation of theses transformations and more details on the options available can be found in the following methods:
|
Compute the transformation from the |
|
Compute the inverse transformation from the |
Examples#
Create an distortion object with a specific order of Zernike polynomials and parameters:
import numpy
from pycvcam import ZernikeDistortion
# Create a distortion object with 6 parameters
distortion = ZernikeDistortion(numpy.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])) # Model with n_zer=1, -> n_params=6
Then you can use the distortion object to transform normalized_points to distorted_points:
normalized_points = numpy.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]) # shape (n_points, 2)
result = distortion.transform(normalized_points)
distorted_points = result.distorted_points # Shape (n_points, 2)
print(distorted_points)
You can also access to the jacobian of the distortion transformation:
result = distortion.transform(normalized_points, dx=True, dp=True)
distorted_points_dx = result.jacobian_dx # Shape (n_points, 2, 2)
distorted_points_dp = result.jacobian_dp # Shape (n_points, 2, n_params = 5)
print(distorted_points_dx)
print(distorted_points_dp)
The inverse transformation can be computed using the inverse_transform method:
inverse_result = distortion.inverse_transform(distorted_points, dx=True, dp=True)
normalized_points = inverse_result.normalized_points # Shape (n_points, 2)
print(normalized_points)
Note
The jacobian with respect to the depth is not computed.
See also
For more information about the transformation process, see:
pycvcam.ZernikeDistortion._transform()to transform thenormalized_pointstodistorted_points.pycvcam.ZernikeDistortion._inverse_transform()to transform thedistorted_pointsback tonormalized_points.
If you want to define the Zernike unit disk to encapsulate an image, with a centered distortion and a circular distortion in the image plane, you can use the constants parameter:
import numpy
import cv2
from pycvcam import ZernikeDistortion
# Load the image
image = cv2.imread('image.jpg')
image_height, image_width = image.shape[:2]
# Compute the center and radius of the unit disk in the image plane
x0 = (image_width - 1) / 2
y0 = (image_height - 1) / 2
R_x = R_y = numpy.sqrt(((image_width - 1) / 2) ** 2 + ((image_height - 1) / 2) ** 2)
# Extract the intrinsic focal length (fx, fy) from the camera calibration and the principal point (cx, cy) form the intrinsic transformation
x0 = (x0 - cx) / fx
y0 = (y0 - cy) / fy
R_x /= fx
R_y /= fy
# Create a distortion object with a specific unit disk
constants = numpy.array([R_x, R_y, x0, y0])
distortion = ZernikeDistortion(
parameters=numpy.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6]),
constants=constants
)