o
    oi,<                     @  s   d dl mZ d dlmZmZmZmZ d dlZd dlm	Z	 d dl
mZ d dlmZ d dlmZmZ d dlmZ d	gZG d
d	 d	e	ZdS )    )annotations)AnyOptionalSequenceUnionN)MixAugmentationBaseV2)_validate_input_dtype)DataKey)Tensortensor)KORNIA_CHECKRandomTransplantationc                      sl   e Zd ZdZ				d+d, fddZd-ddZd.ddZd.ddZddd/d#d$Zddd%d0d)d*Z	  Z
S )1r   a  RandomTransplantation augmentation.

    .. image:: _static/img/RandomTransplantation.png

    Randomly transplant (copy and paste) image features and corresponding segmentation masks between images in a batch.
    The transplantation transform works as follows:

        1. Based on the parameter `p`, a certain number of images in the batch are selected as acceptor of a
           transplantation.
        2. For each acceptor, the image below in the batch is selected as donor (via circling: :math:`i - 1 \mod B`).
        3. From the donor, a random label is selected and the corresponding image features and segmentation mask are
           transplanted to the acceptor.

    The augmentation is described in `Semantic segmentation of surgical hyperspectral images under geometric domain
    shifts` :cite:`sellner2023semantic`.

    Args:
        excluded_labels: sequence of labels which should not be transplanted from a donor. This can be useful if only
          parts of the image are annotated and the non-annotated regions (with a specific label index) should be
          excluded from the augmentation. If no label is left in the donor image, nothing is transplanted.
        p: probability for applying an augmentation to an image. This parameter controls how many images in a batch
          receive a transplant.
        p_batch: probability for applying an augmentation to a batch. This param controls the augmentation
          probabilities batch-wise.
        data_keys: the input type sequential for applying augmentations. There must be at least one "mask" tensor. If no
          data keys are given, the first tensor is assumed to be `DataKey.INPUT` and the second tensor `DataKey.MASK`.
          Accepts "input", "mask".

    Note:
        - This augmentation requires that segmentation masks are available for all images in the batch and that at
          least some objects in the image are annotated.
        - When using this class directly (`RandomTransplantation()(...)`), it works for arbitrary spatial dimensions
          including 2D and 3D images. When wrapping in :class:`kornia.augmentation.AugmentationSequential`, use
          :class:`kornia.augmentation.RandomTransplantation` for 2D and
          :class:`kornia.augmentation.RandomTransplantation3D` for 3D images.

    Inputs:
        - Segmentation mask tensor which is used to determine the objects for transplantation: :math:`(B, *)`.
        - (optional) Additional image or mask tensors where the features are transplanted based on the first
          segmentation mask: :math:`(B, C, *)` (`DataKey.INPUT`) or :math:`(B, *)` (`DataKey.MASK`).

    Returns:
        Tensor | list[Tensor]:

        Tensor:
            - Augmented mask tensors: :math:`(B, *)`.
        list[Tensor]:
            - Augmented mask tensors: :math:`(B, *)`.
            - Additional augmented image or mask tensors: :math:`(B, C, *)` (`DataKey.INPUT`) or :math:`(B, *)`
              (`DataKey.MASK`).

    Examples:
        >>> import torch
        >>> rng = torch.manual_seed(0)
        >>> aug = RandomTransplantation(p=1.)
        >>> image = torch.randn(2, 3, 5, 5)
        >>> mask = torch.randint(0, 3, (2, 5, 5))
        >>> mask
        tensor([[[0, 0, 1, 1, 0],
                 [1, 2, 0, 0, 0],
                 [1, 2, 1, 1, 0],
                 [0, 0, 0, 0, 2],
                 [2, 2, 2, 0, 2]],
        <BLANKLINE>
                [[2, 0, 0, 2, 1],
                 [2, 1, 0, 2, 1],
                 [2, 0, 1, 0, 2],
                 [2, 2, 2, 0, 2],
                 [2, 1, 0, 0, 0]]])
        >>> image_out, mask_out = aug(image, mask)
        >>> image_out.shape
        torch.Size([2, 3, 5, 5])
        >>> mask_out.shape
        torch.Size([2, 5, 5])
        >>> mask_out
        tensor([[[2, 0, 1, 2, 0],
                 [2, 2, 0, 2, 0],
                 [2, 2, 1, 1, 2],
                 [2, 2, 2, 0, 2],
                 [2, 2, 2, 0, 2]],
        <BLANKLINE>
                [[0, 0, 0, 2, 0],
                 [2, 1, 0, 0, 0],
                 [2, 0, 1, 0, 0],
                 [0, 0, 0, 0, 2],
                 [2, 1, 0, 0, 0]]])
        >>> aug._params["selected_labels"]  # Image 0 received label 2 from image 1 and image 1 label 0 from image 0
        tensor([2, 0])

    You can apply the same augmentation again in which case the same objects get transplanted between the images:

        >>> aug._params["selection"]  # The pixels (objects) which get transplanted
        tensor([[[ True, False, False,  True, False],
                 [ True, False, False,  True, False],
                 [ True, False, False, False,  True],
                 [ True,  True,  True, False,  True],
                 [ True, False, False, False, False]],
        <BLANKLINE>
                [[ True,  True, False, False,  True],
                 [False, False,  True,  True,  True],
                 [False, False, False, False,  True],
                 [ True,  True,  True,  True, False],
                 [False, False, False,  True, False]]])
        >>> image2 = torch.zeros(2, 3, 5, 5)
        >>> image2[1] = 1
        >>> image2[:, 0]
        tensor([[[0., 0., 0., 0., 0.],
                 [0., 0., 0., 0., 0.],
                 [0., 0., 0., 0., 0.],
                 [0., 0., 0., 0., 0.],
                 [0., 0., 0., 0., 0.]],
        <BLANKLINE>
                [[1., 1., 1., 1., 1.],
                 [1., 1., 1., 1., 1.],
                 [1., 1., 1., 1., 1.],
                 [1., 1., 1., 1., 1.],
                 [1., 1., 1., 1., 1.]]])
        >>> image_out2, mask_out2 = aug(image2, mask, params=aug._params)
        >>> image_out2[:, 0]
        tensor([[[1., 0., 0., 1., 0.],
                 [1., 0., 0., 1., 0.],
                 [1., 0., 0., 0., 1.],
                 [1., 1., 1., 0., 1.],
                 [1., 0., 0., 0., 0.]],
        <BLANKLINE>
                [[0., 0., 1., 1., 0.],
                 [1., 1., 0., 0., 0.],
                 [1., 1., 1., 1., 0.],
                 [0., 0., 0., 0., 1.],
                 [1., 1., 1., 0., 1.]]])

    N      ?      ?excluded_labels&Optional[Union[Sequence[int], Tensor]]pfloatp_batch	data_keys#Optional[list[str | int | DataKey]]returnNonec                   s   t  j||d |d u rg }t|tst|}|| _t| jjdkd| jj d |d u r3tj	tj
g}dd |D | _d| _d S )N)r   r      z:excluded_labels must be a 1-dimensional sequence, but got  dimensions.c                 S     g | ]}t |qS  r	   get.0inpr   r   _/home/ubuntu/.local/lib/python3.10/site-packages/kornia/augmentation/_2d/mix/transplantation.py
<listcomp>       z2RandomTransplantation.__init__.<locals>.<listcomp>)super__init__
isinstancer
   r   r   r   ndimr	   INPUTMASKr   _channel_dim)selfr   r   r   r   	__class__r   r"   r&      s   


zRandomTransplantation.__init__inputr
   paramsdict[str, Tensor]flagsdict[str, Any]c                 C  s   |S Nr   )r,   r/   r0   r2   r   r   r"   apply_non_transform_mask   s   z.RandomTransplantation.apply_non_transform_maskacceptordonor	selectionc                 C  s$   |j | jd|}|| ||< |S )Ndim)	unsqueezer+   	expand_asr,   r6   r7   r8   r   r   r"   transform_input   s   z%RandomTransplantation.transform_inputc                 C  s   || ||< |S r4   r   r=   r   r   r"   transform_mask   s   z$RandomTransplantation.transform_mask)
extra_argslist[DataKey]r@   'Optional[dict[DataKey, dict[str, Any]]]c             	     s  t t|t|kdt| dt| d ||tj }t||D ]?\}}|tjkrbt |j|jd kd|j d|j d t | t	
 fdd	t| D kd
|  d|  d q#d|vrtt	|d dkd |d< d|vr|d d t|d  |d< d|vr jj|jkr j|j _g }tt|d D ]7}	||d |	  }
|
 }||dd jddkjdd }t|dkr|t	t|d  }|| qt|dkrt	|nt	d|d< d|vr[t	jt|d g|jdd R t	j|jd}|d }t |jdkd|j d t t|t|d kdt| dt|d  d ttt|d |D ]\}	}||d |	  }
||	 |
|kd q?||d< |S )ai  Compute parameters for the transformation which are based on one or more input tensors.

        This function is, for example, called by :class:`kornia.augmentation.container.ops.AugmentationSequentialOps`
        before the augmentation is applied on the individual input tensors.

        Args:
            *input: All input tensors passed to the augmentation pipeline.
            data_keys: Associated data key for every input tensor.
            params: Dictionary of parameters computed so far by the augmentation pipeline (e.g. including the
                    `batch_prob`).
            extra_args: Optional dictionary of extra arguments with specific options for different input types.

        Returns:
             Updated dictionary of parameters with the necessary information to apply the augmentation on all input
             tensors separately.

        zLength of keys (z#) does not match number of inputs (z).r   zmEvery image input must have one additional dimension (channel dimension) than the segmentation mask, but got z for the input image and z for the segmentation mask.c                   s   g | ]\}}| j kr|qS r   )r+   )r    isr,   r   r"   r#      s    z;RandomTransplantation.params_from_input.<locals>.<listcomp>zmThe dimensions of the input image and segmentation mask must match except for the channel dimension, but got acceptor_indices
batch_probr   r   donor_indicesselected_labelsr9   r8   N)dtypedevicez8selected_labels must be a 1-dimensional tensor, but got r   z&There cannot be more selected labels (z9) than images where this augmentation should be applied (T)r   lenindexr	   r*   zipr)   r(   sizetorchSize	enumeratewherer   rL   torangeuniqueviewallrandpermappendstackemptyzerosshapeboolmasked_fill_)r,   r   r0   r@   r/   mask_inputkeydonor_labelsdcurrent_masklabelsselected_labelr8   rI   r   rE   r"   params_from_input   sz   
&&
$
$
z'RandomTransplantation.params_from_input)r0   r   Optional[dict[str, Tensor]]kwargsTensor | list[Tensor]c             	     s  |d u r j }ndd |D }|d u r#||tj } |j _n| _t fdddD r? j j	|| jd g }t
||D ]{\}}	|	 jd   }
|	 jd  }|tjkrt|	tjtjtjgd	  |
| jd
 } |	 j j}| jd f | j j}n,|tjkr |
| jd
 } |	 j j}| jd f | j j}nt|| qFt|dkr|d S |S )Nc                 S  r   r   r   r   r   r   r"   r#   3  r$   z1RandomTransplantation.forward.<locals>.<listcomp>c                 3  s    | ]}| j vV  qd S r4   )_params)r    krE   r   r"   	<genexpr>;  s    z0RandomTransplantation.forward.<locals>.<genexpr>)rF   rH   r8   )r   r0   rF   rH   )accepted_dtypesr8   r   r   )r   rN   r	   r*   forward_parametersr_   rn   anyupdaterj   rO   cloner)   r   rQ   float16float32float64r>   apply_non_transformr2   	index_putr5   r?   NotImplementedErrorr[   rM   )r,   r0   r   r/   rl   keysrb   outputsdcaterc   r6   r7   appliedoutputr   rE   r"   forward(  sB   



zRandomTransplantation.forward)Nr   r   N)
r   r   r   r   r   r   r   r   r   r   )r/   r
   r0   r1   r2   r3   r   r
   )r6   r
   r7   r
   r8   r
   r   r
   )
r/   r
   r   rA   r0   r1   r@   rB   r   r1   )
r/   r
   r0   rk   r   r   rl   r3   r   rm   )__name__
__module____qualname____doc__r&   r5   r>   r?   rj   r   __classcell__r   r   r-   r"   r   !   s      


	_)
__future__r   typingr   r   r   r   rQ    kornia.augmentation._2d.mix.baser   kornia.augmentation.utilsr   kornia.constantsr	   kornia.corer
   r   kornia.core.checkr   __all__r   r   r   r   r"   <module>   s   