use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct AreaRange {
pub label: String,
pub range: [f64; 2],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum IouType {
Bbox,
Segm,
Keypoints,
Obb,
}
impl fmt::Display for IouType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IouType::Bbox => write!(f, "bbox"),
IouType::Segm => write!(f, "segm"),
IouType::Keypoints => write!(f, "keypoints"),
IouType::Obb => write!(f, "obb"),
}
}
}
impl From<IouType> for crate::primitives::sim::SimKind {
fn from(iou_type: IouType) -> Self {
use crate::primitives::sim::SimKind;
match iou_type {
IouType::Bbox => SimKind::Bbox,
IouType::Segm => SimKind::Mask,
IouType::Keypoints => SimKind::Oks,
IouType::Obb => SimKind::Obb,
}
}
}
impl FromStr for IouType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bbox" => Ok(IouType::Bbox),
"segm" => Ok(IouType::Segm),
"keypoints" => Ok(IouType::Keypoints),
"obb" => Ok(IouType::Obb),
_ => Err(format!(
"Unknown iou_type: '{}'. Expected 'bbox', 'segm', 'keypoints', or 'obb'",
s
)),
}
}
}
pub(crate) const AREA_SMALL: f64 = 32.0 * 32.0;
pub(crate) const AREA_LARGE: f64 = 96.0 * 96.0;
pub(crate) const KPT_OKS_SIGMAS: [f64; 17] = [
0.026, 0.025, 0.025, 0.035, 0.035, 0.079, 0.079, 0.072, 0.072, 0.062, 0.062, 0.107, 0.107,
0.087, 0.087, 0.089, 0.089,
];
fn linspace(start: f64, stop: f64, num: usize) -> Vec<f64> {
if num == 0 {
return Vec::new();
}
if num == 1 {
return vec![start];
}
let step = (stop - start) / (num - 1) as f64;
let mut out: Vec<f64> = (0..num).map(|i| i as f64 * step + start).collect();
out[num - 1] = stop;
out
}
pub(crate) fn default_iou_thrs() -> Vec<f64> {
linspace(0.5, 0.95, 10)
}
pub fn default_rec_thrs() -> Vec<f64> {
linspace(0.0, 1.0, 101)
}
#[derive(Debug, Clone)]
pub struct Params {
pub iou_type: IouType,
pub img_ids: Vec<u64>,
pub cat_ids: Vec<u64>,
pub iou_thrs: Vec<f64>,
pub rec_thrs: Vec<f64>,
pub max_dets: Vec<usize>,
pub area_ranges: Vec<AreaRange>,
pub use_cats: bool,
pub kpt_oks_sigmas: Vec<f64>,
pub expand_dt: bool,
}
impl Params {
pub fn area_range_idx(&self, label: &str) -> Option<usize> {
self.area_ranges.iter().position(|ar| ar.label == label)
}
pub fn all_area_idx(&self) -> usize {
self.area_range_idx("all").unwrap_or(0)
}
pub fn all_area_range(&self) -> [f64; 2] {
self.area_ranges[self.all_area_idx()].range
}
pub fn max_det(&self) -> usize {
self.max_dets.iter().copied().max().unwrap_or(100)
}
pub fn max_det_idx(&self) -> usize {
let cap = self.max_det();
self.max_dets.iter().position(|&d| d == cap).unwrap_or(0)
}
pub fn iou_thr_idx(&self, thr: f64) -> Option<usize> {
self.iou_thrs
.iter()
.enumerate()
.filter(|&(_, &t)| (t - thr).abs() < 1e-9)
.min_by(|&(_, &a), &(_, &b)| (a - thr).abs().total_cmp(&(b - thr).abs()))
.map(|(i, _)| i)
}
pub fn nearest_iou_thr_idx(&self, thr: f64) -> usize {
self.iou_thrs
.iter()
.enumerate()
.min_by(|&(_, &a), &(_, &b)| (a - thr).abs().total_cmp(&(b - thr).abs()))
.map_or(0, |(i, _)| i)
}
pub fn new(iou_type: IouType) -> Self {
let (max_dets, area_ranges) = match iou_type {
IouType::Keypoints => (
vec![20],
vec![
AreaRange {
label: "all".into(),
range: [0.0, 1e10],
},
AreaRange {
label: "medium".into(),
range: [AREA_SMALL, AREA_LARGE],
},
AreaRange {
label: "large".into(),
range: [AREA_LARGE, 1e10],
},
],
),
_ => (
vec![1, 10, 100],
vec![
AreaRange {
label: "all".into(),
range: [0.0, 1e10],
},
AreaRange {
label: "small".into(),
range: [0.0, AREA_SMALL],
},
AreaRange {
label: "medium".into(),
range: [AREA_SMALL, AREA_LARGE],
},
AreaRange {
label: "large".into(),
range: [AREA_LARGE, 1e10],
},
],
),
};
let kpt_oks_sigmas = KPT_OKS_SIGMAS.to_vec();
let iou_thrs = default_iou_thrs();
let rec_thrs = default_rec_thrs();
Params {
iou_type,
img_ids: Vec::new(),
cat_ids: Vec::new(),
iou_thrs,
rec_thrs,
max_dets,
area_ranges,
use_cats: true,
kpt_oks_sigmas,
expand_dt: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_grids_match_numpy_linspace_bitwise() {
const IOU_BITS: [u64; 10] = [
4602678819172646912,
4603129179135383962,
4603579539098121011,
4604029899060858061,
4604480259023595110,
4604930618986332160,
4605380978949069210,
4605831338911806259,
4606281698874543308,
4606732058837280358,
];
const REC_BITS: [u64; 101] = [
0,
4576918229304087675,
4581421828931458171,
4584304132692975288,
4585925428558828667,
4587366580439587226,
4588807732320345784,
4589708452245819884,
4590429028186199163,
4591149604126578442,
4591870180066957722,
4592590756007337001,
4593311331947716280,
4593851763903000740,
4594212051873190380,
4594572339843380019,
4594932627813569659,
4595292915783759299,
4595653203753948938,
4596013491724138578,
4596373779694328218,
4596734067664517857,
4597094355634707497,
4597454643604897137,
4597814931575086776,
4598175219545276416,
4598355363530371236,
4598535507515466056,
4598715651500560876,
4598895795485655695,
4599075939470750515,
4599256083455845335,
4599436227440940155,
4599616371426034975,
4599796515411129795,
4599976659396224615,
4600156803381319434,
4600336947366414254,
4600517091351509074,
4600697235336603894,
4600877379321698714,
4601057523306793534,
4601237667291888353,
4601417811276983173,
4601597955262077993,
4601778099247172813,
4601958243232267633,
4602138387217362453,
4602318531202457272,
4602498675187552092,
4602678819172646912,
4602768891165194322,
4602858963157741732,
4602949035150289142,
4603039107142836552,
4603129179135383962,
4603219251127931372,
4603309323120478782,
4603399395113026191,
4603489467105573601,
4603579539098121011,
4603669611090668421,
4603759683083215831,
4603849755075763241,
4603939827068310651,
4604029899060858061,
4604119971053405471,
4604210043045952881,
4604300115038500291,
4604390187031047701,
4604480259023595111,
4604570331016142520,
4604660403008689930,
4604750475001237340,
4604840546993784750,
4604930618986332160,
4605020690978879570,
4605110762971426980,
4605200834963974390,
4605290906956521800,
4605380978949069210,
4605471050941616620,
4605561122934164030,
4605651194926711440,
4605741266919258849,
4605831338911806259,
4605921410904353669,
4606011482896901079,
4606101554889448489,
4606191626881995899,
4606281698874543309,
4606371770867090719,
4606461842859638129,
4606551914852185539,
4606641986844732949,
4606732058837280359,
4606822130829827768,
4606912202822375178,
4607002274814922588,
4607092346807469998,
4607182418800017408,
];
let iou = default_iou_thrs();
assert_eq!(iou.len(), IOU_BITS.len());
for (i, (&got, &want)) in iou.iter().zip(IOU_BITS.iter()).enumerate() {
assert_eq!(
got.to_bits(),
want,
"iou_thrs[{i}] = {got:?}, numpy has {:?}",
f64::from_bits(want)
);
}
let rec = default_rec_thrs();
assert_eq!(rec.len(), REC_BITS.len());
for (i, (&got, &want)) in rec.iter().zip(REC_BITS.iter()).enumerate() {
assert_eq!(
got.to_bits(),
want,
"rec_thrs[{i}] = {got:?}, numpy has {:?}",
f64::from_bits(want)
);
}
assert_eq!(iou[9], 0.95);
assert_eq!(rec[100], 1.0);
}
#[test]
fn max_det_idx_follows_the_cap_not_the_last_slot() {
let mut p = Params::new(IouType::Bbox);
assert_eq!(p.max_det(), 100);
assert_eq!(p.max_det_idx(), 2);
p.max_dets = vec![100, 10, 1];
assert_eq!(p.max_det(), 100);
assert_eq!(p.max_det_idx(), 0);
p.max_dets = vec![10, 300, 100];
assert_eq!(p.max_det_idx(), 1);
p.max_dets = vec![];
assert_eq!(p.max_det_idx(), 0);
}
#[test]
fn all_area_range_agrees_with_all_area_idx() {
let p = Params::new(IouType::Bbox);
assert_eq!(p.all_area_range(), p.area_ranges[p.all_area_idx()].range);
assert_eq!(p.all_area_range(), [0.0, 1e10]);
}
#[test]
fn linspace_handles_degenerate_lengths() {
assert!(linspace(0.0, 1.0, 0).is_empty());
assert_eq!(linspace(0.25, 1.0, 1), vec![0.25]);
assert_eq!(linspace(0.0, 1.0, 2), vec![0.0, 1.0]);
}
}