
    voii             	          S SK J r   S SKrS SKrS SKJrJr  S SKJr  S SKrS SK	r
S SKJr  SSKJrJrJrJrJr  SSKJr  S	 rS(S
 jrS(S jrS rS rS r " S S\5      rS r " S S\5      r " S S\5      r " S S\5      r " S S\5      r \\ " S S\ 5      5       5       r! " S S\5      r"S)S jr# " S S \ 5      r$\\ " S! S"\$5      5       5       r% " S# S$\5      r&\$\%\!\"\ \\\&S%.r'S& r(S' r)g)*    )copyN)ABCabstractmethod)Self)spatial   )safe_as_int_deprecate_estimate_update_from_estimate_docstring_deprecate_inherited_estimateFailedEstimation)NP_COPY_IF_NEEDEDc                 F   U R                   nS[        R                  " SSU-  -   5      -   S-  S-
  n[        [        R                  " U5      5      nX#:w  a  [        SU 35      e[        R                  " US-   5      n[        R                  " XUS-   45      USS2SS24'   U$ )z8Affine matrix from linearized (d, d + 1) matrix entries.      r   z2Invalid number of elements for linearized matrix: N)sizenpsqrtintround
ValueErroreyereshape)vnparamddimensionalitymatrixs        X/var/www/html/land-ocr/venv/lib/python3.13/site-packages/skimage/transform/_geometric.py_affine_matrix_from_vectorr!      s    VVF	
RWWQV^$	$)A-A!%NCF8L
 	
 VVNQ&'FZZNQ4F#GHF3B36NM    c           	      d   U R                   u  p#UR                  5       n[        R                  " US-   5      nUS:X  a  U$ [        R                  " U SS9nX-
  nUS:X  a.  [        R
                  " [        R                  " US-  5      5      nOmUS:X  aX  [        R                  " [        R
                  " [        R                  " US-  SS95      5      [        R
                  " U5      -  nO[        SU S	35      eUS:X  a  U[        R                  -   $ U* US
U2U4'   US
U2S
S
24==   U-  ss'   U$ )a~  Calculate transformation `matrix` to center and normalize image points.

Points are an array of shape (N, D).

For `scaling` of 'raw', transformation returned `matrix` will be ``np.eye(D
+ 1)``.  For other values of `scaling`, `matrix` expresses a two-step
translation and scaling procedure.  Points transformed with this `matrix`
usually give better conditioning for fundamental matrix estimation than the
original `points` [1]_.

The two steps of transformation, for `scaling` other than 'raw', are:

* Center the image points, such that the new coordinate system has its
  origin at the centroid of the image points.
* Normalize the image points, such that the mean coordinate value of the
  centered points is 1 (`scaling` == 'rms') or such that the
  mean distance from the points to the origin of the coordinate system is
  ``sqrt(D)`` (`scaling` == 'mrs').

If `scaling` != 'raw' and the points are all identical, the returned
`matrix` will be all ``np.nan``.

The 'mrs' scaling corresponds to the isotropic transformation
algorithm in [1]_. 'rms' is the default, and gives very similar
conditioning.

Parameters
----------
points : (N, D) array
    The coordinates of the image points.
scaling : {'rms', 'mrs', 'raw'}, optional
    Scaling algorithm adjusting for magnitude of `points` after applying
    calculated translation. See above for explanation.

Returns
-------
matrix : (D+1, D+1) array_like
    The transformation matrix to obtain the new points.

References
----------
.. [1] Hartley, Richard I. "In defense of the eight-point algorithm."
       Pattern Analysis and Machine Intelligence, IEEE Transactions on 19.6
       (1997): 580-593.

r   rawr   axisrmsr   mrszUnexpected "scaling" of ""N)	shapelowerr   r   meanr   sumr   nan)pointsscalingnr   r   centroidcentereddivisors           r    _calc_center_normalizer5   $   s	   ^ <<DAmmoGVVAE]F%wwvA&H H%''"''(A+./	E	''"''"&&11"=>?"''!*L4WIQ?@@ !|IF2A2q5M
2A2q5MWMMr"   c                     [        X5      n[        R                  " [        R                  " U5      5      (       d8  U[        R                  -   [        R
                  " U [        R                  5      4$ U[        X 5      4$ )zwConvenience function to calculate and apply scaling

See: :func:`_calc_center_normalize` for details of the algorithm.
)r5   r   allisfiniter.   	full_like_apply_homogeneous)r/   r0   r   s      r    _center_and_normalize_pointsr;   o   sY    
 $F4F66"++f%&&VRVV <<<%f555r"   c                    [         R                  " U[        SS9n[        U5      nX R                  -  nUSS2S4   n[         R
                  " US:H  [         R                  " [        5      R                  U5      nUSS2SS24   USS2S4   -  $ )a  Transform (N, D) `points` array with homogeneous (D+1, D+1) `matrix`.

Parameters
----------
matrix : (D+1, D+1) array_like
    The transformation matrix to obtain the new points. Note that any
    object with an `__array__` method [1]_ that returns a matrix with the
    correct dimensions can be used as input here. This includes all
    subclasses of :class:`ProjectiveTransform`, for example.
points : (N, D) array
    The coordinates of the image points.

Returns
-------
new_points : (N, D) array
    The transformed image points.

References
----------
.. [1]:
    https://numpy.org/doc/stable/user/basics.interoperability.html#using-arbitrary-objects-in-numpy
r   )r   ndminNr   r   )	r   arrayr   _append_homogeneous_dimTwherefinfofloateps)r   r/   points_hnew_points_hdivss        r    r:   r:   z   s    . XXf#4A>F&v.Hhh&L 2D88DAIrxx22D9D3B3$q$w-//r"   c                 p    [         R                  " U [         R                  " [        U 5      S45      45      $ )a  Append a column of ones to the right of `points`.

This creates the representation of the points in the homogeneous coordinate
space used by homogeneous matrix transforms.

Parameters
----------
points : array, shape (N, D)
    The input coordinates, where N is the number of points and D is the
    dimension of the coordinate space.

Returns
-------
points_h : array, shape (N, D+1)
    The same points as homogeneous coordinates.
r   )r   hstackoneslen)r/   s    r    r?   r?      s*    " 99fbggs6{A&67899r"   c                    [         R                  " U 5      n [         R                  " U5      nU R                  S   nU R                  S   nU R                  SS9nUR                  SS9nX-
  nX-
  nUR                  U-  U-  n	[         R
                  " U4[         R                  S9n
[         R                  R                  U	5      S:  a  SXS-
  '   [         R                  " US-   [         R                  S9n[         R                  R                  U	5      u  pnUR                  5       [         R                  " U	R                  5      -  [         R                  " [        5      R                  -  n[         R                  " X:  5      nUS:X  a  [         R                   U-  $ UUS-
  :X  a  [         R                  R                  U5      [         R                  R                  U5      -  S:  a  X-  USU2SU24'   O`XS-
     nSXS-
  '   U[         R"                  " U
5      -  U-  USU2SU24'   UXS-
  '   O%U[         R"                  " U
5      -  U-  USU2SU24'   U(       a&  SUR%                  SS9R'                  5       -  X-  -  nOSnUUUSU2SU24   UR                  -  -  -
  USU2U4'   USU2SU24==   U-  ss'   U$ )aR  Estimate N-D similarity transformation with or without scaling.

Parameters
----------
src : (M, N) array_like
    Source coordinates.
dst : (M, N) array_like
    Destination coordinates.
estimate_scale : bool
    Whether to estimate scaling factor.

Returns
-------
T : (N + 1, N + 1)
    The homogeneous similarity transformation matrix. The matrix contains
    NaN values only if the problem is not well-conditioned.

References
----------
.. [1] "Least-squares estimation of transformation parameters between two
        point patterns", Shinji Umeyama, PAMI 1991, :DOI:`10.1109/34.88573`

r   r   r%   dtyper   Ng      ?)r   asarrayr*   r,   r@   rJ   float64linalgdetr   svdmaxrB   rC   rD   count_nonzeror.   diagvarr-   )srcdstestimate_scalenumdimsrc_meandst_mean
src_demean
dst_demeanAr   r@   USVtolranksscales                      r    _umeyamari      ss   0 **S/C
**S/C
))A,C
))A,C xxQxHxxQxH JJ 	z!C'A 	bjj)A	yy}}Q!'

sQwbjj)AiimmAGA!
 %%'BFF177O
#bhhuo&9&9
9CAG$Dqyvvz	q99==biimmA..2EAdsdDSDjM'
AAAgJ
NQ.AdsdDSDjMAAgJBGGAJ*$3$*jnn!n,0022ae<eq#tt}xzz'ABBAdsdCiLdsdDSDjMUMHr"   c                       \ rS rSrSr\S 5       r\\S 5       5       rS r	\
\SS j5       5       r\
S 5       r\
S	\\-  4S
 j5       rSrg)_GeometricTransform   z2Abstract base class for geometric transformations.c                     g)zApply forward transformation.

Parameters
----------
coords : (N, 2) array_like
    Source coordinates.

Returns
-------
coords : (N, 2) array
    Destination coordinates.

N selfcoordss     r    __call___GeometricTransform.__call__      r"   c                     g)3Return a transform object representing the inverse.Nrn   rp   s    r    inverse_GeometricTransform.inverse  rt   r"   c                 l    [         R                  " [         R                  " U " U5      U-
  S-  SS95      $ )ak  Determine residuals of transformed destination coordinates.

For each transformed source coordinate the Euclidean distance to the
respective destination coordinate is determined.

Parameters
----------
src : (N, 2) array
    Source coordinates.
dst : (N, 2) array
    Destination coordinates.

Returns
-------
residuals : (N,) array
    Residual for coordinate.

r   r   r%   )r   r   r-   rp   rX   rY   s      r    	residuals_GeometricTransform.residuals  s+    & wwrvvtCy3141=>>r"   Nc                     g)@  Identity transform

Parameters
----------
dimensionality : {None, 2}, optional
    This transform only allows dimensionality of 2, where None
    corresponds to 2. The parameter exists for compatibility with other
    transforms.

Returns
-------
tform : transform
    Transform such that ``np.all(tform(pts) == pts)``.
Nrn   clsr   s     r    identity_GeometricTransform.identity-  rt   r"   c                     [         R                  " U5      n[         R                  " U5      nU R                  UR                  S   5      X4$ )z:Create identity transform and make sure points are arrays.r   )r   rO   r   r*   r   rX   rY   s      r    _prepare_estimation'_GeometricTransform._prepare_estimation?  s:     jjojjo||CIIaL)333r"   returnc                 "    [        XU/UQ70 UD6$ )a  Estimate transform.

Parameters
----------
src : (N, M) array_like
    Source coordinates.
dst : (N, M) array_like
    Destination coordinates.
\*args : sequence
    Any other positional arguments.
\*\*kwargs : dict
    Any other keyword arguments.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = TransformClass.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")
_from_estimate)r   rX   rY   argskwargss        r    from_estimate!_GeometricTransform.from_estimateF  s    : c=d=f==r"   rn   N)__name__
__module____qualname____firstlineno____doc__r   rr   propertyrx   r|   classmethodr   r   r   r   r   __static_attributes__rn   r"   r    rk   rk      s    <  B  B?*     4 4 >@P9P > >r"   rk   c                     U R                  X5      u  pQnUR                  " X/UQ70 UD6nUc  U$ [        U R                   SU 35      $ )z8Detached function for from_estimate base implementation.: )r   	_estimater   r   )r   rX   rY   r   r   tfmsgs          r    r   r   f  sR    **34LBS
,,s
1$
1&
1C2L"2cll^2cU3K"LLr"   c                   Z    \ rS rSrSrSSS.S jjrS rS r\SS j5       r	\
S	 5       rS
rg)_HMatrixTransformim  z0Transform accepting homogeneous matrix as input.Nr   c                    Uc!  Uc  SOUn[         R                  " US-   5      nO[         R                  " U5      nU R                  X5        U R	                  UR
                  S   S-
  5        Xl        g )Nr   r   r   )r   r   rO   _check_matrix_check_dimsr*   params)rp   r   r   r   s       r    __init___HMatrixTransform.__init__p  s`    >#+AVVAE]FZZ'F62a1,-r"   c                     Ub&  X!R                   S   S-
  :w  a  [        SU SU 35      eUR                   S   nUR                   X34:w  a  [        S5      eg )Nr   r   zDimensionality z does not match matrix z&Invalid shape of transformation matrix)r*   r   )rp   r   r   ms       r    r   _HMatrixTransform._check_matrixz  sk    %a1!44 %n%55Lh   LLO<<A6!EFF "r"   c                 @    US:X  a  g [        S[        U 5       S35      e)Nr   
Input for z should result in 2D transformNotImplementedErrortyperp   r   s     r    r   _HMatrixTransform._check_dims  s*    6!d$BC
 	
r"   c                 J    Uc  SOUnU " [         R                  " US-   5      S9$ )r   r   r   r   )r   r   )r   r   r   s      r    r   _HMatrixTransform.identity  s'       'A^"&&Q-((r"   c                 :    U R                   R                  S   S-
  $ )Nr   r   )r   r*   rw   s    r    r    _HMatrixTransform.dimensionality  s    {{  #a''r"   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   rn   r"   r    r   r   m  sD    :d 	G
 ) )$ ( (r"   r   c                   r   ^  \ rS rSrSrSrS r\S 5       rS r	\
U 4S j5       rS rS	 r\S
 5       rSrU =r$ )FundamentalMatrixTransformi  a  Fundamental matrix transformation.

The fundamental matrix relates corresponding points between a pair of
uncalibrated images. The matrix transforms homogeneous image points in one
image to epipolar lines in the other image.

The fundamental matrix is only defined for a pair of moving images. In the
case of pure rotation or planar scenes, the homography describes the
geometric relation between two images (`ProjectiveTransform`). If the
intrinsic calibration of the images is known, the essential matrix describes
the metric relation between the two images (`EssentialMatrixTransform`).

Notes
-----
See [1]_ and [2]_ for details of the estimation procedure.  [2]_ is a good
place to start.

References
----------
.. [1] Hartley, Richard, and Andrew Zisserman. Multiple view geometry in
       computer vision. Cambridge university press, 2003.
.. [2] Zhang, Zhengyou. "Determining the epipolar geometry and its
       uncertainty: A review." International journal of computer vision 27
       (1998): 161-195.
       :DOI:`10.1023/A:1007941100561`
       https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/RR-2927.pdf

Parameters
----------
matrix : (3, 3) array_like, optional
    Fundamental matrix.
dimensionality : int, optional
    Fallback number of dimensions when `matrix` not specified, in which
    case, must equal 2 (the default).

Attributes
----------
params : (3, 3) array
    Fundamental matrix.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define source and destination points:

>>> src = np.array([1.839035, 1.924743,
...                 0.543582, 0.375221,
...                 0.473240, 0.142522,
...                 0.964910, 0.598376,
...                 0.102388, 0.140092,
...                15.994343, 9.622164,
...                 0.285901, 0.430055,
...                 0.091150, 0.254594]).reshape(-1, 2)
>>> dst = np.array([1.002114, 1.129644,
...                 1.521742, 1.846002,
...                 1.084332, 0.275134,
...                 0.293328, 0.588992,
...                 0.839509, 0.087290,
...                 1.779735, 1.116857,
...                 0.878616, 0.602447,
...                 0.642616, 1.028681]).reshape(-1, 2)

Estimate the transformation matrix:

>>> tform = ski.transform.FundamentalMatrixTransform.from_estimate(
...      src, dst)
>>> tform.params
array([[-0.21785884,  0.41928191, -0.03430748],
       [-0.07179414,  0.04516432,  0.02160726],
       [ 0.24806211, -0.42947814,  0.02210191]])

Compute the Sampson distance:

>>> tform.residuals(src, dst)
array([0.0053886 , 0.00526101, 0.08689701, 0.01850534, 0.09418259,
       0.00185967, 0.06160489, 0.02655136])

Apply inverse transformation:

>>> tform.inverse(dst)
array([[-0.0513591 ,  0.04170974,  0.01213043],
       [-0.21599496,  0.29193419,  0.00978184],
       [-0.0079222 ,  0.03758889, -0.00915389],
       [ 0.14187184, -0.27988959,  0.02476507],
       [ 0.05890075, -0.07354481, -0.00481342],
       [-0.21985267,  0.36717464, -0.01482408],
       [ 0.01339569, -0.03388123,  0.00497605],
       [ 0.03420927, -0.1135812 ,  0.02228236]])

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((8, 2))
>>> bad_tform = ski.transform.FundamentalMatrixTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...

r'   c                 F    [        U5      U R                  R                  -  $ )zApply forward transformation.

Parameters
----------
coords : (N, 2) array_like
    Source coordinates.

Returns
-------
coords : (N, 3) array
    Epipolar lines in the destination image.

)r?   r   r@   ro   s     r    rr   #FundamentalMatrixTransform.__call__  s     'v.>>r"   c                 H    [        U 5      " U R                  R                  S9$ )zReturn a transform object representing the inverse.

See Hartley & Zisserman, Ch. 8: Epipolar Geometry and the Fundamental
Matrix, for an explanation of why F.T gives the inverse.

r   )r   r   r@   rw   s    r    rx   "FundamentalMatrixTransform.inverse-  s     Dz//r"   c                    [         R                  " U5      n[         R                  " U5      nUR                  UR                  :w  a  [        S5      eUR                  S   S:  a  [        S5      e[	        XR
                  5      n[	        X R
                  5      n[         R                  " [         R                  " X4-   5      5      (       aS  [         R                  " S[         R                  5      U l
        S[         R                  " S[         R                  5      /-  $ [        [        X15      5      n[        [        XB5      5      nUR                   VVs/ s H  ouR                    H  oU-  PM	     M     n	nn[         R                  " U	SS9n
[         R                  R!                  U
5      u    pUS	S
S
24   R#                  SS5      nXU4$ s  snnf )aT  Setup and solve the homogeneous epipolar constraint matrix::

    dst' * F * src = 0.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
F_normalized : (3, 3) array
    The normalized solution to the homogeneous system. If the system
    is not well-conditioned, this matrix contains NaNs.
src_matrix : (3, 3) array
    The transformation matrix to obtain the normalized source
    coordinates.
dst_matrix : (3, 3) array
    The transformation matrix to obtain the normalized destination
    coordinates.

z%src and dst shapes must be identical.r      z,src.shape[0] must be equal or larger than 8.   r   r   r   r%   r   N)r   rO   r*   r   r5   r0   anyisnanfullr.   r   r?   r:   r@   stackrQ   rS   r   )rp   rX   rY   
src_matrix
dst_matrixsrc_hdst_hd_vs_vcolsra   _rd   F_normalizeds                 r    _setup_constraint_matrix3FundamentalMatrixTransform._setup_constraint_matrix7  s[   2 jjojjo99		!DEE99Q<!KLL ,C>
+C>
66"((:2344''&"&&1DK/000'(::(KL'(::(KL (-wwBw''3s'wBHHT" ))--"1Qx''1-33 Cs   !Gc                 "   > [         TU ]  X5      $ )a  Estimate fundamental matrix using 8-point algorithm.

The 8-point algorithm requires at least 8 corresponding point pairs.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = FundamentalMatrixTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

Raises
------
ValueError
    If `src` has fewer than 8 rows.
superr   r   rX   rY   	__class__s      r    r   (FundamentalMatrixTransform.from_estimatem  s    @ w$S..r"   c                 J   U R                  X5      u  p4n[        R                  " [        R                  " X4-   U-   5      5      (       a  g[        R                  R                  U5      u  pgnSUS'   U[        R                  " U5      -  U-  n	UR                  U	-  U-  U l        g )NScaling failed for input pointsr   r   	r   r   r   r   rQ   rS   rV   r@   r   )
rp   rX   rY   r   r   r   rb   rc   rd   Fs
             r    r   $FundamentalMatrixTransform._estimate  s    /3/L/LS/V,*66"((<4zABCC4 ))---a!
NQ llQ&3r"   c                    [        U5      n[        U5      nU R                  UR                  -  nU R                  R                  UR                  -  n[        R                  " XER                  -  SS9n[        R
                  " U5      [        R                  " US   S-  US   S-  -   US   S-  -   US   S-  -   5      -  $ )a  Compute the Sampson distance.

The Sampson distance is the first approximation to the geometric error.

Parameters
----------
src : (N, 2) array
    Source coordinates.
dst : (N, 2) array
    Destination coordinates.

Returns
-------
residuals : (N,) array
    Sampson distance.

r   r%   r   r   )r?   r   r@   r   r-   absr   )rp   rX   rY   src_homogeneousdst_homogeneousF_srcFt_dst	dst_F_srcs           r    r|   $FundamentalMatrixTransform.residuals  s    $ 2#61#6o///!2!22FF?WW41=	vvi 277!HME!HM)F1IN:VAY!^K$
 
 	
r"   c                 (    U R                  X5      SL $ )a  Estimate fundamental matrix using 8-point algorithm.

The 8-point algorithm requires at least 8 corresponding point pairs for
a well-conditioned solution, otherwise the over-determined solution is
estimated.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
success : bool
    True, if model estimation succeeds.

Nr   r{   s      r    estimate#FundamentalMatrixTransform.estimate      * ~~c'4//r"   r   )r   r   r   r   r   r0   rr   r   rx   r   r   r   r   r|   r
   r   r   __classcell__r   s   @r    r   r     sc    tl G?  0 044l / /B
< 0 0r"   r   c                   t   ^  \ rS rSrSrSrSrSSSSS.U 4S jjrS r\	U 4S j5       r
S	 r\S
 5       rSrU =r$ )EssentialMatrixTransformi  a&  Essential matrix transformation.

The essential matrix relates corresponding points between a pair of
calibrated images. The matrix transforms normalized, homogeneous image
points in one image to epipolar lines in the other image.

The essential matrix is only defined for a pair of moving images capturing a
non-planar scene. In the case of pure rotation or planar scenes, the
homography describes the geometric relation between two images
(`ProjectiveTransform`). If the intrinsic calibration of the images is
unknown, the fundamental matrix describes the projective relation between
the two images (`FundamentalMatrixTransform`).

References
----------
.. [1] Hartley, Richard, and Andrew Zisserman. Multiple view geometry in
       computer vision. Cambridge university press, 2003.

Parameters
----------
rotation : (3, 3) array_like, optional
    Rotation matrix of the relative camera motion.
translation : (3, 1) array_like, optional
    Translation vector of the relative camera motion. The vector must
    have unit length.
matrix : (3, 3) array_like, optional
    Essential matrix.
dimensionality : int, optional
    Fallback number of dimensions when `matrix` not specified, in which
    case, must equal 2 (the default).

Attributes
----------
params : (3, 3) array
    Essential matrix.

Examples
--------
>>> import numpy as np
>>> import skimage as ski
>>>
>>> tform = ski.transform.EssentialMatrixTransform(
...     rotation=np.eye(3), translation=np.array([0, 0, 1])
... )
>>> tform.params
array([[ 0., -1.,  0.],
       [ 1.,  0.,  0.],
       [ 0.,  0.,  0.]])
>>> src = np.array([[ 1.839035, 1.924743],
...                 [ 0.543582, 0.375221],
...                 [ 0.47324 , 0.142522],
...                 [ 0.96491 , 0.598376],
...                 [ 0.102388, 0.140092],
...                 [15.994343, 9.622164],
...                 [ 0.285901, 0.430055],
...                 [ 0.09115 , 0.254594]])
>>> dst = np.array([[1.002114, 1.129644],
...                 [1.521742, 1.846002],
...                 [1.084332, 0.275134],
...                 [0.293328, 0.588992],
...                 [0.839509, 0.08729 ],
...                 [1.779735, 1.116857],
...                 [0.878616, 0.602447],
...                 [0.642616, 1.028681]])
>>> tform = ski.transform.EssentialMatrixTransform.from_estimate(src, dst)
>>> tform.residuals(src, dst)
array([0.42455187, 0.01460448, 0.13847034, 0.12140951, 0.27759346,
       0.32453118, 0.00210776, 0.26512283])

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((8, 2))
>>> bad_tform = ski.transform.EssentialMatrixTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...
gư>N)rotationtranslationr   r   c                   > [        S X4 5       5      nUS:X  a  [        S5      eUS:X  a  Ub  [        S5      eU R                  X5      n[        TU ]  X4S9  g )Nc              3   (   #    U  H  oS L v   M
     g 7fr   rn   .0ps     r    	<genexpr>4EssentialMatrixTransform.__init__.<locals>.<genexpr>=       C+BaT	+B   r   z=Both rotation and translation required when one is specified.r   z@Do not specify rotation or translation when matrix is specified.r   r   )r-   r   
_rt2matrixr   r   )rp   r   r   r   r   	n_rt_noner   s         r    r   !EssentialMatrixTransform.__init__:  sp     CH+BCC	>O  !^! +  __X;FFr"   c                 8   [         R                  " U5      n[         R                  " U5      nUR                  S:w  a  [        S5      e[	        [         R
                  R                  U5      S-
  5      U R                  :  a  [        S5      eUR                  S:w  a  [        S5      e[	        [         R
                  R                  U5      S-
  5      U R                  :  a  [        S5      eUu  p4n[         R                  " SU* U/USU* /U* US//[        S	9nXa-  $ )
Nr   z Invalid shape of rotation matrixr   z*Rotation matrix must have unit determinantr   z#Invalid shape of translation vectorz(Translation vector must have unit lengthr   rM   )r   rO   r*   r   r   rQ   rR   _rot_det_tolr   norm_trans_len_tolr>   rC   )rp   r   r   t0t1t2t_arrs          r    r   #EssentialMatrixTransform._rt2matrixK  s    ::h'jj->>V#?@@ryy}}X&*+d.?.??IJJq BCCryy~~k*Q./$2E2EEGHH 
1rc2,QsBlC5Qr"   c                 "   > [         TU ]  X5      $ )a]  Estimate essential matrix using 8-point algorithm.

The 8-point algorithm requires at least 8 corresponding point pairs for
a well-conditioned solution, otherwise the over-determined solution is
estimated.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = EssentialMatrixTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

Raises
------
ValueError
    If `src` has fewer than 8 rows.

r   r   s      r    r   &EssentialMatrixTransform.from_estimate[  s    F w$S..r"   c                 |   U R                  X5      u  p4n[        R                  " [        R                  " X4-   U-   5      5      (       a  g[        R                  R                  U5      u  pgnUS   US   -   S-  US'   US   US'   SUS'   U[        R                  " U5      -  U-  n	UR                  U	-  U-  U l        g )Nr   r   r   g       @r   r   )
rp   rX   rY   E_normalizedr   r   rb   rc   rd   Es
             r    r   "EssentialMatrixTransform._estimate  s    /3/L/LS/V,*66"((<4zABCC4 ))---a!qts"!t!!
NQ llQ&3r"   c                 (    U R                  X5      SL $ )a  Estimate essential matrix using 8-point algorithm.

The 8-point algorithm requires at least 8 corresponding point pairs for
a well-conditioned solution, otherwise the over-determined solution is
estimated.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
success : bool
    True, if model estimation succeeds.

Nr   r{   s      r    r   !EssentialMatrixTransform.estimate  r   r"   r   )r   r   r   r   r   r   r   r   r   r   r   r   r
   r   r   r   r   s   @r    r   r     sb    ]@ L N DdG G"   "/ "/H" 0 0r"   r   c                      ^  \ rS rSrSrSr\S 5       rS r\S 5       r	SS jr
S r\S	 5       r\SU 4S
 jj5       rSS jrS rS rS rS r\S 5       r\SU 4S jj5       r\SS j5       rSrU =r$ )ProjectiveTransformi  a
  Projective transformation.

Apply a projective transformation (homography) on coordinates.

For each homogeneous coordinate :math:`\mathbf{x} = [x, y, 1]^T`, its
target position is calculated by multiplying with the given matrix,
:math:`H`, to give :math:`H \mathbf{x}`::

  [[a0 a1 a2]
   [b0 b1 b2]
   [c0 c1 1 ]].

E.g., to rotate by theta degrees clockwise, the matrix should be::

  [[cos(theta) -sin(theta) 0]
   [sin(theta)  cos(theta) 0]
   [0            0         1]]

or, to translate x by 10 and y by 20::

  [[1 0 10]
   [0 1 20]
   [0 0 1 ]].

Parameters
----------
matrix : (D+1, D+1) array_like, optional
    Homogeneous transformation matrix.
dimensionality : int, optional
    Fallback number of dimensions when `matrix` not specified.

Attributes
----------
params : (D+1, D+1) array
    Homogeneous transformation matrix.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transform with an homogeneous transformation matrix:

>>> tform = ski.transform.ProjectiveTransform(np.diag([2., 3., 1.]))
>>> tform.params
array([[2., 0., 0.],
       [0., 3., 0.],
       [0., 0., 1.]])

You can estimate a transformation to map between source and destination
points:

>>> src = np.array([[150, 150],
...                 [250, 100],
...                 [150, 200]])
>>> dst = np.array([[200, 200],
...                 [300, 150],
...                 [150, 400]])
>>> tform = ski.transform.ProjectiveTransform.from_estimate(src, dst)
>>> np.allclose(tform.params, [[ -16.56,    5.82,  895.81],
...                            [ -10.31,   -8.29, 2075.43],
...                            [  -0.05,    0.02,    1.  ]], atol=0.01)
True

Apply the transformation to some image data.

>>> img = ski.data.astronaut()
>>> warped = ski.transform.warp(img, inverse_map=tform.inverse)

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((3, 2))
>>> bad_tform = ski.transform.ProjectiveTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...
r'   c                 F    [        U R                  R                  S-
  5      $ z?Indices into flat ``self.params`` with coefficients to estimater   )ranger   r   rw   s    r    _coeff_indsProjectiveTransform._coeff_inds  s     T[[%%)**r"   c                 @    US:  a  g [        S[        U 5       S35      e)Nr   r   z# should result in transform of >=2Dr   r   s     r    r   ProjectiveTransform._check_dims  s*    6!d$GH
 	
r"   c                 T    [         R                  R                  U R                  5      $ r   )r   rQ   invr   rw   s    r    _inv_matrixProjectiveTransform._inv_matrix  s    yy}}T[[))r"   c                 V    Uc  U R                   $ U R                   R                  U5      $ r   )r   astype)rp   rN   r   s      r    	__array__ProjectiveTransform.__array__  s$    #mt{{J1C1CE1JJr"   c                 .    [        U R                  U5      $ )zApply forward transformation.

Parameters
----------
coords : (N, D) array_like
    Source coordinates.

Returns
-------
coords_out : (N, D) array
    Destination coordinates.

)r:   r   ro   s     r    rr   ProjectiveTransform.__call__  s     "$++v66r"   c                 4    [        U 5      " U R                  S9$ )rv   r   )r   r  rw   s    r    rx   ProjectiveTransform.inverse.  s     Dz!1!122r"   c                 $   > [         TU ]  XU5      $ )a	  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

The transformation is defined as::

    X = (a0*x + a1*y + a2) / (c0*x + c1*y + 1)
    Y = (b0*x + b1*y + b2) / (c0*x + c1*y + 1)

These equations can be transformed to the following form::

    0 = a0*x + a1*y + a2 - c0*x*X - c1*y*X - X
    0 = b0*x + b1*y + b2 - c0*x*Y - c1*y*Y - Y

which exist for each set of corresponding points, so we have a set of
N * 2 equations. The coefficients appear linearly so we can write
A x = 0, where::

    A   = [[x y 1 0 0 0 -x*X -y*X -X]
           [0 0 0 x y 1 -x*Y -y*Y -Y]
            ...
            ...
          ]
    x.T = [a0 a1 a2 b0 b1 b2 c0 c1 c3]

In case of total least-squares the solution of this homogeneous system
of equations is the right singular vector of A which corresponds to the
smallest singular value normed by the coefficient c3.

Weights can be applied to each pair of corresponding points to
indicate, particularly in an overdetermined system, if point pairs have
higher or lower confidence or uncertainties associated with them. From
the matrix treatment of least squares problems, these weight values are
normalized, square-rooted, then built into a diagonal matrix, by which
A is multiplied.

In case of the affine transformation the coefficients c0 and c1 are 0.
Thus the system of equations is::

    A   = [[x y 1 0 0 0 -X]
           [0 0 0 x y 1 -Y]
            ...
            ...
          ]
    x.T = [a0 a1 a2 b0 b1 b2 c3]

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.
weights : (N,) array_like, optional
    Relative weight values for each pair of points.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = ProjectiveTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

r   )r   rX   rY   weightsr   s       r    r   !ProjectiveTransform.from_estimate3  s    V w$Sw77r"   c           
         [         R                  " U5      n[         R                  " U5      nUR                  u  pE[         R                  " US-   US-   4[         R                  5      n[        U5      u  pq[        U5      u  p[         R                  " [         R                  " Xx-   5      5      (       d  X`l        g[         R                  " XE-  US-   S-  45      n	[        U5       H  n
XX-  U
S-   U-  2XS-   -  XS-   -  U-   24'   SXU-  U
S-   U-  2XS-   -  U-   4'   XX-  U
S-   U-  2U* S-
  S24'   SXU-  U
S-   U-  2S4'   XU-  U
S-   U-  2U* S-
  S 24==   US S 2XS-   24   * -  ss'   M     U	S S 2[        U R                  5      S/-   4   n	Uc#  [         R                  R                  U	5      u    pO[         R                  " U5      n[         R                  " [         R                   " [         R"                  " U[         R$                  " U5      -  5      U5      5      n[         R                  R                  X-  5      u    p[         R                  " US-   US-   45      n[         R&                  " US   S5      (       a  X`l        gUSS S24   * US   -  UR(                  [        U R                  5      S/-   '   SXU4'   [         R                  R+                  U5      U-  U-  nXS   -  nXl        g )Nr   zScaling generated NaN valuesr   r   r   r   r   z)Right singular vector has 0 final element)r   rO   r*   r   r.   r;   r7   r8   r   zerosr  listr  rQ   rS   rV   tiler   rT   iscloseflatr  )rp   rX   rY   r"  r1   r   fail_matrixr   r   ra   ddimr   rd   WHs                  r    r   ProjectiveTransform._estimate  s   jjojjoyyggq1ua!enbff56s;
6s;
vvbkk*"9:;;%K1 HHaea!e\*+ !HDPSdh$(a'Q$a%.1:L)LLM?@AQh$(a'Q!);;<8;dh$(a'!a"45/1AQh$(a'+,Qh$(a'!a12s1dQh>O;O7P6PP2  ad&&'2$../ ?iimmA&GAq!jj)G"&&/(A BAFGAiimmAE*GAqHHa!eQU^$ ::ai##%K>122ss7ai0GtD$$%,-Q$ IIMM*%)J6 	
vYr"   c                     [        U[        5      (       aJ  [        U 5      [        U5      :X  a  U R                  nO[        nU" UR                  U R                  -  5      $ [        S5      e)z)Combine this transformation with another.z2Cannot combine transformations of differing types.)
isinstancer  r   r   r   	TypeError)rp   othertforms      r    __add__ProjectiveTransform.__add__  sT    e011 DzT%[(+344STTr"   c                     [        U S5      (       d  g[        R                  " U R                  SS9nS[        R
                  " US5      -   $ )z.common 'paramstr' used by __str__ and __repr__r   z<not yet initialized>z, )	separatorzmatrix=
z    )hasattrr   array2stringr   textwrapindent)rp   npstrings     r    __nice__ProjectiveTransform.__nice__  s<    tX&&*??4;;$?X__Xv>>>r"   c           
          S[        U 5      R                   SU R                  5        S[        [	        U 5      5       S3$ )z5Add standard repr formatting around a __nice__ string<(z) at >)r   r   r>  hexidrw   s    r    __repr__ProjectiveTransform.__repr__  s7    4:&&'q(9s2d8}oQOOr"   c                 V    S[        U 5      R                   SU R                  5        S3$ )z4Add standard str formatting around a __nice__ stringrA  rB  z)>)r   r   r>  rw   s    r    __str__ProjectiveTransform.__str__  s)    4:&&'q(9<<r"   c                 :    U R                   R                  S   S-
  $ )z)The dimensionality of the transformation.r   r   )r   r*   rw   s    r    r   "ProjectiveTransform.dimensionality  s     {{  #a''r"   c                    > [         TU ]  US9$ )zIdentity transform

Parameters
----------
dimensionality : {None, int}, optional
    Dimensionality of identity transform.

Returns
-------
tform : transform
    Transform such that ``np.all(tform(pts) == pts)``.
r   )r   r   )r   r   r   s     r    r   ProjectiveTransform.identity  s     w~>>r"   c                 *    U R                  XU5      SL $ )a}  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

The transformation is defined as::

    X = (a0*x + a1*y + a2) / (c0*x + c1*y + 1)
    Y = (b0*x + b1*y + b2) / (c0*x + c1*y + 1)

These equations can be transformed to the following form::

    0 = a0*x + a1*y + a2 - c0*x*X - c1*y*X - X
    0 = b0*x + b1*y + b2 - c0*x*Y - c1*y*Y - Y

which exist for each set of corresponding points, so we have a set of
N * 2 equations. The coefficients appear linearly so we can write
A x = 0, where::

    A   = [[x y 1 0 0 0 -x*X -y*X -X]
           [0 0 0 x y 1 -x*Y -y*Y -Y]
            ...
            ...
          ]
    x.T = [a0 a1 a2 b0 b1 b2 c0 c1 c3]

In case of total least-squares the solution of this homogeneous system
of equations is the right singular vector of A which corresponds to the
smallest singular value normed by the coefficient c3.

Weights can be applied to each pair of corresponding points to
indicate, particularly in an overdetermined system, if point pairs have
higher or lower confidence or uncertainties associated with them. From
the matrix treatment of least squares problems, these weight values are
normalized, square-rooted, then built into a diagonal matrix, by which
A is multiplied.

In case of the affine transformation the coefficients c0 and c1 are 0.
Thus the system of equations is::

    A   = [[x y 1 0 0 0 -X]
           [0 0 0 x y 1 -Y]
            ...
            ...
          ]
    x.T = [a0 a1 a2 b0 b1 b2 c3]

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.
weights : (N,) array_like, optional
    Relative weight values for each pair of points.

Returns
-------
success : bool
    True, if model estimation succeeds.

Nr   )rp   rX   rY   r"  s       r    r   ProjectiveTransform.estimate  s    D ~~c0D88r"   r   NNr   )r   r   r   r   r   r0   r   r  r   r  r  rr   rx   r   r   r   r5  r>  rF  rI  r   r   r
   r   r   r   r   s   @r    r  r    s    ]~ G+ +
 * *K7  3 3 J8 J8X7rU?P= ( ( ? ? A9 A9r"   r  c                      ^  \ rS rSrSr SSSSSSS.U 4S jjjr\S 5       rS r\S 5       r	\S	 5       r
\S
 5       r\S 5       rSrU =r$ )AffineTransformi/  a  Affine transformation.

Has the following form::

    X = a0 * x + a1 * y + a2
      =   sx * x * [cos(rotation) + tan(shear_y) * sin(rotation)]
        - sy * y * [tan(shear_x) * cos(rotation) + sin(rotation)]
        + translation_x

    Y = b0 * x + b1 * y + b2
      =   sx * x * [sin(rotation) - tan(shear_y) * cos(rotation)]
        - sy * y * [tan(shear_x) * sin(rotation) - cos(rotation)]
        + translation_y

where ``sx`` and ``sy`` are scale factors in the x and y directions.

This is equivalent to applying the operations in the following order:

1. Scale
2. Shear
3. Rotate
4. Translate

The homogeneous transformation matrix is::

    [[a0  a1  a2]
     [b0  b1  b2]
     [0   0    1]]

In 2D, the transformation parameters can be given as the homogeneous
transformation matrix, above, or as the implicit parameters, scale,
rotation, shear, and translation in x (a2) and y (b2). For 3D and higher,
only the matrix form is allowed.

In narrower transforms, such as the Euclidean (only rotation and
translation) or Similarity (rotation, translation, and a global scale
factor) transforms, it is possible to specify 3D transforms using implicit
parameters also.

Parameters
----------
matrix : (D+1, D+1) array_like, optional
    Homogeneous transformation matrix. If this matrix is provided, it is an
    error to provide any of scale, rotation, shear, or translation.
scale : {s as float or (sx, sy) as array, list or tuple}, optional
    Scale factor(s). If a single value, it will be assigned to both
    sx and sy. Only available for 2D.

    .. versionadded:: 0.17
       Added support for supplying a single scalar value.
shear : float or 2-tuple of float, optional
    The x and y shear angles, clockwise, by which these axes are
    rotated around the origin [2].
    If a single value is given, take that to be the x shear angle, with
    the y angle remaining 0. Only available in 2D.
rotation : float, optional
    Rotation angle, clockwise, as radians. Only available for 2D.
translation : (tx, ty) as array, list or tuple, optional
    Translation parameters. Only available for 2D.
dimensionality : int, optional
    Fallback number of dimensions for transform when none of `matrix`,
    `scale`, `rotation`, `shear` or `translation` are specified.  If any of
    `scale`, `rotation`, `shear` or `translation` are specified, must equal
    2 (the default).

Attributes
----------
params : (D+1, D+1) array
    Homogeneous transformation matrix.

Raises
------
ValueError
    If both ``matrix`` and any of the other parameters are provided.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transform with an homogeneous transformation matrix:

>>> tform = ski.transform.AffineTransform(np.diag([2., 3., 1.]))
>>> tform.params
array([[2., 0., 0.],
       [0., 3., 0.],
       [0., 0., 1.]])

Define a transform with parameters:

>>> tform = ski.transform.AffineTransform(scale=4, rotation=0.2)
>>> np.round(tform.params, 2)
array([[ 3.92, -0.79,  0.  ],
       [ 0.79,  3.92,  0.  ],
       [ 0.  ,  0.  ,  1.  ]])

You can estimate a transformation to map between source and destination
points:

>>> src = np.array([[150, 150],
...                 [250, 100],
...                 [150, 200]])
>>> dst = np.array([[200, 200],
...                 [300, 150],
...                 [150, 400]])
>>> tform = ski.transform.AffineTransform.from_estimate(src, dst)
>>> np.allclose(tform.params, [[   0.5,   -1. ,  275. ],
...                            [   1.5,    4. , -625. ],
...                            [   0. ,    0. ,    1. ]])
True

Apply the transformation to some image data.

>>> img = ski.data.astronaut()
>>> warped = ski.transform.warp(img, inverse_map=tform.inverse)

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((3, 2))
>>> bad_tform = ski.transform.AffineTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...

References
----------
.. [1] Wikipedia, "Affine transformation",
       https://en.wikipedia.org/wiki/Affine_transformation#Image_transformation
.. [2] Wikipedia, "Shear mapping",
       https://en.wikipedia.org/wiki/Shear_mapping
N)rh   shearr   r   r   c                   > [        S X$X54 5       5      nUS:w  aR  Ub  [        S5      eUb  US:  a  [        S5      eU R                  X$X55      nUR                  S   S:w  a  [        S5      e[        TU ]  XS	9  g )
Nc              3   (   #    U  H  oS L v   M
     g 7fr   rn   r   s     r    r   +AffineTransform.__init__.<locals>.<genexpr>  s     S-Rt)-Rr   r   @Do not specify any implicit parameters when matrix is specified.r   z0Implicit parameters only valid for 2D transformsr   r   z+Implicit parameters must give 2D transformsr   )r-   r   _srst2matrixr*   r   r   )	rp   r   rh   rT  r   r   r   n_srst_noner   s	           r    r   AffineTransform.__init__  s     Seu-RSS!! +  )nq.@ !STT&&uKF||A!# !NOOFr"   c                 L    [        U R                  U R                  S-   -  5      $ r  )r  r   rw   s    r    r  AffineTransform._coeff_inds  s%     T((D,?,?!,CDEEr"   c                    Uc  SOUn[         R                  " U5      (       a  X4OUu  pVUc  SOUn[         R                  " U5      (       d  [        S5      eUc  SOUn[         R                  " U5      (       a  US4OUu  pxUc  SOUn[         R                  " U5      (       a  [        S5      eUu  pU[        R                  " U5      [        R
                  " U5      [        R                  " U5      -  -   -  nU* [        R
                  " U5      [        R                  " U5      -  [        R                  " U5      -   -  nU[        R                  " U5      [        R
                  " U5      [        R                  " U5      -  -
  -  nU* [        R
                  " U5      [        R                  " U5      -  [        R                  " U5      -
  -  n[         R                  " XU	/XU
// SQ/5      $ )Nr   r   r   z%rotation must be scalar (2D rotation)r   r   ztranslation must be length 2r   r   r   )r   isscalarr   mathcostansinr>   )rp   rh   r   rT  r   sxsyshear_xshear_ya2b2a0a1b0b1s                  r    rY  AffineTransform._srst2matrix  s   -U#%;;u#5#5%5 (1h{{8$$DEE])+U););E1: + 3f;;{##;<<488H%(9DHHX<N(NNOSDHHW%(::TXXh=OOP488H%(9DHHX<N(NNOSDHHW%(::TXXh=OOPxx""|Y?@@r"   c                    U R                   S:w  aC  [        R                  " [        R                  " U R                  S-  SS95      S U R                    $ [        R                  " U R                  S-  SS9nUS   [
        R                  " U R                  5      S-  S-   -  US'   [        R                  " U5      S U R                    $ )Nr   r   r%   r   )r   r   r   r-   r   rc  re  rT  )rp   sss     r    rh   AffineTransform.scale  s    !#77266$++q.q9:;PT=P=PQQVVDKKN+1$**-2Q671wwr{0T0011r"   c                     U R                   S:w  a  [        S5      e[        R                  " U R                  S   U R                  S   5      $ )Nr   z<The rotation property is only implemented for 2D transforms.r   r   r`  )r   r   rc  atan2r   rw   s    r    r   AffineTransform.rotation  sE    !#%N  zz$++d+T[[->??r"   c                     U R                   S:w  a  [        S5      e[        R                  " U R                  S   * U R                  S   5      nXR
                  -
  $ )Nr   z9The shear property is only implemented for 2D transforms.)r   r   r_  )r   r   rc  rw  r   r   )rp   betas     r    rT  AffineTransform.shear	  sT    !#%K  zz4;;t,,dkk$.?@mm##r"   c                 P    U R                   SU R                  2U R                  4   $ Nr   r   r   rw   s    r    r   AffineTransform.translation  '    {{1t222D4G4GGHHr"   rn   r   )r   r   r   r   r   r   r   r  rY  rh   r   rT  r   r   r   r   s   @r    rS  rS  /  s    Sn G G G2 F FA( 2 2 @ @ $ $ I Ir"   rS  c                   |   ^  \ rS rSrSrS r\U 4S j5       rS rS r	\
S 5       r\SS j5       r\S	 5       rS
rU =r$ )PiecewiseAffineTransformi  am  Piecewise affine transformation.

Control points are used to define the mapping. The transform is based on
a Delaunay triangulation of the points to form a mesh. Each triangle is
used to find a local affine transform.

Attributes
----------
affines : list of AffineTransform objects
    Affine transformations for each triangle in the mesh.
inverse_affines : list of AffineTransform objects
    Inverse affine transformations for each triangle in the mesh.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transformation by estimation:

>>> src = [[-12.3705, -10.5075],
...        [-10.7865, 15.4305],
...        [8.6985, 10.8675],
...        [11.4975, -9.5715],
...        [7.8435, 7.4835],
...        [-5.3325, 6.5025],
...        [6.7905, -6.3765],
...        [-6.1695, -0.8235]]
>>> dst = [[0, 0],
...        [0, 5800],
...        [4900, 5800],
...        [4900, 0],
...        [4479, 4580],
...        [1176, 3660],
...        [3754, 790],
...        [1024, 1931]]
>>> tform = ski.transform.PiecewiseAffineTransform.from_estimate(src, dst)

Calling the transform applies the transformation to the points:

>>> np.allclose(tform(src), dst)
True

You can apply the inverse transform:

>>> np.allclose(tform.inverse(dst), src)
True

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = [[1, 1]] * 6 + src[6:]
>>> bad_tform = ski.transform.PiecewiseAffineTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...
c                 <    S U l         S U l        S U l        S U l        g r   )_tesselation_inverse_tesselationaffinesinverse_affinesrw   s    r    r   !PiecewiseAffineTransform.__init__b  s!     $(!#r"   c                 "   > [         TU ]  X5      $ )a  Estimate the transformation from a set of corresponding points.

Number of source and destination coordinates must match.

Parameters
----------
src : (N, D) array_like
    Source coordinates.
dst : (N, D) array_like
    Destination coordinates.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = PiecewiseAffineTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

r   r   s      r    r   &PiecewiseAffineTransform.from_estimateh  s    8 w$S..r"   c                     [         R                  " U5      n[         R                  " U5      nUR                  u  p4[        R                  " U5      U l        [         R                  " US-   US-   4[         R                  5      n/ U l        / n[        U R
                  R                  5       Hy  u  px[        R                  XS S 24   X(S S 24   5      n	U	(       d0  UR                  SU SU	 35        [        UR                  5       5      n	U R                  R                  U	5        M{     [        R                  " U5      U l        / U l        [        U R                  R                  5       Hy  u  px[        R                  X(S S 24   XS S 24   5      n	U	(       d0  UR                  SU SU	 35        [        UR                  5       5      n	U R                   R                  U	5        M{     U(       a  SR#                  U5      $ S $ )Nr   zFailure at forward simplex r   zFailure at inverse simplex z; )r   rO   r*   r   Delaunayr  r   r.   r  	enumerate	simplicesrS  r   appendr   r  r  join)
rp   rX   rY   NDr+  messagesitriaffines
             r    r   "PiecewiseAffineTransform._estimate  s   jjojjoyy $,,S1ggq1ua!enbff5  1 1 ; ;<FA$223Av;FLF"=aS6( KL()9)9);<LL' = %,$4$4S$9!! 9 9 C CDFA$223Av;FLF"=aS6( KL()9)9);<  ''/ E '/tyy"8D8r"   c                 t   [         R                  " U5      n[         R                  " U[         R                  5      nU R                  R                  U5      nSX#S:H  SS24'   [        [        U R                  R                  5      5       H*  nU R                  U   nX4:H  nU" XSS24   5      X&SS24'   M,     U$ )zApply forward transformation.

Coordinates outside of the mesh will be set to `- 1`.

Parameters
----------
coords : (N, D) array_like
    Source coordinates.

Returns
-------
coords : (N, 2) array
    Transformed coordinates.

r   N)
r   rO   
empty_likerP   r  find_simplexr  rK   r  r  )rp   rq   outsimplexindexr  
index_masks          r    rr   !PiecewiseAffineTransform.__call__  s      F#mmFBJJ/ ##008 !#rM13t00::;<E\\%(F )J!'1}(=!>CA = 
r"   c                     [        U 5      " 5       n[        U R                  5      Ul        [        U R                  5      Ul        [        U R                  5      Ul        [        U R
                  5      Ul        U$ )rv   )r   r   r  r  r  r  )rp   r4  s     r    rx    PiecewiseAffineTransform.inverse  s]     T
!$";";<%)$*;*;%<"T112 $T\\ 2r"   c                     U " 5       $ )a>  Identity transform

Parameters
----------
dimensionality : optional
    This transform does not use the `dimensionality` parameter, so the
    value is ignored.  The parameter exists for compatibility with
    other transforms.

Returns
-------
tform : transform
    Transform such that ``np.all(tform(pts) == pts)``.
rn   r   s     r    r   !PiecewiseAffineTransform.identity  s      ur"   c                 (    U R                  X5      SL $ )aX  Estimate the transformation from a set of corresponding points.

Number of source and destination coordinates must match.

Parameters
----------
src : (N, D) array_like
    Source coordinates.
dst : (N, D) array_like
    Destination coordinates.

Returns
-------
success : bool
    True, if all pieces of the model are successfully estimated.

Nr   r{   s      r    r   !PiecewiseAffineTransform.estimate  s    & ~~c'4//r"   )r  r  r  r  r   )r   r   r   r   r   r   r   r   r   rr   r   rx   r   r
   r   r   r   r   s   @r    r  r    sk    HT$ / /:!9F!F    " 0 0r"   r  c                 n    [         R                  R                  R                  SXS9R	                  5       $ )a  Produce an Euler rotation matrix from the given intrinsic rotation angles
for the axes x, y and z.

Parameters
----------
angles : array of float, shape (3,)
    The transformation angles in radians.
degrees : bool, optional
    If True, then the given angles are assumed to be in degrees. Default is False.

Returns
-------
R : array of float, shape (3, 3)
    The Euler rotation matrix.

XYZanglesdegrees)r   	transformRotation
from_euler	as_matrixr  s     r    _euler_rotation_matrixr    s4    " %%00f 1 ikr"   c                      ^  \ rS rSrSrSr SSSSS.U 4S jjjrS rS r\	S	\
\-  4S
 j5       rS r\S 5       r\S 5       r\S 5       rSrU =r$ )EuclideanTransformi  a  Euclidean transformation, also known as a rigid transform.

Has the following form::

    X = a0 * x - b0 * y + a1 =
      = x * cos(rotation) - y * sin(rotation) + a1

    Y = b0 * x + a0 * y + b1 =
      = x * sin(rotation) + y * cos(rotation) + b1

where the homogeneous transformation matrix is::

    [[a0 -b0  a1]
     [b0  a0  b1]
     [0   0   1 ]]

The Euclidean transformation is a rigid transformation with rotation and
translation parameters. The similarity transformation extends the Euclidean
transformation with a single scaling factor.

In 2D and 3D, the transformation parameters may be provided either via
`matrix`, the homogeneous transformation matrix, above, or via the
implicit parameters `rotation` and/or `translation` (where `a1` is the
translation along `x`, `b1` along `y`, etc.). Beyond 3D, if the
transformation is only a translation, you may use the implicit parameter
`translation`; otherwise, you must use `matrix`.

The implicit parameters are applied in the following order:

1. Rotation;
2. Translation.

Parameters
----------
matrix : (D+1, D+1) array_like, optional
    Homogeneous transformation matrix.
rotation : float or sequence of float, optional
    Rotation angle, clockwise, in radians. If given as a vector, it is
    interpreted as Euler rotation angles [1]_. Only 2D (single rotation)
    and 3D (Euler rotations) values are supported. For higher dimensions,
    you must provide or estimate the transformation matrix instead, and
    pass that as `matrix` above.
translation : (x, y[, z, ...]) sequence of float, length D, optional
    Translation parameters for each axis.
dimensionality : int, optional
    Fallback number of dimensions for transform when no other parameter
    is specified.  Otherwise ignored, and we infer dimensionality from the
    input parameters.

Attributes
----------
params : (D+1, D+1) array
    Homogeneous transformation matrix.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transform with an homogeneous transformation matrix:

>>> tform = ski.transform.EuclideanTransform(np.diag([2., 3., 1.]))
>>> tform.params
array([[2., 0., 0.],
       [0., 3., 0.],
       [0., 0., 1.]])

Define a transform with parameters:

>>> tform = ski.transform.EuclideanTransform(
...             rotation=0.2, translation=[1, 2])
>>> np.round(tform.params, 2)
array([[ 0.98, -0.2 ,  1.  ],
       [ 0.2 ,  0.98,  2.  ],
       [ 0.  ,  0.  ,  1.  ]])

You can estimate a transformation to map between source and destination
points:

>>> src = np.array([[150, 150],
...                 [250, 100],
...                 [150, 200]])
>>> dst = np.array([[200, 200],
...                 [300, 150],
...                 [150, 400]])
>>> tform = ski.transform.EuclideanTransform.from_estimate(src, dst)
>>> np.allclose(tform.params, [[ 0.99, 0.12,  16.77],
...                            [-0.12, 0.99, 122.91],
...                            [ 0.  , 0.  ,   1.  ]], atol=0.01)
True

Apply the transformation to some image data.

>>> img = ski.data.astronaut()
>>> warped = ski.transform.warp(img, inverse_map=tform.inverse)

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((3, 2))
>>> bad_tform = ski.transform.EuclideanTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...

References
----------
.. [1] https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions

FN)r   r   r   c                   > [        S X#4 5       5      nUS:w  aA  Ub  [        S5      eU R                  X#5      u  pgUb  [        U5      eU R                  X#U5      n[        TU ]  XS9  g )Nc              3   (   #    U  H  oS L v   M
     g 7fr   rn   r   s     r    r   .EuclideanTransform.__init__.<locals>.<genexpr>  r   r   r   rX  r   )r-   r   _rt2ndims_msgr   r   r   )	rp   r   r   r   r   r   n_dimschk_msgr   s	           r    r   EuclideanTransform.__init__  s|     CH+BCC	>! +  #00GOF" ))__XFCFFr"   c                     Ub@  [         R                  " U5      (       a  SO
[        U5      nUS;  a  SOS nUS:X  a  SU4$ UU4$ Ub,  [         R                  " U5      (       a  SS 4$ [        U5      S 4$ g)Nr   )r   r   z2``rotations`` must be scalar (3D) or length 3 (3D)r   rQ  )r   rb  rK   )rp   r   r   r  r   s        r    r   EuclideanTransform._rt2ndims_msg  s    [[**HA F? E 
 Q1s**As**"[11ANNs;7GNNr"   c                 R   Uc  SU-  nUc  US:X  a  SO[         R                  " S5      n[         R                  " US-   5      nUS:X  a<  [        R                  " U5      [        R
                  " U5      peXV* /Xe//US S2S S24'   OUS:X  a  [        U5      US S2S S24'   X$SU2U4'   U$ )N)r   r   r   r   r   )r   r&  r   rc  rd  rf  r  )rp   r   r   r  r   cos_rsin_rs          r    r   EuclideanTransform._rt2matrix  s    -K"aKqRXXa[H
#Q;88H-txx/A5$fo~>F2A2rr6Nq[3H=F2A2rr6N#.qx r"   r   c                     [        XU5      $ )a.  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = EuclideanTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

r   r   s      r    r    EuclideanTransform.from_estimate  s    B c,,r"   c                     [        XU R                  5      U l        [        R                  " [        R
                  " U R                  5      5      (       a  S$ S $ )Nz Poor conditioning for estimation)ri   _estimate_scaler   r   r   r   r{   s      r    r   EuclideanTransform._estimate  sH    s)=)=>
 vvbhht{{+,, /	
 	
r"   c                     U R                   S:X  a1  [        R                  " U R                  S   U R                  S   5      $ U R                   S:X  a  U R                  S S2S S24   $ [	        S5      e)Nr   rv  r_  r   z3Rotation only implemented for 2D and 3D transforms.)r   rc  rw  r   r   rw   s    r    r   EuclideanTransform.rotation  sl    !#::dkk$/T1BCC  A%;;rr2A2v&&%E r"   c                 P    U R                   SU R                  2U R                  4   $ r}  r~  rw   s    r    r   EuclideanTransform.translation  r  r"   c                 (    U R                  X5      SL $ )a  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.

Returns
-------
success : bool
    True, if model estimation succeeds.

Nr   r{   s      r    r   EuclideanTransform.estimate   s    , ~~c'4//r"   r   r   )r   r   r   r   r   r  r   r  r   r   r   r   r   r   r   r   r   r
   r   r   r   r   s   @r    r  r    s    }@ O G'+dG G   -/?(?  -  -D
 	 	 I I 0 0r"   r  c                   Z   ^  \ rS rSrSrSr S
SSSSS.U 4S jjjrS r\S 5       r	S	r
U =r$ )SimilarityTransformi  a  Similarity transformation.

Has the following form in 2D::

    X = a0 * x - b0 * y + a1 =
      = s * x * cos(rotation) - s * y * sin(rotation) + a1

    Y = b0 * x + a0 * y + b1 =
      = s * x * sin(rotation) + s * y * cos(rotation) + b1

where ``s`` is a scale factor and the homogeneous transformation matrix is::

    [[a0 -b0  a1]
     [b0  a0  b1]
     [0   0   1 ]]

The similarity transformation extends the Euclidean transformation with a
single scaling factor in addition to the rotation and translation
parameters.

The implicit parameters are applied in the following order:

1. Scale;
2. Rotation;
3. Translation.

Parameters
----------
matrix : (dim+1, dim+1) array_like, optional
    Homogeneous transformation matrix.
scale : float, optional
    Scale factor. Implemented only for 2D and 3D.
rotation : float, optional
    Rotation angle, clockwise, as radians.
    Implemented only for 2D and 3D. For 3D, this is given in ZYX Euler
    angles.
translation : (dim,) array_like, optional
    x, y[, z] translation parameters. Implemented only for 2D and 3D.
dimensionality : int, optional
    The dimensionality of the transform, corresponding to ``dim`` above.
    Ignored if `matrix` is not None, and set to ``matrix.shape[0] - 1``.
    Otherwise, must be one of 2 or 3.

Attributes
----------
params : (dim+1, dim+1) array
    Homogeneous transformation matrix.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transform with an homogeneous transformation matrix:

>>> tform = ski.transform.SimilarityTransform(np.diag([2., 3., 1.]))
>>> tform.params
array([[2., 0., 0.],
       [0., 3., 0.],
       [0., 0., 1.]])

Define a transform with parameters:

>>> tform = ski.transform.SimilarityTransform(
...             rotation=0.2, translation=[1, 2])
>>> np.round(tform.params, 2)
array([[ 0.98, -0.2 ,  1.  ],
       [ 0.2 ,  0.98,  2.  ],
       [ 0.  ,  0.  ,  1.  ]])

You can estimate a transformation to map between source and destination
points:

>>> src = np.array([[150, 150],
...                 [250, 100],
...                 [150, 200]])
>>> dst = np.array([[200, 200],
...                 [300, 150],
...                 [150, 400]])
>>> tform = ski.transform.SimilarityTransform.from_estimate(src, dst)
>>> np.allclose(tform.params, [[ 1.79, 0.21, -142.86],
...                            [-0.21, 1.79,   21.43],
...                            [ 0.  , 0.  ,    1.  ]], atol=0.01)
True

Apply the transformation to some image data.

>>> img = ski.data.astronaut()
>>> warped = ski.transform.warp(img, inverse_map=tform.inverse)

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((3, 2))
>>> bad_tform = ski.transform.SimilarityTransform.from_estimate(
...      bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...
TN)rh   r   r   r   c                  > [        S X#U4 5       5      nUS:w  a  Ub  [        S5      eU R                  X#U4U5        Ub(  [        R                  " U5      (       d  [        U5      S pOU R                  X45      u  pxUb  [        U5      eUb  UOUb  UOSnU R                  X4U5      nUS;  a  US U2S U24==   U-  ss'   [        T	U ]%  XS9  g )Nc              3   (   #    U  H  oS L v   M
     g 7fr   rn   r   s     r    r   /SimilarityTransform.__init__.<locals>.<genexpr>  s     K,Jqd,Jr   r   rX  r   )Nr   r   )
r-   r   _check_scaler   rb  rK   r  r   r   r   )
rp   r   rh   r   r   r   
n_srt_noner  r  r   s
            r    r   SimilarityTransform.__init__  s     KUk,JKK
?! +  e%<nM U););"%e*d"&"4"4X"K" )) %  "- $  __XFCFI%ww'(E1(Fr"   c                     US;   d  Ub  [         R                  " U5      (       d  g[        S U 5       5      (       a  [        R                  " S[
        SS9  gg)zCheck, warn for scalar scaling)Nr   Nc              3   (   #    U  H  oS L v   M
     g 7fr   rn   r   s     r    r   3SimilarityTransform._check_scale.<locals>.<genexpr>  s     /,QDy,r   a  In the future, it will be a ValueError to pass a scalar `scale` value with a ``dimensionality`` > 2
,and without other implicit parameters to indicate the dimensionality of the transform.
Please indicate dimensionality by passing a vector of suitable length to `scale`.r   )
stacklevel)r   rb  r7   warningswarnFutureWarning)rp   rh   other_paramsr   s       r    r   SimilarityTransform._check_scale  sN    Y&%-r{{5?Q?Q/,///MM1 	 0r"   c                 L   U R                   S:X  a=  [        R                  " [        R                  R	                  U R
                  5      5      $ U R                   S:X  a=  [        R                  " [        R                  R	                  U R
                  5      5      $ [        S5      e)Nr   r   z(Scale is only implemented for 2D and 3D.)r   r   r   rQ   rR   r   cbrtr   rw   s    r    rh   SimilarityTransform.scale  sl     !#77299==566  A%77299==566%&PQQr"   rn   r   )r   r   r   r   r   r  r   r  r   rh   r   r   r   s   @r    r  r    sR    rj O #G #G #GJ  R Rr"   r  c                      ^  \ rS rSrSrSSS.S jjr\SU 4S jj5       rSS jrS r	\SS	 j5       r
\S
 5       r\SS j5       rSrU =r$ )PolynomialTransformi  a  2D polynomial transformation.

Has the following form::

    X = sum[j=0:order]( sum[i=0:j]( a_ji * x**(j - i) * y**i ))
    Y = sum[j=0:order]( sum[i=0:j]( b_ji * x**(j - i) * y**i ))

Parameters
----------
params : (2, N) array_like, optional
    Polynomial coefficients where `N * 2 = (order + 1) * (order + 2)`. So,
    a_ji is defined in `params[0, :]` and b_ji in `params[1, :]`.
dimensionality : int, optional
    Must have value 2 (the default) for polynomial transforms.

Attributes
----------
params : (2, N) array
    Polynomial coefficients where `N * 2 = (order + 1) * (order + 2)`. So,
    a_ji is defined in `params[0, :]` and b_ji in `params[1, :]`.

Examples
--------
>>> import numpy as np
>>> import skimage as ski

Define a transformation by estimation:

>>> src = [[-12.3705, -10.5075],
...        [-10.7865, 15.4305],
...        [8.6985, 10.8675],
...        [11.4975, -9.5715],
...        [7.8435, 7.4835],
...        [-5.3325, 6.5025],
...        [6.7905, -6.3765],
...        [-6.1695, -0.8235]]
>>> dst = [[0, 0],
...        [0, 5800],
...        [4900, 5800],
...        [4900, 0],
...        [4479, 4580],
...        [1176, 3660],
...        [3754, 790],
...        [1024, 1931]]
>>> tform = ski.transform.PolynomialTransform.from_estimate(src, dst)

Calling the transform applies the transformation to the points:

>>> pts = tform(src)
>>> np.allclose(pts, [[   7.54,   12.27],
...                   [   2.98, 5796.95],
...                   [4870.44, 5766.59],
...                   [4889.72,   -6.72],
...                   [4515.62, 4617.5 ],
...                   [1183.25, 3694.  ],
...                   [3767.57,  800.53],
...                   [ 998.02, 1881.97]], atol=0.01)
True
Nr   c                   Uc  SnOUS:w  a  [        S5      e[        R                  " Uc  / SQ/ SQ/OU5      U l        U R                  R                  S:X  d  U R                  R                  S   S:w  a  [        S5      eg )Nr   z2Polynomial transforms are only implemented for 2D.)r   r   r   ra  rn   r   z.Transformation parameters must be shape (2, N))r   r   r>   r   r*   r   )rp   r   r   s      r    r   PolynomialTransform.__init__	  s|    !Nq %D  hh	95VT;;"dkk&7&7&:a&?MNN '@r"   c                 $   > [         TU ]  XX45      $ )a	  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

The transformation is defined as::

    X = sum[j=0:order]( sum[i=0:j]( a_ji * x**(j - i) * y**i ))
    Y = sum[j=0:order]( sum[i=0:j]( b_ji * x**(j - i) * y**i ))

These equations can be transformed to the following form::

    0 = sum[j=0:order]( sum[i=0:j]( a_ji * x**(j - i) * y**i )) - X
    0 = sum[j=0:order]( sum[i=0:j]( b_ji * x**(j - i) * y**i )) - Y

which exist for each set of corresponding points, so we have a set of
N * 2 equations. The coefficients appear linearly so we can write
A x = 0, where::

    A   = [[1 x y x**2 x*y y**2 ... 0 ...             0 -X]
           [0 ...                 0 1 x y x**2 x*y y**2 -Y]
            ...
            ...
          ]
    x.T = [a00 a10 a11 a20 a21 a22 ... ann
           b00 b10 b11 b20 b21 b22 ... bnn c3]

In case of total least-squares the solution of this homogeneous system
of equations is the right singular vector of A which corresponds to the
smallest singular value normed by the coefficient c3.

Weights can be applied to each pair of corresponding points to
indicate, particularly in an overdetermined system, if point pairs have
higher or lower confidence or uncertainties associated with them. From
the matrix treatment of least squares problems, these weight values are
normalized, square-rooted, then built into a diagonal matrix, by which
A is multiplied.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.
order : int, optional
    Polynomial order (number of coefficients is order + 1).
weights : (N,) array_like, optional
    Relative weight values for each pair of points.

Returns
-------
tf : Self or ``FailedEstimation``
    An instance of the transformation if the estimation succeeded.
    Otherwise, we return a special ``FailedEstimation`` object to
    signal a failed estimation. Testing the truth value of the failed
    estimation object will return ``False``. E.g.

    .. code-block:: python

        tf = PolynomialTransform.from_estimate(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

r   )r   rX   rY   orderr"  r   s        r    r   !PolynomialTransform.from_estimate	  s    H w$Su>>r"   c           
         [         R                  " U5      n[         R                  " U5      nUS S 2S4   nUS S 2S4   nUS S 2S4   nUS S 2S4   nUR                  S   n	[        U5      nUS-   US-   -  n
[         R                  " U	S-  U
S-   45      nSn[        US-   5       HG  n[        US-   5       H2  nX]U-
  -  Xn-  -  US U	2U4'   X]U-
  -  Xn-  -  XS 2XS-  -   4'   US-  nM4     MI     X{S U	2S4'   XU	S 2S4'   Uc$  [         R                  R                  U5      u    nnO[         R                  " U5      n[         R                  " [         R                  " [         R                  " U[         R                  " U5      -  5      S5      5      n[         R                  R                  UU-  5      u    nnUSS S24   * US   -  nUR                  SU
S-  45      U l        g )Nr   r   r   r   r%  )r   rO   r*   r	   r&  r  rQ   rS   rV   r(  r   rT   r   r   )rp   rX   rY   r  r"  xsysxdydrowsura   pidxjr  r   rd   r-  r   s                      r    r   PolynomialTransform._estimatea	  s   jjojjoAYAYAYAYyy| E"QY519%HHdQhA&'uqy!A1q5\!#A!6%4%+*,Q-"%*?%Q&'	 " " %4%)$%) ?iimmA&GAq!jj)G"&&/(A BAFGAiimmAE*GAq! BG*qy(nnaa[1r"   c           	      N   [         R                  " U5      nUSS2S4   nUSS2S4   n[        U R                  R	                  5       5      n[        S[        R                  " SSSU-
  -  -
  5      -   S-  5      n[         R                  " UR                  5      nSn[        US-   5       Hw  n[        US-   5       Hb  n	USS2S4==   U R                  SU4   X(U	-
  -  -  X9-  -  -  ss'   USS2S4==   U R                  SU4   X(U	-
  -  -  X9-  -  -  ss'   US-  nMd     My     U$ )zApply forward transformation.

Parameters
----------
coords : (N, 2) array_like
    source coordinates

Returns
-------
coords : (N, 2) array
    Transformed coordinates.

Nr   r   	   r   r   )r   rO   rK   r   ravelr   rc  r   r&  r*   r  )
rp   rq   xyr  r  rY   r  r  r  s
             r    rr   PolynomialTransform.__call__	  s    F#1a4L1a4L!!#$R$))AQUO449:hhv||$uqy!A1q5\AqD	T[[D1Aa%L@14GG	AqD	T[[D1Aa%L@14GG		 " " 
r"   c                     U " SUS9$ )r   Nr~  rn   r   s     r    r   PolynomialTransform.identity	  s      $~>>r"   c                     [        S5      e)NzThere is no explicit way to do the inverse polynomial transformation. Instead, estimate the inverse transformation parameters by exchanging source and destination coordinates,then apply the forward transformation.)r   rw   s    r    rx   PolynomialTransform.inverse	  s    !5
 	
r"   c                 *    U R                  XX45      SL $ )a  Estimate the transformation from a set of corresponding points.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

The transformation is defined as::

    X = sum[j=0:order]( sum[i=0:j]( a_ji * x**(j - i) * y**i ))
    Y = sum[j=0:order]( sum[i=0:j]( b_ji * x**(j - i) * y**i ))

These equations can be transformed to the following form::

    0 = sum[j=0:order]( sum[i=0:j]( a_ji * x**(j - i) * y**i )) - X
    0 = sum[j=0:order]( sum[i=0:j]( b_ji * x**(j - i) * y**i )) - Y

which exist for each set of corresponding points, so we have a set of
N * 2 equations. The coefficients appear linearly so we can write
A x = 0, where::

    A   = [[1 x y x**2 x*y y**2 ... 0 ...             0 -X]
           [0 ...                 0 1 x y x**2 x*y y**2 -Y]
            ...
            ...
          ]
    x.T = [a00 a10 a11 a20 a21 a22 ... ann
           b00 b10 b11 b20 b21 b22 ... bnn c3]

In case of total least-squares the solution of this homogeneous system
of equations is the right singular vector of A which corresponds to the
smallest singular value normed by the coefficient c3.

Weights can be applied to each pair of corresponding points to
indicate, particularly in an overdetermined system, if point pairs have
higher or lower confidence or uncertainties associated with them. From
the matrix treatment of least squares problems, these weight values are
normalized, square-rooted, then built into a diagonal matrix, by which
A is multiplied.

Parameters
----------
src : (N, 2) array_like
    Source coordinates.
dst : (N, 2) array_like
    Destination coordinates.
order : int, optional
    Polynomial order (number of coefficients is order + 1).
weights : (N,) array_like, optional
    Relative weight values for each pair of points.

Returns
-------
success : bool
    True, if model estimation succeeds.

Nr   )rp   rX   rY   r  r"  s        r    r   PolynomialTransform.estimate	  s    v ~~c74??r"   r   r   )r   N)r   r   r   r   r   r   r   r   r   rr   r   r   rx   r
   r   r   r   r   s   @r    r  r    sw    :x	Od 	O C? C?J'R> ? ?" 
 
 :@ :@r"   r  )	euclidean
similarityr  zpiecewise-affine
projectivefundamental	essential
polynomialc                     U R                  5       n U [        ;  a  [        SU  S35      e[        U    R                  " X/UQ70 UD6$ )a  Estimate 2D geometric transformation parameters.

You can determine the over-, well- and under-determined parameters
with the total least-squares method.

Number of source and destination coordinates must match.

Parameters
----------
ttype : {'euclidean', similarity', 'affine', 'piecewise-affine',              'projective', 'polynomial'}
    Type of transform.
kwargs : array_like or int
    Function parameters (src, dst, n, angle)::

        NAME / TTYPE        FUNCTION PARAMETERS
        'euclidean'         `src, `dst`
        'similarity'        `src, `dst`
        'affine'            `src, `dst`
        'piecewise-affine'  `src, `dst`
        'projective'        `src, `dst`
        'polynomial'        `src, `dst`, `order` (polynomial order,
                                                  default order is 2)

    Also see examples below.

Returns
-------
tf : :class:`_GeometricTransform` or ``FailedEstimation``
    An instance of the requested transformation if the estimation
    Otherwise, we return a special ``FailedEstimation`` object to signal a
    failed estimation. Testing the truth value of the failed estimation
    object will return ``False``. E.g.

    .. code-block:: python

        tf = estimate_transform(...)
        if not tf:
            raise RuntimeError(f"Failed estimation: {tf}")

Examples
--------
>>> import numpy as np
>>> import skimage as ski

>>> # estimate transformation parameters
>>> src = np.array([0, 0, 10, 10]).reshape((2, 2))
>>> dst = np.array([12, 14, 1, -20]).reshape((2, 2))

>>> tform = ski.transform.estimate_transform('similarity', src, dst)

>>> np.allclose(tform.inverse(tform(src)), src)
True

>>> # warp image using the estimated transformation
>>> image = ski.data.camera()

>>> ski.transform.warp(image, inverse_map=tform.inverse) # doctest: +SKIP

>>> # create transformation with explicit parameters
>>> tform2 = ski.transform.SimilarityTransform(scale=1.1, rotation=1,
...     translation=(10, 20))

>>> # unite transformations, applied in order from left to right
>>> tform3 = tform + tform2
>>> np.allclose(tform3(src), tform2(tform(src)))
True

The estimation can fail - for example, if all the input or output points
are the same.  If this happens, you will get a transform that is not
"truthy" - meaning that ``bool(tform)`` is ``False``:

>>> # A successfully estimated model is truthy (applying ``bool()``
>>> # gives ``True``):
>>> if tform:
...     print("Estimation succeeded.")
Estimation succeeded.
>>> # Not so for a degenerate transform with identical points.
>>> bad_src = np.ones((2, 2))
>>> bad_tform = ski.transform.estimate_transform('similarity',
...                                              bad_src, dst)
>>> if not bad_tform:
...     print("Estimation failed.")
Estimation failed.

Trying to use this failed estimation transform result will give a suitable
error:

>>> bad_tform.params  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  ...
FailedEstimationAccessError: No attribute "params" for failed estimation ...

zthe transformation type 'z' is not implemented)r+   
TRANSFORMSr   r   )ttyperX   rY   r   r   s        r    estimate_transformr  
  sN    ~ KKMEJ5eW<QRSSe**3EdEfEEr"   c                 $    [        U5      " U 5      $ )zApply 2D matrix transform.

Parameters
----------
coords : (N, 2) array_like
    x, y coordinates to transform
matrix : (3, 3) array_like
    Homogeneous transformation matrix.

Returns
-------
coords : (N, 2) array
    Transformed coordinates.

)r  )rq   r   s     r    matrix_transformr  t
  s      v&v..r"   )r'   )F)*r   rc  r;  abcr   r   typingr   r  numpyr   scipyr   _shared.utilsr	   r
   r   r   r   _shared.compatr   r!   r5   r;   r:   r?   ri   rk   r   r   r   r   r  rS  r  r  r  r  r  r  r  r  rn   r"   r    <module>r     s]      #      /HV60D:(M`c># c>LM4(+ 4(nm0!2 m0`	R09 R0jC9+ C9L !cI) cI  !cILe02 e0P,A0, A0H !uR, uR  !uRpl@- l@`	 $%0%-)%	
cFL/r"   