use std::ffi::c_int;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct EfDetectBox {
pub xmin: f32,
pub ymin: f32,
pub xmax: f32,
pub ymax: f32,
pub score: f32,
pub label: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct EfSegmentation {
pub xmin: f32,
pub ymin: f32,
pub xmax: f32,
pub ymax: f32,
pub mask: *const u8,
pub width: u32,
pub height: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct EfMergeConfig {
pub metric: u32,
pub threshold: f32,
pub class_agnostic: c_int,
pub max_det: usize,
pub score_threshold: f32,
pub mode: u32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TilePlacement {
pub index: usize,
pub count: usize,
pub origin: (f32, f32),
pub crop_size: (f32, f32),
pub content_region: Option<[f32; 4]>,
pub frame_dims: (f32, f32),
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct EfTilePlacement {
pub index: usize,
pub count: usize,
pub origin_x: f32,
pub origin_y: f32,
pub crop_width: f32,
pub crop_height: f32,
pub has_content_region: c_int,
pub content_region: [f32; 4],
pub frame_width: f32,
pub frame_height: f32,
}
impl From<&TilePlacement> for EfTilePlacement {
fn from(p: &TilePlacement) -> Self {
let (has_content_region, content_region) = match p.content_region {
Some(lb) => (1, lb),
None => (0, [0.0; 4]),
};
Self {
index: p.index,
count: p.count,
origin_x: p.origin.0,
origin_y: p.origin.1,
crop_width: p.crop_size.0,
crop_height: p.crop_size.1,
has_content_region,
content_region,
frame_width: p.frame_dims.0,
frame_height: p.frame_dims.1,
}
}
}
impl From<&EfTilePlacement> for TilePlacement {
fn from(p: &EfTilePlacement) -> Self {
Self {
index: p.index,
count: p.count,
origin: (p.origin_x, p.origin_y),
crop_size: (p.crop_width, p.crop_height),
content_region: if p.has_content_region != 0 {
Some(p.content_region)
} else {
None
},
frame_dims: (p.frame_width, p.frame_height),
}
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EfFit {
Stretch = 0,
Letterbox = 1,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EfLetterboxPlacement {
Center = 0,
TopLeft = 1,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EfPadSpace {
Pixel = 0,
Normalized = 1,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EfInterpolation {
Bilinear = 0,
Nearest = 1,
Bicubic = 2,
Area = 3,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct EfPreprocessSpec {
pub width: u32,
pub height: u32,
pub fit: u32,
pub placement: u32,
pub pad_space: u32,
pub pad: [f32; 3],
pub mean: [f32; 3],
pub std: [f32; 3],
pub interpolation: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_crate_has_no_dependencies_and_never_may() {
let manifest = include_str!("../Cargo.toml");
assert!(
!manifest.contains("[dependencies."),
"dependency sub-tables are dependencies too"
);
let deps = manifest
.split("[dependencies]")
.nth(1)
.expect("manifest has a [dependencies] table");
let body = deps.split("\n[").next().unwrap_or(deps);
let lines: Vec<&str> = body
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();
assert!(
lines.is_empty(),
"edgefirst-decoder-abi grew dependencies: {lines:?}"
);
}
#[test]
fn detect_box_is_six_packed_32_bit_fields() {
assert_eq!(std::mem::size_of::<EfDetectBox>(), 24);
assert_eq!(std::mem::align_of::<EfDetectBox>(), 4);
}
#[test]
fn segmentation_layout_is_pinned() {
assert_eq!(std::mem::size_of::<EfSegmentation>(), 32);
assert_eq!(std::mem::align_of::<EfSegmentation>(), 8);
}
#[test]
fn merge_config_layout_is_pinned() {
assert_eq!(std::mem::size_of::<EfMergeConfig>(), 32);
assert_eq!(std::mem::align_of::<EfMergeConfig>(), 8);
assert_eq!(std::mem::offset_of!(EfMergeConfig, metric), 0);
assert_eq!(std::mem::offset_of!(EfMergeConfig, threshold), 4);
assert_eq!(std::mem::offset_of!(EfMergeConfig, class_agnostic), 8);
assert_eq!(std::mem::offset_of!(EfMergeConfig, max_det), 16);
assert_eq!(std::mem::offset_of!(EfMergeConfig, score_threshold), 24);
assert_eq!(std::mem::offset_of!(EfMergeConfig, mode), 28);
}
#[test]
fn tile_placement_layout_is_pinned() {
assert_eq!(std::mem::size_of::<EfTilePlacement>(), 64);
assert_eq!(std::mem::align_of::<EfTilePlacement>(), 8);
}
#[test]
fn preprocess_spec_layout_is_pinned() {
type S = EfPreprocessSpec;
assert_eq!(std::mem::size_of::<S>(), 60);
assert_eq!(std::mem::align_of::<S>(), 4);
assert_eq!(std::mem::offset_of!(S, width), 0);
assert_eq!(std::mem::offset_of!(S, height), 4);
assert_eq!(std::mem::offset_of!(S, fit), 8);
assert_eq!(std::mem::offset_of!(S, placement), 12);
assert_eq!(std::mem::offset_of!(S, pad_space), 16);
assert_eq!(std::mem::offset_of!(S, pad), 20);
assert_eq!(std::mem::offset_of!(S, mean), 32);
assert_eq!(std::mem::offset_of!(S, std), 44);
assert_eq!(std::mem::offset_of!(S, interpolation), 56);
}
#[test]
fn tile_placement_offsets_are_pinned() {
type P = EfTilePlacement;
assert_eq!(std::mem::offset_of!(P, has_content_region), 32);
assert_eq!(std::mem::offset_of!(P, content_region), 36);
assert_eq!(std::mem::offset_of!(P, frame_width), 52);
}
#[test]
fn detect_h_declares_the_preprocess_vocabulary() {
let h = include_str!("../include/edgefirst/detect.h");
let codes = [
("EF_FIT_STRETCH", EfFit::Stretch as u32),
("EF_FIT_LETTERBOX", EfFit::Letterbox as u32),
(
"EF_LETTERBOX_PLACEMENT_CENTER",
EfLetterboxPlacement::Center as u32,
),
(
"EF_LETTERBOX_PLACEMENT_TOP_LEFT",
EfLetterboxPlacement::TopLeft as u32,
),
("EF_PAD_SPACE_PIXEL", EfPadSpace::Pixel as u32),
("EF_PAD_SPACE_NORMALIZED", EfPadSpace::Normalized as u32),
(
"EF_INTERPOLATION_BILINEAR",
EfInterpolation::Bilinear as u32,
),
("EF_INTERPOLATION_NEAREST", EfInterpolation::Nearest as u32),
("EF_INTERPOLATION_BICUBIC", EfInterpolation::Bicubic as u32),
("EF_INTERPOLATION_AREA", EfInterpolation::Area as u32),
];
for (name, code) in codes {
let line = format!("{name} = {code},");
assert!(h.contains(&line), "detect.h does not declare `{line}`");
}
assert!(h.contains("typedef struct ef_preprocess_spec {"));
}
}