Skip to content

Register now for

# Reference for `ultralytics/utils/metrics.py`

Note

This file is available at https://github.com/ultralytics/ultralytics/blob/main/ultralytics/utils/metrics.py. If you spot a problem please help fix it by contributing a Pull Request 🛠️. Thank you 🙏!

## `` ultralytics.utils.metrics.ConfusionMatrix

    ConfusionMatrix(
        names: dict[int, str] = [], task: str = "detect", save_matches: bool = False
    )

Bases: `DataExportMixin`

A class for calculating and updating a confusion matrix for object detection and classification tasks.

Attributes:

Name | Type | Description
--- | --- | ---
`task` | `str` | The type of task, either 'detect' or 'classify'.
`matrix` | `ndarray` | The confusion matrix, with dimensions depending on the task.
`nc` | `int` | The number of category.
`names` | `list[str]` | The names of the classes, used as labels on the plot.
`matches` | `dict` | Contains the indices of ground truths and predictions categorized into TP, FP and FN.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`names` | `dict[int, str]` | Names of classes, used as labels on the plot. | `[]`
`task` | `str` | Type of task, either 'detect' or 'classify'. | `'detect'`
`save_matches` | `bool` | Save the indices of GTs, TPs, FPs, FNs for visualization. | `False`

Source code in `ultralytics/utils/metrics.py`

    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    def __init__(self, names: dict[int, str] = [], task: str = "detect", save_matches: bool = False):
        """
        Initialize a ConfusionMatrix instance.

        Args:
            names (dict[int, str], optional): Names of classes, used as labels on the plot.
            task (str, optional): Type of task, either 'detect' or 'classify'.
            save_matches (bool, optional): Save the indices of GTs, TPs, FPs, FNs for visualization.
        """
        self.task = task
        self.nc = len(names)  # number of classes
        self.matrix = np.zeros((self.nc, self.nc)) if self.task == "classify" else np.zeros((self.nc + 1, self.nc + 1))
        self.names = names  # name of classes
        self.matches = {} if save_matches else None

### `` matrix

    matrix()

Return the confusion matrix.

Source code in `ultralytics/utils/metrics.py`

    458
    459
    460
    def matrix(self):
        """Return the confusion matrix."""
        return self.matrix

### `` plot

    plot(normalize: bool = True, save_dir: str = '', on_plot=None)

Plot the confusion matrix using matplotlib and save it to a file.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | Whether to normalize the confusion matrix. | `True`
`save_dir` | `str` | Directory where the plot will be saved. | `''`
`on_plot` | `callable` | An optional callback to pass plots path and data when they are rendered. | `None`

Source code in `ultralytics/utils/metrics.py`

    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    @TryExcept(msg="ConfusionMatrix plot failure")
    @plt_settings()
    def plot(self, normalize: bool = True, save_dir: str = "", on_plot=None):
        """
        Plot the confusion matrix using matplotlib and save it to a file.

        Args:
            normalize (bool, optional): Whether to normalize the confusion matrix.
            save_dir (str, optional): Directory where the plot will be saved.
            on_plot (callable, optional): An optional callback to pass plots path and data when they are rendered.
        """
        import matplotlib.pyplot as plt  # scope for faster 'import ultralytics'

        array = self.matrix / ((self.matrix.sum(0).reshape(1, -1) + 1e-9) if normalize else 1)  # normalize columns
        array[array < 0.005] = np.nan  # don't annotate (would appear as 0.00)

        fig, ax = plt.subplots(1, 1, figsize=(12, 9))
        names, n = list(self.names.values()), self.nc
        if self.nc >= 100:  # downsample for large class count
            k = max(2, self.nc // 60)  # step size for downsampling, always > 1
            keep_idx = slice(None, None, k)  # create slice instead of array
            names = names[keep_idx]  # slice class names
            array = array[keep_idx, :][:, keep_idx]  # slice matrix rows and cols
            n = (self.nc + k - 1) // k  # number of retained classes
        nc = nn = n if self.task == "classify" else n + 1  # adjust for background if needed
        ticklabels = (names + ["background"]) if (0 < nn < 99) and (nn == nc) else "auto"
        xy_ticks = np.arange(len(ticklabels))
        tick_fontsize = max(6, 15 - 0.1 * nc)  # Minimum size is 6
        label_fontsize = max(6, 12 - 0.1 * nc)
        title_fontsize = max(6, 12 - 0.1 * nc)
        btm = max(0.1, 0.25 - 0.001 * nc)  # Minimum value is 0.1
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")  # suppress empty matrix RuntimeWarning: All-NaN slice encountered
            im = ax.imshow(array, cmap="Blues", vmin=0.0, interpolation="none")
            ax.xaxis.set_label_position("bottom")
            if nc < 30:  # Add score for each cell of confusion matrix
                color_threshold = 0.45 * (1 if normalize else np.nanmax(array))  # text color threshold
                for i, row in enumerate(array[:nc]):
                    for j, val in enumerate(row[:nc]):
                        val = array[i, j]
                        if np.isnan(val):
                            continue
                        ax.text(
                            j,
                            i,
                            f"{val:.2f}" if normalize else f"{int(val)}",
                            ha="center",
                            va="center",
                            fontsize=10,
                            color="white" if val > color_threshold else "black",
                        )
            cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.05)
        title = "Confusion Matrix" + " Normalized" * normalize
        ax.set_xlabel("True", fontsize=label_fontsize, labelpad=10)
        ax.set_ylabel("Predicted", fontsize=label_fontsize, labelpad=10)
        ax.set_title(title, fontsize=title_fontsize, pad=20)
        ax.set_xticks(xy_ticks)
        ax.set_yticks(xy_ticks)
        ax.tick_params(axis="x", bottom=True, top=False, labelbottom=True, labeltop=False)
        ax.tick_params(axis="y", left=True, right=False, labelleft=True, labelright=False)
        if ticklabels != "auto":
            ax.set_xticklabels(ticklabels, fontsize=tick_fontsize, rotation=90, ha="center")
            ax.set_yticklabels(ticklabels, fontsize=tick_fontsize)
        for s in {"left", "right", "bottom", "top", "outline"}:
            if s != "outline":
                ax.spines[s].set_visible(False)  # Confusion matrix plot don't have outline
            cbar.ax.spines[s].set_visible(False)
        fig.subplots_adjust(left=0, right=0.84, top=0.94, bottom=btm)  # Adjust layout to ensure equal margins
        plot_fname = Path(save_dir) / f"{title.lower().replace(' ', '_')}.png"
        fig.savefig(plot_fname, dpi=250)
        plt.close(fig)
        if on_plot:
            on_plot(plot_fname)

### `` plot_matches

    plot_matches(img: Tensor, im_file: str, save_dir: Path) -> None

Plot grid of GT, TP, FP, FN for each image.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`img` | `Tensor` | Image to plot onto. | required
`im_file` | `str` | Image filename to save visualizations. | required
`save_dir` | `Path` | Location to save the visualizations to. | required

Source code in `ultralytics/utils/metrics.py`

    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    def plot_matches(self, img: torch.Tensor, im_file: str, save_dir: Path) -> None:
        """
        Plot grid of GT, TP, FP, FN for each image.

        Args:
            img (torch.Tensor): Image to plot onto.
            im_file (str): Image filename to save visualizations.
            save_dir (Path): Location to save the visualizations to.
        """
        if not self.matches:
            return
        from .ops import xyxy2xywh
        from .plotting import plot_images

        # Create batch of 4 (GT, TP, FP, FN)
        labels = defaultdict(list)
        for i, mtype in enumerate(["GT", "FP", "TP", "FN"]):
            mbatch = self.matches[mtype]
            if "conf" not in mbatch:
                mbatch["conf"] = torch.tensor([1.0] * mbatch["bboxes"].shape[0], device=img.device)
            mbatch["batch_idx"] = torch.ones(mbatch["bboxes"].shape[0], device=img.device) * i
            for k in mbatch.keys():
                labels[k] += mbatch[k]

        labels = {k: torch.stack(v, 0) if len(v) else v for k, v in labels.items()}
        if self.task != "obb" and labels["bboxes"].shape[0]:
            labels["bboxes"] = xyxy2xywh(labels["bboxes"])
        (save_dir / "visualizations").mkdir(parents=True, exist_ok=True)
        plot_images(
            labels,
            img.repeat(4, 1, 1, 1),
            paths=["Ground Truth", "False Positives", "True Positives", "False Negatives"],
            fname=save_dir / "visualizations" / Path(im_file).name,
            names=self.names,
            max_subplots=4,
            conf_thres=0.001,
        )

### `` print

    print()

Print the confusion matrix to the console.

Source code in `ultralytics/utils/metrics.py`

    587
    588
    589
    590
    def print(self):
        """Print the confusion matrix to the console."""
        for i in range(self.matrix.shape[0]):
            LOGGER.info(" ".join(map(str, self.matrix[i])))

### `` process_batch

    process_batch(
        detections: dict[str, Tensor],
        batch: dict[str, Any],
        conf: float = 0.25,
        iou_thres: float = 0.45,
    ) -> None

Update confusion matrix for object detection task.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`detections` | `dict[str, Tensor]` | Dictionary containing detected bounding boxes and their associated information. Should contain 'cls', 'conf', and 'bboxes' keys, where 'bboxes' can be Array[N, 4] for regular boxes or Array[N, 5] for OBB with angle. | required
`batch` | `dict[str, Any]` | Batch dictionary containing ground truth data with 'bboxes' (Array[M, 4]| Array[M, 5]) and 'cls' (Array[M]) keys, where M is the number of ground truth objects. | required
`conf` | `float` | Confidence threshold for detections. | `0.25`
`iou_thres` | `float` | IoU threshold for matching detections to ground truth. | `0.45`

Source code in `ultralytics/utils/metrics.py`

    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    def process_batch(
        self,
        detections: dict[str, torch.Tensor],
        batch: dict[str, Any],
        conf: float = 0.25,
        iou_thres: float = 0.45,
    ) -> None:
        """
        Update confusion matrix for object detection task.

        Args:
            detections (dict[str, torch.Tensor]): Dictionary containing detected bounding boxes and their associated information.
                                       Should contain 'cls', 'conf', and 'bboxes' keys, where 'bboxes' can be
                                       Array[N, 4] for regular boxes or Array[N, 5] for OBB with angle.
            batch (dict[str, Any]): Batch dictionary containing ground truth data with 'bboxes' (Array[M, 4]| Array[M, 5]) and
                'cls' (Array[M]) keys, where M is the number of ground truth objects.
            conf (float, optional): Confidence threshold for detections.
            iou_thres (float, optional): IoU threshold for matching detections to ground truth.
        """
        gt_cls, gt_bboxes = batch["cls"], batch["bboxes"]
        if self.matches is not None:  # only if visualization is enabled
            self.matches = {k: defaultdict(list) for k in {"TP", "FP", "FN", "GT"}}
            for i in range(gt_cls.shape[0]):
                self._append_matches("GT", batch, i)  # store GT
        is_obb = gt_bboxes.shape[1] == 5  # check if boxes contains angle for OBB
        conf = 0.25 if conf in {None, 0.01 if is_obb else 0.001} else conf  # apply 0.25 if default val conf is passed
        no_pred = detections["cls"].shape[0] == 0
        if gt_cls.shape[0] == 0:  # Check if labels is empty
            if not no_pred:
                detections = {k: detections[k][detections["conf"] > conf] for k in detections}
                detection_classes = detections["cls"].int().tolist()
                for i, dc in enumerate(detection_classes):
                    self.matrix[dc, self.nc] += 1  # FP
                    self._append_matches("FP", detections, i)
            return
        if no_pred:
            gt_classes = gt_cls.int().tolist()
            for i, gc in enumerate(gt_classes):
                self.matrix[self.nc, gc] += 1  # FN
                self._append_matches("FN", batch, i)
            return

        detections = {k: detections[k][detections["conf"] > conf] for k in detections}
        gt_classes = gt_cls.int().tolist()
        detection_classes = detections["cls"].int().tolist()
        bboxes = detections["bboxes"]
        iou = batch_probiou(gt_bboxes, bboxes) if is_obb else box_iou(gt_bboxes, bboxes)

        x = torch.where(iou > iou_thres)
        if x[0].shape[0]:
            matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()
            if x[0].shape[0] > 1:
                matches = matches[matches[:, 2].argsort()[::-1]]
                matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
                matches = matches[matches[:, 2].argsort()[::-1]]
                matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
        else:
            matches = np.zeros((0, 3))

        n = matches.shape[0] > 0
        m0, m1, _ = matches.transpose().astype(int)
        for i, gc in enumerate(gt_classes):
            j = m0 == i
            if n and sum(j) == 1:
                dc = detection_classes[m1[j].item()]
                self.matrix[dc, gc] += 1  # TP if class is correct else both an FP and an FN
                if dc == gc:
                    self._append_matches("TP", detections, m1[j].item())
                else:
                    self._append_matches("FP", detections, m1[j].item())
                    self._append_matches("FN", batch, i)
            else:
                self.matrix[self.nc, gc] += 1  # FN
                self._append_matches("FN", batch, i)

        for i, dc in enumerate(detection_classes):
            if not any(m1 == i):
                self.matrix[dc, self.nc] += 1  # FP
                self._append_matches("FP", detections, i)

### `` process_cls_preds

    process_cls_preds(preds: list[Tensor], targets: list[Tensor]) -> None

Update confusion matrix for classification task.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`preds` | `list[N, min(nc, 5)]` | Predicted class labels. | required
`targets` | `list[N, 1]` | Ground truth class labels. | required

Source code in `ultralytics/utils/metrics.py`

    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    def process_cls_preds(self, preds: list[torch.Tensor], targets: list[torch.Tensor]) -> None:
        """
        Update confusion matrix for classification task.

        Args:
            preds (list[N, min(nc,5)]): Predicted class labels.
            targets (list[N, 1]): Ground truth class labels.
        """
        preds, targets = torch.cat(preds)[:, 0], torch.cat(targets)
        for p, t in zip(preds.cpu().numpy(), targets.cpu().numpy()):
            self.matrix[p][t] += 1

### `` summary

    summary(normalize: bool = False, decimals: int = 5) -> list[dict[str, float]]

Generate a summarized representation of the confusion matrix as a list of dictionaries, with optional normalization. This is useful for exporting the matrix to various formats such as CSV, XML, HTML, JSON, or SQL.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | Whether to normalize the confusion matrix values. | `False`
`decimals` | `int` | Number of decimal places to round the output values to. | `5`

Returns:

Type | Description
--- | ---
`list[dict[str, float]]` | A list of dictionaries, each representing one predicted class with corresponding values for all actual classes.

Examples:

    >>> results = model.val(data="coco8.yaml", plots=True)
    >>> cm_dict = results.confusion_matrix.summary(normalize=True, decimals=5)
    >>> print(cm_dict)

Source code in `ultralytics/utils/metrics.py`

    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    def summary(self, normalize: bool = False, decimals: int = 5) -> list[dict[str, float]]:
        """
        Generate a summarized representation of the confusion matrix as a list of dictionaries, with optional
        normalization. This is useful for exporting the matrix to various formats such as CSV, XML, HTML, JSON, or SQL.

        Args:
            normalize (bool): Whether to normalize the confusion matrix values.
            decimals (int): Number of decimal places to round the output values to.

        Returns:
            (list[dict[str, float]]): A list of dictionaries, each representing one predicted class with corresponding values for all actual classes.

        Examples:
            >>> results = model.val(data="coco8.yaml", plots=True)
            >>> cm_dict = results.confusion_matrix.summary(normalize=True, decimals=5)
            >>> print(cm_dict)
        """
        import re

        names = list(self.names.values()) if self.task == "classify" else list(self.names.values()) + ["background"]
        clean_names, seen = [], set()
        for name in names:
            clean_name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
            original_clean = clean_name
            counter = 1
            while clean_name.lower() in seen:
                clean_name = f"{original_clean}_{counter}"
                counter += 1
            seen.add(clean_name.lower())
            clean_names.append(clean_name)
        array = (self.matrix / ((self.matrix.sum(0).reshape(1, -1) + 1e-9) if normalize else 1)).round(decimals)
        return [
            dict({"Predicted": clean_names[i]}, **{clean_names[j]: array[i, j] for j in range(len(clean_names))})
            for i in range(len(clean_names))
        ]

### `` tp_fp

    tp_fp() -> tuple[np.ndarray, np.ndarray]

Return true positives and false positives.

Returns:

Name | Type | Description
--- | --- | ---
`tp` | `ndarray` | True positives.
`fp` | `ndarray` | False positives.

Source code in `ultralytics/utils/metrics.py`

    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    def tp_fp(self) -> tuple[np.ndarray, np.ndarray]:
        """
        Return true positives and false positives.

        Returns:
            tp (np.ndarray): True positives.
            fp (np.ndarray): False positives.
        """
        tp = self.matrix.diagonal()  # true positives
        fp = self.matrix.sum(1) - tp  # false positives
        # fn = self.matrix.sum(0) - tp  # false negatives (missed detections)
        return (tp, fp) if self.task == "classify" else (tp[:-1], fp[:-1])  # remove background class if task=detect

## `` ultralytics.utils.metrics.Metric

    Metric()

Bases: `SimpleClass`

Class for computing evaluation metrics for Ultralytics YOLO models.

Attributes:

Name | Type | Description
--- | --- | ---
`p` | `list` | Precision for each class. Shape: (nc,).
`r` | `list` | Recall for each class. Shape: (nc,).
`f1` | `list` | F1 score for each class. Shape: (nc,).
`all_ap` | `list` | AP scores for all classes and all IoU thresholds. Shape: (nc, 10).
`ap_class_index` | `list` | Index of class for each AP score. Shape: (nc,).
`nc` | `int` | Number of classes.

Methods:

Name | Description
--- | ---
`ap50` | AP at IoU threshold of 0.5 for all classes.
`ap` | AP at IoU thresholds from 0.5 to 0.95 for all classes.
`mp` | Mean precision of all classes.
`mr` | Mean recall of all classes.
`map50` | Mean AP at IoU threshold of 0.5 for all classes.
`map75` | Mean AP at IoU threshold of 0.75 for all classes.
`map` | Mean AP at IoU thresholds from 0.5 to 0.95 for all classes.
`mean_results` | Mean of results, returns mp, mr, map50, map.
`class_result` | Class-aware result, returns p[i], r[i], ap50[i], ap[i].
`maps` | mAP of each class.
`fitness` | Model fitness as a weighted combination of metrics.
`update` | Update metric attributes with new evaluation results.
`curves` | Provides a list of curves for accessing specific metrics like precision, recall, F1, etc.
`curves_results` | Provide a list of results for accessing specific metrics like precision, recall, F1, etc.

Source code in `ultralytics/utils/metrics.py`

    886
    887
    888
    889
    890
    891
    892
    893
    def __init__(self) -> None:
        """Initialize a Metric instance for computing evaluation metrics for the YOLOv8 model."""
        self.p = []  # (nc, )
        self.r = []  # (nc, )
        self.f1 = []  # (nc, )
        self.all_ap = []  # (nc, 10)
        self.ap_class_index = []  # (nc, )
        self.nc = 0

### `` ap `property`

    ap: ndarray | list

Return the Average Precision (AP) at an IoU threshold of 0.5-0.95 for all classes.

Returns:

Type | Description
--- | ---
`ndarray | list` | Array of shape (nc,) with AP50-95 values per class, or an empty list if not available.

### `` ap50 `property`

    ap50: ndarray | list

Return the Average Precision (AP) at an IoU threshold of 0.5 for all classes.

Returns:

Type | Description
--- | ---
`ndarray | list` | Array of shape (nc,) with AP50 values per class, or an empty list if not available.

### `` curves `property`

    curves: list

Return a list of curves for accessing specific metrics curves.

### `` curves_results `property`

    curves_results: list[list]

Return a list of curves for accessing specific metrics curves.

### `` map `property`

    map: float

Return the mean Average Precision (mAP) over IoU thresholds of 0.5 - 0.95 in steps of 0.05.

Returns:

Type | Description
--- | ---
`float` | The mAP over IoU thresholds of 0.5 - 0.95 in steps of 0.05.

### `` map50 `property`

    map50: float

Return the mean Average Precision (mAP) at an IoU threshold of 0.5.

Returns:

Type | Description
--- | ---
`float` | The mAP at an IoU threshold of 0.5.

### `` map75 `property`

    map75: float

Return the mean Average Precision (mAP) at an IoU threshold of 0.75.

Returns:

Type | Description
--- | ---
`float` | The mAP at an IoU threshold of 0.75.

### `` maps `property`

    maps: ndarray

Return mAP of each class.

### `` mp `property`

    mp: float

Return the Mean Precision of all classes.

Returns:

Type | Description
--- | ---
`float` | The mean precision of all classes.

### `` mr `property`

    mr: float

Return the Mean Recall of all classes.

Returns:

Type | Description
--- | ---
`float` | The mean recall of all classes.

### `` class_result

    class_result(i: int) -> tuple[float, float, float, float]

Return class-aware result, p[i], r[i], ap50[i], ap[i].

Source code in `ultralytics/utils/metrics.py`

    969
    970
    971
    def class_result(self, i: int) -> tuple[float, float, float, float]:
        """Return class-aware result, p[i], r[i], ap50[i], ap[i]."""
        return self.p[i], self.r[i], self.ap50[i], self.ap[i]

### `` fitness

    fitness() -> float

Return model fitness as a weighted combination of metrics.

Source code in `ultralytics/utils/metrics.py`

    981
    982
    983
    984
    def fitness(self) -> float:
        """Return model fitness as a weighted combination of metrics."""
        w = [0.0, 0.0, 0.0, 1.0]  # weights for [P, R, mAP@0.5, mAP@0.5:0.95]
        return (np.nan_to_num(np.array(self.mean_results())) * w).sum()

### `` mean_results

    mean_results() -> list[float]

Return mean of results, mp, mr, map50, map.

Source code in `ultralytics/utils/metrics.py`

    965
    966
    967
    def mean_results(self) -> list[float]:
        """Return mean of results, mp, mr, map50, map."""
        return [self.mp, self.mr, self.map50, self.map]

### `` update

    update(results: tuple)

Update the evaluation metrics with a new set of results.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`results` | `tuple` | A tuple containing evaluation metrics: - p (list): Precision for each class. - r (list): Recall for each class. - f1 (list): F1 score for each class. - all_ap (list): AP scores for all classes and all IoU thresholds. - ap_class_index (list): Index of class for each AP score. - p_curve (list): Precision curve for each class. - r_curve (list): Recall curve for each class. - f1_curve (list): F1 curve for each class. - px (list): X values for the curves. - prec_values (list): Precision values for each class. | required

Source code in `ultralytics/utils/metrics.py`

     986
     987
     988
     989
     990
     991
     992
     993
     994
     995
     996
     997
     998
     999
    1000
    1001
    1002
    1003
    1004
    1005
    1006
    1007
    1008
    1009
    1010
    1011
    1012
    1013
    1014
    def update(self, results: tuple):
        """
        Update the evaluation metrics with a new set of results.

        Args:
            results (tuple): A tuple containing evaluation metrics:
                - p (list): Precision for each class.
                - r (list): Recall for each class.
                - f1 (list): F1 score for each class.
                - all_ap (list): AP scores for all classes and all IoU thresholds.
                - ap_class_index (list): Index of class for each AP score.
                - p_curve (list): Precision curve for each class.
                - r_curve (list): Recall curve for each class.
                - f1_curve (list): F1 curve for each class.
                - px (list): X values for the curves.
                - prec_values (list): Precision values for each class.
        """
        (
            self.p,
            self.r,
            self.f1,
            self.all_ap,
            self.ap_class_index,
            self.p_curve,
            self.r_curve,
            self.f1_curve,
            self.px,
            self.prec_values,
        ) = results

## `` ultralytics.utils.metrics.DetMetrics

    DetMetrics(names: dict[int, str] = {})

Bases: `SimpleClass`, `DataExportMixin`

Utility class for computing detection metrics such as precision, recall, and mean average precision (mAP).

Attributes:

Name | Type | Description
--- | --- | ---
`names` | `dict[int, str]` | A dictionary of class names.
`box` | `Metric` | An instance of the Metric class for storing detection results.
`speed` | `dict[str, float]` | A dictionary for storing execution times of different parts of the detection process.
`task` | `str` | The task type, set to 'detect'.
`stats` | `dict[str, list]` | A dictionary containing lists for true positives, confidence scores, predicted classes, target classes, and target images.
`nt_per_class` |  | Number of targets per class.
`nt_per_image` |  | Number of targets per image.

Methods:

Name | Description
--- | ---
`update_stats` | Update statistics by appending new values to existing stat collections.
`process` | Process predicted results for object detection and update metrics.
`clear_stats` | Clear the stored statistics.
`keys` | Return a list of keys for accessing specific metrics.
`mean_results` | Calculate mean of detected objects & return precision, recall, mAP50, and mAP50-95.
`class_result` | Return the result of evaluating the performance of an object detection model on a specific class.
`maps` | Return mean Average Precision (mAP) scores per class.
`fitness` | Return the fitness of box object.
`ap_class_index` | Return the average precision index per class.
`results_dict` | Return dictionary of computed performance metrics and statistics.
`curves` | Return a list of curves for accessing specific metrics curves.
`curves_results` | Return a list of computed performance metrics and statistics.
`summary` | Generate a summarized representation of per-class detection metrics as a list of dictionaries.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names. | `{}`

Source code in `ultralytics/utils/metrics.py`

    1061
    1062
    1063
    1064
    1065
    1066
    1067
    1068
    1069
    1070
    1071
    1072
    1073
    1074
    def __init__(self, names: dict[int, str] = {}) -> None:
        """
        Initialize a DetMetrics instance with a save directory, plot flag, and class names.

        Args:
            names (dict[int, str], optional): Dictionary of class names.
        """
        self.names = names
        self.box = Metric()
        self.speed = {"preprocess": 0.0, "inference": 0.0, "loss": 0.0, "postprocess": 0.0}
        self.task = "detect"
        self.stats = dict(tp=[], conf=[], pred_cls=[], target_cls=[], target_img=[])
        self.nt_per_class = None
        self.nt_per_image = None

### `` ap_class_index `property`

    ap_class_index: list

Return the average precision index per class.

### `` curves `property`

    curves: list[str]

Return a list of curves for accessing specific metrics curves.

### `` curves_results `property`

    curves_results: list[list]

Return a list of computed performance metrics and statistics.

### `` fitness `property`

    fitness: float

Return the fitness of box object.

### `` keys `property`

    keys: list[str]

Return a list of keys for accessing specific metrics.

### `` maps `property`

    maps: ndarray

Return mean Average Precision (mAP) scores per class.

### `` results_dict `property`

    results_dict: dict[str, float]

Return dictionary of computed performance metrics and statistics.

### `` class_result

    class_result(i: int) -> tuple[float, float, float, float]

Return the result of evaluating the performance of an object detection model on a specific class.

Source code in `ultralytics/utils/metrics.py`

    1133
    1134
    1135
    def class_result(self, i: int) -> tuple[float, float, float, float]:
        """Return the result of evaluating the performance of an object detection model on a specific class."""
        return self.box.class_result(i)

### `` clear_stats

    clear_stats()

Clear the stored statistics.

Source code in `ultralytics/utils/metrics.py`

    1119
    1120
    1121
    1122
    def clear_stats(self):
        """Clear the stored statistics."""
        for v in self.stats.values():
            v.clear()

### `` mean_results

    mean_results() -> list[float]

Calculate mean of detected objects & return precision, recall, mAP50, and mAP50-95.

Source code in `ultralytics/utils/metrics.py`

    1129
    1130
    1131
    def mean_results(self) -> list[float]:
        """Calculate mean of detected objects & return precision, recall, mAP50, and mAP50-95."""
        return self.box.mean_results()

### `` process

    process(
        save_dir: Path = Path("."), plot: bool = False, on_plot=None
    ) -> dict[str, np.ndarray]

Process predicted results for object detection and update metrics.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`save_dir` | `Path` | Directory to save plots. Defaults to Path("."). | `Path('.')`
`plot` | `bool` | Whether to plot precision-recall curves. Defaults to False. | `False`
`on_plot` | `callable` | Function to call after plots are generated. Defaults to None. | `None`

Returns:

Type | Description
--- | ---
`dict[str, ndarray]` | Dictionary containing concatenated statistics arrays.

Source code in `ultralytics/utils/metrics.py`

    1087
    1088
    1089
    1090
    1091
    1092
    1093
    1094
    1095
    1096
    1097
    1098
    1099
    1100
    1101
    1102
    1103
    1104
    1105
    1106
    1107
    1108
    1109
    1110
    1111
    1112
    1113
    1114
    1115
    1116
    1117
    def process(self, save_dir: Path = Path("."), plot: bool = False, on_plot=None) -> dict[str, np.ndarray]:
        """
        Process predicted results for object detection and update metrics.

        Args:
            save_dir (Path): Directory to save plots. Defaults to Path(".").
            plot (bool): Whether to plot precision-recall curves. Defaults to False.
            on_plot (callable, optional): Function to call after plots are generated. Defaults to None.

        Returns:
            (dict[str, np.ndarray]): Dictionary containing concatenated statistics arrays.
        """
        stats = {k: np.concatenate(v, 0) for k, v in self.stats.items()}  # to numpy
        if not stats:
            return stats
        results = ap_per_class(
            stats["tp"],
            stats["conf"],
            stats["pred_cls"],
            stats["target_cls"],
            plot=plot,
            save_dir=save_dir,
            names=self.names,
            on_plot=on_plot,
            prefix="Box",
        )[2:]
        self.box.nc = len(self.names)
        self.box.update(results)
        self.nt_per_class = np.bincount(stats["target_cls"].astype(int), minlength=len(self.names))
        self.nt_per_image = np.bincount(stats["target_img"].astype(int), minlength=len(self.names))
        return stats

### `` summary

    summary(normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]

Generate a summarized representation of per-class detection metrics as a list of dictionaries. Includes shared scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | For Detect metrics, everything is normalized by default [0-1]. | `True`
`decimals` | `int` | Number of decimal places to round the metrics values to. | `5`

Returns:

Type | Description
--- | ---
`list[dict[str, Any]]` | A list of dictionaries, each representing one class with corresponding metric values.

Examples:

    >>> results = model.val(data="coco8.yaml")
    >>> detection_summary = results.summary()
    >>> print(detection_summary)

Source code in `ultralytics/utils/metrics.py`

    1169
    1170
    1171
    1172
    1173
    1174
    1175
    1176
    1177
    1178
    1179
    1180
    1181
    1182
    1183
    1184
    1185
    1186
    1187
    1188
    1189
    1190
    1191
    1192
    1193
    1194
    1195
    1196
    1197
    1198
    1199
    1200
    1201
    def summary(self, normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]:
        """
        Generate a summarized representation of per-class detection metrics as a list of dictionaries. Includes shared
        scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

        Args:
           normalize (bool): For Detect metrics, everything is normalized  by default [0-1].
           decimals (int): Number of decimal places to round the metrics values to.

        Returns:
           (list[dict[str, Any]]): A list of dictionaries, each representing one class with corresponding metric values.

        Examples:
           >>> results = model.val(data="coco8.yaml")
           >>> detection_summary = results.summary()
           >>> print(detection_summary)
        """
        per_class = {
            "Box-P": self.box.p,
            "Box-R": self.box.r,
            "Box-F1": self.box.f1,
        }
        return [
            {
                "Class": self.names[self.ap_class_index[i]],
                "Images": self.nt_per_image[self.ap_class_index[i]],
                "Instances": self.nt_per_class[self.ap_class_index[i]],
                **{k: round(v[i], decimals) for k, v in per_class.items()},
                "mAP50": round(self.class_result(i)[2], decimals),
                "mAP50-95": round(self.class_result(i)[3], decimals),
            }
            for i in range(len(per_class["Box-P"]))
        ]

### `` update_stats

    update_stats(stat: dict[str, Any]) -> None

Update statistics by appending new values to existing stat collections.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`stat` | `dict[str, any]` | Dictionary containing new statistical values to append. Keys should match existing keys in self.stats. | required

Source code in `ultralytics/utils/metrics.py`

    1076
    1077
    1078
    1079
    1080
    1081
    1082
    1083
    1084
    1085
    def update_stats(self, stat: dict[str, Any]) -> None:
        """
        Update statistics by appending new values to existing stat collections.

        Args:
            stat (dict[str, any]): Dictionary containing new statistical values to append.
                         Keys should match existing keys in self.stats.
        """
        for k in self.stats.keys():
            self.stats[k].append(stat[k])

## `` ultralytics.utils.metrics.SegmentMetrics

    SegmentMetrics(names: dict[int, str] = {})

Bases: `DetMetrics`

Calculate and aggregate detection and segmentation metrics over a given set of classes.

Attributes:

Name | Type | Description
--- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names.
`box` | `Metric` | An instance of the Metric class for storing detection results.
`seg` | `Metric` | An instance of the Metric class to calculate mask segmentation metrics.
`speed` | `dict[str, float]` | A dictionary for storing execution times of different parts of the detection process.
`task` | `str` | The task type, set to 'segment'.
`stats` | `dict[str, list]` | A dictionary containing lists for true positives, confidence scores, predicted classes, target classes, and target images.
`nt_per_class` |  | Number of targets per class.
`nt_per_image` |  | Number of targets per image.

Methods:

Name | Description
--- | ---
`process` | Process the detection and segmentation metrics over the given set of predictions.
`keys` | Return a list of keys for accessing metrics.
`mean_results` | Return the mean metrics for bounding box and segmentation results.
`class_result` | Return classification results for a specified class index.
`maps` | Return mAP scores for object detection and semantic segmentation models.
`fitness` | Return the fitness score for both segmentation and bounding box models.
`curves` | Return a list of curves for accessing specific metrics curves.
`curves_results` | Provide a list of computed performance metrics and statistics.
`summary` | Generate a summarized representation of per-class segmentation metrics as a list of dictionaries.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names. | `{}`

Source code in `ultralytics/utils/metrics.py`

    1230
    1231
    1232
    1233
    1234
    1235
    1236
    1237
    1238
    1239
    1240
    def __init__(self, names: dict[int, str] = {}) -> None:
        """
        Initialize a SegmentMetrics instance with a save directory, plot flag, and class names.

        Args:
            names (dict[int, str], optional): Dictionary of class names.
        """
        DetMetrics.__init__(self, names)
        self.seg = Metric()
        self.task = "segment"
        self.stats["tp_m"] = []  # add additional stats for masks

### `` curves `property`

    curves: list[str]

Return a list of curves for accessing specific metrics curves.

### `` curves_results `property`

    curves_results: list[list]

Return a list of computed performance metrics and statistics.

### `` fitness `property`

    fitness: float

Return the fitness score for both segmentation and bounding box models.

### `` keys `property`

    keys: list[str]

Return a list of keys for accessing metrics.

### `` maps `property`

    maps: ndarray

Return mAP scores for object detection and semantic segmentation models.

### `` class_result

    class_result(i: int) -> list[float]

Return classification results for a specified class index.

Source code in `ultralytics/utils/metrics.py`

    1284
    1285
    1286
    def class_result(self, i: int) -> list[float]:
        """Return classification results for a specified class index."""
        return DetMetrics.class_result(self, i) + self.seg.class_result(i)

### `` mean_results

    mean_results() -> list[float]

Return the mean metrics for bounding box and segmentation results.

Source code in `ultralytics/utils/metrics.py`

    1280
    1281
    1282
    def mean_results(self) -> list[float]:
        """Return the mean metrics for bounding box and segmentation results."""
        return DetMetrics.mean_results(self) + self.seg.mean_results()

### `` process

    process(
        save_dir: Path = Path("."), plot: bool = False, on_plot=None
    ) -> dict[str, np.ndarray]

Process the detection and segmentation metrics over the given set of predictions.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`save_dir` | `Path` | Directory to save plots. Defaults to Path("."). | `Path('.')`
`plot` | `bool` | Whether to plot precision-recall curves. Defaults to False. | `False`
`on_plot` | `callable` | Function to call after plots are generated. Defaults to None. | `None`

Returns:

Type | Description
--- | ---
`dict[str, ndarray]` | Dictionary containing concatenated statistics arrays.

Source code in `ultralytics/utils/metrics.py`

    1242
    1243
    1244
    1245
    1246
    1247
    1248
    1249
    1250
    1251
    1252
    1253
    1254
    1255
    1256
    1257
    1258
    1259
    1260
    1261
    1262
    1263
    1264
    1265
    1266
    1267
    1268
    def process(self, save_dir: Path = Path("."), plot: bool = False, on_plot=None) -> dict[str, np.ndarray]:
        """
        Process the detection and segmentation metrics over the given set of predictions.

        Args:
            save_dir (Path): Directory to save plots. Defaults to Path(".").
            plot (bool): Whether to plot precision-recall curves. Defaults to False.
            on_plot (callable, optional): Function to call after plots are generated. Defaults to None.

        Returns:
            (dict[str, np.ndarray]): Dictionary containing concatenated statistics arrays.
        """
        stats = DetMetrics.process(self, save_dir, plot, on_plot=on_plot)  # process box stats
        results_mask = ap_per_class(
            stats["tp_m"],
            stats["conf"],
            stats["pred_cls"],
            stats["target_cls"],
            plot=plot,
            on_plot=on_plot,
            save_dir=save_dir,
            names=self.names,
            prefix="Mask",
        )[2:]
        self.seg.nc = len(self.names)
        self.seg.update(results_mask)
        return stats

### `` summary

    summary(normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]

Generate a summarized representation of per-class segmentation metrics as a list of dictionaries. Includes both box and mask scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | For Segment metrics, everything is normalized by default [0-1]. | `True`
`decimals` | `int` | Number of decimal places to round the metrics values to. | `5`

Returns:

Type | Description
--- | ---
`list[dict[str, Any]]` | A list of dictionaries, each representing one class with corresponding metric values.

Examples:

    >>> results = model.val(data="coco8-seg.yaml")
    >>> seg_summary = results.summary(decimals=4)
    >>> print(seg_summary)

Source code in `ultralytics/utils/metrics.py`

    1313
    1314
    1315
    1316
    1317
    1318
    1319
    1320
    1321
    1322
    1323
    1324
    1325
    1326
    1327
    1328
    1329
    1330
    1331
    1332
    1333
    1334
    1335
    1336
    1337
    1338
    def summary(self, normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]:
        """
        Generate a summarized representation of per-class segmentation metrics as a list of dictionaries. Includes both
        box and mask scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

        Args:
            normalize (bool): For Segment metrics, everything is normalized  by default [0-1].
            decimals (int): Number of decimal places to round the metrics values to.

        Returns:
            (list[dict[str, Any]]): A list of dictionaries, each representing one class with corresponding metric values.

        Examples:
            >>> results = model.val(data="coco8-seg.yaml")
            >>> seg_summary = results.summary(decimals=4)
            >>> print(seg_summary)
        """
        per_class = {
            "Mask-P": self.seg.p,
            "Mask-R": self.seg.r,
            "Mask-F1": self.seg.f1,
        }
        summary = DetMetrics.summary(self, normalize, decimals)  # get box summary
        for i, s in enumerate(summary):
            s.update({**{k: round(v[i], decimals) for k, v in per_class.items()}})
        return summary

## `` ultralytics.utils.metrics.PoseMetrics

    PoseMetrics(names: dict[int, str] = {})

Bases: `DetMetrics`

Calculate and aggregate detection and pose metrics over a given set of classes.

Attributes:

Name | Type | Description
--- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names.
`pose` | `Metric` | An instance of the Metric class to calculate pose metrics.
`box` | `Metric` | An instance of the Metric class for storing detection results.
`speed` | `dict[str, float]` | A dictionary for storing execution times of different parts of the detection process.
`task` | `str` | The task type, set to 'pose'.
`stats` | `dict[str, list]` | A dictionary containing lists for true positives, confidence scores, predicted classes, target classes, and target images.
`nt_per_class` |  | Number of targets per class.
`nt_per_image` |  | Number of targets per image.

Methods:

Name | Description
--- | ---
`process` | Process the detection and pose metrics over the given set of predictions. R
`keys` | Return a list of keys for accessing metrics.
`mean_results` | Return the mean results of box and pose.
`class_result` | Return the class-wise detection results for a specific class i.
`maps` | Return the mean average precision (mAP) per class for both box and pose detections.
`fitness` | Return combined fitness score for pose and box detection.
`curves` | Return a list of curves for accessing specific metrics curves.
`curves_results` | Provide a list of computed performance metrics and statistics.
`summary` | Generate a summarized representation of per-class pose metrics as a list of dictionaries.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names. | `{}`

Source code in `ultralytics/utils/metrics.py`

    1367
    1368
    1369
    1370
    1371
    1372
    1373
    1374
    1375
    1376
    1377
    def __init__(self, names: dict[int, str] = {}) -> None:
        """
        Initialize the PoseMetrics class with directory path, class names, and plotting options.

        Args:
            names (dict[int, str], optional): Dictionary of class names.
        """
        super().__init__(names)
        self.pose = Metric()
        self.task = "pose"
        self.stats["tp_p"] = []  # add additional stats for pose

### `` curves `property`

    curves: list[str]

Return a list of curves for accessing specific metrics curves.

### `` curves_results `property`

    curves_results: list[list]

Return a list of computed performance metrics and statistics.

### `` fitness `property`

    fitness: float

Return combined fitness score for pose and box detection.

### `` keys `property`

    keys: list[str]

Return a list of evaluation metric keys.

### `` maps `property`

    maps: ndarray

Return the mean average precision (mAP) per class for both box and pose detections.

### `` class_result

    class_result(i: int) -> list[float]

Return the class-wise detection results for a specific class i.

Source code in `ultralytics/utils/metrics.py`

    1421
    1422
    1423
    def class_result(self, i: int) -> list[float]:
        """Return the class-wise detection results for a specific class i."""
        return DetMetrics.class_result(self, i) + self.pose.class_result(i)

### `` mean_results

    mean_results() -> list[float]

Return the mean results of box and pose.

Source code in `ultralytics/utils/metrics.py`

    1417
    1418
    1419
    def mean_results(self) -> list[float]:
        """Return the mean results of box and pose."""
        return DetMetrics.mean_results(self) + self.pose.mean_results()

### `` process

    process(
        save_dir: Path = Path("."), plot: bool = False, on_plot=None
    ) -> dict[str, np.ndarray]

Process the detection and pose metrics over the given set of predictions.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`save_dir` | `Path` | Directory to save plots. Defaults to Path("."). | `Path('.')`
`plot` | `bool` | Whether to plot precision-recall curves. Defaults to False. | `False`
`on_plot` | `callable` | Function to call after plots are generated. | `None`

Returns:

Type | Description
--- | ---
`dict[str, ndarray]` | Dictionary containing concatenated statistics arrays.

Source code in `ultralytics/utils/metrics.py`

    1379
    1380
    1381
    1382
    1383
    1384
    1385
    1386
    1387
    1388
    1389
    1390
    1391
    1392
    1393
    1394
    1395
    1396
    1397
    1398
    1399
    1400
    1401
    1402
    1403
    1404
    1405
    def process(self, save_dir: Path = Path("."), plot: bool = False, on_plot=None) -> dict[str, np.ndarray]:
        """
        Process the detection and pose metrics over the given set of predictions.

        Args:
            save_dir (Path): Directory to save plots. Defaults to Path(".").
            plot (bool): Whether to plot precision-recall curves. Defaults to False.
            on_plot (callable, optional): Function to call after plots are generated.

        Returns:
            (dict[str, np.ndarray]): Dictionary containing concatenated statistics arrays.
        """
        stats = DetMetrics.process(self, save_dir, plot, on_plot=on_plot)  # process box stats
        results_pose = ap_per_class(
            stats["tp_p"],
            stats["conf"],
            stats["pred_cls"],
            stats["target_cls"],
            plot=plot,
            on_plot=on_plot,
            save_dir=save_dir,
            names=self.names,
            prefix="Pose",
        )[2:]
        self.pose.nc = len(self.names)
        self.pose.update(results_pose)
        return stats

### `` summary

    summary(normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]

Generate a summarized representation of per-class pose metrics as a list of dictionaries. Includes both box and pose scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | For Pose metrics, everything is normalized by default [0-1]. | `True`
`decimals` | `int` | Number of decimal places to round the metrics values to. | `5`

Returns:

Type | Description
--- | ---
`list[dict[str, Any]]` | A list of dictionaries, each representing one class with corresponding metric values.

Examples:

    >>> results = model.val(data="coco8-pose.yaml")
    >>> pose_summary = results.summary(decimals=4)
    >>> print(pose_summary)

Source code in `ultralytics/utils/metrics.py`

    1454
    1455
    1456
    1457
    1458
    1459
    1460
    1461
    1462
    1463
    1464
    1465
    1466
    1467
    1468
    1469
    1470
    1471
    1472
    1473
    1474
    1475
    1476
    1477
    1478
    1479
    def summary(self, normalize: bool = True, decimals: int = 5) -> list[dict[str, Any]]:
        """
        Generate a summarized representation of per-class pose metrics as a list of dictionaries. Includes both box and
        pose scalar metrics (mAP, mAP50, mAP75) alongside precision, recall, and F1-score for each class.

        Args:
            normalize (bool): For Pose metrics, everything is normalized  by default [0-1].
            decimals (int): Number of decimal places to round the metrics values to.

        Returns:
            (list[dict[str, Any]]): A list of dictionaries, each representing one class with corresponding metric values.

        Examples:
            >>> results = model.val(data="coco8-pose.yaml")
            >>> pose_summary = results.summary(decimals=4)
            >>> print(pose_summary)
        """
        per_class = {
            "Pose-P": self.pose.p,
            "Pose-R": self.pose.r,
            "Pose-F1": self.pose.f1,
        }
        summary = DetMetrics.summary(self, normalize, decimals)  # get box summary
        for i, s in enumerate(summary):
            s.update({**{k: round(v[i], decimals) for k, v in per_class.items()}})
        return summary

## `` ultralytics.utils.metrics.ClassifyMetrics

    ClassifyMetrics()

Bases: `SimpleClass`, `DataExportMixin`

Class for computing classification metrics including top-1 and top-5 accuracy.

Attributes:

Name | Type | Description
--- | --- | ---
`top1` | `float` | The top-1 accuracy.
`top5` | `float` | The top-5 accuracy.
`speed` | `dict` | A dictionary containing the time taken for each step in the pipeline.
`task` | `str` | The task type, set to 'classify'.

Methods:

Name | Description
--- | ---
`process` | Process target classes and predicted classes to compute metrics.
`fitness` | Return mean of top-1 and top-5 accuracies as fitness score.
`results_dict` | Return a dictionary with model's performance metrics and fitness score.
`keys` | Return a list of keys for the results_dict property.
`curves` | Return a list of curves for accessing specific metrics curves.
`curves_results` | Provide a list of computed performance metrics and statistics.
`summary` | Generate a single-row summary of classification metrics (Top-1 and Top-5 accuracy).

Source code in `ultralytics/utils/metrics.py`

    1502
    1503
    1504
    1505
    1506
    1507
    def __init__(self) -> None:
        """Initialize a ClassifyMetrics instance."""
        self.top1 = 0
        self.top5 = 0
        self.speed = {"preprocess": 0.0, "inference": 0.0, "loss": 0.0, "postprocess": 0.0}
        self.task = "classify"

### `` curves `property`

    curves: list

Return a list of curves for accessing specific metrics curves.

### `` curves_results `property`

    curves_results: list

Return a list of curves for accessing specific metrics curves.

### `` fitness `property`

    fitness: float

Return mean of top-1 and top-5 accuracies as fitness score.

### `` keys `property`

    keys: list[str]

Return a list of keys for the results_dict property.

### `` results_dict `property`

    results_dict: dict[str, float]

Return a dictionary with model's performance metrics and fitness score.

### `` process

    process(targets: Tensor, pred: Tensor)

Process target classes and predicted classes to compute metrics.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`targets` | `Tensor` | Target classes. | required
`pred` | `Tensor` | Predicted classes. | required

Source code in `ultralytics/utils/metrics.py`

    1509
    1510
    1511
    1512
    1513
    1514
    1515
    1516
    1517
    1518
    1519
    1520
    def process(self, targets: torch.Tensor, pred: torch.Tensor):
        """
        Process target classes and predicted classes to compute metrics.

        Args:
            targets (torch.Tensor): Target classes.
            pred (torch.Tensor): Predicted classes.
        """
        pred, targets = torch.cat(pred), torch.cat(targets)
        correct = (targets[:, None] == pred).float()
        acc = torch.stack((correct[:, 0], correct.max(1).values), dim=1)  # (top1, top5) accuracy
        self.top1, self.top5 = acc.mean(0).tolist()

### `` summary

    summary(normalize: bool = True, decimals: int = 5) -> list[dict[str, float]]

Generate a single-row summary of classification metrics (Top-1 and Top-5 accuracy).

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`normalize` | `bool` | For Classify metrics, everything is normalized by default [0-1]. | `True`
`decimals` | `int` | Number of decimal places to round the metrics values to. | `5`

Returns:

Type | Description
--- | ---
`list[dict[str, float]]` | A list with one dictionary containing Top-1 and Top-5 classification accuracy.

Examples:

    >>> results = model.val(data="imagenet10")
    >>> classify_summary = results.summary(decimals=4)
    >>> print(classify_summary)

Source code in `ultralytics/utils/metrics.py`

    1547
    1548
    1549
    1550
    1551
    1552
    1553
    1554
    1555
    1556
    1557
    1558
    1559
    1560
    1561
    1562
    1563
    def summary(self, normalize: bool = True, decimals: int = 5) -> list[dict[str, float]]:
        """
        Generate a single-row summary of classification metrics (Top-1 and Top-5 accuracy).

        Args:
            normalize (bool): For Classify metrics, everything is normalized  by default [0-1].
            decimals (int): Number of decimal places to round the metrics values to.

        Returns:
            (list[dict[str, float]]): A list with one dictionary containing Top-1 and Top-5 classification accuracy.

        Examples:
            >>> results = model.val(data="imagenet10")
            >>> classify_summary = results.summary(decimals=4)
            >>> print(classify_summary)
        """
        return [{"top1_acc": round(self.top1, decimals), "top5_acc": round(self.top5, decimals)}]

## `` ultralytics.utils.metrics.OBBMetrics

    OBBMetrics(names: dict[int, str] = {})

Bases: `DetMetrics`

Metrics for evaluating oriented bounding box (OBB) detection.

Attributes:

Name | Type | Description
--- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names.
`box` | `Metric` | An instance of the Metric class for storing detection results.
`speed` | `dict[str, float]` | A dictionary for storing execution times of different parts of the detection process.
`task` | `str` | The task type, set to 'obb'.
`stats` | `dict[str, list]` | A dictionary containing lists for true positives, confidence scores, predicted classes, target classes, and target images.
`nt_per_class` |  | Number of targets per class.
`nt_per_image` |  | Number of targets per image.

References

https://arxiv.org/pdf/2106.06072.pdf

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`names` | `dict[int, str]` | Dictionary of class names. | `{}`

Source code in `ultralytics/utils/metrics.py`

    1583
    1584
    1585
    1586
    1587
    1588
    1589
    1590
    1591
    1592
    def __init__(self, names: dict[int, str] = {}) -> None:
        """
        Initialize an OBBMetrics instance with directory, plotting, and class names.

        Args:
            names (dict[int, str], optional): Dictionary of class names.
        """
        DetMetrics.__init__(self, names)
        # TODO: probably remove task as well
        self.task = "obb"

## `` ultralytics.utils.metrics.bbox_ioa

    bbox_ioa(
        box1: ndarray, box2: ndarray, iou: bool = False, eps: float = 1e-07
    ) -> np.ndarray

Calculate the intersection over box2 area given box1 and box2.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`box1` | `ndarray` | A numpy array of shape (N, 4) representing N bounding boxes in x1y1x2y2 format. | required
`box2` | `ndarray` | A numpy array of shape (M, 4) representing M bounding boxes in x1y1x2y2 format. | required
`iou` | `bool` | Calculate the standard IoU if True else return inter_area/box2_area. | `False`
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`ndarray` | A numpy array of shape (N, M) representing the intersection over box2 area.

Source code in `ultralytics/utils/metrics.py`

    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    def bbox_ioa(box1: np.ndarray, box2: np.ndarray, iou: bool = False, eps: float = 1e-7) -> np.ndarray:
        """
        Calculate the intersection over box2 area given box1 and box2.

        Args:
            box1 (np.ndarray): A numpy array of shape (N, 4) representing N bounding boxes in x1y1x2y2 format.
            box2 (np.ndarray): A numpy array of shape (M, 4) representing M bounding boxes in x1y1x2y2 format.
            iou (bool, optional): Calculate the standard IoU if True else return inter_area/box2_area.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (np.ndarray): A numpy array of shape (N, M) representing the intersection over box2 area.
        """
        # Get the coordinates of bounding boxes
        b1_x1, b1_y1, b1_x2, b1_y2 = box1.T
        b2_x1, b2_y1, b2_x2, b2_y2 = box2.T

        # Intersection area
        inter_area = (np.minimum(b1_x2[:, None], b2_x2) - np.maximum(b1_x1[:, None], b2_x1)).clip(0) * (
            np.minimum(b1_y2[:, None], b2_y2) - np.maximum(b1_y1[:, None], b2_y1)
        ).clip(0)

        # Box2 area
        area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
        if iou:
            box1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
            area = area + box1_area[:, None] - inter_area

        # Intersection over box2 area
        return inter_area / (area + eps)

## `` ultralytics.utils.metrics.box_iou

    box_iou(box1: Tensor, box2: Tensor, eps: float = 1e-07) -> torch.Tensor

Calculate intersection-over-union (IoU) of boxes.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`box1` | `Tensor` | A tensor of shape (N, 4) representing N bounding boxes in (x1, y1, x2, y2) format. | required
`box2` | `Tensor` | A tensor of shape (M, 4) representing M bounding boxes in (x1, y1, x2, y2) format. | required
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | An NxM tensor containing the pairwise IoU values for every element in box1 and box2.

References

https://github.com/pytorch/vision/blob/main/torchvision/ops/boxes.py

Source code in `ultralytics/utils/metrics.py`

    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    def box_iou(box1: torch.Tensor, box2: torch.Tensor, eps: float = 1e-7) -> torch.Tensor:
        """
        Calculate intersection-over-union (IoU) of boxes.

        Args:
            box1 (torch.Tensor): A tensor of shape (N, 4) representing N bounding boxes in (x1, y1, x2, y2) format.
            box2 (torch.Tensor): A tensor of shape (M, 4) representing M bounding boxes in (x1, y1, x2, y2) format.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (torch.Tensor): An NxM tensor containing the pairwise IoU values for every element in box1 and box2.

        References:
            https://github.com/pytorch/vision/blob/main/torchvision/ops/boxes.py
        """
        # NOTE: Need .float() to get accurate iou values
        # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
        (a1, a2), (b1, b2) = box1.float().unsqueeze(1).chunk(2, 2), box2.float().unsqueeze(0).chunk(2, 2)
        inter = (torch.min(a2, b2) - torch.max(a1, b1)).clamp_(0).prod(2)

        # IoU = inter / (area1 + area2 - inter)
        return inter / ((a2 - a1).prod(2) + (b2 - b1).prod(2) - inter + eps)

## `` ultralytics.utils.metrics.bbox_iou

    bbox_iou(
        box1: Tensor,
        box2: Tensor,
        xywh: bool = True,
        GIoU: bool = False,
        DIoU: bool = False,
        CIoU: bool = False,
        eps: float = 1e-07,
    ) -> torch.Tensor

Calculate the Intersection over Union (IoU) between bounding boxes.

This function supports various shapes for `box1` and `box2` as long as the last dimension is 4. For instance, you may pass tensors shaped like (4,), (N, 4), (B, N, 4), or (B, N, 1, 4). Internally, the code will split the last dimension into (x, y, w, h) if `xywh=True`, or (x1, y1, x2, y2) if `xywh=False`.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`box1` | `Tensor` | A tensor representing one or more bounding boxes, with the last dimension being 4. | required
`box2` | `Tensor` | A tensor representing one or more bounding boxes, with the last dimension being 4. | required
`xywh` | `bool` | If True, input boxes are in (x, y, w, h) format. If False, input boxes are in (x1, y1, x2, y2) format. | `True`
`GIoU` | `bool` | If True, calculate Generalized IoU. | `False`
`DIoU` | `bool` | If True, calculate Distance IoU. | `False`
`CIoU` | `bool` | If True, calculate Complete IoU. | `False`
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | IoU, GIoU, DIoU, or CIoU values depending on the specified flags.

Source code in `ultralytics/utils/metrics.py`

     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    def bbox_iou(
        box1: torch.Tensor,
        box2: torch.Tensor,
        xywh: bool = True,
        GIoU: bool = False,
        DIoU: bool = False,
        CIoU: bool = False,
        eps: float = 1e-7,
    ) -> torch.Tensor:
        """
        Calculate the Intersection over Union (IoU) between bounding boxes.

        This function supports various shapes for `box1` and `box2` as long as the last dimension is 4.
        For instance, you may pass tensors shaped like (4,), (N, 4), (B, N, 4), or (B, N, 1, 4).
        Internally, the code will split the last dimension into (x, y, w, h) if `xywh=True`,
        or (x1, y1, x2, y2) if `xywh=False`.

        Args:
            box1 (torch.Tensor): A tensor representing one or more bounding boxes, with the last dimension being 4.
            box2 (torch.Tensor): A tensor representing one or more bounding boxes, with the last dimension being 4.
            xywh (bool, optional): If True, input boxes are in (x, y, w, h) format. If False, input boxes are in
                                   (x1, y1, x2, y2) format.
            GIoU (bool, optional): If True, calculate Generalized IoU.
            DIoU (bool, optional): If True, calculate Distance IoU.
            CIoU (bool, optional): If True, calculate Complete IoU.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (torch.Tensor): IoU, GIoU, DIoU, or CIoU values depending on the specified flags.
        """
        # Get the coordinates of bounding boxes
        if xywh:  # transform from xywh to xyxy
            (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
            w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
            b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
            b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
        else:  # x1, y1, x2, y2 = box1
            b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
            b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
            w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
            w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps

        # Intersection area
        inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * (
            b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)
        ).clamp_(0)

        # Union Area
        union = w1 * h1 + w2 * h2 - inter + eps

        # IoU
        iou = inter / union
        if CIoU or DIoU or GIoU:
            cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1)  # convex (smallest enclosing box) width
            ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1)  # convex height
            if CIoU or DIoU:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
                c2 = cw.pow(2) + ch.pow(2) + eps  # convex diagonal squared
                rho2 = (
                    (b2_x1 + b2_x2 - b1_x1 - b1_x2).pow(2) + (b2_y1 + b2_y2 - b1_y1 - b1_y2).pow(2)
                ) / 4  # center dist**2
                if CIoU:  # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
                    v = (4 / math.pi**2) * ((w2 / h2).atan() - (w1 / h1).atan()).pow(2)
                    with torch.no_grad():
                        alpha = v / (v - iou + (1 + eps))
                    return iou - (rho2 / c2 + v * alpha)  # CIoU
                return iou - rho2 / c2  # DIoU
            c_area = cw * ch + eps  # convex area
            return iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdf
        return iou  # IoU

## `` ultralytics.utils.metrics.mask_iou

    mask_iou(mask1: Tensor, mask2: Tensor, eps: float = 1e-07) -> torch.Tensor

Calculate masks IoU.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`mask1` | `Tensor` | A tensor of shape (N, n) where N is the number of ground truth objects and n is the product of image width and height. | required
`mask2` | `Tensor` | A tensor of shape (M, n) where M is the number of predicted objects and n is the product of image width and height. | required
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | A tensor of shape (N, M) representing masks IoU.

Source code in `ultralytics/utils/metrics.py`

    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    def mask_iou(mask1: torch.Tensor, mask2: torch.Tensor, eps: float = 1e-7) -> torch.Tensor:
        """
        Calculate masks IoU.

        Args:
            mask1 (torch.Tensor): A tensor of shape (N, n) where N is the number of ground truth objects and n is the
                            product of image width and height.
            mask2 (torch.Tensor): A tensor of shape (M, n) where M is the number of predicted objects and n is the
                            product of image width and height.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (torch.Tensor): A tensor of shape (N, M) representing masks IoU.
        """
        intersection = torch.matmul(mask1, mask2.T).clamp_(0)
        union = (mask1.sum(1)[:, None] + mask2.sum(1)[None]) - intersection  # (area1 + area2) - intersection
        return intersection / (union + eps)

## `` ultralytics.utils.metrics.kpt_iou

    kpt_iou(
        kpt1: Tensor,
        kpt2: Tensor,
        area: Tensor,
        sigma: list[float],
        eps: float = 1e-07,
    ) -> torch.Tensor

Calculate Object Keypoint Similarity (OKS).

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`kpt1` | `Tensor` | A tensor of shape (N, 17, 3) representing ground truth keypoints. | required
`kpt2` | `Tensor` | A tensor of shape (M, 17, 3) representing predicted keypoints. | required
`area` | `Tensor` | A tensor of shape (N,) representing areas from ground truth. | required
`sigma` | `list` | A list containing 17 values representing keypoint scales. | required
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | A tensor of shape (N, M) representing keypoint similarities.

Source code in `ultralytics/utils/metrics.py`

    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    def kpt_iou(
        kpt1: torch.Tensor, kpt2: torch.Tensor, area: torch.Tensor, sigma: list[float], eps: float = 1e-7
    ) -> torch.Tensor:
        """
        Calculate Object Keypoint Similarity (OKS).

        Args:
            kpt1 (torch.Tensor): A tensor of shape (N, 17, 3) representing ground truth keypoints.
            kpt2 (torch.Tensor): A tensor of shape (M, 17, 3) representing predicted keypoints.
            area (torch.Tensor): A tensor of shape (N,) representing areas from ground truth.
            sigma (list): A list containing 17 values representing keypoint scales.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (torch.Tensor): A tensor of shape (N, M) representing keypoint similarities.
        """
        d = (kpt1[:, None, :, 0] - kpt2[..., 0]).pow(2) + (kpt1[:, None, :, 1] - kpt2[..., 1]).pow(2)  # (N, M, 17)
        sigma = torch.tensor(sigma, device=kpt1.device, dtype=kpt1.dtype)  # (17, )
        kpt_mask = kpt1[..., 2] != 0  # (N, 17)
        e = d / ((2 * sigma).pow(2) * (area[:, None, None] + eps) * 2)  # from cocoeval
        # e = d / ((area[None, :, None] + eps) * sigma) ** 2 / 2  # from formula
        return ((-e).exp() * kpt_mask[:, None]).sum(-1) / (kpt_mask.sum(-1)[:, None] + eps)

## `` ultralytics.utils.metrics._get_covariance_matrix

    _get_covariance_matrix(
        boxes: Tensor,
    ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]

Generate covariance matrix from oriented bounding boxes.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`boxes` | `Tensor` | A tensor of shape (N, 5) representing rotated bounding boxes, with xywhr format. | required

Returns:

Type | Description
--- | ---
`Tensor` | Covariance matrices corresponding to original rotated bounding boxes.

Source code in `ultralytics/utils/metrics.py`

    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    def _get_covariance_matrix(boxes: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
        """
        Generate covariance matrix from oriented bounding boxes.

        Args:
            boxes (torch.Tensor): A tensor of shape (N, 5) representing rotated bounding boxes, with xywhr format.

        Returns:
            (torch.Tensor): Covariance matrices corresponding to original rotated bounding boxes.
        """
        # Gaussian bounding boxes, ignore the center points (the first two columns) because they are not needed here.
        gbbs = torch.cat((boxes[:, 2:4].pow(2) / 12, boxes[:, 4:]), dim=-1)
        a, b, c = gbbs.split(1, dim=-1)
        cos = c.cos()
        sin = c.sin()
        cos2 = cos.pow(2)
        sin2 = sin.pow(2)
        return a * cos2 + b * sin2, a * sin2 + b * cos2, (a - b) * cos * sin

## `` ultralytics.utils.metrics.probiou

    probiou(
        obb1: Tensor, obb2: Tensor, CIoU: bool = False, eps: float = 1e-07
    ) -> torch.Tensor

Calculate probabilistic IoU between oriented bounding boxes.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`obb1` | `Tensor` | Ground truth OBBs, shape (N, 5), format xywhr. | required
`obb2` | `Tensor` | Predicted OBBs, shape (N, 5), format xywhr. | required
`CIoU` | `bool` | If True, calculate CIoU. | `False`
`eps` | `float` | Small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | OBB similarities, shape (N,).

Notes

OBB format: [center_x, center_y, width, height, rotation_angle].

References

https://arxiv.org/pdf/2106.06072v1.pdf

Source code in `ultralytics/utils/metrics.py`

    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    def probiou(obb1: torch.Tensor, obb2: torch.Tensor, CIoU: bool = False, eps: float = 1e-7) -> torch.Tensor:
        """
        Calculate probabilistic IoU between oriented bounding boxes.

        Args:
            obb1 (torch.Tensor): Ground truth OBBs, shape (N, 5), format xywhr.
            obb2 (torch.Tensor): Predicted OBBs, shape (N, 5), format xywhr.
            CIoU (bool, optional): If True, calculate CIoU.
            eps (float, optional): Small value to avoid division by zero.

        Returns:
            (torch.Tensor): OBB similarities, shape (N,).

        Notes:
            OBB format: [center_x, center_y, width, height, rotation_angle].

        References:
            https://arxiv.org/pdf/2106.06072v1.pdf
        """
        x1, y1 = obb1[..., :2].split(1, dim=-1)
        x2, y2 = obb2[..., :2].split(1, dim=-1)
        a1, b1, c1 = _get_covariance_matrix(obb1)
        a2, b2, c2 = _get_covariance_matrix(obb2)

        t1 = (
            ((a1 + a2) * (y1 - y2).pow(2) + (b1 + b2) * (x1 - x2).pow(2)) / ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2) + eps)
        ) * 0.25
        t2 = (((c1 + c2) * (x2 - x1) * (y1 - y2)) / ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2) + eps)) * 0.5
        t3 = (
            ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2))
            / (4 * ((a1 * b1 - c1.pow(2)).clamp_(0) * (a2 * b2 - c2.pow(2)).clamp_(0)).sqrt() + eps)
            + eps
        ).log() * 0.5
        bd = (t1 + t2 + t3).clamp(eps, 100.0)
        hd = (1.0 - (-bd).exp() + eps).sqrt()
        iou = 1 - hd
        if CIoU:  # only include the wh aspect ratio part
            w1, h1 = obb1[..., 2:4].split(1, dim=-1)
            w2, h2 = obb2[..., 2:4].split(1, dim=-1)
            v = (4 / math.pi**2) * ((w2 / h2).atan() - (w1 / h1).atan()).pow(2)
            with torch.no_grad():
                alpha = v / (v - iou + (1 + eps))
            return iou - v * alpha  # CIoU
        return iou

## `` ultralytics.utils.metrics.batch_probiou

    batch_probiou(
        obb1: Tensor | ndarray, obb2: Tensor | ndarray, eps: float = 1e-07
    ) -> torch.Tensor

Calculate the probabilistic IoU between oriented bounding boxes.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`obb1` | `Tensor | ndarray` | A tensor of shape (N, 5) representing ground truth obbs, with xywhr format. | required
`obb2` | `Tensor | ndarray` | A tensor of shape (M, 5) representing predicted obbs, with xywhr format. | required
`eps` | `float` | A small value to avoid division by zero. | `1e-07`

Returns:

Type | Description
--- | ---
`Tensor` | A tensor of shape (N, M) representing obb similarities.

References

https://arxiv.org/pdf/2106.06072v1.pdf

Source code in `ultralytics/utils/metrics.py`

    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    def batch_probiou(obb1: torch.Tensor | np.ndarray, obb2: torch.Tensor | np.ndarray, eps: float = 1e-7) -> torch.Tensor:
        """
        Calculate the probabilistic IoU between oriented bounding boxes.

        Args:
            obb1 (torch.Tensor | np.ndarray): A tensor of shape (N, 5) representing ground truth obbs, with xywhr format.
            obb2 (torch.Tensor | np.ndarray): A tensor of shape (M, 5) representing predicted obbs, with xywhr format.
            eps (float, optional): A small value to avoid division by zero.

        Returns:
            (torch.Tensor): A tensor of shape (N, M) representing obb similarities.

        References:
            https://arxiv.org/pdf/2106.06072v1.pdf
        """
        obb1 = torch.from_numpy(obb1) if isinstance(obb1, np.ndarray) else obb1
        obb2 = torch.from_numpy(obb2) if isinstance(obb2, np.ndarray) else obb2

        x1, y1 = obb1[..., :2].split(1, dim=-1)
        x2, y2 = (x.squeeze(-1)[None] for x in obb2[..., :2].split(1, dim=-1))
        a1, b1, c1 = _get_covariance_matrix(obb1)
        a2, b2, c2 = (x.squeeze(-1)[None] for x in _get_covariance_matrix(obb2))

        t1 = (
            ((a1 + a2) * (y1 - y2).pow(2) + (b1 + b2) * (x1 - x2).pow(2)) / ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2) + eps)
        ) * 0.25
        t2 = (((c1 + c2) * (x2 - x1) * (y1 - y2)) / ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2) + eps)) * 0.5
        t3 = (
            ((a1 + a2) * (b1 + b2) - (c1 + c2).pow(2))
            / (4 * ((a1 * b1 - c1.pow(2)).clamp_(0) * (a2 * b2 - c2.pow(2)).clamp_(0)).sqrt() + eps)
            + eps
        ).log() * 0.5
        bd = (t1 + t2 + t3).clamp(eps, 100.0)
        hd = (1.0 - (-bd).exp() + eps).sqrt()
        return 1 - hd

## `` ultralytics.utils.metrics.smooth_bce

    smooth_bce(eps: float = 0.1) -> tuple[float, float]

Compute smoothed positive and negative Binary Cross-Entropy targets.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`eps` | `float` | The epsilon value for label smoothing. | `0.1`

Returns:

Name | Type | Description
--- | --- | ---
`pos` | `float` | Positive label smoothing BCE target.
`neg` | `float` | Negative label smoothing BCE target.

References

https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441

Source code in `ultralytics/utils/metrics.py`

    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    def smooth_bce(eps: float = 0.1) -> tuple[float, float]:
        """
        Compute smoothed positive and negative Binary Cross-Entropy targets.

        Args:
            eps (float, optional): The epsilon value for label smoothing.

        Returns:
            pos (float): Positive label smoothing BCE target.
            neg (float): Negative label smoothing BCE target.

        References:
            https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441
        """
        return 1.0 - 0.5 * eps, 0.5 * eps

## `` ultralytics.utils.metrics.smooth

    smooth(y: ndarray, f: float = 0.05) -> np.ndarray

Box filter of fraction f.

Source code in `ultralytics/utils/metrics.py`

    629
    630
    631
    632
    633
    634
    def smooth(y: np.ndarray, f: float = 0.05) -> np.ndarray:
        """Box filter of fraction f."""
        nf = round(len(y) * f * 2) // 2 + 1  # number of filter elements (must be odd)
        p = np.ones(nf // 2)  # ones padding
        yp = np.concatenate((p * y[0], y, p * y[-1]), 0)  # y padded
        return np.convolve(yp, np.ones(nf) / nf, mode="valid")  # y-smoothed

## `` ultralytics.utils.metrics.plot_pr_curve

    plot_pr_curve(
        px: ndarray,
        py: ndarray,
        ap: ndarray,
        save_dir: Path = Path("pr_curve.png"),
        names: dict[int, str] = {},
        on_plot=None,
    )

Plot precision-recall curve.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`px` | `ndarray` | X values for the PR curve. | required
`py` | `ndarray` | Y values for the PR curve. | required
`ap` | `ndarray` | Average precision values. | required
`save_dir` | `Path` | Path to save the plot. | `Path('pr_curve.png')`
`names` | `dict[int, str]` | Dictionary mapping class indices to class names. | `{}`
`on_plot` | `callable` | Function to call after plot is saved. | `None`

Source code in `ultralytics/utils/metrics.py`

    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    @plt_settings()
    def plot_pr_curve(
        px: np.ndarray,
        py: np.ndarray,
        ap: np.ndarray,
        save_dir: Path = Path("pr_curve.png"),
        names: dict[int, str] = {},
        on_plot=None,
    ):
        """
        Plot precision-recall curve.

        Args:
            px (np.ndarray): X values for the PR curve.
            py (np.ndarray): Y values for the PR curve.
            ap (np.ndarray): Average precision values.
            save_dir (Path, optional): Path to save the plot.
            names (dict[int, str], optional): Dictionary mapping class indices to class names.
            on_plot (callable, optional): Function to call after plot is saved.
        """
        import matplotlib.pyplot as plt  # scope for faster 'import ultralytics'

        fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True)
        py = np.stack(py, axis=1)

        if 0 < len(names) < 21:  # display per-class legend if < 21 classes
            for i, y in enumerate(py.T):
                ax.plot(px, y, linewidth=1, label=f"{names[i]} {ap[i, 0]:.3f}")  # plot(recall, precision)
        else:
            ax.plot(px, py, linewidth=1, color="grey")  # plot(recall, precision)

        ax.plot(px, py.mean(1), linewidth=3, color="blue", label=f"all classes {ap[:, 0].mean():.3f} mAP@0.5")
        ax.set_xlabel("Recall")
        ax.set_ylabel("Precision")
        ax.set_xlim(0, 1)
        ax.set_ylim(0, 1)
        ax.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
        ax.set_title("Precision-Recall Curve")
        fig.savefig(save_dir, dpi=250)
        plt.close(fig)
        if on_plot:
            on_plot(save_dir)

## `` ultralytics.utils.metrics.plot_mc_curve

    plot_mc_curve(
        px: ndarray,
        py: ndarray,
        save_dir: Path = Path("mc_curve.png"),
        names: dict[int, str] = {},
        xlabel: str = "Confidence",
        ylabel: str = "Metric",
        on_plot=None,
    )

Plot metric-confidence curve.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`px` | `ndarray` | X values for the metric-confidence curve. | required
`py` | `ndarray` | Y values for the metric-confidence curve. | required
`save_dir` | `Path` | Path to save the plot. | `Path('mc_curve.png')`
`names` | `dict[int, str]` | Dictionary mapping class indices to class names. | `{}`
`xlabel` | `str` | X-axis label. | `'Confidence'`
`ylabel` | `str` | Y-axis label. | `'Metric'`
`on_plot` | `callable` | Function to call after plot is saved. | `None`

Source code in `ultralytics/utils/metrics.py`

    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    @plt_settings()
    def plot_mc_curve(
        px: np.ndarray,
        py: np.ndarray,
        save_dir: Path = Path("mc_curve.png"),
        names: dict[int, str] = {},
        xlabel: str = "Confidence",
        ylabel: str = "Metric",
        on_plot=None,
    ):
        """
        Plot metric-confidence curve.

        Args:
            px (np.ndarray): X values for the metric-confidence curve.
            py (np.ndarray): Y values for the metric-confidence curve.
            save_dir (Path, optional): Path to save the plot.
            names (dict[int, str], optional): Dictionary mapping class indices to class names.
            xlabel (str, optional): X-axis label.
            ylabel (str, optional): Y-axis label.
            on_plot (callable, optional): Function to call after plot is saved.
        """
        import matplotlib.pyplot as plt  # scope for faster 'import ultralytics'

        fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True)

        if 0 < len(names) < 21:  # display per-class legend if < 21 classes
            for i, y in enumerate(py):
                ax.plot(px, y, linewidth=1, label=f"{names[i]}")  # plot(confidence, metric)
        else:
            ax.plot(px, py.T, linewidth=1, color="grey")  # plot(confidence, metric)

        y = smooth(py.mean(0), 0.1)
        ax.plot(px, y, linewidth=3, color="blue", label=f"all classes {y.max():.2f} at {px[y.argmax()]:.3f}")
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        ax.set_xlim(0, 1)
        ax.set_ylim(0, 1)
        ax.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
        ax.set_title(f"{ylabel}-Confidence Curve")
        fig.savefig(save_dir, dpi=250)
        plt.close(fig)
        if on_plot:
            on_plot(save_dir)

## `` ultralytics.utils.metrics.compute_ap

    compute_ap(
        recall: list[float], precision: list[float]
    ) -> tuple[float, np.ndarray, np.ndarray]

Compute the average precision (AP) given the recall and precision curves.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`recall` | `list` | The recall curve. | required
`precision` | `list` | The precision curve. | required

Returns:

Name | Type | Description
--- | --- | ---
`ap` | `float` | Average precision.
`mpre` | `ndarray` | Precision envelope curve.
`mrec` | `ndarray` | Modified recall curve with sentinel values added at the beginning and end.

Source code in `ultralytics/utils/metrics.py`

    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    def compute_ap(recall: list[float], precision: list[float]) -> tuple[float, np.ndarray, np.ndarray]:
        """
        Compute the average precision (AP) given the recall and precision curves.

        Args:
            recall (list): The recall curve.
            precision (list): The precision curve.

        Returns:
            ap (float): Average precision.
            mpre (np.ndarray): Precision envelope curve.
            mrec (np.ndarray): Modified recall curve with sentinel values added at the beginning and end.
        """
        # Append sentinel values to beginning and end
        mrec = np.concatenate(([0.0], recall, [1.0]))
        mpre = np.concatenate(([1.0], precision, [0.0]))

        # Compute the precision envelope
        mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))

        # Integrate area under curve
        method = "interp"  # methods: 'continuous', 'interp'
        if method == "interp":
            x = np.linspace(0, 1, 101)  # 101-point interp (COCO)
            func = np.trapezoid if checks.check_version(np.__version__, ">=2.0") else np.trapz  # np.trapz deprecated
            ap = func(np.interp(x, mrec, mpre), x)  # integrate
        else:  # 'continuous'
            i = np.where(mrec[1:] != mrec[:-1])[0]  # points where x-axis (recall) changes
            ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])  # area under curve

        return ap, mpre, mrec

## `` ultralytics.utils.metrics.ap_per_class

    ap_per_class(
        tp: ndarray,
        conf: ndarray,
        pred_cls: ndarray,
        target_cls: ndarray,
        plot: bool = False,
        on_plot=None,
        save_dir: Path = Path(),
        names: dict[int, str] = {},
        eps: float = 1e-16,
        prefix: str = "",
    ) -> tuple

Compute the average precision per class for object detection evaluation.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`tp` | `ndarray` | Binary array indicating whether the detection is correct (True) or not (False). | required
`conf` | `ndarray` | Array of confidence scores of the detections. | required
`pred_cls` | `ndarray` | Array of predicted classes of the detections. | required
`target_cls` | `ndarray` | Array of true classes of the detections. | required
`plot` | `bool` | Whether to plot PR curves or not. | `False`
`on_plot` | `callable` | A callback to pass plots path and data when they are rendered. | `None`
`save_dir` | `Path` | Directory to save the PR curves. | `Path()`
`names` | `dict[int, str]` | Dictionary of class names to plot PR curves. | `{}`
`eps` | `float` | A small value to avoid division by zero. | `1e-16`
`prefix` | `str` | A prefix string for saving the plot files. | `''`

Returns:

Name | Type | Description
--- | --- | ---
`tp` | `ndarray` | True positive counts at threshold given by max F1 metric for each class.
`fp` | `ndarray` | False positive counts at threshold given by max F1 metric for each class.
`p` | `ndarray` | Precision values at threshold given by max F1 metric for each class.
`r` | `ndarray` | Recall values at threshold given by max F1 metric for each class.
`f1` | `ndarray` | F1-score values at threshold given by max F1 metric for each class.
`ap` | `ndarray` | Average precision for each class at different IoU thresholds.
`unique_classes` | `ndarray` | An array of unique classes that have data.
`p_curve` | `ndarray` | Precision curves for each class.
`r_curve` | `ndarray` | Recall curves for each class.
`f1_curve` | `ndarray` | F1-score curves for each class.
`x` | `ndarray` | X-axis values for the curves.
`prec_values` | `ndarray` | Precision values at mAP@0.5 for each class.

Source code in `ultralytics/utils/metrics.py`

    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    840
    841
    842
    843
    844
    845
    846
    847
    848
    849
    850
    851
    852
    853
    854
    def ap_per_class(
        tp: np.ndarray,
        conf: np.ndarray,
        pred_cls: np.ndarray,
        target_cls: np.ndarray,
        plot: bool = False,
        on_plot=None,
        save_dir: Path = Path(),
        names: dict[int, str] = {},
        eps: float = 1e-16,
        prefix: str = "",
    ) -> tuple:
        """
        Compute the average precision per class for object detection evaluation.

        Args:
            tp (np.ndarray): Binary array indicating whether the detection is correct (True) or not (False).
            conf (np.ndarray): Array of confidence scores of the detections.
            pred_cls (np.ndarray): Array of predicted classes of the detections.
            target_cls (np.ndarray): Array of true classes of the detections.
            plot (bool, optional): Whether to plot PR curves or not.
            on_plot (callable, optional): A callback to pass plots path and data when they are rendered.
            save_dir (Path, optional): Directory to save the PR curves.
            names (dict[int, str], optional): Dictionary of class names to plot PR curves.
            eps (float, optional): A small value to avoid division by zero.
            prefix (str, optional): A prefix string for saving the plot files.

        Returns:
            tp (np.ndarray): True positive counts at threshold given by max F1 metric for each class.
            fp (np.ndarray): False positive counts at threshold given by max F1 metric for each class.
            p (np.ndarray): Precision values at threshold given by max F1 metric for each class.
            r (np.ndarray): Recall values at threshold given by max F1 metric for each class.
            f1 (np.ndarray): F1-score values at threshold given by max F1 metric for each class.
            ap (np.ndarray): Average precision for each class at different IoU thresholds.
            unique_classes (np.ndarray): An array of unique classes that have data.
            p_curve (np.ndarray): Precision curves for each class.
            r_curve (np.ndarray): Recall curves for each class.
            f1_curve (np.ndarray): F1-score curves for each class.
            x (np.ndarray): X-axis values for the curves.
            prec_values (np.ndarray): Precision values at mAP@0.5 for each class.
        """
        # Sort by objectness
        i = np.argsort(-conf)
        tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]

        # Find unique classes
        unique_classes, nt = np.unique(target_cls, return_counts=True)
        nc = unique_classes.shape[0]  # number of classes, number of detections

        # Create Precision-Recall curve and compute AP for each class
        x, prec_values = np.linspace(0, 1, 1000), []

        # Average precision, precision and recall curves
        ap, p_curve, r_curve = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000))
        for ci, c in enumerate(unique_classes):
            i = pred_cls == c
            n_l = nt[ci]  # number of labels
            n_p = i.sum()  # number of predictions
            if n_p == 0 or n_l == 0:
                continue

            # Accumulate FPs and TPs
            fpc = (1 - tp[i]).cumsum(0)
            tpc = tp[i].cumsum(0)

            # Recall
            recall = tpc / (n_l + eps)  # recall curve
            r_curve[ci] = np.interp(-x, -conf[i], recall[:, 0], left=0)  # negative x, xp because xp decreases

            # Precision
            precision = tpc / (tpc + fpc)  # precision curve
            p_curve[ci] = np.interp(-x, -conf[i], precision[:, 0], left=1)  # p at pr_score

            # AP from recall-precision curve
            for j in range(tp.shape[1]):
                ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j])
                if j == 0:
                    prec_values.append(np.interp(x, mrec, mpre))  # precision at mAP@0.5

        prec_values = np.array(prec_values) if prec_values else np.zeros((1, 1000))  # (nc, 1000)

        # Compute F1 (harmonic mean of precision and recall)
        f1_curve = 2 * p_curve * r_curve / (p_curve + r_curve + eps)
        names = {i: names[k] for i, k in enumerate(unique_classes) if k in names}  # dict: only classes that have data
        if plot:
            plot_pr_curve(x, prec_values, ap, save_dir / f"{prefix}PR_curve.png", names, on_plot=on_plot)
            plot_mc_curve(x, f1_curve, save_dir / f"{prefix}F1_curve.png", names, ylabel="F1", on_plot=on_plot)
            plot_mc_curve(x, p_curve, save_dir / f"{prefix}P_curve.png", names, ylabel="Precision", on_plot=on_plot)
            plot_mc_curve(x, r_curve, save_dir / f"{prefix}R_curve.png", names, ylabel="Recall", on_plot=on_plot)

        i = smooth(f1_curve.mean(0), 0.1).argmax()  # max F1 index
        p, r, f1 = p_curve[:, i], r_curve[:, i], f1_curve[:, i]  # max-F1 precision, recall, F1 values
        tp = (r * nt).round()  # true positives
        fp = (tp / (p + eps) - tp).round()  # false positives
        return tp, fp, p, r, f1, ap, unique_classes.astype(int), p_curve, r_curve, f1_curve, x, prec_values

📅 Created 1 year ago ✏️ Updated 8 months ago