Optical distortions

Author: Yannick Copin y.copin@ipnl.in2p3.fr

In [1]:
# Technical stuff related to the Jupyter notebook
%load_ext autoreload
%autoreload 2
%matplotlib inline
import mpld3
mpld3.enable_notebook()
import warnings
warnings.filterwarnings("ignore")

Geometrical distortions

In [2]:
import numpy as N
from spectrogrism.spectrogrism import GeometricDistortion

The GeometricDistortion class implements the Brown-Conrady (achromatic) distortion model:

\[\begin{align} x_d &= x_u \times (1 + K_1 r^2 + K_2 r^4 + \ldots) \\ &+ \left(P_2(r^2 + 2x_u^2) + 2P_1 x_u y_u\right) (1 + P_3 r^2 + P_4 r^4 + \ldots) \\ y_d &= y_u \times (1 + K_1r^2 + K_2r^4 + \ldots) \\ &+ \left(P_1(r^2 + 2y_u^2) + 2P_2 x_u y_u\right) (1 + P_3 r^2 + P_4 r^4 + \ldots) \end{align}\]

where:

  • \(x_u + j y_u\) is the undistorted complex position,
  • \(x_d + j y_d\) is the distorted complex position,
  • \(r^2 = (x_u - x_0)^2 + (y_u - y_0)^2\)
  • \(x_0 + j y_0\) is the complex center of distortion.

The K-coefficients (resp. P-coefficients) model the radial (resp. tangential) distortion.

Reference: Optical distortion

Radial distortions

In [3]:
barrel = GeometricDistortion(Kcoeffs=[-0.05])
print(barrel)
ax = barrel.plot()
ax.set_title(ax.get_title() + " - Barrel distortion")
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[-0.05], P-coeffs=[]
Out[3]:
<matplotlib.text.Text at 0x7fa04c02ce90>
In [4]:
xyu = 1. + 1j
xyd = barrel.forward(xyu)
try:
    assert(N.isclose(xyu, barrel.backward(xyd)))
except RuntimeError:
    print("Distortion is not invertible.")
except AssertionError:
    print("Distortion inversion is invalid.")
else:
    print("Distortion inversion is OK.")
Distortion inversion is OK.
In [5]:
cushion = GeometricDistortion(Kcoeffs=[+0.05])
print(cushion)
ax = cushion.plot()
ax.set_title(ax.get_title() + " - Pincushion distortion")
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[ 0.05], P-coeffs=[]
Out[5]:
<matplotlib.text.Text at 0x7fa03fb9fdd0>
In [6]:
moustache = GeometricDistortion(Kcoeffs=[-0.07, +0.01])
print(moustache)
ax = moustache.plot()
ax.set_title(ax.get_title() + " - Moustache distortion")
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[-0.07  0.01], P-coeffs=[]
Out[6]:
<matplotlib.text.Text at 0x7fa03fb8b350>

Tangential distortions

First two P-coefficients will correspond to a purely tangential distortion, while higher order cofficients will introduce radial dependencies.

In [7]:
td1 = GeometricDistortion(Pcoeffs=[+0.05])
print(td1)
ax = td1.plot()
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[], P-coeffs=[ 0.05  0.  ]
In [8]:
td2 = GeometricDistortion(Pcoeffs=[0, -0.05])
print(td2)
ax = td2.plot()
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[], P-coeffs=[ 0.   -0.05]
In [9]:
td3 = GeometricDistortion(Pcoeffs=[0.05, -0.05, 0.02])
print(td3)
ax = td3.plot()
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[], P-coeffs=[ 0.05 -0.05  0.02]

Beware that some P-coefficient sets may lead to strong locally non-invertible tangential distortions:

In [10]:
td4 = GeometricDistortion(Pcoeffs=[-0.05, 0.05, -0.1])
print(td4)
ax = td4.plot()
Geometric distortion: center=(+0.000, +0.000) mm, K-coeffs=[], P-coeffs=[-0.05  0.05 -0.1 ]
In [11]:
xyu = -1.5 + 1.5j
xyd = td4.forward(xyu)
try:
    xyb = td4.backward(xyd)
    assert(N.isclose(xyu, xyb))
except RuntimeError:
    print("Distortion is not invertible.")
except AssertionError:
    print("Distortion inversion is invalid: {} vs. {}.".format(xyb, xyu))
else:
    print("Distortion inversion is OK.")
Distortion inversion is OK.