o
    oij6                     @   s   d dl mZmZmZ d dlZd dlmZ d dlmZm	Z	m
Z
 g dZG dd deZ	
ddedededededeeeee f fddZddededededef
ddZddededededef
ddZdS )    )ListOptionalTupleN)ImageModule)Tensorconcatenatetensor)ZCAWhiteninglinear_transformzca_mean
zca_whitenc                       s   e Zd ZdZ					ddededed	ed
eddf fddZdedd fddZ	ddededefddZ
dedefddZ  ZS )r	   as	  Compute the ZCA whitening matrix transform and the mean vector and applies the transform to the data.

    The data tensor is flattened, and the mean :math:`\mathbf{\mu}`
    and covariance matrix :math:`\mathbf{\Sigma}` are computed from
    the flattened data :math:`\mathbf{X} \in \mathbb{R}^{N \times D}`, where
    :math:`N` is the sample size and :math:`D` is flattened dimensionality
    (e.g. for a tensor with size 5x3x2x2 :math:`N = 5` and :math:`D = 12`). The ZCA whitening
    transform is given by:

    .. math::

        \mathbf{X}_{\text{zca}} = (\mathbf{X - \mu})(US^{-\frac{1}{2}}U^T)^T

    where :math:`U` are the eigenvectors of :math:`\Sigma` and :math:`S` contain the corresponding
    eigenvalues of :math:`\Sigma`. After the transform is applied, the output is reshaped to same shape.

    Args:
        dim: Determines the dimension that represents the samples axis.
        eps: a small number used for numerical stability.
        unbiased: Whether to use the biased estimate of the covariance matrix.
        compute_inv: Compute the inverse transform matrix.
        detach_transforms: Detaches gradient from the ZCA fitting.

    shape:
        - x: :math:`(D_0,...,D_{\text{dim}},...,D_N)` is a batch of N-D tensors.
        - x_whiten: :math:`(D_0,...,D_{\text{dim}},...,D_N)` same shape as input.

    .. note::
       See a working example `here <https://colab.sandbox.google.com/github/kornia/tutorials/
       blob/master/source/zca_whitening.ipynb>`__.

    Examples:
        >>> x = torch.tensor([[0,1],[1,0],[-1,0],[0,-1]], dtype = torch.float32)
        >>> zca = ZCAWhitening().fit(x)
        >>> x_whiten = zca(x)
        >>> zca = ZCAWhitening()
        >>> x_whiten = zca(x, include_fit = True) # Includes the fitting step
        >>> x_whiten = zca(x) # Can run now without the fitting set
        >>> # Enable backprop through ZCA fitting process
        >>> zca = ZCAWhitening(detach_transforms = False)
        >>> x_whiten = zca(x, include_fit = True) # Includes the fitting step

    Note:
        This implementation uses :py:meth:`~torch.svd` which yields NaNs in the backwards step
        if the singular values are not unique. See `here <https://pytorch.org/docs/stable/torch.html#torch.svd>`_ for
        more information.

    References:
        [1] `Stanford PCA & ZCA whitening tutorial <http://ufldl.stanford.edu/tutorial/unsupervised/PCAWhitening/>`_

    r   ư>TFdimepsunbiaseddetach_transformscompute_invreturnNc                    s>   t    || _|| _|| _|| _|| _d| _|  |  |  d S )NF)super__init__r   r   r   r   r   fitted)selfr   r   r   r   r   	__class__ F/home/ubuntu/.local/lib/python3.10/site-packages/kornia/enhance/zca.pyr   Q   s   
zZCAWhitening.__init__xc                 C   s|   t || j| j| j| j\}}}|| _|| _|du r!tdg| _	n|| _	| j
r9| j | _| j | _| j	 | _	d| _| S )zFit ZCA whitening matrices to the data.

        Args:
            x: Input data.

        Returns:
            Returns a fitted ZCAWhiten object instance.

        Nr   T)r   r   r   r   r   mean_vectortransform_matrixtorchemptytransform_invr   detachr   )r   r   TmeanT_invr   r   r   fitg   s   
zZCAWhitening.fitinclude_fitc                 C   s4   |r|  | | jstdt|| j| j| j}|S )zApply the whitening transform to the data.

        Args:
            x: Input data.
            include_fit: Indicates whether to fit the data as part of the forward pass.

        Returns:
            The transformed data.

        TNeeds to be fitted first before running. Please call fit or set include_fit to True.)r&   r   RuntimeErrorr
   r   r   r   )r   r   r'   x_whitenr   r   r   forward   s   
zZCAWhitening.forwardc                 C   sP   | j std| jstd| jdu rtd| j| j }t|| j|}|S )zApply the inverse transform to the whitened data.

        Args:
            x: Whitened data.

        Returns:
            Original data.

        r(   z;Did not compute inverse ZCA. Please set compute_inv to TrueNz6The transform inverse should be a Tensor. Gotcha None.)	r   r)   r   r!   	TypeErrorr   mmr   r
   )r   r   mean_invyr   r   r   inverse_transform   s   

zZCAWhitening.inverse_transform)r   r   TTF)F)__name__
__module____qualname____doc__intfloatboolr   r   r&   r+   r0   __classcell__r   r   r   r   r	      s.    6r	   Tr   Finpr   r   r   return_inverser   c                 C   s6  t | tstdt|  t |tstdt| t |ts*tdt| t |ts8tdt| t |tsFtdt| |  }|t|ksW|t| k rkt	dt|  dt|d  d	| |d
k rut|| }t
td
|t|d t|g}t
t|g|g }| |}|| }	t|d
| ||d d  }
tt|
 }tj|d
dd}|d|f}||	|f| }| |}|r|t|	d  }n|t|	 }tj|\}}}|dd}t|| }|||  }d}|r|t|| |  }|||fS )aU  Compute the ZCA whitening matrix and mean vector.

    The output can be used with :py:meth:`~kornia.color.linear_transform`.
    See :class:`~kornia.color.ZCAWhitening` for details.

    Args:
        inp: input data tensor.
        dim: Specifies the dimension that serves as the samples dimension.
        unbiased: Whether to use the unbiased estimate of the covariance matrix.
        eps: a small number used for numerical stability.
        return_inverse: Whether to return the inverse ZCA transform.

    Shapes:
        - inp: :math:`(D_0,...,D_{\text{dim}},...,D_N)` is a batch of N-D tensors.
        - transform_matrix: :math:`(\Pi_{d=0,d\neq \text{dim}}^N D_d, \Pi_{d=0,d\neq \text{dim}}^N D_d)`
        - mean_vector: :math:`(1, \Pi_{d=0,d\neq \text{dim}}^N D_d)`
        - inv_transform: same shape as the transform matrix

    Returns:
        A tuple containing the ZCA matrix and the mean vector. If return_inverse is set to True,
        then it returns the inverse ZCA matrix, otherwise it returns None.

    .. note::
       See a working example `here <https://colab.sandbox.google.com/github/kornia/tutorials/
       blob/master/source/zca_whitening.ipynb>`__.

    Examples:
        >>> x = torch.tensor([[0,1],[1,0],[-1,0],[0,-1]], dtype = torch.float32)
        >>> transform_matrix, mean_vector,_ = zca_mean(x) # Returns transformation matrix and data mean
        >>> x = torch.rand(3,20,2,2)
        >>> transform_matrix, mean_vector, inv_transform = zca_mean(x, dim = 1, return_inverse = True)
        >>> # transform_matrix.size() equals (12,12) and the mean vector.size equal (1,12)

     Input type is not a Tensor. Got eps type is not a float. Gotunbiased type is not bool. Got(Argument 'dim' must be of type int. Got z-Argument return_inverse must be of type bool 4Dimension out of range (expected to be in range of [,   ], but got r   NT)r   keepdim)
isinstancer   r,   typer6   r7   r5   sizelen
IndexErrorr   r   aranger   tolistpermuteproditemr$   reshapetr-   linalgsvdrsqrtsqrt)r9   r   r   r   r:   inp_size	feat_dims	new_orderinp_permuteNfeature_sizesnum_featuresr$   inp_center_flatcovUS_
S_inv_rootr#   r%   r   r   r   r      sL   
%



"$
 
r   c                 C   s   t | tstdt|  t |tstdt| t |ts*tdt| t |ts8tdt| t| |||d\}}}t| |||}|S )a  Apply ZCA whitening transform.

    See :class:`~kornia.color.ZCAWhitening` for details.

    Args:
        inp: input data tensor.
        dim: Specifies the dimension that serves as the samples dimension.
        unbiased: Whether to use the unbiased estimate of the covariance matrix.
        eps: a small number used for numerical stability.

    Returns:
        Whiten Input data.

    .. note::
       See a working example `here <https://colab.sandbox.google.com/github/kornia/tutorials/
       blob/master/source/zca_whitening.ipynb>`__.

    Examples:
        >>> x = torch.tensor([[0,1],[1,0],[-1,0]], dtype = torch.float32)
        >>> zca_whiten(x)
        tensor([[ 0.0000,  1.1547],
                [ 1.0000, -0.5773],
                [-1.0000, -0.5773]])

    r;   r<   r=   r>   F)	rE   r   r,   rF   r6   r7   r5   r   r
   )r9   r   r   r   	transformr$   r`   
inp_whitenr   r   r   r     s   



r   r   r   c                 C   s&  |   }|t|ks|t| k r%tdt|  dt|d  d| |dk r/t|| }ttd|t|d t|g}tt|g|g}t|}| }| }	t|d| ||d d  }
t	t
|
 }| |}|d|f}|| }||}||  }||	}|S )a2  Given a transformation matrix and a mean vector, this function will flatten the input tensor along the given
    dimension and subtract the mean vector from it. Then the dot product with the transformation matrix will be
    computed and then the resulting tensor is reshaped to the original input shape.

    .. math::

        \mathbf{X}_{T} = (\mathbf{X - \mu})(T)

    Args:
        inp: Input data :math:`X`.
        transform_matrix: Transform matrix :math:`T`.
        mean_vector: mean vector :math:`\mu`.
        dim: Batch dimension.

    Shapes:
        - inp: :math:`(D_0,...,D_{\text{dim}},...,D_N)` is a batch of N-D tensors.
        - transform_matrix: :math:`(\Pi_{d=0,d\neq \text{dim}}^N D_d, \Pi_{d=0,d\neq \text{dim}}^N D_d)`
        - mean_vector: :math:`(1, \Pi_{d=0,d\neq \text{dim}}^N D_d)`

    Returns:
        Transformed data.

    Example:
        >>> # Example where dim = 3
        >>> inp = torch.ones((10,3,4,5))
        >>> transform_mat = torch.ones((10*3*4,10*3*4))
        >>> mean = 2*torch.ones((1,10*3*4))
        >>> out = linear_transform(inp, transform_mat, mean, 3)
        >>> print(out.shape, out.unique())  # Should a be (10,3,4,5) tensor of -120s
        torch.Size([10, 3, 4, 5]) tensor([-120.])

        >>> # Example where dim = 0
        >>> inp = torch.ones((10,2))
        >>> transform_mat = torch.ones((2,2))
        >>> mean = torch.zeros((1,2))
        >>> out = linear_transform(inp, transform_mat, mean)
        >>> print(out.shape, out.unique()) # Should a be (10,2) tensor of 2s
        torch.Size([10, 2]) tensor([2.])

    r?   r@   rA   rB   r   NrD   )rG   rH   rI   r   r   rJ   r   argsortrK   r5   rM   rN   rL   rO   r-   )r9   r   r   r   rU   rV   permperm_invrW   	inv_orderrZ   r[   rX   inp_flat
inp_centerinp_transformedr   r   r   r
   A  s*   )"$
 


r
   )r   Tr   F)r   Tr   )r   )typingr   r   r   r   kornia.corer   Moduler   r   r   __all__r	   r5   r7   r6   r   r   r
   r   r   r   r   <module>   s.    
 b$-