def per_image_tpr(binclf_curves: torch.Tensor) -> torch.Tensor:
    """Compute True Positive Rate (TPR) for each image at each threshold.

    TPR = TP / P = TP / (TP + FN)

    Where:
        - TP: True Positives
        - FN: False Negatives
        - P: Total Positives (TP + FN)

    Args:
        binclf_curves: Binary classification curves of shape ``(N, K, 2, 2)``
            See :func:`binary_classification_curve`

    Returns:
        TPR values of shape ``(N, K)`` and dtype ``float64`` where:
            - N: number of images
            - K: number of thresholds

        TPR is in descending order since thresholds are sorted ascending.
        TPR will be NaN for normal images (P = 0).

    Example:
        >>> curves = torch.randint(0, 10, (5, 10, 2, 2))  # 5 imgs, 10 thresh
        >>> tpr = per_image_tpr(curves)
        >>> tpr.shape
        torch.Size([5, 10])
    """
    # shape: (num images, num thresholds)
    tps = binclf_curves[..., 1, 1]
    pos = binclf_curves[..., 1, :].sum(dim=2)

    # tprs will be nan if pos == 0 (normal image), which is expected
    return tps.to(torch.float64) / pos.to(torch.float64)
Compute True Positive Rate (TPR) for each image at each threshold.

    TPR = TP / P = TP / (TP + FN)

    Where:
        - TP: True Positives
        - FN: False Negatives
        - P: Total Positives (TP + FN)

    Args:
        binclf_curves: Binary classification curves of shape ``(N, K, 2, 2)``
            See :func:`binary_classification_curve`

    Returns:
        TPR values of shape ``(N, K)`` and dtype ``float64`` where:
            - N: number of images
            - K: number of thresholds

        TPR is in descending order since thresholds are sorted ascending.
        TPR will be NaN for normal images (P = 0).

    Example:
        >>> curves = torch.randint(0, 10, (5, 10, 2, 2))  # 5 imgs, 10 thresh
        >>> tpr = per_image_tpr(curves)
        >>> tpr.shape
        torch.Size([5, 10])
    
src/anomalib/metrics/pimo/binary_classification_curve.py