o
    oiO                     @   s  d dl Z d dlmZmZmZmZ d dlZd dlm  m	Z
 d dlmZmZmZmZmZmZmZ d dlmZmZ d dlmZmZmZ d dlmZ d dlmZ ded	efd
dZded	efddZded	efddZ deded	efddZ!deded	efddZ"dHdedee dee d	efddZ#dedee$ef d	efddZ%dIdede$d	efd d!Z&d"ed	efd#d$Z'dJded&e(d	efd'd(Z)dKded)e(d	eee( ee( f fd*d+Z*ded,ed	efd-d.Z+ded,ed	efd/d0Z,dLd2eded3e(d	efd4d5Z-	6dMd2eded3e(d7e.d	ef
d8d9Z/	6dMd2eded3e(d7e.d	ef
d:d;Z0dKded,ed<e(d	efd=d>Z1ded	efd?d@Z2dAed	efdBdCZ3dDedEed	efdFdGZ4dS )N    N)ListOptionalTupleUnion)Tensorconcatenatecossinstacktensorzeros)KORNIA_CHECK_LAFKORNIA_CHECK_SHAPE)angle_to_rotation_matrixconvert_points_from_homogeneousrad2deg)transform_points)pyrdown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 )zReturn a scale of the LAFs.

    Args:
        LAF: :math:`(B, N, 2, 3)`

    Returns:
        scale :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   F/home/ubuntu/.local/lib/python3.10/site-packages/kornia/feature/laf.pyget_laf_scale   s   \r   c                 C   s   t |  | d }|S )a^  Return a center (keypoint) of the LAFs.

    The convention is that center of 5-pixel image (coordinates from 0 to 4) is 2, and not 2.5.

    Args:
        LAF: :math:`(B, N, 2, 3)`

    Returns:
        xy :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_center3   s   r    c                 C   s*   t |  t| d | d }t|dS )a  Return orientation of the LAFs, in degrees.

    Args:
        LAF: :math:`(B, N, 2, 3)`

    Returns:
        angle in degrees :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   	angle_radr   r   r   get_laf_orientationH   s   r&   angles_degreesc                 C   s   t |  | jdd \}}t||| dd}|  }t| ddddddddf || dd|||dd|ddddddddf< |S )aC  Apply additional rotation to the LAFs.

    Compared to `set_laf_orientation`, the resulting rotation is original LAF orientation plus angles_degrees.

    Args:
        LAF: :math:`(B, N, 2, 3)`
        angles_degrees: :math:`(B, N, 1)` in degrees.

    Returns:
        LAF oriented with angles :math:`(B, N, 2, 3)`

    Nr   )r   shaper   viewcloner"   bmmreshape)r   r'   BNrotmatout_lafr   r   r   
rotate_laf[   s   ^r1   c                 C   s6   t |  | jdd \}}t| |}t| || S )zChange the orientation of the LAFs.

    Args:
        LAF: :math:`(B, N, 2, 3)`
        angles_degrees: :math:`(B, N, 1)` in degrees.

    Returns:
        LAF oriented with angles :math:`(B, N, 2, 3)`

    Nr   )r   r(   r&   
reshape_asr1   )r   r'   _B_Norir   r   r   set_laf_orientationp   s   r6   xyscaler5   c           	      C   s   t | g d | j}| j}| jdd \}}|du r%tj||dd||d}|du r2t||d||d}t |g d t |g d tt|	d| 
dgdd	}t||}|S )
az  Create a LAF from keypoint center, scale and orientation.

    Useful to create kornia LAFs from OpenCV keypoints.

    Args:
        xy: :math:`(B, N, 2)`.
        scale: :math:`(B, N, 1, 1)`. If not provided, scale = 1.0 is assumed
        ori: angle in degrees :math:`(B, N, 1)`. If not provided orientation = 0 is assumed

    Returns:
        LAF :math:`(B, N, 2, 3)`

    )r-   r.   2Nr   r   )devicedtype)r-   r.   1r<   )r-   r.   r<   r!   dim)r   r:   r;   r(   r"   onesr   r   r   squeezer$   	scale_laf)	r7   r8   r5   r:   r;   r-   r.   unscaled_laflafr   r   r   laf_from_center_scale_ori   s    
rD   rC   
scale_coefc                 C   sx   t |ttfstdt| t|  | ddddddddf }t|| | 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: :math:`(B, N, 2, 3)`
        scale_coef: broadcastable tensor or float.

    Returns:
        LAF :math:`(B, N, 2, 3)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> scale = 0.5
        >>> output = scale_laf(input, scale)  # BxNx2x3

    z*scale_coef should be float or Tensor. Got Nr      r=   )
isinstancefloatr   	TypeErrortyper   r   )rC   rE   centerless_lafr   r   r   rA      s
    0rA   &.>r   c                 C   s  t |  t| }|}t| dddddf d | dddddf d  | }t||  t|gdd}t| 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t||gdd| dddddf gdd}t||S )a2  Rectify the affine matrix, so that it becomes upright.

    Args:
        laf: :math:`(B, N, 2, 3)`
        eps: for safe division.

    Returns:
        laf: :math:`(B, N, 2, 3)`

    Example:
        >>> input = torch.ones(1, 5, 2, 3)  # BxNx2x3
        >>> output = make_upright(input)  #  BxNx2x3

    .r   r   r   rF   r=   N)r   r   r"   r   r   
contiguous
zeros_likerA   )rC   r   detr8   b2a2laf1_elllaf2_elllaf_unit_scaler   r   r   make_upright   s   >^
.
rT   ellsc           
      C   s   t | g d | j\}}}| dddf   }t|}| dddf   }| dddf || d }t||||gdd	||dd	 }t
|| dd
df ||ddgdd	}	|	S )a  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 :math:`(B, N, 5)` of ellipses in Oxford format [x y a b c].

    Returns:
        LAF :math:`(B, N, 2, 3)`

    Example:
        >>> input = torch.ones(1, 10, 5)  # BxNx5
        >>> output = ellipse_to_laf(input)  #  BxNx2x3

    )r-   r.   5.r   rF         rL   r!   r=   Nr   )r   r(   r   r   r"   rN   clampr
   r)   inverser   )
rU   r-   r.   _a11a12a22a21Ar   r   r   r   ellipse_to_laf   s   
$(ra   2   n_ptsc           	   	   C   s<  t |  |  \}}}}tttddtj |d dt	tddtj |d dt
|d dgdd}ttg ddd|gddd|| |d}|| j| j}tg dddd|| dd}t| ddd|| j| jgdd}t||dddddd}t||||dS )a,  Convert LAFs to boundary points of the regions + center.

    Used for local features visualization, see visualize_laf function.

    Args:
        LAF: :math:`(B, N, 2, 3)`
        n_pts: number of points to output.

    Returns:
        tensor of boundary points LAF: :math:`(B, N, n_pts, 2)`

    r   r   r   r!   r=   )        rd         ?rF   )r   sizer   r	   r"   linspacemathpir$   r   r?   r   r)   expandtor:   r;   r+   permuter   )	r   rc   r-   r.   r[   ptsauxHLAFpts_hr   r   r   laf_to_boundary_points  s     4	$* rq   img_idxc                 C   sN   t |  t| ||d  d }| ddd }|d  |d  fS )a  Return list for drawing LAFs (local features).

    Args:
        LAF: :math:`(B, N, 2, 3)`
        img_idx: which points to output.

    Returns:
        List of boundary points x, y`

    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   rq   detachrl   cputolist)r   rr   rm   pts_npr   r   r   get_laf_pts_to_draw"  s   ry   imagesc           	      C   sv   t |  | \}}}}t|d }t|d }t||}tjdddd| j| jd| }||d< ||d< || |  S )a  De-normalize LAFs from scale to image scale.

    The convention is that center of 5-pixel image (coordinates from 0 to 4) is 2, and not 2.5.

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

    Args:
        LAF: :math:`(B, N, 2, 3)`
        images: :math:`(B, CH, H, W)`

    Returns:
        the denormalized LAF: :math:`(B, N, 2, 3)`, scale in pixels

    r   r   rF   r;   r:   r   r   r   r   r   r   r   r   	r   rf   rH   minr"   r?   r;   r:   	expand_as	r   rz   r[   hwwfhfmin_sizecoefr   r   r   denormalize_laf;  s   
r   c           	      C   s~   t |  | \}}}}t|d }t|d }t||}tjdddd| j| jd| }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 - 1, W -1)
        [a11 a21 x]
        [a21 a22 y]
        becomes:
        [a11/MIN_SIZE a21/MIN_SIZE x/(W-1)]
        [a21/MIN_SIZE a22/MIN_SIZE y/(H-1)]

    Args:
        LAF: :math:`(B, N, 2, 3)`
        images: :math:`(B, CH, H, W)`

    Returns:
        the denormalized LAF: :math:`(B, N, 2, 3)`, scale in image percentage (0, 1)

    r   r   rF   r{   re   r|   r}   r~   r   r   r   r   normalize_laf[  s   
r       imgPSc                 C   s   t | | \}}}}|  \}}}}t|| }	tj|	|| dd|| |||gdd}
d|
ddddf   t|d	  d
 |
ddddf< d|
dddd	f   t|d	  d
 |
dddd	f< |
S )zGenerate affine grid.

    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 :math:`(B*N, PS, PS, 2)`

    r   rF   F)align_corners       @.Nr   r   re   )r   rf   r   Faffine_gridr)   r*   rH   )r   r   r   r-   r.   r[   chr   r   
LAF_renormgridr   r   r   'generate_patch_grid_from_normalized_LAFz  s   
*88r   T normalize_lafs_before_extractionc              
   C   s   t | |rt|| }n|}|  \}}}}| \}	}
}}g }t|	D ]3}t| ||d  |||d  || j}|tj	| ||d  
|d||||ddd q$t|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  :math:`(B, CH, H, W)`.
        laf: :math:`(B, N, 2, 3)`.
        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padding_moder   r=   )r   r   rf   ranger   rk   r:   appendr   grid_samplerj   r   r)   )r   rC   r   r   nlafr[   r   r   r   r-   r.   r   ir   r   r   r   extract_patches_simple  s   ,&r   c              	   C   s  t | |rt|| }n|}| \}}}}|  \}}}	}
dtt||  t| }t| d| d| }| jdt	d|d d
 }| }d}t||||||j|j}d}|r| \}}}	}
t|D ]`}|| |k }|   dkrqm|dkd	}t|||d  |||d |d
d
d
d
f |}tj|||d  |jd ||	|
|ddd}|| |d	ddd||j qmt|d|d|k}|s	 |S t|}|d7 }|sa|S )a  Extract patches defined by LAFs from image tensor.

    Patches are extracted from appropriate pyramid level.

    Args:
        img: images, LAFs are detected in  :math:`(B, CH, H, W)`.
        laf: :math:`(B, N, 2, 3)`.
        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   rF   rd   r   r   )r   maxTr!   Nr   Fr   )r   r   rf   r   r   rH   r   log2rY   r   longr"   r   rk   r;   r:   r   r@   sumitemr)   r   r   r   rj   r(   masked_scatter_r   )r   rC   r   r   r   r-   r.   r[   r   r   r   r8   	max_levelpyr_idxcur_imgcur_pyr_levelr   we_are_in_businessr   
scale_maskr   patchesr   r   r   extract_patches_from_pyramid  sB    "4&&r   r   c                 C   sn   t |  | \}}}}t| d}|d |k|d || k |d |k |d || k }|jddd }|S )aa  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)`.

       rs   rt   r   r=   r   )r   rf   rq   r   )rC   rz   r   r[   r   r   rm   good_lafs_maskr   r   r   laf_is_inside_image  s   
6r   c                 C   s:   t |  t| 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   rs   rt   r!   r=   )r   r
   )rC   	three_ptsr   r   r   laf_to_three_points  s   .r   threeptsc                 C   s2   t | 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)`.

    rs   r   rt   r!   r=   )r
   )r   rC   r   r   r   laf_from_three_points  s   .r   trans_01lafs_1c           	      C   s   t | t| std| j|jkstd| jd |jd ks%td| jd dkr3| jd dks7td| \}}}}t|}|	dd	dd

||d d
}t| |}|||dd
	dd	dd
}t|S )a  Apply 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 Tensorz!Tensor must be in the same devicer   z2Input batch size must be the same for both tensorsr!   rF   z#Transformation should be homographyr   r   )r   r"   	is_tensorrI   r:   r(   
ValueErrorrf   r   rl   r,   r   r)   r   )	r   r   bsnr[   
threepts_1points_1points_0
threepts_0r   r   r   perspective_transform_lafs$  s   ,

r   )NN)rL   )rb   )r   )r   )r   T)5rh   typingr   r   r   r   r"   torch.nn.functionalnn
functionalr   kornia.corer   r   r   r	   r
   r   r   kornia.core.checkr   r   kornia.geometry.conversionsr   r   r   kornia.geometry.linalgr   kornia.geometry.transformr   r   r    r&   r1   r6   rD   rH   rA   rT   ra   intrq   ry   r   r   r   boolr   r   r   r   r   r   r   r   r   r   <module>   sf   $$!*(  
%
4