Source code for itasc.tracking_ultrack.scoring
"""Shared similarity scoring used by both the linker and the greedy retracker."""
from __future__ import annotations
import numpy as np
def _rasterize(coords: np.ndarray, mins: np.ndarray, shape: tuple) -> np.ndarray:
canvas = np.zeros(shape, dtype=bool)
idx = np.rint(coords - mins).astype(int)
valid = np.ones(len(idx), dtype=bool)
for axis, size in enumerate(shape):
valid &= (idx[:, axis] >= 0) & (idx[:, axis] < size)
idx = idx[valid]
if idx.size:
canvas[tuple(idx.T)] = True
return canvas
[docs]
def centroid_corrected_iou_from_coords(
src_coords: np.ndarray,
src_centroid: np.ndarray,
target_coords: np.ndarray,
target_centroid: np.ndarray,
) -> float:
"""IoU after shifting target so its centroid matches source's."""
if len(src_coords) == 0 or len(target_coords) == 0:
return 0.0
shifted = target_coords + (src_centroid - target_centroid)
all_coords = np.vstack([src_coords, shifted])
mins = np.floor(all_coords.min(axis=0)).astype(int) - 1
maxs = np.ceil(all_coords.max(axis=0)).astype(int) + 1
shape = tuple((maxs - mins + 1).tolist())
if any(dim <= 0 for dim in shape):
return 0.0
mins_f = mins.astype(np.float32)
src_canvas = _rasterize(src_coords, mins_f, shape)
tgt_canvas = _rasterize(shifted, mins_f, shape)
union = np.logical_or(src_canvas, tgt_canvas).sum()
if union == 0:
return 0.0
return float(np.logical_and(src_canvas, tgt_canvas).sum() / union)
[docs]
def centroid_corrected_iou(mask_a: np.ndarray, mask_b: np.ndarray) -> float:
"""Convenience wrapper for full-frame boolean masks."""
coords_a = np.argwhere(mask_a).astype(np.float32)
coords_b = np.argwhere(mask_b).astype(np.float32)
if len(coords_a) == 0 or len(coords_b) == 0:
return 0.0
centroid_a = coords_a.mean(axis=0)
centroid_b = coords_b.mean(axis=0)
return centroid_corrected_iou_from_coords(coords_a, centroid_a, coords_b, centroid_b)
[docs]
def similarity_score(
*,
area_ratio: float,
centroid_corrected_iou: float,
distance: float,
area_weight: float,
iou_weight: float,
distance_weight: float,
) -> float:
"""Additive similarity score (higher = more preferred).
Shape terms are positive rewards in [0, 1]; distance is a raw penalty in
pixels. Result can go negative for far candidates, matching the default
Ultrack linker's convention so ILP appear/disappear weights stay calibrated.
"""
return (
area_weight * area_ratio
+ iou_weight * centroid_corrected_iou
- distance_weight * distance
)