from .k_alpha_observations_matrix import KAlphaObservationsMatrix
from .k_alpha_unit import KAlphaUnit
from .ground_truth_protocol import GroundTruthProtocol
import numpy as np
from dataclasses import dataclass, field
from typing import Optional
[docs]
@dataclass(slots=True, kw_only=True)
class Result:
"""
Represents the result of comparing detected objects against ground truth data.
Attributes:
gts (list[GroundTruthProtocol]): List of ground truth datasets used in the comparison.
iou_thr (float): The Intersection over Union (IoU) threshold used for matching.
iom (bool): Whether Intersection over Minimum (IoM) matching was used.
matching_algorithm (MatchingAlgorithm | None): The algorithm used for matching annotations (only for Kappa).
kappa (float | None): The calculated Kappa statistic (only for Kappa).
mean_kappa (float | None): The mean Kappa across images (only for Kappa).
kappa_per_image (dict[str, float]): Kappa values per image (only for Kappa).
counts (ComparisonValues | None): Overall comparison counts (only for Kappa).
counts_per_image (dict[str, ComparisonValues]): Comparison counts per image (only for Kappa).
alpha (float | None): The calculated Alpha statistic (only for Alpha).
alpha_per_image (dict[str, float]): Alpha values per image (only for Alpha).
mean_alpha (float | None): The mean Alpha across images (only for Alpha).
units (list[KAlphaUnit]): List of KAlphaUnit objects used in Alpha calculation (only for Alpha).
observations (KAlphaObservationsMatrix | None): Observations matrix for Alpha calculation (only for Alpha).
unfiltered_observations (KAlphaObservationsMatrix | None): Unfiltered observations matrix for Alpha calculation (only for Alpha).
mismatched_bounding_boxes (int | None): Number of mismatched bounding boxes (only for Alpha).
"""
# General fields
gts: list[GroundTruthProtocol]
iou_thr: float
iom: bool
# Alpha-specific fields
alpha: float | None = None
alpha_per_image: dict[str, float] = field(default_factory=dict)
mean_alpha: float | None = None
units: list[KAlphaUnit] = field(default_factory=list)
observations: KAlphaObservationsMatrix | None = None
unfiltered_observations: KAlphaObservationsMatrix | None = None
mismatched_bounding_boxes: int | None = None
@property
def mismatched_bounding_boxes_percentage(self) -> Optional[float]:
"""
Calculate the percentage of mismatched bounding boxes out of the total number of bounding boxes in the ground truths.
Returns:
Optional[float]: The percentage of mismatched bounding boxes, or None if mismatched_bounding_boxes is not set.
"""
if not self.mismatched_bounding_boxes:
return None
total_bboxes: int = 0
for gt in self.gts:
for anns in gt.annotations.values():
total_bboxes += len(anns)
return (self.mismatched_bounding_boxes / total_bboxes) * 100
[docs]
def brief(self) -> str:
"""
Generate a brief summary of the Result object, including key fields and metrics.
Returns:
str: A formatted string summarizing the Result object.
"""
# Selected GTs
s = "Selected GTs:\n"
for gt in self.gts:
s += f"\t- {gt.name}\n"
s += "-" * 80 + "\n"
# IoU Threshold (and IoM)
s += f"IoU Threshold: {self.iou_thr} (IoM: {self.iom})\n"
s += "-" * 80 + "\n"
# Alpha-related fields
if self.alpha:
s += "Alpha Results:\n"
s += f"\tAlpha: {self.alpha} (Mean: {self.mean_alpha})\n"
s += f"\tMismatched bounding boxes: {self.mismatched_bounding_boxes} ({self.mismatched_bounding_boxes_percentage:.2f}%)\n"
s += f"\tUnits: {len(self.units)}\n"
s += "-" * 80 + "\n"
return s
def __str__(self):
# General fields
s = "Selected GTs:\n"
for gt in self.gts:
s += f"\t- {gt.name}\n"
s += f"IoU Threshold: {self.iou_thr}\n"
s += f"IoM: {self.iom}\n"
# Alpha-specific fields
if self.alpha is not None:
s += f"Alpha: {self.alpha}\n"
if self.mean_alpha is not None:
s += f"Mean Alpha: {self.mean_alpha}\n"
if self.mismatched_bounding_boxes is not None:
s += f"Mismatched bounding boxes: {self.mismatched_bounding_boxes} ({self.mismatched_bounding_boxes_percentage:.2f}%)\n"
if self.alpha_per_image:
s += "Alpha per image:\n"
for img, alpha in self.alpha_per_image.items():
if not np.isnan(alpha):
s += f"\t- {img}: {alpha}\n"
# Units
if self.units is not None:
s += f"Number of units: {len(self.units)}\n"
# Observations matrix
if self.observations is not None:
s += f"Observations:\n"
s += f"{self.observations}\n"
return s