leibniz 0.2.0

The package provides a differentiable vector graphics rasterization loss.
Documentation
//! Boundary gradients.

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};

/// Differentiate one rendered shape.
///
/// Returns one segment-gradient tensor per contour.
///
/// # Panics
///
/// Panics if there are no contours, if the signal is empty, if the
/// configuration is invalid, or if any contour is invalid.
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
}