Source code for astrophot.models.func.zernike
from functools import lru_cache
from scipy.special import binom
import numpy as np
[docs]
@lru_cache(maxsize=1024)
def coefficients(n: int, m: int) -> list[tuple[int, float]]:
C = []
for k in range(int((n - abs(m)) / 2) + 1):
C.append(
(
k,
(-1) ** k * binom(n - k, k) * binom(n - 2 * k, (n - abs(m)) / 2 - k),
)
)
return C
[docs]
def zernike_n_m_list(n: int) -> list[tuple[int, int]]:
nm = []
for n_i in range(n + 1):
for m_i in range(-n_i, n_i + 1, 2):
nm.append((n_i, m_i))
return nm
[docs]
def zernike_n_m_modes(rho: np.ndarray, phi: np.ndarray, n: int, m: int) -> np.ndarray:
Z = np.zeros_like(rho)
for k, c in coefficients(n, m):
R = rho ** (n - 2 * k)
T = 1.0
if m < 0:
T = np.sin(abs(m) * phi)
elif m > 0:
T = np.cos(m * phi)
Z = Z + c * R * T
return Z * (rho <= 1).astype(np.float64)
[docs]
def zernike_basis(order: int, N: int) -> np.ndarray:
nm = zernike_n_m_list(order)
X, Y = np.meshgrid(
np.linspace(-1, 1, N) * (N - 1) / N,
np.linspace(-1, 1, N) * (N - 1) / N,
indexing="ij",
)
R = np.sqrt(X**2 + Y**2)
Phi = np.arctan2(Y, X)
basis = []
for n, m in nm:
basis.append(zernike_n_m_modes(R, Phi, n, m))
return np.stack(basis, axis=0)