Source code for sdt.optimize.affine_fit

# SPDX-FileCopyrightText: 2020 Lukas Schrangl <lukas.schrangl@tuwien.ac.at>
#
# SPDX-License-Identifier: BSD-3-Clause

from typing import Dict
import numpy as np


def _affine_trafo(x: np.ndarray, transform: np.ndarray) -> np.ndarray:
    """Perform affine transformation (implementation)

    Parameters
    ----------
    x
        Row-wise coordinates of points to transform
    transform
        Transformation matrix

    Returns
    -------
    Row-wise transformed x
    """
    n_dim = transform.shape[1] - 1
    return x @ transform[:n_dim, :n_dim].T + transform[:n_dim, n_dim]


[docs]class AffineModel: """Fit an affine transformation to pairs of points This provides a similar programming interface as `lmfit` and other fitting classes in the :py:mod:`sdt.optimize` module. """
[docs] def fit(self, data: np.ndarray, x: np.ndarray) -> "AffineModelResult": """Perform the fit Parameters ---------- data Row-wise coordinates of transformed points x Row-wise coordinates of original points Returns ------- Fit results """ n_dim = data.shape[1] x_embedded = np.hstack([x, np.ones((len(data), 1))]) par = np.empty((n_dim + 1,) * 2) par[:n_dim, :] = np.linalg.lstsq(x_embedded, data, rcond=-1)[0].T par[n_dim, :n_dim] = 0. par[n_dim, n_dim] = 1. return AffineModelResult(self, par)
[docs] @staticmethod def eval(x: np.ndarray, transform: np.ndarray) -> np.ndarray: """Perform an affine transformation Parameters ---------- x Row-wise coordinates of points to transform transform Transformation matrix Returns ------- Row-wise transformed x """ return _affine_trafo(x, transform)
[docs] def __call__(self, *args, **kwargs): """Alias for :py:meth:`eval`""" return self.eval(*args, **kwargs)
[docs]class AffineModelResult: """Result of fitting an affine transformation to pairs of points""" model: AffineModel """Model instance used for fitting""" transform: np.ndarray """Transformation matrix""" best_values: Dict[str, float] """Fit parameters in a lmfit-compatible way. Contains only a "transform" entry. """ def __init__(self, model: AffineModel, transform: np.ndarray): """Parameters ---------- model: Model instance used for fitting transform: Transformation matrix """ self.model = model self.best_values = {"transform": transform} # compatibility with lmfit self.transform = transform
[docs] def eval(self, x: np.ndarray) -> np.ndarray: """Perform an affine transformation with fitted parameters Parameters ---------- x Row-wise coordinates of points to transform Returns ------- Row-wise transformed x """ return _affine_trafo(x, self.transform)
[docs] def __call__(self, *args, **kwargs): """Alias for :py:meth:`eval`""" return self.eval(*args, **kwargs)