from iaa_od.models import GroundTruthProtocol, Result, STD_IOU_THR, ScaleComplexity
from iaa_od.utils import collapse_categories_interactive, validate_macrocats
from iaa_od.metrics import alpha
from typing import Optional
from numpy import isnan
from copy import deepcopy
[docs]
def scale_complexity(gts: list[GroundTruthProtocol], /, *, collapsed_categories: Optional[dict[str, list[int]]], iou_threshold: float = STD_IOU_THR, use_iom: bool = False, precomputed_result: Optional[Result] = None) -> ScaleComplexity:
"""
Compute the scale complexity metric as defined in "Leadership of Data Annotation Teams" by McCulloh et al.
Parameters
gts (list[GroundTruthProtocol]): A list of GroundTruth objects representing the ground truth annotations.
collapsed_categories (Optional[dict[str, list[int]]]): A dictionary mapping new category names to lists of original category IDs to be collapsed. If None, the function will prompt for interactive input.
iou_threshold (float): The IoU threshold to use for matching. Default is STD_IOU_THR.
use_iom (bool): Whether to use IoM matching. Default is False.
precomputed_result (Optional[Result]): A precomputed Result object containing alpha value, IoU threshold, and lenient IoU settings. If provided, these settings will be used instead of computing them anew.
Returns
ScaleComplexity: The computed scale complexity value (global and per image), along with the alpha values before and after collapsing categories, both globally and per image.
"""
# Check that either iou_threshold and lenient_iou are default or precomputed_result is provided
if precomputed_result and (iou_threshold != STD_IOU_THR or use_iom):
raise ValueError("You cannot provide IoU settings when using a precomputed result.")
# If no collapsed categories are provided, run the collapse_categories_interactive function to obtain them
collapsed_categories_dict: dict[str, list[int]] = validate_macrocats(gts, collapsed_categories) if collapsed_categories is not None else collapse_categories_interactive(gts)
# Check whether the "Excluded" category is included in the collapsed categories
excluded: bool = False
if "Excluded" in collapsed_categories_dict:
excluded = True
# Check the precomputed result: if it is passed, use its alpha value, IoU threshold and lenient IoU settings
alpha_all: float
iou_thr: float
iom: bool
alpha_per_image: dict[str, float]
# Compute or retrieve the overall alpha value
if precomputed_result:
if precomputed_result.alpha is None:
raise ValueError("The provided precomputed result does not contain an alpha value.")
alpha_all = precomputed_result.alpha
iou_thr = precomputed_result.iou_thr
iom = precomputed_result.iom
alpha_per_image = deepcopy(precomputed_result.alpha_per_image)
else:
new_result: Result = alpha(gts,
iou_threshold=iou_threshold,
use_iom=use_iom
)
if new_result.alpha is None:
raise ValueError("Something very wrong has happened. Check the alpha computation.")
alpha_all = new_result.alpha
iou_thr = iou_threshold
iom = use_iom
alpha_per_image = deepcopy(new_result.alpha_per_image)
# Compute the new alpha value with collapsed categories
collapsed_result: Result = alpha(gts,
iou_threshold=iou_thr,
use_iom=iom,
collapsed_categories=collapsed_categories_dict
)
if collapsed_result.alpha is None:
raise ValueError("Something very wrong has happened. Check the alpha computation.")
alpha_collapsed: float = collapsed_result.alpha
alpha_per_image_collapsed: dict[str, float] = deepcopy(collapsed_result.alpha_per_image)
# Compute the scale complexity
scale_complexity_value: float = alpha_collapsed - alpha_all
scale_complexity_per_image: dict[str, float] = {}
if not excluded:
for image_filename in alpha_per_image:
if image_filename not in alpha_per_image_collapsed:
raise ValueError(f"Image filename {image_filename} is missing in the collapsed alpha per image results.")
if not isnan(alpha_per_image[image_filename]) and not isnan(alpha_per_image_collapsed[image_filename]):
scale_complexity_per_image[image_filename] = alpha_per_image_collapsed[image_filename] - alpha_per_image[image_filename]
else:
scale_complexity_per_image[image_filename] = float('nan')
# Create the ScaleComplexity object
scale_complexity = ScaleComplexity(
global_sc=scale_complexity_value,
per_image_sc=scale_complexity_per_image,
global_alpha=alpha_all,
per_image_alpha=alpha_per_image,
global_alpha_collapsed=alpha_collapsed,
per_image_alpha_collapsed=alpha_per_image_collapsed,
partial=excluded
)
# Return the value
return scale_complexity