a
    dAN                     @   s@  d dl Z d dlmZmZ d dlZd dlm  mZ d dl	m
Z
mZmZ d dlmZ d dlmZ d dlmZmZ ejejddd	Zejejdd
dZejejdddZejejejdddZd@ejeej eej ejdddZejeeejf ejdddZdAejeejdddZejejdddZdBejeejdd d!ZdCejed"d#d$Z ejejejd%d&d'Z!ejejejd%d(d)Z"dDejejeejd+d,d-Z#dEejejee$ejd/d0d1Z%dFejejee$ejd/d2d3Z&dGejejeejd4d5d6Z'ejd7d8d9Z(ejd:d;d<Z)ejejejd=d>d?Z*dS )H    N)OptionalUnion)angle_to_rotation_matrixconvert_points_from_homogeneousrad2deg)transform_points)pyrdown)KORNIA_CHECK_LAFKORNIA_CHECK_SHAPE)LAFreturnc                 C   st   t |  d}| dddddf | dddddf  | dddddf | dddddf   | }|  S )aR  Return a scale of the LAFs.

    Args:
        LAF: tensor [BxNx2x3] or [BxNx2x2].

    Returns:
        tensor  BxNx1x1.

    Shape:
        - Input: :math: `(B, N, 2, 3)`
        - Output: :math: `(B, N, 1, 1)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> output = get_laf_scale(input)  # BxNx1x1
    g|=.r         )r	   abssqrt)r   epsout r   [/var/www/html/stable-diffusion-webui/venv/lib/python3.9/site-packages/kornia/feature/laf.pyget_laf_scale   s    \r   c                 C   s   t |  | d }|S )aK  Return a center (keypoint) of the LAFs.

    Args:
        LAF: tensor [BxNx2x3].

    Returns:
        tensor  BxNx2.

    Shape:
        - Input: :math: `(B, N, 2, 3)`
        - Output: :math: `(B, N, 2)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> output = get_laf_center(input)  # BxNx2
    .r   )r	   )r   r   r   r   r   get_laf_center$   s    r   c                 C   s*   t |  t| d | d }t|dS )as  Return orientation of the LAFs, in degrees.

    Args:
        LAF: (torch.Tensor): tensor [BxNx2x3].

    Returns:
        torch.Tensor: tensor  BxNx1 .

    Shape:
        - Input: :math: `(B, N, 2, 3)`
        - Output: :math: `(B, N, 1)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> output = get_laf_orientation(input)  # BxNx1
    ).r   r   ).r   r   )r	   torchatan2r   	unsqueeze)r   Z	angle_radr   r   r   get_laf_orientation:   s    r   )r   angles_degreesr   c                 C   s   t |  | jdd \}}t||| dd}tjtt| || ddddddddf || || ddddddddf gdd||dd}|S )a  Change the orientation of the LAFs.

    Args:
        LAF: tensor [BxNx2x3].
        angles: tensor BxNx1, in degrees.

    Returns:
        tensor [BxNx2x3].

    Shape:
        - Input: :math: `(B, N, 2, 3)`, `(B, N, 1)`
        - Output: :math: `(B, N, 2, 3)`
    Nr      dim)r	   shaper   viewr   catbmmmake_upright)r   r   BNZrotmatZlaf_outr   r   r   set_laf_orientationP   s    \r(   )xyscaleorir   c           	      C   s   t | g d | j}| j}| jdd \}}|du rJtj||dd||d}|du rftj||d||d}t |g d t |g d tjt|	d| 
dgdd	}t||}|S )
a@  Return orientation of the LAFs, in radians. Useful to create kornia LAFs from OpenCV keypoints.

    Args:
        xy: tensor [BxNx2].
        scale: tensor [BxNx1x1]. If not provided, scale = 1 is assumed
        ori: tensor [BxNx1]. If not provided orientation = 0 is assumed

    Returns:
        tensor BxNx2x3.
    )r&   r'   2Nr   r   )devicedtype)r&   r'   1r/   )r&   r'   r/   r   r   )r
   r-   r.   r!   r   oneszerosr#   r   squeezer   	scale_laf)	r)   r*   r+   r-   r.   r&   r'   Zunscaled_laflafr   r   r   laf_from_center_scale_orig   s    "
r5   )r4   
scale_coefr   c                 C   s   t |tur,t |tjur,tdt |t|  | ddddddddf }tj|| | ddddddddf gddS )a[  Multiplies region part of LAF ([:, :, :2, :2]) by a scale_coefficient.

    So the center, shape and orientation of the local feature stays the same, but the region area changes.

    Args:
        laf: tensor [BxNx2x3] or [BxNx2x2].
        scale_coef: broadcastable tensor or float.

    Returns:
        tensor BxNx2x3.

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Input: :math:`(B, N,)` or ()
        - Output: :math:`(B, N, 1, 1)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> scale = 0.5
        >>> output = scale_laf(input, scale)  # BxNx2x3
    z1scale_coef should be float or torch.Tensor Got {}Nr   r   r   )typefloatr   Tensor	TypeErrorformatr	   r#   )r4   r6   Zcenterless_lafr   r   r   r3      s
     r3   &.>)r4   r   r   c                 C   s$  t |  t| }|}t| dddddf d | dddddf d  | }tj||  t|gdd}tj| dddddf | dddddf  | dddddf | dddddf   ||  ||  gdd}tjtj||gdd| dddddf gdd}t||S )a  Rectify the affine matrix, so that it becomes upright.

    Args:
        laf: tensor of LAFs.
        eps : for safe division.

    Returns:
        tensor of same shape.

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, 2, 3)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> output = make_upright(input)  #  BxNx2x3
    .r   r   r   r   r   N)r	   r   r   r   r#   
contiguous
zeros_liker3   )r4   r   Zdetr*   Zb2a2Zlaf1_ellZlaf2_ellZlaf_unit_scaler   r   r   r%      s    > ^
2r%   )ellsr   c                 C   s   t |  }|dkr&td|  |  \}}}|dkrNtd|  | dddf   }t|}| dddf   }| dddf || d }tj	||||gdd	
||dd }	tj|	| dd
df 
||ddgdd	}
|
S )ac  Convert ellipse regions to LAF format.

    Ellipse (a, b, c) and upright covariance matrix [a11 a12; 0 a22] are connected
    by inverse matrix square root: A = invsqrt([a b; b c]).

    See also https://github.com/vlfeat/vlfeat/blob/master/toolbox/sift/vl_frame2oell.m

    Args:
        ells: tensor of ellipses in Oxford format [x y a b c].

    Returns:
        tensor of ellipses in LAF format.

    Shape:
        - Input: :math:`(B, N, 5)`
        - Output:  :math:`(B, N, 2, 3)`

    Example:
        >>> input = torch.ones(1, 10, 5)  # BxNx5
        >>> output = ellipse_to_laf(input)  #  BxNx2x3
    r   z/ellipse shape should be must be [BxNx5]. Got {}   .r      r<   r   r   Nr   )lensizer:   r;   r   r   r   r>   clampstackr"   Zinverser#   )r?   Zn_dimsr&   r'   r    Za11Za12Za22Za21Ar   r   r   r   ellipse_to_laf   s    
&*rG   2   )r   n_ptsr   c           	   
   C   sJ  t |  |  \}}}}tjttddtj |d dt	tddtj |d dt
|d dgdd}tjtg ddd|gddd|| |d}|| j| j}tg dddd|| dd}tj| ddd|| j| jgdd}t||dddddd}t||||dS )aW  Convert LAFs to boundary points of the regions + center.

    Used for local features visualization, see visualize_laf function.

    Args:
        LAF:
        n_pts: number of points to output.

    Returns:
        tensor of boundary points.

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, n_pts, 2)`
    r   r   r   r   r   )        rJ         ?r   )r	   rC   r   r#   sinZlinspacemathpir   cosr0   Ztensorr"   expandtor-   r.   r$   permuter   )	r   rI   r&   r'   _ptsZauxZHLAFZpts_hr   r   r   laf_to_boundary_points   s    ""	8&, rU   )r   img_idxc                 C   sJ   t |  t| ||d  d }| ddd  }|d |d fS )a  Return numpy array for drawing LAFs (local features).

    Args:
        LAF:
        n_pts: number of boundary points to output.

    Returns:
        tensor of boundary points.

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, n_pts, 2)`

    Examples:
        x, y = get_laf_pts_to_draw(LAF, img_idx)
        plt.figure()
        plt.imshow(kornia.utils.tensor_to_image(img[img_idx]))
        plt.plot(x, y, 'r')
        plt.show()
    r   r   r   .r   .r   )r	   rU   detachrR   cpunumpy)r   rV   rT   Zpts_npr   r   r   get_laf_pts_to_draw  s    r\   )r   imagesr   c           	      C   st   t |  | \}}}}t|}t|}t||}tdddd| j| j| }||d< ||d< |	| |  S )a  De-normalize LAFs from scale to image scale.

        B,N,H,W = images.size()
        MIN_SIZE = min(H,W)
        [a11 a21 x]
        [a21 a22 y]
        becomes
        [a11*MIN_SIZE a21*MIN_SIZE x*W]
        [a21*MIN_SIZE a22*MIN_SIZE y*H]

    Args:
        LAF:
        images: images, LAFs are detected in.

    Returns:
        the denormalized lafs.

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, 2, 3)`
    r   r   r   r   r   r   r   r   r   r   r   
r	   rC   r8   minr   r0   rQ   r.   r-   Z	expand_as	r   r]   rS   hwZwfZhfZmin_sizeZcoefr   r   r   denormalize_laf4  s    
$re   c           	      C   s|   t |  | \}}}}t|}t|}t||}tdddd| j| j| }d| |d< d| |d< |	| |  S )a  Normalize LAFs to [0,1] scale from pixel scale. See below:
        B,N,H,W = images.size()
        MIN_SIZE = min(H,W)
        [a11 a21 x]
        [a21 a22 y]
        becomes:
        [a11/MIN_SIZE a21/MIN_SIZE x/W]
        [a21/MIN_SIZE a22/MIN_SIZE y/H]

    Args:
        LAF: (torch.Tensor).
        images: (torch.Tensor) images, LAFs are detected in

    Returns:
        LAF: (torch.Tensor).

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, 2, 3)`
    r   r   r   rK   r^   r_   r`   rb   r   r   r   normalize_lafU  s    
$rf       )imgr   PSr   c                 C   s   t | | \}}}}|  \}}}}t|| }	tj|	|| dd|| |||gdd}
d|
ddddf   t| d	 |
ddddf< d|
dddd
f   t| d	 |
dddd
f< |
S )zHelper function for affine grid generation.

    Args:
        img: image tensor of shape :math:`(B, CH, H, W)`.
        LAF: laf with shape :math:`(B, N, 2, 3)`.
        PS: patch size to be extracted.

    Returns:
        grid
    r   r   F)align_corners       @.Nr   rK   r   )r	   rC   re   FZaffine_gridr"   cloner8   )rh   r   ri   r&   r'   rS   chrc   rd   Z
LAF_renormgridr   r   r   'generate_patch_grid_from_normalized_LAFu  s    
*44rp   T)rh   r4   ri    normalize_lafs_before_extractionr   c              
   C   s   t | |rt|| }n|}|  \}}}}| \}	}
}}g }t|	D ]f}t| ||d  |||d  || j}|tj	| ||d  
|d||||ddd qHtj|dd|	|
|||S )a  Extract patches defined by LAFs from image tensor.

    No smoothing applied, huge aliasing (better use extract_patches_from_pyramid).

    Args:
        img: images, LAFs are detected in.
        laf:
        PS: patch size.
        normalize_lafs_before_extraction: if True, lafs are normalized to image size.

    Returns:
        patches with shape :math:`(B, N, CH, PS,PS)`.
    r   r   borderFZpadding_moderj   r   )r	   rf   rC   rangerp   rQ   r-   appendrl   grid_samplerP   r   r#   r"   )rh   r4   ri   rq   nlafrS   rn   rc   rd   r&   r'   r   iro   r   r   r   extract_patches_simple  s$    , ry   c              	   C   s  t | |rt|| }n|}| \}}}}|  \}}}	}
dtt||  t| }|   }| }d}t	
||||||j|j}t|d|d|kr| \}}}	}
t|D ]}|| |k }|  dkrq|dkd}t|||d  |||d |ddddf |}tj|||d  |d||	|
|dd	d
}|| |dddd| qt|}|d7 }q|S )aj  Extract patches defined by LAFs from image tensor.

    Patches are extracted from appropriate pyramid level.

    Args:
        laf:
        images: images, LAFs are detected in.
        PS: patch size.
        normalize_lafs_before_extraction: if True, lafs are normalized to image size.

    Returns:
        patches with shape :math:`(B, N, CH, PS,PS)`.
    rk   r   r   r   r   r   Nrr   Frs   )r	   rf   rC   r   re   r8   log2Zrelulongr   r1   rQ   r.   r-   ra   rt   r2   sumr"   rp   rl   rv   rP   Zmasked_scatter_r   )rh   r4   ri   rq   rw   r&   r'   rS   rn   rc   rd   r*   Zpyr_idxZcur_imgZcur_pyr_levelr   rx   Z
scale_maskro   Zpatchesr   r   r   extract_patches_from_pyramid  s:    "4 
r}   )r4   r]   rr   r   c                 C   sn   t |  | \}}}}t| d}|d |k|d || k |d |k |d || k }|jddd }|S )a`  Check if the LAF is touching or partly outside the image boundary.

    Returns the mask of LAFs, which are fully inside the image, i.e. valid.

    Args:
        laf:  :math:`(B, N, 2, 3)`.
        images: images, lafs are detected in :math:`(B, CH, H, W)`.
        border: additional border.

    Returns:
        mask with shape :math:`(B, N)`.
       rW   rX   r   r   r   )r	   rC   rU   ra   )r4   r]   rr   rS   rc   rd   rT   Zgood_lafs_maskr   r   r   laf_is_inside_image  s    
6r   )r4   c                 C   s<   t |  tj| d | d  | d | d  | d gdd}|S )zConvert local affine frame(LAF) to alternative representation: coordinates of LAF center, LAF-x unit vector,
    LAF-y unit vector.

    Args:
        laf:  :math:`(B, N, 2, 3)`.

    Returns:
        threepts :math:`(B, N, 2, 3)`.
    r   rW   rX   r   r   )r	   r   rE   )r4   Z	three_ptsr   r   r   laf_to_three_points  s    
0r   )threeptsc                 C   s4   t j| d | d  | d | d  | d gdd}|S )zConvert three points to local affine frame.

    Order is (0,0), (0, 1), (1, 0).

    Args:
        threepts: :math:`(B, N, 2, 3)`.

    Returns:
        laf :math:`(B, N, 2, 3)`.
    rW   r   rX   r   r   )r   rE   )r   r4   r   r   r   laf_from_three_points  s    &r   )trans_01lafs_1r   c           	      C   s   t | t| std| j|jks.td| jd |jd ksJtd| jd dkrf| jd dksntd| \}}}}t|}|	dd	dd

||d d
}t| |}|||dd
	dd	dd
}t|S )a  Function that applies perspective transformations to a set of local affine frames (LAFs).

    Args:
        trans_01: tensor for perspective transformations of shape :math:`(B, 3, 3)`.
        lafs_1: tensor of lafs of shape :math:`(B, N, 2, 3)`.

    Returns:
        tensor of N-dimensional points of shape :math:`(B, N, 2, 3)`.

    Examples:
        >>> rng = torch.manual_seed(0)
        >>> lafs_1 = torch.rand(2, 4, 2, 3)  # BxNx2x3
        >>> lafs_1
        tensor([[[[0.4963, 0.7682, 0.0885],
                  [0.1320, 0.3074, 0.6341]],
        <BLANKLINE>
                 [[0.4901, 0.8964, 0.4556],
                  [0.6323, 0.3489, 0.4017]],
        <BLANKLINE>
                 [[0.0223, 0.1689, 0.2939],
                  [0.5185, 0.6977, 0.8000]],
        <BLANKLINE>
                 [[0.1610, 0.2823, 0.6816],
                  [0.9152, 0.3971, 0.8742]]],
        <BLANKLINE>
        <BLANKLINE>
                [[[0.4194, 0.5529, 0.9527],
                  [0.0362, 0.1852, 0.3734]],
        <BLANKLINE>
                 [[0.3051, 0.9320, 0.1759],
                  [0.2698, 0.1507, 0.0317]],
        <BLANKLINE>
                 [[0.2081, 0.9298, 0.7231],
                  [0.7423, 0.5263, 0.2437]],
        <BLANKLINE>
                 [[0.5846, 0.0332, 0.1387],
                  [0.2422, 0.8155, 0.7932]]]])
        >>> trans_01 = torch.eye(3).repeat(2, 1, 1)  # Bx3x3
        >>> trans_01.shape
        torch.Size([2, 3, 3])
        >>> lafs_0 = perspective_transform_lafs(trans_01, lafs_1)  # BxNx2x3
    z Input type is not a torch.Tensorz!Tensor must be in the same devicer   z2Input batch size must be the same for both tensorsr   r   z#Transformation should be homographyr   r   )r	   r   Z	is_tensorr:   r-   r!   
ValueErrorrC   r   rR   Zreshaper   r"   r   )	r   r   bsnrS   Z
threepts_1Zpoints_1Zpoints_0Z
threepts_0r   r   r   perspective_transform_lafs  s    +

r   )NN)r<   )rH   )r   )rg   )rg   T)rg   T)r   )+rM   typingr   r   r   Ztorch.nn.functionalnnZ
functionalrl   Zkornia.geometry.conversionsr   r   r   Zkornia.geometry.linalgr   Zkornia.geometry.transformr   Zkornia.testingr	   r
   r9   r   r   r   r(   r5   r8   r3   r%   rG   intrU   r\   re   rf   rp   boolry   r}   r   r   r   r   r   r   r   r   <module>   sL     $1#!  ' 1