use teeny_core::{dtype::Float, graph::{Op, SymTensor}, name_scope::name_scope};
use super::conv::{conv, conv_plain, dwconv};
pub struct DetectOutput {
pub boxes: SymTensor,
pub scores: SymTensor,
}
pub struct DualDetectOutput {
pub one2many: DetectOutput,
pub one2one: DetectOutput,
}
fn channel_cat_flat(tensors: Vec<SymTensor>) -> SymTensor {
let c_total: usize = tensors.iter()
.map(|t| {
t.shape[1].unwrap_or(0)
* t.shape[2].unwrap_or(1)
* t.shape[3].unwrap_or(1)
})
.sum();
let first = &tensors[0];
let shape = vec![first.shape[0], Some(c_total), Some(1), Some(1)];
let inputs: Vec<usize> = tensors.iter().map(|t| t.node_id).collect();
let node_id = first.graph.borrow_mut().add_node(
Op::ChannelCat { c_total },
inputs,
first.dtype,
shape.clone(),
);
SymTensor { node_id, graph: first.graph.clone(), dtype: first.dtype, shape }
}
pub enum DetectHead {
OneToMany,
OneToOne,
}
pub fn detect<D: Float + 'static>(
nc: usize,
ch: &[usize],
head: DetectHead,
) -> impl Fn(Vec<SymTensor>) -> DetectOutput + use<D> {
let reg_max = 1usize;
let c2 = [16usize, ch[0] / 4, reg_max * 4].into_iter().max().unwrap();
let c3 = ch[0].max(nc.min(100));
let (cv2_prefix, cv3_prefix) = match head {
DetectHead::OneToMany => ("cv2", "cv3"),
DetectHead::OneToOne => ("one2one_cv2", "one2one_cv3"),
};
let cv2: Vec<Box<dyn Fn(SymTensor) -> SymTensor>> = ch
.iter()
.map(|&c_in| {
let l1 = conv::<D>(c_in, c2, 3, 1);
let l2 = conv::<D>(c2, c2, 3, 1);
let l3 = conv_plain::<D>(c2, 4 * reg_max, 1, 1);
Box::new(move |x: SymTensor| {
let x = { let _g = name_scope("0"); l1(x) };
let x = { let _g = name_scope("1"); l2(x) };
{ let _g = name_scope("2"); l3(x) }
}) as Box<dyn Fn(SymTensor) -> SymTensor>
})
.collect();
let cv3: Vec<Box<dyn Fn(SymTensor) -> SymTensor>> = ch
.iter()
.map(|&c_in| {
let dw1 = dwconv::<D>(c_in, 3, 1);
let pw1 = conv::<D>(c_in, c3, 1, 1);
let dw2 = dwconv::<D>(c3, 3, 1);
let pw2 = conv::<D>(c3, c3, 1, 1);
let out = conv_plain::<D>(c3, nc, 1, 1);
Box::new(move |x: SymTensor| {
let x = { let _g = name_scope("0.0"); dw1(x) };
let x = { let _g = name_scope("0.1"); pw1(x) };
let x = { let _g = name_scope("1.0"); dw2(x) };
let x = { let _g = name_scope("1.1"); pw2(x) };
{ let _g = name_scope("2"); out(x) }
}) as Box<dyn Fn(SymTensor) -> SymTensor>
})
.collect();
move |feats: Vec<SymTensor>| {
let box_tensors: Vec<SymTensor> = feats.iter().enumerate()
.zip(cv2.iter())
.map(|((i, x), f)| { let _g = name_scope(format!("{cv2_prefix}.{i}")); f(x.clone()) })
.collect();
let cls_tensors: Vec<SymTensor> = feats.iter().enumerate()
.zip(cv3.iter())
.map(|((i, x), f)| { let _g = name_scope(format!("{cv3_prefix}.{i}")); f(x.clone()) })
.collect();
let boxes = channel_cat_flat(box_tensors);
let scores = channel_cat_flat(cls_tensors);
DetectOutput { boxes, scores }
}
}