mod distribution;
mod geometry;
mod gradient;
mod signal;
#[cfg(test)]
mod tests;
use ::burn::tensor::{Tensor, backend::Backend};
use crate::base::Config;
use super::{
filter,
geometry::{Contour, contour},
};
use distribution::{distribution, unit_samples};
pub fn differentiate<B: Backend>(
contours: &[Contour<B>],
signal: Tensor<B, 2>,
config: Config,
) -> Vec<contour::Arguments<B>> {
let x_sample_count = config.x_sample_count();
let y_sample_count = config.y_sample_count();
let [height, width] = signal.dims();
assert!(
!contours.is_empty() && height > 0 && width > 0,
"contours and signal dimensions must be non-empty"
);
let contours = contours
.iter()
.map(|contour| contour.clone().detach())
.collect::<Vec<_>>();
let device = signal.device();
let sample_count = height * width * x_sample_count * y_sample_count;
let samples = unit_samples(sample_count, &device);
let weights = filter::weight_image(
height,
width,
x_sample_count,
y_sample_count,
config.radius(),
&device,
);
let scale = 1.0 / (height * width) as f32;
let mut gradients = Vec::with_capacity(contours.len());
for contour in &contours {
let segment_count = contour.dims()[0];
let distribution = distribution(contour.clone());
let records = distribution.sample(samples.clone());
let boundary_geometry = geometry::evaluate(contour.clone(), &records);
let signal = signal::sample(
signal.clone(),
boundary_geometry.points(),
config.radius(),
weights.clone(),
);
let jumps = gradient::jumps(
contours.iter().cloned(),
boundary_geometry.points(),
boundary_geometry.normals(),
);
gradients.push(
gradient::segment_gradients(segment_count, &boundary_geometry, signal, jumps) * scale,
);
}
gradients
}