o  i_@sdZddlmZddlmZddlmZddlZddl Z ddl m Z m Z m Z ddlmZmZmZmZmZmZmZddlmZmZdd lmZdd lmZmZmZmZdd l m!Z!d d l"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*gdZ+GdddeZ,GdddeZ-GdddeZ.GdddeZ/GdddeZ0GdddeZ1GdddeZ2GdddeZ3Gdd d eZ4Gd!d"d"eZ5Gd#d$d$eZ6Gd%d&d&eZ7dS)'zr A collection of "vanilla" transforms for box operations https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design ) annotations)Sequence)AnyN) DtypeLikeNdarrayOrTensor NdarrayTensor)BoxModeclip_boxes_to_imageconvert_box_modeconvert_box_to_standard_modeget_spatial_dimsspatial_crop_boxesstandardize_empty_box)Rotate90 SpatialCrop) Transform) ensure_tupleensure_tuple_repfall_back_tuplelook_up_option)TransformBackends)apply_affine_to_boxesconvert_box_to_maskconvert_mask_to_box flip_boxes resize_boxes rot90_boxes select_labels zoom_boxes) StandardizeEmptyBoxConvertBoxToStandardModeConvertBoxMode AffineBoxZoomBox ResizeBoxFlipBoxClipBoxToImage BoxToMask MaskToBoxSpatialCropBox RotateBox90c@s0eZdZdZejejgZd ddZdd d Z d S)r z When boxes are empty, this transform standardize it to shape of (0,4) or (0,6). Args: spatial_dims: number of spatial dimensions of the bounding boxes. spatial_dimsintreturnNonecC ||_dSNr,)selfr,r4g/home/dell461/cl/sdc2/last_ska_mid/HISourceFinder-master-l/src/monai/apps/detection/transforms/array.py__init__L zStandardizeEmptyBox.__init__boxesrcCt||jdS)zl Args: boxes: source bounding boxes, Nx4 or Nx6 or 0xM torch tensor or ndarray. r2)rr,r3r8r4r4r5__call__OszStandardizeEmptyBox.__call__N)r,r-r.r/r8rr.r __name__ __module__ __qualname____doc__rTORCHNUMPYbackendr6r;r4r4r4r5r Bs   r c@s6eZdZdZejejgZ  dddd Zdd d Z dS)r"a This transform converts the boxes in src_mode to the dst_mode. Args: src_mode: source box mode. If it is not given, this func will assume it is ``StandardMode()``. dst_mode: target box mode. If it is not given, this func will assume it is ``StandardMode()``. Note: ``StandardMode`` = :class:`~monai.data.box_utils.CornerCornerModeTypeA`, also represented as "xyxy" for 2D and "xyzxyz" for 3D. src_mode and dst_mode can be: #. str: choose from :class:`~monai.utils.enums.BoxModeName`, for example, - "xyxy": boxes has format [xmin, ymin, xmax, ymax] - "xyzxyz": boxes has format [xmin, ymin, zmin, xmax, ymax, zmax] - "xxyy": boxes has format [xmin, xmax, ymin, ymax] - "xxyyzz": boxes has format [xmin, xmax, ymin, ymax, zmin, zmax] - "xyxyzz": boxes has format [xmin, ymin, xmax, ymax, zmin, zmax] - "xywh": boxes has format [xmin, ymin, xsize, ysize] - "xyzwhd": boxes has format [xmin, ymin, zmin, xsize, ysize, zsize] - "ccwh": boxes has format [xcenter, ycenter, xsize, ysize] - "cccwhd": boxes has format [xcenter, ycenter, zcenter, xsize, ysize, zsize] #. BoxMode class: choose from the subclasses of :class:`~monai.data.box_utils.BoxMode`, for example, - CornerCornerModeTypeA: equivalent to "xyxy" or "xyzxyz" - CornerCornerModeTypeB: equivalent to "xxyy" or "xxyyzz" - CornerCornerModeTypeC: equivalent to "xyxy" or "xyxyzz" - CornerSizeMode: equivalent to "xywh" or "xyzwhd" - CenterSizeMode: equivalent to "ccwh" or "cccwhd" #. BoxMode object: choose from the subclasses of :class:`~monai.data.box_utils.BoxMode`, for example, - CornerCornerModeTypeA(): equivalent to "xyxy" or "xyzxyz" - CornerCornerModeTypeB(): equivalent to "xxyy" or "xxyyzz" - CornerCornerModeTypeC(): equivalent to "xyxy" or "xyxyzz" - CornerSizeMode(): equivalent to "xywh" or "xyzwhd" - CenterSizeMode(): equivalent to "ccwh" or "cccwhd" #. None: will assume mode is ``StandardMode()`` Example: .. code-block:: python boxes = torch.ones(10,4) # convert boxes with format [xmin, ymin, xmax, ymax] to [xcenter, ycenter, xsize, ysize]. box_converter = ConvertBoxMode(src_mode="xyxy", dst_mode="ccwh") box_converter(boxes) Nsrc_mode$str | BoxMode | type[BoxMode] | Nonedst_moder.r/cC||_||_dSr1rErG)r3rErGr4r4r5r6s zConvertBoxMode.__init__r8rcCst||j|jdS)aS Converts the boxes in src_mode to the dst_mode. Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` Returns: bounding boxes with target mode, with same data type as ``boxes``, does not share memory with ``boxes`` rI)r rErGr:r4r4r5r;s zConvertBoxMode.__call__)NN)rErFrGrFr.r/r<r=r4r4r4r5r"Ws - r"c@s2eZdZdZejejgZd dddZdd d Z dS)r!a Convert given boxes to standard mode. Standard mode is "xyxy" or "xyzxyz", representing box format of [xmin, ymin, xmax, ymax] or [xmin, ymin, zmin, xmax, ymax, zmax]. Args: mode: source box mode. If it is not given, this func will assume it is ``StandardMode()``. It follows the same format with ``src_mode`` in :class:`~monai.apps.detection.transforms.array.ConvertBoxMode` . Example: .. code-block:: python boxes = torch.ones(10,6) # convert boxes with format [xmin, xmax, ymin, ymax, zmin, zmax] to [xmin, ymin, zmin, xmax, ymax, zmax] box_converter = ConvertBoxToStandardMode(mode="xxyyzz") box_converter(boxes) NmoderFr.r/cCr0r1rJ)r3rJr4r4r5r6r7z!ConvertBoxToStandardMode.__init__r8rcCr9)a Convert given boxes to standard mode. Standard mode is "xyxy" or "xyzxyz", representing box format of [xmin, ymin, xmax, ymax] or [xmin, ymin, zmin, xmax, ymax, zmax]. Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` Returns: bounding boxes with standard mode, with same data type as ``boxes``, does not share memory with ``boxes`` rK)r rJr:r4r4r5r;s z!ConvertBoxToStandardMode.__call__r1)rJrFr.r/r<r=r4r4r4r5r!s   r!c@&eZdZdZejejgZd ddZd S) r#z, Applies affine matrix to the boxes r8raffineNdarrayOrTensor | Noner.cCs|dur|St||dS)z Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` affine: affine matrix to be applied to the box coordinate N)rM)r)r3r8rMr4r4r5r;s zAffineBox.__call__N)r8rrMrNr.r r>r?r@rArrBrCrDr;r4r4r4r5r#s r#c@s4eZdZdZejejgZddd d ZddddZ d S)r$a Zooms an ND Box with same padding or slicing setting with Zoom(). Args: zoom: The zoom factor along the spatial axes. If a float, zoom is the same for each spatial axis. If a sequence, zoom should contain one value for each spatial axis. keep_size: Should keep original size (padding/slicing if needed), default is True. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. FzoomSequence[float] | float keep_sizeboolkwargsrr.r/cK||_||_||_dSr1)rPrRrT)r3rPrRrTr4r4r5r6s zZoomBox.__init__Nr8rsrc_spatial_sizeSequence[int] | int | Nonec Csnt|d}t|j||_|jst||jS|durtdt||}ddt|j|D}tddt||D|_t||j}t t |t |st t||D]a\}\}}||} t | d} | d kr|dd|f| |dd|f<|dd||f| |dd||f<qS| d kr|dd|f| |dd|f<|dd||f| |dd||f<qS|S) z Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` src_spatial_size: original image spatial size before zooming, used only when keep_size=True. r8Nz2keep_size=True, src_spatial_size must be provided.cSs g|] \}}tt||qSr4r-round).0zssr4r4r5 s z$ZoomBox.__call__..css |] \}}|t|VqdSr1)float)r[r]dsr4r4r5 z#ZoomBox.__call__..r)r rrP_zoomrRr ValueErrorziptuplenpallclosearray enumerateabs) r3r8rVr,Zdst_spatial_sizeZ zoomed_boxesaxisodZzddiffhalfr4r4r5r;s,      * (zZoomBox.__call__F)rPrQrRrSrTrr.r/r1)r8rrVrWr.rr=r4r4r4r5r$s  r$c@s2eZdZdZejejgZddd d ZdddZ dS)r%a Resize the input boxes when the corresponding image is resized to given spatial size (with scaling, not cropping/padding). Args: spatial_size: expected shape of spatial dimensions after resize operation. if some components of the `spatial_size` are non-positive values, the transform will use the corresponding components of img size. For example, `spatial_size=(32, -1)` will be adapted to `(32, 64)` if the second spatial dimension size of img is `64`. size_mode: should be "all" or "longest", if "all", will use `spatial_size` for all the spatial dims, if "longest", rescale the image so that only the longest side is equal to specified `spatial_size`, which must be an int number in this case, keeping the aspect ratio of the initial image, refer to: https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ #albumentations.augmentations.geometric.resize.LongestMaxSize. kwargs: other arguments for the `np.pad` or `torch.pad` function. note that `np.pad` treats channel dimension as the first dimension. all spatial_sizeSequence[int] | int size_modestrrTrr.r/cKst|ddg|_||_dS)Nrrlongest)rrurs)r3rsrurTr4r4r5r6!s zResizeBox.__init__r8rrVcst|d}t||}|jdkr,tt|j}||kr%td|d|dt|j|}nt|jt s6td|jt |t fdd|D}t |||S) aU Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` src_spatial_size: original image spatial size before resizing. Raises: ValueError: When ``self.spatial_size`` length is less than ``boxes`` spatial dimensions. rXrrzWlen(spatial_size) must be greater or equal to img spatial dimensions, got spatial_size=z img=.z=spatial_size must be an int number if size_mode is 'longest'.c3s |] }tt|VqdSr1rY)r[sscaler4r5ra>rbz%ResizeBox.__call__..) r rrulenrrsrer isinstancer-maxrgr)r3r8rV input_ndimZsrc_spatial_size_ output_ndimZ spatial_size_r4rzr5r;%s$    zResizeBox.__call__N)rr)rsrtrurvrTrr.r/)r8rrVrtr.rr=r4r4r4r5r% s   r%c@s2eZdZdZejejgZddddZdd dZ dS)r&a Reverses the box coordinates along the given spatial axis. Preserves shape. Args: spatial_axis: spatial axes along which to flip over. Default is None. The default `axis=None` will flip over all of the axes of the input array. If axis is negative it counts from the last to the first axis. If axis is a tuple of ints, flipping is performed on all of the axes specified in the tuple. N spatial_axisrWr.r/cCr0r1)r)r3rr4r4r5r6Rr7zFlipBox.__init__r8rrsrtcCst|||jdS)z Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` spatial_size: image spatial size. )rsZ flip_axes)rrr3r8rsr4r4r5r;UszFlipBox.__call__r1)rrWr.r/)r8rrsrtr=r4r4r4r5r&Cs  r&c@s2eZdZdZejejgZddddZdddZ dS)r'a0 Clip the bounding boxes and the associated labels/scores to make sure they are within the image. There might be multiple arrays of labels/scores associated with one array of boxes. Args: remove_empty: whether to remove the boxes and corresponding labels that are actually empty F remove_emptyrSr.r/cCr0r1)r)r3rr4r4r5r6jr7zClipBoxToImage.__init__r8rlabels+Sequence[NdarrayOrTensor] | NdarrayOrTensorrsrt/tuple[NdarrayOrTensor, tuple | NdarrayOrTensor]cCs4t|d}t||}t|||j\}}|t||fS)a Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` labels: Sequence of array. Each element represents classification labels or scores corresponding to ``boxes``, sized (N,). spatial_size: The spatial size of the image where the boxes are attached. len(spatial_size) should be in [2, 3]. Returns: - clipped boxes, does not share memory with original boxes - clipped labels, does not share memory with original labels Example: .. code-block:: python box_clipper = ClipBoxToImage(remove_empty=True) boxes = torch.ones(2, 6) class_labels = torch.Tensor([0, 1]) pred_scores = torch.Tensor([[0.4,0.3,0.3], [0.5,0.1,0.4]]) labels = (class_labels, pred_scores) spatial_size = [32, 32, 32] boxes_clip, labels_clip_tuple = box_clipper(boxes, labels, spatial_size) rX)r rr rr)r3r8rrsr,Z boxes_clipkeepr4r4r5r;ms  zClipBoxToImage.__call__Nrq)rrSr.r/)r8rrrrsrtr.rr=r4r4r4r5r'_s   r'c@s.eZdZdZejgZddd d ZdddZdS)r(a Convert box to int16 mask image, which has the same size with the input image. Args: bg_label: background labels for the output mask image, make sure it is smaller than any foreground(fg) labels. ellipse_mask: bool. - If True, it assumes the object shape is close to ellipse or ellipsoid. - If False, it assumes the object shape is close to rectangle or cube and well occupies the bounding box. - If the users are going to apply random rotation as data augmentation, we suggest setting ellipse_mask=True See also Kalra et al. "Towards Rotation Invariance in Object Detection", ICCV 2021. Fbg_labelr- ellipse_maskrSr.r/cCrHr1)rr)r3rrr4r4r5r6s zBoxToMask.__init__r8rrrsrtcCst||||j|jS)a Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode``. labels: classification foreground(fg) labels corresponding to `boxes`, dtype should be int, sized (N,). spatial_size: image spatial size. Return: - int16 array, sized (num_box, H, W). Each channel represents a box. The foreground region in channel c has intensity of labels[c]. The background intensity is bg_label. )rrr)r3r8rrsr4r4r5r;szBoxToMask.__call__N)rF)rr-rrSr.r/)r8rrrrsrtr.r) r>r?r@rArrCrDr6r;r4r4r4r5r(s  r(c@s8eZdZdZejgZdejej fdd d Z dddZ dS)r)a Convert int16 mask image to box, which has the same size with the input image. Pairs with :py:class:`monai.apps.detection.transforms.array.BoxToMask`. Please make sure the same ``min_fg_label`` is used when using the two transforms in pairs. Args: bg_label: background labels for the output mask image, make sure it is smaller than any foreground(fg) labels. box_dtype: output dtype for boxes label_dtype: output dtype for labels rrr- box_dtypeDtypeLike | torch.dtype label_dtyper.r/cCrUr1)rrr)r3rrrr4r4r5r6s zMaskToBox.__init__ boxes_maskr'tuple[NdarrayOrTensor, NdarrayOrTensor]cCst||j|j|jS)a Args: boxes_mask: int16 array, sized (num_box, H, W). Each channel represents a box. The foreground region in channel c has intensity of labels[c]. The background intensity is bg_label. Return: - bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode``. - classification foreground(fg) labels, dtype should be int, sized (N,). )rrrr)r3rr4r4r5r;s zMaskToBox.__call__N)rr-rrrrr.r/)rrr.r) r>r?r@rArrCrDtorchfloat32longr6r;r4r4r4r5r)s   r)csDeZdZdZejejgZ     ddfd d ZdddZ Z S)r*a General purpose box cropper when the corresponding image is cropped by SpatialCrop(*) with the same ROI. The difference is that we do not support negative indexing for roi_slices. If a dimension of the expected ROI size is bigger than the input image size, will not crop that dimension. So the cropped result may be smaller than the expected ROI, and the cropped results of several images may not have exactly the same shape. It can support to crop ND spatial boxes. The cropped region can be parameterised in various ways: - a list of slices for each spatial dimension (do not allow for use of negative indexing) - a spatial center and size - the start and end coordinates of the ROI Args: roi_center: voxel coordinates for center of the crop ROI. roi_size: size of the crop ROI, if a dimension of ROI size is bigger than image size, will not crop that dimension of the image. roi_start: voxel coordinates for start of the crop ROI. roi_end: voxel coordinates for end of the crop ROI, if a coordinate is out of image, use the end coordinate of image. roi_slices: list of slices for each of the spatial dimensions. N roi_center&Sequence[int] | NdarrayOrTensor | Noneroi_size roi_startroi_end roi_slicesSequence[slice] | Noner.r/csTt||||||jD]}|jdks#|jdks#|jdur'|jdkr'tdq dS)Nrz@Currently negative indexing is not supported for SpatialCropBox.)superr6slicesstartstopstepre)r3rrrrrry __class__r4r5r6s  (zSpatialCropBox.__init__r8rrr-tuple[NdarrayTensor, tuple | NdarrayOrTensor]csXttjt|d}t|fddt|Dfddt|D\}}|t||fS)aF Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` labels: Sequence of array. Each element represents classification labels or scores Returns: - cropped boxes, does not share memory with original boxes - cropped labels, does not share memory with original labels Example: .. code-block:: python box_cropper = SpatialCropPadBox(roi_start=[0, 1, 4], roi_end=[21, 15, 8]) boxes = torch.ones(2, 6) class_labels = torch.Tensor([0, 1]) pred_scores = torch.Tensor([[0.4,0.3,0.3], [0.5,0.1,0.4]]) labels = (class_labels, pred_scores) boxes_crop, labels_crop_tuple = box_cropper(boxes, labels) rXcg|]}j|jqSr4)rrr[rmr3r4r5r^z+SpatialCropBox.__call__..crr4)rrrrr4r5r^r)minr|rr r ranger)r3r8rr,Z boxes_croprr4rr5r;szSpatialCropBox.__call__)NNNNN) rrrrrrrrrrr.r/)r8rrrr.r) r>r?r@rArrBrCrDr6r; __classcell__r4r4rr5r*s  r*c@rL) r+a Rotate a boxes by 90 degrees in the plane specified by `axes`. See box_ops.rot90_boxes for additional details Args: k: number of times to rotate by 90 degrees. spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. If axis is negative it counts from the last to the first axis. r8rrsrtr.cCst|||j|jS)zm Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), )rk spatial_axesrr4r4r5r;0szRotateBox90.__call__N)r8rrsrtr.rrOr4r4r4r5r+"s r+)8rA __future__rcollections.abcrtypingrnumpyrhrmonai.config.type_definitionsrrrmonai.data.box_utilsrr r r r r rmonai.transformsrrmonai.transforms.transformr monai.utilsrrrrmonai.utils.enumsrbox_opsrrrrrrrr__all__r r"r!r#r$r%r&r'r(r)r*r+r4r4r4r5s4   $  ( E'671%&G