use teeny_core::{dtype::Float, graph::SymTensor, name_scope::name_scope};
use blocks::{
c2psa::c2psa,
c3k2::{c3k2, c3k2_psa},
concat::concat,
conv::conv,
detect::{DetectHead, DetectOutput, DualDetectOutput, detect},
sppf::sppf,
upsample::upsample,
};
pub mod blocks;
#[derive(Clone, Copy, Debug)]
pub struct Yolo26Config {
pub depth: f32,
pub width: f32,
pub mc: usize,
}
#[derive(Clone, Copy, Debug)]
pub enum Yolo26Variant {
N,
S,
M,
L,
XL,
}
impl Yolo26Variant {
pub fn config(&self) -> Yolo26Config {
match self {
Yolo26Variant::N => Yolo26Config {
depth: 0.5,
width: 0.25,
mc: 1024,
},
Yolo26Variant::S => Yolo26Config {
depth: 0.5,
width: 0.50,
mc: 1024,
},
Yolo26Variant::M => Yolo26Config {
depth: 0.5,
width: 1.00,
mc: 512,
},
Yolo26Variant::L => Yolo26Config {
depth: 1.0,
width: 1.00,
mc: 512,
},
Yolo26Variant::XL => Yolo26Config {
depth: 1.0,
width: 1.50,
mc: 512,
},
}
}
}
fn ch(base: usize, width: f32, mc: usize) -> usize {
(base.min(mc) as f32 * width).round() as usize
}
fn rep(base: usize, depth: f32) -> usize {
if base > 1 {
((base as f32 * depth).max(1.0)).round() as usize
} else {
base
}
}
fn build_neck<D: Float + Send + Sync + 'static>(
variant: &Yolo26Variant,
) -> (impl Fn(SymTensor) -> (SymTensor, SymTensor, SymTensor), usize, usize, usize) {
let cfg = variant.config();
let (d, w, mc) = (cfg.depth, cfg.width, cfg.mc);
let c0 = ch(64, w, mc);
let c1 = ch(128, w, mc);
let c2 = ch(256, w, mc);
let c3 = ch(512, w, mc);
let c4 = ch(1024, w, mc);
let n = rep(2, d);
let l0 = conv::<D>(3, c0, 3, 2);
let l1 = conv::<D>(c0, c1, 3, 2);
let l2 = c3k2::<D>(c1, c2, n, false, true, 0.25);
let l3 = conv::<D>(c2, c2, 3, 2);
let l4 = c3k2::<D>(c2, c3, n, false, true, 0.25);
let l5 = conv::<D>(c3, c3, 3, 2);
let l6 = c3k2::<D>(c3, c3, n, true, true, 0.5);
let l7 = conv::<D>(c3, c4, 3, 2);
let l8 = c3k2::<D>(c4, c4, n, true, true, 0.5);
let l9 = sppf::<D>(c4, c4, true);
let l10 = c2psa::<D>(c4, c4, n, 0.5);
let up = upsample(2, 2);
let cat = concat();
let l13 = c3k2::<D>(c4 + c3, c3, n, true, true, 0.5);
let l16 = c3k2::<D>(c3 + c3, c2, n, true, true, 0.5);
let l17 = conv::<D>(c2, c2, 3, 2);
let l19 = c3k2::<D>(c2 + c3, c3, n, true, true, 0.5);
let l20 = conv::<D>(c3, c3, 3, 2);
let l22 = c3k2_psa::<D>(c3 + c4, c4, 1, true, 0.5);
let neck_fn = move |x: SymTensor| -> (SymTensor, SymTensor, SymTensor) {
let x = { let _g = name_scope("model.0"); l0(x) };
let x = { let _g = name_scope("model.1"); l1(x) };
let x = { let _g = name_scope("model.2"); l2(x) };
let x = { let _g = name_scope("model.3"); l3(x) };
let p3 = { let _g = name_scope("model.4"); l4(x) };
let x = { let _g = name_scope("model.5"); l5(p3.clone()) };
let p4 = { let _g = name_scope("model.6"); l6(x) };
let x = { let _g = name_scope("model.7"); l7(p4.clone()) };
let x = { let _g = name_scope("model.8"); l8(x) };
let x = { let _g = name_scope("model.9"); l9(x) };
let p5 = { let _g = name_scope("model.10"); l10(x) };
let x = up(p5.clone());
let x = cat(vec![x, p4]);
let nk4 = { let _g = name_scope("model.13"); l13(x) };
let x = up(nk4.clone());
let x = cat(vec![x, p3]);
let p3d = { let _g = name_scope("model.16"); l16(x) };
let x = { let _g = name_scope("model.17"); l17(p3d.clone()) };
let x = cat(vec![x, nk4]);
let p4d = { let _g = name_scope("model.19"); l19(x) };
let x = { let _g = name_scope("model.20"); l20(p4d.clone()) };
let x = cat(vec![x, p5]);
let p5d = { let _g = name_scope("model.22"); l22(x) };
(p3d, p4d, p5d)
};
(neck_fn, c2, c3, c4)
}
pub fn yolo26<D: Float + Send + Sync + 'static>(
nc: usize,
variant: &Yolo26Variant,
head: DetectHead,
) -> impl Fn(SymTensor) -> DetectOutput {
let (neck, c2, c3, c4) = build_neck::<D>(variant);
let head_fn = detect::<D>(nc, &[c2, c3, c4], head);
move |x: SymTensor| {
let (p3d, p4d, p5d) = neck(x);
{ let _g = name_scope("model.23"); head_fn(vec![p3d, p4d, p5d]) }
}
}
pub fn yolo26_dual<D: Float + Send + Sync + 'static>(
nc: usize,
variant: &Yolo26Variant,
) -> impl Fn(SymTensor) -> DualDetectOutput {
let (neck, c2, c3, c4) = build_neck::<D>(variant);
let head_o2m = detect::<D>(nc, &[c2, c3, c4], DetectHead::OneToMany);
let head_o2o = detect::<D>(nc, &[c2, c3, c4], DetectHead::OneToOne);
move |x: SymTensor| {
let (p3d, p4d, p5d) = neck(x);
let one2many = {
let _g = name_scope("model.23");
head_o2m(vec![p3d.clone(), p4d.clone(), p5d.clone()])
};
let one2one = {
let _g = name_scope("model.23");
head_o2o(vec![p3d, p4d, p5d])
};
DualDetectOutput { one2many, one2one }
}
}