///|
/// An axis-aligned rectangle in continuous pixel coordinates.
pub struct Box {
x1 : Double
y1 : Double
x2 : Double
y2 : Double
} derive(Debug, Eq)
///|
pub extend Box with Debug::{to_repr}
///|
pub extend Box with Eq::{equal, not_equal}
///|
pub(all) suberror EvalError {
InvalidBox(String)
InvalidInput(String)
InvalidConfig(String)
} derive(Debug)
///|
pub extend EvalError with Debug::{to_repr}
///|
pub fn EvalError::message(self : EvalError) -> String {
match self {
InvalidBox(reason) => "invalid box: " + reason
InvalidInput(reason) => "invalid input: " + reason
InvalidConfig(reason) => "invalid configuration: " + reason
}
}
///|
fn finite(value : Double) -> Bool {
!value.is_nan() && !value.is_inf()
}
///|
/// Creates a nonempty box. Coordinates outside an image are allowed.
pub fn Box::new(
x1 : Double,
y1 : Double,
x2 : Double,
y2 : Double,
) -> Box raise EvalError {
if !finite(x1) ||
!finite(y1) ||
!finite(x2) ||
!finite(y2) ||
x2 <= x1 ||
y2 <= y1 {
raise InvalidBox("expected finite coordinates with x2 > x1 and y2 > y1")
}
let width = x2 - x1
let height = y2 - y1
if !finite(width) || !finite(height) || !finite(width * height) {
raise InvalidBox("box dimensions or area overflow")
}
{ x1, y1, x2, y2, }
}
///|
/// Creates a box from COCO-style top-left coordinates and dimensions.
pub fn Box::from_xywh(
x : Double,
y : Double,
width : Double,
height : Double,
) -> Box raise EvalError {
Box::new(x, y, x + width, y + height)
}
///|
pub fn Box::to_xywh(self : Box) -> (Double, Double, Double, Double) {
(self.x1, self.y1, self.x2 - self.x1, self.y2 - self.y1)
}
///|
pub fn Box::area(self : Box) -> Double {
(self.x2 - self.x1) * (self.y2 - self.y1)
}
///|
/// Computes IoU for two ordinary (non-crowd) boxes.
pub fn Box::iou(self : Box, other : Box) -> Double {
let width = (self.x2.min(other.x2) - self.x1.max(other.x1)).max(0.0)
let height = (self.y2.min(other.y2) - self.y1.max(other.y1)).max(0.0)
let intersection = width * height
if intersection == 0.0 {
return 0.0
}
let left_area = self.area()
let right_area = other.area()
let scale = left_area.max(right_area)
let scaled_intersection = intersection / scale
scaled_intersection /
(left_area / scale + right_area / scale - scaled_intersection)
}