use rayon::prelude::*;
use std::sync::Mutex;
#[derive(Default)]
pub struct BufferPool {
bufs: Mutex<Vec<Vec<f32>>>,
}
impl BufferPool {
pub fn take(&self, len: usize) -> Vec<f32> {
let mut g = self.bufs.lock().unwrap_or_else(|e| e.into_inner());
let pos = g.iter().enumerate().filter(|(_, b)| b.capacity() >= len).min_by_key(|(_, b)| b.capacity()).map(|(i, _)| i);
let mut v = match pos {
Some(i) => g.swap_remove(i),
None => g.pop().unwrap_or_default(),
};
drop(g);
v.clear();
v.resize(len, 0.0);
v
}
pub fn give(&self, v: Vec<f32>) {
if v.capacity() == 0 {
return;
}
let mut g = self.bufs.lock().unwrap_or_else(|e| e.into_inner());
if g.len() < 64 {
g.push(v);
}
}
}
pub fn gaussian_kernel(sigma: f32) -> Vec<f32> {
let r = ((4.0 * sigma - 0.5).ceil() as i64).clamp(2, 16) as usize;
let mut k: Vec<f32> = (0..=2 * r)
.map(|i| {
let d = i as f32 - r as f32;
(-(d * d) / (2.0 * sigma * sigma)).exp()
})
.collect();
let s: f32 = k.iter().sum();
k.iter_mut().for_each(|v| *v /= s);
k
}
#[allow(clippy::needless_range_loop)]
pub fn gaussian_blur(src: &[f32], dst: &mut [f32], tmp: &mut [f32], w: usize, h: usize, sigma: f32) {
let k = gaussian_kernel(sigma);
let r = k.len() / 2;
tmp.par_chunks_mut(w).zip(src.par_chunks(w)).for_each_init(
|| vec![0f32; w + 2 * r],
|pad, (out, row)| {
let first = row[0];
let last = row[w - 1];
pad[..r].fill(first);
pad[r..r + w].copy_from_slice(row);
pad[r + w..].fill(last);
let kc = k[r];
for (o, p) in out.iter_mut().zip(&pad[r..r + w]) {
*o = kc * p;
}
for i in 0..r {
let ki = k[i];
let a = &pad[i..i + w];
let b = &pad[2 * r - i..2 * r - i + w];
for ((o, x), y) in out.iter_mut().zip(a).zip(b) {
*o += ki * (x + y);
}
}
},
);
let tmp: &[f32] = tmp;
dst.par_chunks_mut(w).enumerate().for_each(|(y, out)| {
let row = |yy: isize| -> &[f32] {
let yy = yy.clamp(0, h as isize - 1) as usize;
&tmp[yy * w..(yy + 1) * w]
};
let kc = k[r];
for (o, p) in out.iter_mut().zip(row(y as isize)) {
*o = kc * p;
}
for i in 0..r {
let ki = k[i];
let a = row(y as isize + i as isize - r as isize);
let b = row(y as isize + r as isize - i as isize);
for ((o, x), z) in out.iter_mut().zip(a).zip(b) {
*o += ki * (x + z);
}
}
});
}
pub fn upsample2(src: &[f32], w: usize, h: usize, dst: &mut [f32]) {
let w2 = 2 * w;
dst.par_chunks_mut(w2).enumerate().for_each(|(yy, out)| {
let r = yy / 2;
let a = (yy % 2) as f32 * 0.5;
let r1 = (r + 1).min(h - 1);
let row0 = &src[r * w..(r + 1) * w];
let row1 = &src[r1 * w..(r1 + 1) * w];
for c in 0..w {
let c1 = (c + 1).min(w - 1);
let top0 = row0[c];
let bot0 = row1[c];
let v0 = top0 + a * (bot0 - top0);
let v1 = row0[c1] + a * (row1[c1] - row0[c1]);
out[2 * c] = v0;
out[2 * c + 1] = v0 + 0.5 * (v1 - v0);
}
});
}
pub fn decimate2(src: &[f32], w: usize, dst: &mut [f32], w2: usize) {
dst.par_chunks_mut(w2).enumerate().for_each(|(y, out)| {
let row = &src[2 * y * w..];
for (x, o) in out.iter_mut().enumerate() {
*o = row[2 * x];
}
});
}
pub struct Octave {
pub o: i32,
pub w: usize,
pub h: usize,
pub gauss: Vec<Vec<f32>>,
}
impl Octave {
#[inline]
pub fn level(&self, l: i32) -> &[f32] {
&self.gauss[(l + 1) as usize]
}
}
#[derive(Clone, Copy, Debug)]
pub struct ScaleSpace {
pub s: usize,
pub k: f32,
pub sigma0: f32,
}
impl ScaleSpace {
pub fn new(s: usize) -> Self {
let k = 2f32.powf(1.0 / s as f32);
Self { s, k, sigma0: 1.6 * k }
}
pub fn sigma(&self, l: f32) -> f32 {
self.sigma0 * self.k.powf(l)
}
pub fn sigma_inc(&self, l: i32) -> f32 {
let a = self.sigma(l as f32);
let b = self.sigma(l as f32 - 1.0);
(a * a - b * b).sqrt()
}
}
pub fn auto_num_octaves(w0: usize, h0: usize) -> usize {
let m = w0.min(h0).max(1);
let l = (usize::BITS - 1 - m.leading_zeros()) as i64;
(l - 3).max(1) as usize
}
#[allow(clippy::too_many_arguments)]
pub fn build_pyramid(
base: Vec<f32>,
w0: usize,
h0: usize,
o_min: i32,
num_octaves: usize,
ss: ScaleSpace,
sigma_nominal: f32,
pool: &BufferPool,
) -> Vec<Octave> {
let n_levels = ss.s + 3;
let mut octaves: Vec<Octave> = Vec::with_capacity(num_octaves);
let mut tmp = pool.take(w0 * h0);
let mut src = base;
let (mut w, mut h) = (w0, h0);
for oi in 0..num_octaves {
let o = o_min + oi as i32;
let mut gauss = Vec::with_capacity(n_levels);
if oi == 0 {
let s_first = ss.sigma(-1.0);
let sn = sigma_nominal / 2f32.powi(o_min);
let mut g0 = pool.take(w * h);
if s_first > sn {
gaussian_blur(&src, &mut g0, &mut tmp[..w * h], w, h, (s_first * s_first - sn * sn).sqrt());
pool.give(std::mem::take(&mut src));
} else {
g0.copy_from_slice(&src);
}
gauss.push(g0);
} else {
let prev = &octaves[oi - 1];
let (pw, ph) = (prev.w, prev.h);
let (nw, nh) = (pw / 2, ph / 2);
let mut g0 = pool.take(nw * nh);
decimate2(prev.level(ss.s as i32 - 1), pw, &mut g0, nw);
w = nw;
h = nh;
gauss.push(g0);
}
for l in 0..=(ss.s as i32 + 1) {
let mut g = pool.take(w * h);
gaussian_blur(gauss.last().expect("레벨 있음"), &mut g, &mut tmp[..w * h], w, h, ss.sigma_inc(l));
gauss.push(g);
}
octaves.push(Octave { o, w, h, gauss });
if w / 2 < 4 || h / 2 < 4 {
break;
}
}
pool.give(tmp);
octaves
}