def masks_to_boxes(
    masks: torch.Tensor,
    anomaly_maps: torch.Tensor | None = None,
) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
    """Convert batch of segmentation masks to bounding box coordinates.

    Args:
        masks: Input tensor of masks. Can be one of:
            - shape ``(B, 1, H, W)``
            - shape ``(B, H, W)``
            - shape ``(H, W)``
        anomaly_maps: Optional anomaly maps. Can be one of:
            - shape ``(B, 1, H, W)``
            - shape ``(B, H, W)``
            - shape ``(H, W)``
            Used to determine anomaly scores for converted bounding boxes.

    Returns:
        Tuple containing:
            - List of length ``B`` where each element is tensor of shape ``(N, 4)``
              containing bounding box coordinates in ``xyxy`` format
            - List of length ``B`` where each element is tensor of length ``N``
              containing anomaly scores for each converted box

    Examples:
        >>> import torch
        >>> masks = torch.zeros((2, 1, 32, 32))
        >>> masks[0, 0, 10:20, 15:25] = 1  # Add box in first image
        >>> boxes, scores = masks_to_boxes(masks)
        >>> boxes[0]  # Coordinates for first image
        tensor([[15., 10., 24., 19.]])
    """
    height, width = masks.shape[-2:]
    # reshape to (B, 1, H, W) and cast to float
    masks = masks.view((-1, 1, height, width)).float()
    if anomaly_maps is not None:
        anomaly_maps = anomaly_maps.view((-1,) + masks.shape[-2:])

    if masks.is_cpu:
        batch_comps = connected_components_cpu(masks).squeeze(1)
    else:
        batch_comps = connected_components_gpu(masks).squeeze(1)

    batch_boxes = []
    batch_scores = []
    for im_idx, im_comps in enumerate(batch_comps):
        labels = torch.unique(im_comps)
        im_boxes = []
        im_scores = []
        for label in labels[labels != 0]:
            y_loc, x_loc = torch.where(im_comps == label)
            # add box
            box = torch.Tensor([torch.min(x_loc), torch.min(y_loc), torch.max(x_loc), torch.max(y_loc)]).to(
                masks.device,
            )
            im_boxes.append(box)
            if anomaly_maps is not None:
                im_scores.append(torch.max(anomaly_maps[im_idx, y_loc, x_loc]))
        batch_boxes.append(torch.stack(im_boxes) if im_boxes else torch.empty((0, 4), device=masks.device))
        batch_scores.append(torch.stack(im_scores) if im_scores else torch.empty(0, device=masks.device))

    return batch_boxes, batch_scores
Convert batch of segmentation masks to bounding box coordinates.

    Args:
        masks: Input tensor of masks. Can be one of:
            - shape ``(B, 1, H, W)``
            - shape ``(B, H, W)``
            - shape ``(H, W)``
        anomaly_maps: Optional anomaly maps. Can be one of:
            - shape ``(B, 1, H, W)``
            - shape ``(B, H, W)``
            - shape ``(H, W)``
            Used to determine anomaly scores for converted bounding boxes.

    Returns:
        Tuple containing:
            - List of length ``B`` where each element is tensor of shape ``(N, 4)``
              containing bounding box coordinates in ``xyxy`` format
            - List of length ``B`` where each element is tensor of length ``N``
              containing anomaly scores for each converted box

    Examples:
        >>> import torch
        >>> masks = torch.zeros((2, 1, 32, 32))
        >>> masks[0, 0, 10:20, 15:25] = 1  # Add box in first image
        >>> boxes, scores = masks_to_boxes(masks)
        >>> boxes[0]  # Coordinates for first image
        tensor([[15., 10., 24., 19.]])
    
src/anomalib/data/utils/boxes.py