///|
pub struct BinaryMetrics {
true_positive : Int
false_positive : Int
false_negative : Int
true_negative : Int
} derive(Debug, Eq)
///|
pub fn BinaryMetrics::new(
true_positive~ : Int,
false_positive~ : Int,
false_negative~ : Int,
true_negative~ : Int,
) -> BinaryMetrics {
{ true_positive, false_positive, false_negative, true_negative }
}
///|
pub fn BinaryMetrics::precision(self : BinaryMetrics) -> Int {
let denominator = self.true_positive + self.false_positive
if denominator == 0 {
1000
} else {
self.true_positive * 1000 / denominator
}
}
///|
pub fn BinaryMetrics::recall(self : BinaryMetrics) -> Int {
let denominator = self.true_positive + self.false_negative
if denominator == 0 {
1000
} else {
self.true_positive * 1000 / denominator
}
}
///|
pub fn BinaryMetrics::specificity(self : BinaryMetrics) -> Int {
let denominator = self.true_negative + self.false_positive
if denominator == 0 {
1000
} else {
self.true_negative * 1000 / denominator
}
}
///|
pub fn BinaryMetrics::f1(self : BinaryMetrics) -> Int {
let p = self.precision()
let r = self.recall()
if p + r == 0 {
0
} else {
2 * p * r / (p + r)
}
}
///|
pub fn evaluate_mask(
expected : Mask,
actual : Mask,
positive : Int,
) -> BinaryMetrics {
let count = min_i(expected.values.length(), actual.values.length())
let mut tp = 0
let mut fp = 0
let mut fn_ = 0
let mut tn = 0
for i in 0.. Array[DetectionMatch] {
let out : Array[DetectionMatch] = []
for expected_box in expected {
let mut best_id = -1
let mut best_iou = 0
for actual_box in actual {
let iou = expected_box.iou(actual_box)
if iou >= threshold_per_1000 && iou > best_iou {
best_iou = iou
best_id = actual_box.label.id
}
}
if best_id >= 0 {
out.push(
DetectionMatch::new(
expected_id=expected_box.label.id,
actual_id=best_id,
iou_per_1000=best_iou,
),
)
}
}
out
}
///|
pub fn DetectionMatch::new(
expected_id~ : Int,
actual_id~ : Int,
iou_per_1000~ : Int,
) -> DetectionMatch {
{ expected_id, actual_id, iou_per_1000 }
}
///|
pub struct DetectionMetrics {
matches : Int
expected : Int
actual : Int
mean_iou_per_1000 : Int
} derive(Debug, Eq)
///|
pub fn evaluate_detections(
expected : Array[Box2D],
actual : Array[Box2D],
threshold_per_1000 : Int,
) -> DetectionMetrics {
let matches = match_detections(expected, actual, threshold_per_1000)
let mut total_iou = 0
for item in matches {
total_iou += item.iou_per_1000
}
{
matches: matches.length(),
expected: expected.length(),
actual: actual.length(),
mean_iou_per_1000: if matches.length() == 0 {
0
} else {
total_iou / matches.length()
},
}
}
///|
pub fn DetectionMetrics::precision(self : DetectionMetrics) -> Int {
if self.actual == 0 {
1000
} else {
self.matches * 1000 / self.actual
}
}
///|
pub fn DetectionMetrics::recall(self : DetectionMetrics) -> Int {
if self.expected == 0 {
1000
} else {
self.matches * 1000 / self.expected
}
}
///|
pub fn DetectionMetrics::to_json(self : DetectionMetrics) -> String {
"{\"matches\":\{self.matches},\"expected\":\{self.expected},\"actual\":\{self.actual},\"mean_iou_per_1000\":\{self.mean_iou_per_1000},\"precision_per_1000\":\{self.precision()},\"recall_per_1000\":\{self.recall()}}"
}