
    i                        d dl Z d dlZd dlmZmZ d dlZd dlmZ d dlmZm	Z	m
Z
mZ d dlmZ d dlmZ d dlmZ ded	ed
eeef   fdZded	ed
dfdZ	 	 d3ded	ededee   ded   d
eeef   fdZdeded
efdZded
efdZded
dfdZdedeedf   d
efdZdeded
efdZ	 d4dedee   deeedf      d ed
ef
d!Z	 	 	 d5ded"eeeee   f      d#ed$   d%ed&   d
ef
d'Z 	 	 d6ded	ed(ed)eeeeef   eeeef   f      d
eeeef   eeeeef   f   f
d*Z!	 	 d7ded	ed+ed$   d)eeeee   f      d
ef
d,Z"	 	 	 d8ded	ed+ed$   d)eeeee   f      d-ed
eeeeef   f   fd.Z#e jH                  	 d9d)eeeef   eeeef   f   d/eejJ                     d
eeef   fd0       Z&d9d)eeef   d/eejJ                     d
eeef   fd1Z'e jH                  d9d)eeeef   d/eejJ                     d
eeef   fd2       Z(y):    N)OptionalUnion)Tensor)conv2dconv3dpadunfold)Literal)_check_same_shape)_SCIPY_AVAILABLEpredstargetreturnc                     | j                   d   dkD  r| ddddf   n| } |j                   d   dkD  r|ddddf   n|}| |fS )zQIgnore the background class in the computation assuming it is the first, index 0.   N)shaper   r   s     /Volumes/fast/ai/experiments/voice-extract-mac/.venv/lib/python3.12/site-packages/torchmetrics/functional/segmentation/utils.py_ignore_backgroundr      sL    !KKNQ.E!QR%LEE$ll1o1VAqrE]vF&=    c                 r   | j                         |j                         dz   k(  re| j                  d   |j                  d   k7  s| j                  dd |j                  dd k7  r&t        d| j                   d|j                   d      y| j                         dz   |j                         k(  re| j                  d   |j                  d   k7  s| j                  dd |j                  dd k7  r&t        d| j                   d|j                   d      yt        d| j                   d|j                   d      )zHCheck that predictions and target have the same shape, else raise error.r   r      NzAPredictions and targets are expected to have the same shape, got  and .)dimr   RuntimeErrorr   s     r   _check_mixed_shaper   "   sA   yy{vzz|a'(;;q>V\\!_,AB6<<PQPRCS0SSTYT_T_S``eflfrfresstu  1T ))+/fjjl	*;;q>V\\!_,AB6<<PQPRCS0SSTYT_T_S``eflfrfresstu  1T
 OPUP[P[}\abhbnbnaoopq
 	
r   include_backgroundnum_classesinput_format)one-hotindexmixedc                    |dk(  rt        | |       nt        | |       |dk(  r|t        d      t        j                  j
                  j                  | |      j                  dd      } t        j                  j
                  j                  ||      j                  dd      }n!|dk(  r&|t        |       }t        | |      } t        ||      }n|dk(  r| j                         |j                         dz   k(  rU|t        |       }t        | |      } t        j                  j
                  j                  ||      j                  dd      }nx| j                         dz   |j                         k(  rT|t        |      }t        ||      }t        j                  j
                  j                  | |      j                  dd      } | j                  dk  rt        d	| j                   d
      |st        | |      \  } }| |fS )z1Check and format inputs to the one-hot encodings.r#   r"   zDArgument `num_classes` must be provided when `input_format='index'`.r   r   r!      zJExpected both `preds` and `target` to have at least 3 dimensions, but got r   )r   r   
ValueErrortorchnn
functionalone_hotmovedim_get_num_classes_format_logitsr   ndimr   )r   r   r   r   r    s        r   _segmentation_inputs_formatr1   4   s    w5&)%(wcdd##++E{+KSSTVXYZ$$,,V,MUUVXZ[\		"*51Kuk24		 99;6::<!+,".u5"5+6EXX((00[0QYYZ\^_`FiikAo&**,.".v6#FK8FHH''//;/OWWXZ\]^EzzA~efkfpfpeqqrstt*5&9v&=r   tensorc                     t        j                  |       rM| j                  d      } t         j                  j                  j                  | |      j                  dd      } | S )zATransform logits or probabilities into integer one-hot encodings.r   r   r%   r&   )r)   is_floating_pointargmaxr*   r+   r,   r-   )r2   r   s     r   r/   r/   `   sS    v&1%$$,,V,MUUVXZ[\Mr   c                     	 | j                   d   }|dk(  rt        d| d      |S # t        $ r}t        d|  d      |d}~ww xY w)z/Get num classes from a tensor if it is not set.r   z,Cannot determine `num_classes` from tensor: r   Nr   zBExpected argument `num_classes` to be a positive integer, but got )r   
IndexErrorr(   )r2   r   errs      r   r.   r.   h   si    \ll1o a]^i]jjklmm	  \GxqQRX[[\s   ' 	AA  Axc                 f    t        j                  | j                         | k(        st        d      y)zCheck if tensor is binarized.

    Example:
        >>> from torchmetrics.functional.segmentation.utils import check_if_binarized
        >>> import torch
        >>> check_if_binarized(torch.tensor([0, 1, 1, 0]))

    zInput x should be binarizedN)r)   allboolr(   )r:   s    r   check_if_binarizedr>   s   s+     99QVVX]#677 $r   kernel_size.c                    | j                   dd \  }}| j                  dz
  }|dk(  rt        | |      S |d   |d   z  |d   z  }|dg|D cg c]  }d c}z   }t        j                  || j
                  | j                        }  |j                  |dg| j                  | }t        | |d      }	|	j                  |||z  d      S c c}w )a.  Unfold the input tensor to a matrix. Function supports 3d images e.g. (B, C, D, H, W).

    Inspired by:
    https://github.com/f-dangel/unfoldNd/blob/main/unfoldNd/unfold.py

    Args:
        x: Input tensor to be unfolded.
        kernel_size: The size of the sliding blocks in each dimension.

    Nr   r   r   devicedtype)weightbiasr&   )
r   r0   r	   r)   eyerB   rC   reshaperepeatr   )
r:   r?   
batch_sizechannelsnkernel_size_numel_rH   rD   unfold_xs
             r   _unfoldrO      s     772A;J	
AAva%%#AQ7+a.H]5Aa55FYY(IFF^V^^-q?;?FFOFaT2HJ3D(DbII	 6s   	C
rankconnectivityc                    |dk  rd}| dk  r&t        j                  dgt         j                        S t        j                  t	        |       D cg c]  }t        j
                  d       c}d      }t        j                  t        j                  |d      dz
        }t        j                  |d      }||k  S c c}w )a  Translated version of the function from scipy.ndimage.morphology.

    Args:
        rank: The rank of the structuring element.
        connectivity: The number of neighbors connected to a given pixel.

    Returns:
        The structuring element.

    Examples::
        >>> from torchmetrics.functional.segmentation.utils import generate_binary_structure
        >>> import torch
        >>> generate_binary_structure(2, 1)
        tensor([[False,  True, False],
                [ True,  True,  True],
                [False,  True, False]])
        >>> generate_binary_structure(2, 2)
        tensor([[True,  True,  True],
                [True,  True,  True],
                [True,  True,  True]])
        >>> generate_binary_structure(3, 2)  # doctest: +NORMALIZE_WHITESPACE
        tensor([[[False,  True, False],
                 [ True,  True,  True],
                 [False,  True, False]],
                [[ True,  True,  True],
                 [ True,  True,  True],
                 [ True,  True,  True]],
                [[False,  True, False],
                 [ True,  True,  True],
                 [False,  True, False]]])

    r   )rC   r'   ij)indexingr   r4   )	r)   r2   uint8meshgridrangearangeabsstacksum)rP   rQ   rM   gridsoutputs        r   generate_binary_structurer^      s    B aax||QCu{{33NNU4[A[ELLO[ADQEYYu{{5a0145FYYv1%F\!! Bs   B=image	structureoriginborder_valuec                    t        | t              st        dt        |              | j                  dvrt        d| j                         t        |        |@t        | j                  dz
  d      j                         j                  | j                        }t        |       ||j                  dz  }t        | t        t        |            D cg c]&  }||   |j                  |   ||   z
  dz
  fD ]  }| ( c}}d|      }t        |j!                         |j                  	      }t#        j$                  |      j'                  d
      j'                  d      }||j                         z
  }	|	j)                  d      \  }
}t#        j*                  |
| j                        dz   j-                         S c c}}w )a  Binary erosion of a tensor image.

    Implementation inspired by answer to this question: https://stackoverflow.com/questions/56235733/

    Args:
        image: The image to be eroded, must be a binary tensor with shape ``(batch_size, channels, height, width)``.
        structure: The structuring element used for the erosion. If no structuring element is provided, an element
            is generated with a square connectivity equal to one.
        origin: The origin of the structuring element.
        border_value: The value to be used for the border.

    Examples::
        >>> from torchmetrics.functional.segmentation.utils import binary_erosion
        >>> import torch
        >>> image = torch.tensor([[[[0, 0, 0, 0, 0],
        ...                         [0, 1, 1, 1, 0],
        ...                         [0, 1, 1, 1, 0],
        ...                         [0, 1, 1, 1, 0],
        ...                         [0, 0, 0, 0, 0]]]])
        >>> binary_erosion(image)
        tensor([[[[0, 0, 0, 0, 0],
                  [0, 0, 0, 0, 0],
                  [0, 0, 1, 0, 0],
                  [0, 0, 0, 0, 0],
                  [0, 0, 0, 0, 0]]]], dtype=torch.uint8)
        >>> binary_erosion(image, structure=torch.ones(4, 4))
        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]]]], dtype=torch.uint8)

    z9Expected argument `image` to be of type Tensor but found )      z>Expected argument `image` to be of rank 4 or 5 but found rank r   r   )r   constant)modevalue)r?   r   r&   r4   )
isinstancer   	TypeErrortyper0   r(   r>   r^   inttorB   r   rW   lenr   rO   floatr)   flatten	unsqueezeminrG   byte)r_   r`   ra   rb   ir:   	image_padimage_unfoldstrel_flattensumsresultrM   s               r   binary_erosionrz      s   H eV$STXY^T_S`abbzzYZ_ZdZdYefggu -ejj1na@DDFII%,,W	y!~$& #f+&`&qIOOA<NQWXYQZ<Z]^<^0_10_&`	I 9??,)//JLMM),66q9CCBGM-++--D QIFA MM&%++.288:: 	as   +F;samplingmetric	euclidean
chessboardtaxicabenginepytorchscipyc                    t        | t              st        dt        |        d      | j                  dk7  rt        d| j                   d      |(t        |t
              st        dt        |       d      |dvrt        d| d      |dvrt        d	| d      |d
d
g}n&t        |      dk7  rt        dt        |       d      |dk(  r| j                         } t        j                  | dk(        \  }}t        j                  | d
k(        \  }}|j                  dd
      |j                  d
d      z
  j                         }|j                  dd
      |j                  d
d      z
  j                         }	| j                  \  }
}|dk(  r%|d   |z  dz  |d
   |	z  dz  z   j                         }|dk(  r0t        j                  |d   |z  |d
   |	z        j                         }|dk(  r|d   |z  |d
   |	z  z   j                         }t        j                  d
      \  }}t        j                   |       j                  d      }||||
z  |z   <   |j                  | j                        S t"        st        d      ddlm} |dk(  r.|j)                  | j+                         j-                         |      S |j/                  | j+                         j-                         ||      S )a  Calculate distance transform of a binary tensor.

    This function calculates the distance transform of a binary tensor, replacing each foreground pixel with the
    distance to the closest background pixel. The distance is calculated using the euclidean, chessboard or taxicab
    distance.

    The memory consumption of this function is in the worst cast N/2**2 where N is the number of pixel. Since we need
    to compare all foreground pixels to all background pixels, the memory consumption is quadratic in the number of
    pixels. The memory consumption can be reduced by using the ``scipy`` engine, which is more memory efficient but
    should also be slower for larger images.

    Args:
        x: The binary tensor to calculate the distance transform of.
        sampling: The sampling refers to the pixel spacing in the image, i.e. the distance between two adjacent pixels.
            If not provided, the pixel spacing is assumed to be 1.
        metric: The distance to use for the distance transform. Can be one of ``"euclidean"``, ``"chessboard"``
            or ``"taxicab"``.
        engine: The engine to use for the distance transform. Can be one of ``["pytorch", "scipy"]``. In general,
            the ``pytorch`` engine is faster, but the ``scipy`` engine is more memory efficient.

    Returns:
        The distance transform of the input tensor.

    Examples::
        >>> from torchmetrics.functional.segmentation.utils import distance_transform
        >>> import torch
        >>> x = torch.tensor([[0, 0, 0, 0, 0],
        ...                   [0, 1, 1, 1, 0],
        ...                   [0, 1, 1, 1, 0],
        ...                   [0, 1, 1, 1, 0],
        ...                   [0, 0, 0, 0, 0]])
        >>> distance_transform(x)
        tensor([[0., 0., 0., 0., 0.],
                [0., 1., 1., 1., 0.],
                [0., 1., 2., 1., 0.],
                [0., 1., 1., 1., 0.],
                [0., 0., 0., 0., 0.]])

    z<Expected argument `x` to be of type `torch.Tensor` but got ``.r   z4Expected argument `x` to be of rank 2 but got rank `zLExpected argument `sampling` to either be `None` or of type `list` but got `r}   zZExpected argument `metric` to be one of `['euclidean', 'chessboard', 'taxicab']` but got `r   zHExpected argument `engine` to be one of `['pytorch', 'scipy']` but got `r   z>Expected argument `sampling` to have length 2 but got length `r   r   r&   r~   r   r   r4   zhThe `scipy` engine requires `scipy` to be installed. Either install `scipy` or use the `pytorch` engine.)ndimage)r|   )ri   r   r(   rk   r0   listrn   ro   r)   whereviewrY   r   sqrtmaxrr   
zeros_liker   r   r   distance_transform_edtcpunumpydistance_transform_cdt)r:   r{   r|   r   i0j0i1j1dis_rowdis_colhrM   dismindiszr   s                   r   distance_transformr   	  s   Z a WX\]^X_W``bcddvv{OPQPVPVxWYZ[[Jx$>Z[_`h[iZjjlm
 	
 ;;hiohpprs
 	
 ))cdjckkmnooq6x=A]^abj^k]llnoppGGIQ!V$BQ!V$B772q>BGGArN2779772q>BGGArN2779 ww1[ QK')a/8A;3HQ2NNTTVC\!))HQK'18A;3HIOOQCYA;(8A;+@@GGIC IIcq)	Q$$R("q&2+vvaggv
 	
 --aeegmmoxHH))!%%'--/8F)SSr   cropspacingc                 &   t        | |       | j                  dvrt        d| j                   d      t        |        t        |       |r{| |z  }|j	                         s0t        j                  |       t        j                  |      }}||||fS t        | | j                  ddgz        t        ||j                  ddgz        }} |xt        | j                  d      j                  d            j                         | z  }t        |j                  d      j                  d            j                         |z  }||fS t        || j                        \  }	}
t        |      }|dk(  rt        nt        }t        j                   | j                  d      |j                  d      gd      j#                         } |||
j%                  |            \  }}t        |	      dz
  }|dk7  ||k7  z  }|dk7  ||k7  z  }t        j&                  |	d|j)                  d	      j+                               j-                  |      }t        j&                  |	d|j)                  d	      j+                               j-                  |      }|d   |d   |d   |d   fS )
a  Get the edges of binary segmentation masks.

    Args:
        preds: The predicted binary segmentation mask
        target: The ground truth binary segmentation mask
        crop: Whether to crop the edges to the region of interest. If ``True``, the edges are cropped to the bounding
        spacing: The pixel spacing of the input images. If provided, the edges are calculated using the euclidean

    Returns:
        If spacing is not provided, a 2-tuple containing the edges of the predicted and target mask respectively is
        returned. If spacing is provided, a 4-tuple containing the edges and areas of the predicted and target mask
        respectively is returned.

    )r   r'   z=Expected argument `preds` to be of rank 2 or 3 but got rank `r   r   r   rB   r   r4   r&   )r   r0   r(   r>   anyr)   r   r   rz   rq   squeezeget_neighbour_tablesrB   rn   r   r   rZ   ro   rm   index_selectr   rl   view_as)r   r   r   r   or_valptbe_pred	be_targettablekernelspatial_dimsconv_operatorvolume
code_predscode_targetall_onesedges_predsedges_targetareas_predsareas_targets                        r   
mask_edgesr   m  s_   ( eV$zzXY^YcYcXddfghhuvzz|##E*E,<,<V,DqAaA:E5::A#67VV[[TUWXSYEY9Zv !3!=!=a!@AIIKeS"6#3#3A#6#@#@#CDLLNQWW		!! )FME6w<L*a/FVM[[%//!,f.>.>q.ABJPPRF+FFIIf4EFJ 5zA~H?zX'=>K1$)@AL $$UAzr/B/F/F/HIQQR\]K%%eQ0@0@0D0H0H0JKSST_`Lq><?KNLOKKr   distance_metricc                    | j                   t        j                  k(  r|j                   t        j                  k(  s&t        d| j                    d|j                    d      t        j                  |      s+t        j
                  t        j                  |      z  }||    S t        j                  |       s+t        j
                  t        j                  |       z  }||   S t        | ||      }||    S )a  Calculate the surface distance between two binary edge masks.

    May return infinity if the predicted mask is empty and the target mask is not, or vice versa.

    Args:
        preds: The predicted binary edge mask.
        target: The target binary edge mask.
        distance_metric: The distance metric to use. One of `["euclidean", "chessboard", "taxicab"]`.
        spacing: The spacing between pixels along each spatial dimension.

    Returns:
        A tensor with length equal to the number of edges in predictions e.g. `preds.sum()`. Each element is the
        distance from the corresponding edge in `preds` to the closest edge in `target`.

    Example::
        >>> import torch
        >>> from torchmetrics.functional.segmentation.utils import surface_distance
        >>> preds = torch.tensor([[1, 1, 1, 1, 1],
        ...                       [1, 0, 0, 0, 1],
        ...                       [1, 0, 0, 0, 1],
        ...                       [1, 0, 0, 0, 1],
        ...                       [1, 1, 1, 1, 1]], dtype=torch.bool)
        >>> target = torch.tensor([[1, 1, 1, 1, 0],
        ...                        [1, 0, 0, 1, 0],
        ...                        [1, 0, 0, 1, 0],
        ...                        [1, 0, 0, 1, 0],
        ...                        [1, 1, 1, 1, 0]], dtype=torch.bool)
        >>> surface_distance(preds, target, distance_metric="euclidean", spacing=[1, 1])
        tensor([0., 0., 0., 0., 1., 0., 1., 0., 1., 0., 1., 0., 0., 0., 0., 1.])

    z9Expected both inputs to be of type `torch.bool`, but got r   r   )r{   r|   )rC   r)   r=   r(   r   inf	ones_liker   )r   r   r   r   r   s        r   surface_distancer     s    J KK5::%&,,%***DTUZU`U`Taafgmgsgsfttuvww99Vii%//&11 u:	 yy))eooe44Cv; &7?Su:r   	symmetricc                     t        | |      }|d   j                         |d   j                         }}|rt        ||||      t        ||||      fS t        ||||      S )a5  Extracts the edges from the input masks and calculates the surface distance between them.

    Args:
        preds: The predicted binary edge mask.
        target: The target binary edge mask.
        distance_metric: The distance metric to use. One of `["euclidean", "chessboard", "taxicab"]`.
        spacing: The spacing between pixels along each spatial dimension.
        symmetric: Whether to calculate the symmetric distance between the edges.

    Returns:
        A tensor with length equal to the number of edges in predictions e.g. `preds.sum()`. Each element is the
        distance from the corresponding edge in `preds` to the closest edge in `target`. If `symmetric` is `True`, the
        function returns a tuple containing the distances from the predicted edges to the target edges and vice versa.

    r   r   )r   r   )r   r=   r   )r   r   r   r   r   r]   r   r   s           r   edge_surface_distancer     sn    , v&F &q	 0&)..2BK[,ahi\;ahi
 	
 K`ghhr   rB   c                     t        | t              rt        |       dk(  rt        | |      S t        | t              rt        |       dk(  rt	        | |      S t        d      )a  Create a table that maps neighbour codes to the contour length or surface area of the corresponding contour.

    Args:
        spacing: The spacing between pixels along each spatial dimension.
        device: The device on which the table should be created.

    Returns:
        A tuple containing as its first element the table that maps neighbour codes to the contour length or surface
        area of the corresponding contour and as its second element the kernel used to compute the neighbour codes.

    r   r'   z-The spacing must be a tuple of length 2 or 3.)ri   tuplern   table_contour_lengthtable_surface_arear(   )r   rB   s     r   r   r     sT     '5!c'la&7#GV44'5!c'la&7!'622
D
EEr   c                    t        | t              st        |       dk7  rt        d      | \  }}dt	        j
                  |dz  |dz  z         z  }t        j                  dt        j                  |      }dD ]  }|||<   	 dD ]  }|||<   	 dD ]  }|||<   	 d	D ]
  }d|z  ||<    t        j                  d
dgddgggg|      }||fS )a  Create a table that maps neighbour codes to the contour length of the corresponding contour.

    Adopted from:
    https://github.com/deepmind/surface-distance/blob/master/surface_distance/lookup_tables.py

    Args:
        spacing: The spacing between pixels along each spatial dimension. Should be a tuple of length 2.
        device: The device on which the table should be created.

    Returns:
        A tuple containing as its first element the table that maps neighbour codes to the contour length of the
        corresponding contour and as its second element the kernel used to compute the neighbour codes.

    Example::
        >>> from torchmetrics.functional.segmentation.utils import table_contour_length
        >>> table, kernel = table_contour_length((2,2))
        >>> table
        tensor([0.0000, 1.4142, 1.4142, 2.0000, 1.4142, 2.0000, 2.8284, 1.4142, 1.4142,
                2.8284, 2.0000, 1.4142, 2.0000, 1.4142, 1.4142, 0.0000])
        >>> kernel
        tensor([[[[8, 4],
                  [2, 1]]]])

    r   z(The spacing must be a tuple of length 2.      ?   rC   rB   )r   r   rd                  )r'      )re   
   )   	   r   rd   r   r   )
ri   r   rn   r(   mathr   r)   zerosfloat32	as_tensor)r   rB   firstseconddiagr   rt   r   s           r   r   r     s    2 gu%#g,!*;CDDME65!8fai/00DKK%--?E(a )a a t8a __AA/01&AF&=r   c                 \   t        | t              st        |       dk7  rt        d      g d}t	        j
                  g ||||gg d|||gg d|||gg dg d||gg d|||gg d	g d
||gg dg d||gg dg dg d|gg d|||gg dg d||gg dg d||gg dg dg d|gg dg d||gg dg dg d|gg dg dg d|gg dg d||gg d|||gg dg d||gg dg d||gg dg dg d|gg dg d||gg dg dg d|gg dg dg d|gg dg dg dg dgg dg d||gg dg dg d|gg dg dg d|gg dg dg dg dgg dg dg d|gg dg dg dg d gg dg dg dg dgg dg dg d|gg d|||gg dg d||gg dg d||gg dg d!g d"|gg dg d||gg dg d	g d
|gg dg dg d|gg d#g d$g d!g d	gg dg d||gg dg dg d|gg d%g dg d|gg d"g d"g d!g dgg dg dg d|gg dg dg dg dgg dg d&g dg d'gg dg dg d|gg dg d ||gg d(g dg d|gg d(g dg d)|gg d(g d||gg dg d g d|gg d*g dg dg d gg dg dg dg dgg dg dg d|gg dg dg d |gg d(g d"g dg dgg d+g dg dg dgg d(g d"g d|gg dg d g dg dgg dg dg d|gg dg dg d|gg dg d||gg d|||gg dg d||gg dg d||gg dg dg d|gg dg d||gg d,g dg d|gg dg dg d|gg d-g dg dg d'gg dg d||gg dg dg d|gg dg dg d|gg dg dg dg dgg d.g d)g d|gg d)g d)g dg dgg d/g d&g dg dgg dg d)g d|gg dg d0||gg d%g dg d|gg dg dg d0|gg d	g d1g dg dgg dg d"g d|gg d%g d,||gg dg dg dg d,gg dg dg d,|gg dg d0g d|gg d%g dg dg dgg dg dg dg d0gg dg d
g d
|gg d
g d#g d g dgg d%g dg d|gg dg d
g d
|gg d
g d
||gg dg d||gg dg dg d|gg dg dg d|gg dg d!g d"g dgg dg dg d|gg d,g dg dg dgg dg dg dg dgg dg dg d|gg dg dg d|gg dg dg dg dgg d%g dg dg dgg dg dg d|gg d.g d)g dg dgg dg dg d|gg dg dg d|gg dg d||gg dg d2g d|gg dg dg dg dgg d-g dg dg dgg dg dg d|gg d#g dg dg dgg dg dg d,|gg dg dg d|gg dg d||gg dg dg dg dgg dg dg d|gg d	g d
g d|gg dg d||gg dg dg d|gg dg d||gg dg d||gg d|||gg d|||gg dg d||gg dg d||gg dg dg d|gg dg d||gg d	g d
g d|gg dg dg d|gg dg dg dg dgg dg d||gg dg dg d|gg dg dg d,|gg d#g dg dg dgg dg dg d|gg d-g dg dg dgg dg dg dg dgg dg d2g d|gg dg d||gg dg dg d|gg dg dg d|gg d.g d)g dg dgg dg dg d|gg d%g dg dg dgg dg dg dg dgg dg dg d|gg dg dg d|gg dg dg dg dgg d,g dg dg dgg dg dg d|gg dg d!g d"g dgg dg dg d|gg dg dg d|gg dg d||gg d
g d
||gg dg d
g d
|gg d%g dg d|gg d
g d#g d g dgg dg d
g d
|gg d	g d
g d
g d
gg d%g dg dg dgg dg d0g d|gg dg dg d,|gg dg dg dg d,gg d%g d,||gg dg d"g d|gg d	g d1g dg dgg dg dg d0|gg d%g dg d|gg dg d0||gg dg d)g d|gg d/g d&g dg dgg d)g d)g dg dgg d.g d)g d|gg dg dg dg dgg dg dg d|gg dg dg d|gg dg d||gg d-g dg dg d'gg dg dg d|gg d,g dg d|gg dg d||gg dg dg d|gg dg d||gg dg d||gg d|||gg dg d||gg dg dg d|gg dg dg d|gg dg dg dg dgg d(g d"g d|gg d+g dg dg dgg d(g d"g dg dgg dg dg d |gg dg dg d|gg dg dg dg dgg d*g dg dg d gg dg d g d|gg d(g d||gg d(g dg d)|gg d(g dg d|gg dg d ||gg dg dg d|gg dg d&g dg d'gg dg dg dg dgg dg dg d|gg d"g d"g d!g dgg d%g dg d|gg dg dg d|gg dg d||gg d#g d$g d!g d	gg dg dg d|gg dg d	g d
|gg dg d||gg dg d!g d"|gg dg d||gg dg d||gg d|||gg dg dg d|gg dg dg dg dgg dg dg dg d gg dg dg d|gg dg dg dg dgg dg dg d|gg dg dg d|gg dg d||gg dg dg dg dgg dg dg d|gg dg dg d|gg dg d||gg dg dg d|gg dg d||gg dg d||gg d|||gg dg d||gg dg dg d|gg dg dg d|gg dg d||gg dg dg d|gg dg d||gg dg d||gg d|||gg dg dg d|gg dg d||gg d	g d
||gg d|||gg dg d||gg d|||gg d|||g||||gt        j                  |3      }t	        j                  | d4   | d5   z  | d6   | d5   z  | d6   | d4   z  ggg||j                  7      }t        j                  j                  ||z  d89      }|j                  d8      }t	        j                  d:d;gd<d=ggd>d?gd5d4ggggg|@      }||fS )Aa  Create a table that maps neighbour codes to the surface area of the corresponding surface.

    Adopted from:
    https://github.com/deepmind/surface-distance/blob/master/surface_distance/lookup_tables.py

    Args:
        spacing: The spacing between pixels along each spatial dimension. Should be a tuple of length 3.
        device: The device on which the table should be created.

    Returns:
        A tuple containing as its first element the table that maps neighbour codes to the surface area of the
        corresponding surface and as its second element the kernel used to compute the neighbour codes.

    Example::
        >>> from torchmetrics.functional.segmentation.utils import table_surface_area
        >>> table, kernel = table_surface_area((2,2,2))
        >>> table
        tensor([0.0000, 0.8660, 0.8660, 2.8284, 0.8660, 2.8284, 1.7321, 4.5981, 0.8660,
                1.7321, 2.8284, 4.5981, 2.8284, 4.5981, 4.5981, 4.0000, 0.8660, 2.8284,
                1.7321, 4.5981, 1.7321, 4.5981, 2.5981, 5.1962, 1.7321, 3.6945, 3.6945,
                6.2925, 3.6945, 6.2925, 5.4641, 4.5981, 0.8660, 1.7321, 2.8284, 4.5981,
                1.7321, 3.6945, 3.6945, 6.2925, 1.7321, 2.5981, 4.5981, 5.1962, 3.6945,
                5.4641, 6.2925, 4.5981, 2.8284, 4.5981, 4.5981, 4.0000, 3.6945, 6.2925,
                5.4641, 4.5981, 3.6945, 5.4641, 6.2925, 4.5981, 5.6569, 3.6945, 3.6945,
                2.8284, 0.8660, 1.7321, 1.7321, 3.6945, 2.8284, 4.5981, 3.6945, 6.2925,
                1.7321, 2.5981, 3.6945, 5.4641, 4.5981, 5.1962, 6.2925, 4.5981, 2.8284,
                4.5981, 3.6945, 6.2925, 4.5981, 4.0000, 5.4641, 4.5981, 3.6945, 5.4641,
                5.6569, 3.6945, 6.2925, 4.5981, 3.6945, 2.8284, 1.7321, 2.5981, 3.6945,
                5.4641, 3.6945, 5.4641, 5.6569, 3.6945, 2.5981, 3.4641, 5.4641, 2.5981,
                5.4641, 2.5981, 3.6945, 1.7321, 4.5981, 5.1962, 6.2925, 4.5981, 6.2925,
                4.5981, 3.6945, 2.8284, 5.4641, 2.5981, 3.6945, 1.7321, 3.6945, 1.7321,
                1.7321, 0.8660, 0.8660, 1.7321, 1.7321, 3.6945, 1.7321, 3.6945, 2.5981,
                5.4641, 2.8284, 3.6945, 4.5981, 6.2925, 4.5981, 6.2925, 5.1962, 4.5981,
                1.7321, 3.6945, 2.5981, 5.4641, 2.5981, 5.4641, 3.4641, 2.5981, 3.6945,
                5.6569, 5.4641, 3.6945, 5.4641, 3.6945, 2.5981, 1.7321, 2.8284, 3.6945,
                4.5981, 6.2925, 3.6945, 5.6569, 5.4641, 3.6945, 4.5981, 5.4641, 4.0000,
                4.5981, 6.2925, 3.6945, 4.5981, 2.8284, 4.5981, 6.2925, 5.1962, 4.5981,
                5.4641, 3.6945, 2.5981, 1.7321, 6.2925, 3.6945, 4.5981, 2.8284, 3.6945,
                1.7321, 1.7321, 0.8660, 2.8284, 3.6945, 3.6945, 5.6569, 4.5981, 6.2925,
                5.4641, 3.6945, 4.5981, 5.4641, 6.2925, 3.6945, 4.0000, 4.5981, 4.5981,
                2.8284, 4.5981, 6.2925, 5.4641, 3.6945, 5.1962, 4.5981, 2.5981, 1.7321,
                6.2925, 3.6945, 3.6945, 1.7321, 4.5981, 2.8284, 1.7321, 0.8660, 4.5981,
                5.4641, 6.2925, 3.6945, 6.2925, 3.6945, 3.6945, 1.7321, 5.1962, 2.5981,
                4.5981, 1.7321, 4.5981, 1.7321, 2.8284, 0.8660, 4.0000, 4.5981, 4.5981,
                2.8284, 4.5981, 2.8284, 1.7321, 0.8660, 4.5981, 1.7321, 2.8284, 0.8660,
                2.8284, 0.8660, 0.8660, 0.0000])
        >>> kernel
        tensor([[[[[128,  64],
                   [ 32,  16]],
                  [[  8,   4],
                   [  2,   1]]]]])

    r'   z(The spacing must be a tuple of length 3.)        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   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   )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   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   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   r   )r   r   r   )r   r   r   )r   r   r   r   r   r   r   rA   r&   r4      @       r   r   rd   r   )ri   r   rn   r(   r)   r2   r   r   rC   linalgnormr[   )r   rB   r   r   spacer   r   s          r   r   r   :  s   n gu%#g,!*;CDDELLA	
E5%(A	
"E5%8A	
 %eUE:A	
 !"4eUC	A	

 $UE59A	
 "#4eUCA	
 $%<eUKA	
 13H%PA	
 $UE59A	
 #$:E5IA	
  !3UEBA	
 24KUSA	
  !3UEBA	
 13JERA	
 24JERA	
  oue<!A	
" %eUE:#A	
$ !"3UEB%A	
& %&=ueL'A	
( 13H%P)A	
* $%<eUK+A	
, 13KUS-A	
. %&<>UW\]/A	
0 &'<>PRgh1A	
2 $%<eUK3A	
4 !"35KUS5A	
6  !35LeT7A	
8 #$9;MOab9A	
: %&8:LeT;A	
< #$68PRde=A	
> 79NPef?A	
@ 79NPUVAA	
B $UE59CA	
D #$:E5IEA	
F  !3UEBGA	
H 57JERIA	
J $%;UEJKA	
L $%9;LeTMA	
N  !35KUSOA	
P %&8:PRfgQA	
R $%;UEJSA	
T #$:<RTYZUA	
V 35LeTWA	
X !"57MOfgYA	
Z $%79KUS[A	
\ 13JLbc]A	
^  !8:PRcd_A	
` 35LeTaA	
b  !3UEBcA	
d 13JEReA	
f 46JERgA	
h .u=iA	
j  !35KUSkA	
l &'9;SUghmA	
n #$46KMefoA	
p 46NPUVqA	
r $%79KUSsA	
t 13JLcduA	
v %&9;RTfgwA	
x 13JERyA	
z  !35GI[\{A	
| !"57NPUV}A	
~ #$79LeTA	
@ !"5ueDAA	
B %eUE:CA	
D #$;UEJEA	
F %&=ueLGA	
H %&9;MuUIA	
J  !3UEBKA	
L 13I5QMA	
N  !35LeTOA	
P $%8:QSdeQA	
R %&<eUKSA	
T #$;=SUZ[UA	
V %&8:LeTWA	
X 24KMdeYA	
Z 35KUS[A	
\ "#79PRij]A	
^ !"9;MOef_A	
` 24JERaA	
b  !3UEBcA	
d 24JEReA	
f %&8:LeTgA	
h "#9;NPfgiA	
j 24KUSkA	
l u=mA	
n #$9;M_oA	
p #$6OqA	
r  !35KUSsA	
t 24JLbcuA	
v  !35GI[\wA	
x $%68I5QyA	
z  79KMde{A	
| 24JER}A	
~ #$57H%PA	
@  15%@AA	
B %&<eUKCA	
D #$;=SUZ[EA	
F %&8:LeTGA	
H 57JLcdIA	
J  !35KUSKA	
L 13IKabMA	
N  !35GI[\OA	
P  13JERQA	
R $%;=TV[\SA	
T $%;=TVklUA	
V 35LNefWA	
X #$:<SUZ[YA	
Z 35KMcd[A	
\ #$;=TV[\]A	
^ !"35JER_A	
` #$;UEJaA	
b 35LeTcA	
d !"8:MOfgeA	
f $%68OQcdgA	
h 24JERiA	
j %&8:KMdekA	
l $%8/5QmA	
n #$57H%PoA	
p  15%@qA	
r 13HJ_`sA	
t $%<>SUZ[uA	
v "#46KUSwA	
x #$:E5IyA	
z !"46KUS{A	
| #$;UEJ}A	
~ #$95%HA	
@ #E5%8AA	
B #E5%8CA	
D #$95%HEA	
F #$;UEJGA	
H !"46KUSIA	
J #$:E5IKA	
L "#46KUSMA	
N $%<>SUZ[OA	
P 13HJ_`QA	
R  15%@SA	
T #$57H%PUA	
V $%8/5QWA	
X %&8:KMdeYA	
Z 24JER[A	
\ $%68OQcd]A	
^ !"8:MOfg_A	
` 35LeTaA	
b #$;UEJcA	
d !"35JEReA	
f #$;=TV[\gA	
h 35KMcdiA	
j #$:<SUZ[kA	
l 35LNefmA	
n $%;=TVkloA	
p $%;=TV[\qA	
r  13JERsA	
t !"35FHYZuA	
v 13IKabwA	
x  !35KUSyA	
z 57JLcd{A	
| %&8:LeT}A	
~ #$;=SUZ[A	
@ %&<eUKAA	
B  15%@CA	
D #$57H%PEA	
F 24JERGA	
H  79KMdeIA	
J $%68I5QKA	
L "#46GIZ[MA	
N 24JLbcOA	
P  !35KUSQA	
R #$6OSA	
T #$9;M_UA	
V u=WA	
X 24KUSYA	
Z "#9;NPfg[A	
\ %&8:LeT]A	
^ 24JER_A	
`  !3UEBaA	
b 24JERcA	
d !"9;MOefeA	
f "#79PRijgA	
h 35KUSiA	
j 24KMdekA	
l %&8:LeTmA	
n #$;=SUZ[oA	
p %&<eUKqA	
r $%8:QSdesA	
t  !35LeTuA	
v 13I5QwA	
x  !3UEByA	
z %&9;MuU{A	
| %&=ueL}A	
~ #$;UEJA	
@ %eUE:AA	
B !"5ueDCA	
D #$79LeTEA	
F !"57NPUVGA	
H !"57JL^_IA	
J 13JERKA	
L %&9;RTfgMA	
N 13JLcdOA	
P $%79KUSQA	
R 46NPUVSA	
T #$46KMefUA	
V &'9;SUghWA	
X  !35KUSYA	
Z .u=[A	
\ 46JER]A	
^ 13JER_A	
`  !3UEBaA	
b 35LeTcA	
d  !8:PRcdeA	
f 13JLbcgA	
h $%79KUSiA	
j !"57MOfgkA	
l 35LeTmA	
n #$:<RTYZoA	
p $%;UEJqA	
r %&8:PRfgsA	
t  !35KUSuA	
v $%9;LeTwA	
x $%;UEJyA	
z 57JER{A	
|  !3UEB}A	
~ #$:E5IA	
@ $UE59AA	
B 79NPUVCA	
D 79NPefEA	
F #$68PRdeGA	
H %&8:LeTIA	
J #$9;MOabKA	
L  !35LeTMA	
N !"35KUSOA	
P $%<eUKQA	
R &'<>PRghSA	
T %&<>UW\]UA	
V 13KUSWA	
X $%<eUKYA	
Z 13H%P[A	
\ %&=ueL]A	
^ !"3UEB_A	
` %eUE:aA	
b oue<cA	
d 24JEReA	
f 13JERgA	
h  !3UEBiA	
j 24KUSkA	
l  !3UEBmA	
n #$:E5IoA	
p $UE59qA	
r 13H%PsA	
t $%<eUKuA	
v "#4eUCwA	
x #E5%8yA	
z !"4eUC{A	
| #E5%8}A	
~ #E5%8A	
@ E5%(AA	
D mmIEEN OO1:
"GAJ$;WQZ'RS*=T
U	VWkkE
 <<UU]3DHHRLE__#rRH 5AA7GHIJSYZF&=r   )Nr!   )NNr   )Nr~   r   )TN)r~   N)r~   NF)N))	functoolsr   typingr   r   r)   r   torch.nn.functionalr   r   r   r	   typing_extensionsr
   torchmetrics.utilities.checksr   torchmetrics.utilities.importsr   r   r   r   r=   rl   r1   r/   r.   r>   rO   r^   rz   r   ro   r   r   r   r   	lru_cacherB   r   r   r    r   r   <module>r      sE     "   ; ; % ; ;f f vv~9N 
f 
f 
 
, "&9B))) ) #	)
 56) 66>)X6   V  
8& 
8T 
8Jv JE#s(O J J0("C ("s ("v ("X vwC;C;&v.C;?GcSVh?XC;orC;C;P 6:<G*3	aTaTuVT%[012aT 89aT &'	aT
 aTN FJ	7L7L7L 7L eE#s(OU3S=-AABC	7L
 5 %(F"GGH7Lz FQ48	/// AB/ eFDK/01	/
 /j FQ48iii ABi eFDK/01	i
 i 65(()i@ \`F5c?E#sC-$889FCKELLCYF
66>F F*(%S/ (8ELL;Q (]bcikqcq]r (V Ic3m 4 Ihu||>T I`eflntft`u I Ir   