- cleanup
This commit is contained in:
+2
-3
@@ -12,13 +12,12 @@ documentation = "https://docs.rs/image-registration"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["affine", "transformation", "ndarray"]
|
keywords = ["affine", "transformation", "ndarray"]
|
||||||
categories = ["multimedia::images", "science"]
|
categories = ["multimedia::images", "science"]
|
||||||
exclude = ["/tests"]
|
exclude = ["/test_files"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
algos = "0.6"
|
algos = "0.6"
|
||||||
itertools = "0.15"
|
itertools = "0.15"
|
||||||
ndarray = { version = "0.17", features = ["rayon"] }
|
ndarray = { version = "0.17", features = ["rayon"] }
|
||||||
ndarray-npy = { version = "0.10.0", features = ["npz"] }
|
|
||||||
ndrustfft = "0.6"
|
ndrustfft = "0.6"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
rayon = "1"
|
rayon = "1"
|
||||||
@@ -26,11 +25,11 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
rand = "0.10"
|
rand = "0.10"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
tiffwrite = "2026.6.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
tiff = "0.11"
|
tiff = "0.11"
|
||||||
|
tiffwrite = "2026.6.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|||||||
+4
-243
@@ -1,17 +1,14 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::filter::{fft, ifft};
|
|
||||||
use crate::par_indexed_iter::ParallelIndexedIterMut;
|
use crate::par_indexed_iter::ParallelIndexedIterMut;
|
||||||
use crate::transform::{Transform, transform_point};
|
use crate::transform::{Transform, transform_point};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ndarray::{
|
use ndarray::{
|
||||||
Array, Array1, Array2, ArrayD, ArrayViewMut1, AsArray, Axis, Dimension, IntoDimension, IxDyn,
|
Array, Array1, Array2, ArrayD, ArrayViewMut1, AsArray, Axis, Dimension, IntoDimension, IxDyn,
|
||||||
SliceInfoElem,
|
|
||||||
};
|
};
|
||||||
use num::traits::FloatConst;
|
use num::traits::FloatConst;
|
||||||
use num::{Complex, cast::AsPrimitive, complex::ComplexFloat};
|
use num::{Complex, cast::AsPrimitive, complex::ComplexFloat};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use std::ops::{Deref, MulAssign};
|
use std::ops::{Deref, MulAssign};
|
||||||
use tiffwrite::IJTiffFile;
|
|
||||||
|
|
||||||
fn in_shape(shape_f: &[f64], v: &[f64]) -> bool {
|
fn in_shape(shape_f: &[f64], v: &[f64]) -> bool {
|
||||||
shape_f
|
shape_f
|
||||||
@@ -786,38 +783,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subsample2(&self) -> Result<Self, Error> {
|
|
||||||
let shape = self.coefficients.shape();
|
|
||||||
let v = Self::reduction_filter2(shape)?;
|
|
||||||
let c = fft(self.coefficients.view())?;
|
|
||||||
let mut t = IJTiffFile::new(std::env::home_dir().unwrap().join("tmp/fft.tif")).unwrap();
|
|
||||||
let v0 = v.mapv(|i| i.re).into_dimensionality()?;
|
|
||||||
let c0 = c.mapv(|i| i.re).into_dimensionality()?;
|
|
||||||
let filtered = ifft((v * c).view())?.into_dyn().mapv(|i| i.re);
|
|
||||||
t.save(v0.view(), 0, 0, 0).unwrap();
|
|
||||||
t.save(c0.view(), 1, 0, 0).unwrap();
|
|
||||||
t.save(filtered.view().into_dimensionality()?.view(), 2, 0, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let slice = vec![
|
|
||||||
SliceInfoElem::Slice {
|
|
||||||
start: 0,
|
|
||||||
end: None,
|
|
||||||
step: 2
|
|
||||||
};
|
|
||||||
filtered.ndim()
|
|
||||||
];
|
|
||||||
let coefficients = filtered
|
|
||||||
.slice(slice.as_slice())
|
|
||||||
.to_owned()
|
|
||||||
.into_dimensionality()?;
|
|
||||||
Ok(BSpline {
|
|
||||||
coefficients,
|
|
||||||
max_number_interpolation_points: self.max_number_interpolation_points,
|
|
||||||
points_to_index: self.points_to_index.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evaluate(&self, index: Array<f64, D::Larger>) -> Result<Array<f64, D>, Error>
|
pub fn evaluate(&self, index: Array<f64, D::Larger>) -> Result<Array<f64, D>, Error>
|
||||||
where
|
where
|
||||||
D: Dimension,
|
D: Dimension,
|
||||||
@@ -851,7 +816,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// table III The L2 Polynomial Spline Pyramid, Unser et al. 1993
|
/// table III The L2 Polynomial Spline Pyramid, Unser et al. 1993
|
||||||
pub(crate) fn reduction_filter2(shape: &[usize]) -> Result<Array<Complex<f64>, D>, Error> {
|
pub(crate) fn reduction_filter(shape: &[usize]) -> Result<Array<Complex<f64>, D>, Error> {
|
||||||
let mut filter: Array<Complex<f64>, D> = ArrayD::ones(shape).into_dimensionality()?;
|
let mut filter: Array<Complex<f64>, D> = ArrayD::ones(shape).into_dimensionality()?;
|
||||||
for (i, &s) in shape.iter().enumerate() {
|
for (i, &s) in shape.iter().enumerate() {
|
||||||
let si = s as isize;
|
let si = s as isize;
|
||||||
@@ -880,226 +845,22 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cubic_bspline(x: f64) -> f64 {
|
pub(crate) fn cubic_bspline(x: f64) -> f64 {
|
||||||
(x + 2.0).max(0.0).powi(3) / 6.0 - (x + 1.0).max(0.0).powi(3) / 1.5 + x.max(0.0).powi(3)
|
(x + 2.0).max(0.0).powi(3) / 6.0 - (x + 1.0).max(0.0).powi(3) / 1.5 + x.max(0.0).powi(3)
|
||||||
- (x - 1.0).max(0.0).powi(3) / 1.5
|
- (x - 1.0).max(0.0).powi(3) / 1.5
|
||||||
+ (x - 2.0).max(0.0).powi(3) / 6.0
|
+ (x - 2.0).max(0.0).powi(3) / 6.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn square_bspline(x: f64) -> f64 {
|
pub(crate) fn square_bspline(x: f64) -> f64 {
|
||||||
(x + 1.5).max(0.0).powi(2) / 2.0 - (x + 0.5).max(0.0).powi(2) * 1.5
|
(x + 1.5).max(0.0).powi(2) / 2.0 - (x + 0.5).max(0.0).powi(2) * 1.5
|
||||||
+ (x - 0.5).max(0.0).powi(2) * 1.5
|
+ (x - 0.5).max(0.0).powi(2) * 1.5
|
||||||
- (x - 1.5).max(0.0).powi(2) / 2.0
|
- (x - 1.5).max(0.0).powi(2) / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn square_bspline_integral(x: f64) -> f64 {
|
pub(crate) fn square_bspline_integral(x: f64) -> f64 {
|
||||||
(x + 1.5).max(0.0).powi(3) / 6.0 - (x + 0.5).max(0.0).powi(3) / 2.0
|
(x + 1.5).max(0.0).powi(3) / 6.0 - (x + 0.5).max(0.0).powi(3) / 2.0
|
||||||
+ (x - 0.5).max(0.0).powi(3) / 2.0
|
+ (x - 0.5).max(0.0).powi(3) / 2.0
|
||||||
- (x - 1.5).max(0.0).powi(3) / 6.0
|
- (x - 1.5).max(0.0).powi(3) / 6.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cubic_bspline_parzen_1d(
|
|
||||||
data: &[f64],
|
|
||||||
first_bin: f64,
|
|
||||||
last_bin: f64,
|
|
||||||
n_bins: usize,
|
|
||||||
e: f64,
|
|
||||||
) -> (Array1<f64>, Array1<f64>, Array1<f64>) {
|
|
||||||
let n_bins_i = n_bins as isize;
|
|
||||||
let mut hist = Array1::zeros(n_bins);
|
|
||||||
let mut grad = Array1::zeros(n_bins);
|
|
||||||
let d = ((n_bins - 1) as f64) / (last_bin - first_bin);
|
|
||||||
let m = (2.0 * d / e) as isize;
|
|
||||||
let bins = Array1::linspace(first_bin, last_bin, n_bins);
|
|
||||||
for x in data {
|
|
||||||
let n = (d * (x - first_bin)).round() as isize;
|
|
||||||
for i in (n - m)..=(n + m) {
|
|
||||||
if (0 <= i) && (i < n_bins_i) {
|
|
||||||
let j = i as usize;
|
|
||||||
let k = (x - bins[j]) / e;
|
|
||||||
hist[j] += cubic_bspline(k);
|
|
||||||
grad[j] += square_bspline(k - 0.5) - square_bspline(k + 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let alpha = (data.len() as f64) * e * d;
|
|
||||||
(bins, hist / alpha, grad / alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cubic_bspline_joint_parzen(
|
|
||||||
data_a: &[f64],
|
|
||||||
data_b: &[f64],
|
|
||||||
first_bin: f64,
|
|
||||||
last_bin: f64,
|
|
||||||
n_bins: usize,
|
|
||||||
e: f64,
|
|
||||||
) -> (Array1<f64>, Array2<f64>, Array2<f64>, Array2<f64>) {
|
|
||||||
let n_bins_i = n_bins as isize;
|
|
||||||
let mut hist = Array2::zeros([n_bins, n_bins]);
|
|
||||||
let mut grad_a = Array2::zeros([n_bins, n_bins]);
|
|
||||||
let mut grad_b = Array2::zeros([n_bins, n_bins]);
|
|
||||||
let d = ((n_bins - 1) as f64) / (last_bin - first_bin);
|
|
||||||
let m = (2.0 * d / e) as isize;
|
|
||||||
let bins = Array1::linspace(first_bin, last_bin, n_bins);
|
|
||||||
for xa in data_a {
|
|
||||||
for xb in data_b {
|
|
||||||
let na = (d * (xa - first_bin)).round() as isize;
|
|
||||||
let nb = (d * (xb - first_bin)).round() as isize;
|
|
||||||
for ia in (na - m)..=(na + m) {
|
|
||||||
for ib in (nb - m)..=(nb + m) {
|
|
||||||
if (0 <= ia) && (ia < n_bins_i) && (0 <= ib) && (ib < n_bins_i) {
|
|
||||||
let ja = ia as usize;
|
|
||||||
let jb = ib as usize;
|
|
||||||
let ka = (xa - bins[ja]) / e;
|
|
||||||
let kb = (xb - bins[jb]) / e;
|
|
||||||
let csa = cubic_bspline(ka);
|
|
||||||
let csb = cubic_bspline(kb);
|
|
||||||
hist[[ja, jb]] += csa * csb;
|
|
||||||
grad_a[[ja, jb]] +=
|
|
||||||
(square_bspline(ka - 0.5) - square_bspline(ka + 0.5)) * csb;
|
|
||||||
grad_b[[ja, jb]] +=
|
|
||||||
(square_bspline(kb - 0.5) - square_bspline(kb + 0.5)) * csa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let alpha = (data_a.len() as f64) * e * d * (data_b.len() as f64) * e * d;
|
|
||||||
(bins, hist / alpha, grad_a / alpha, grad_b / alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cubic_bspline_joint_parzen_grad_b(
|
|
||||||
data_a: &[f64],
|
|
||||||
data_b: &[f64],
|
|
||||||
first_bin: f64,
|
|
||||||
last_bin: f64,
|
|
||||||
n_bins: usize,
|
|
||||||
e: f64,
|
|
||||||
) -> (Array1<f64>, Array2<f64>, Array2<f64>) {
|
|
||||||
let n_bins_i = n_bins as isize;
|
|
||||||
let mut hist = Array2::zeros([n_bins, n_bins]);
|
|
||||||
let mut grad_b = Array2::zeros([n_bins, n_bins]);
|
|
||||||
let d = ((n_bins - 1) as f64) / (last_bin - first_bin);
|
|
||||||
let m = (2.0 * d / e) as isize;
|
|
||||||
let bins = Array1::linspace(first_bin, last_bin, n_bins);
|
|
||||||
for (xa, xb) in data_a.iter().zip_eq(data_b.iter()) {
|
|
||||||
let na = (d * (xa - first_bin)).round() as isize;
|
|
||||||
let nb = (d * (xb - first_bin)).round() as isize;
|
|
||||||
for ia in (na - m)..=(na + m) {
|
|
||||||
if (0 <= ia) && (ia < n_bins_i) {
|
|
||||||
let ja = ia as usize;
|
|
||||||
let ka = (xa - bins[ja]) / e;
|
|
||||||
let csa = cubic_bspline(ka);
|
|
||||||
for ib in (nb - m)..=(nb + m) {
|
|
||||||
if (0 <= ib) && (ib < n_bins_i) {
|
|
||||||
let jb = ib as usize;
|
|
||||||
let kb = (xb - bins[jb]) / e;
|
|
||||||
hist[[ja, jb]] += csa * cubic_bspline(kb);
|
|
||||||
grad_b[[ja, jb]] +=
|
|
||||||
csa * (square_bspline(kb - 0.5) - square_bspline(kb + 0.5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let alpha = (data_a.len() as f64) * e * d;
|
|
||||||
// println!("alpha: {}", alpha);
|
|
||||||
(bins, hist / alpha, grad_b / alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{BSpline, BSplineTrait, cubic_bspline_joint_parzen_grad_b};
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::julia_image;
|
|
||||||
use ndarray::{Array1, Array3, Ix1, array, s};
|
|
||||||
use ndarray_npy::NpzWriter;
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bspline_grad() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let a = vec![0.0, 0.0, 1.0, 0.0, 1.0, 5.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let first_bin = 0.0;
|
|
||||||
let last_bin = 5.0;
|
|
||||||
let n_bins = 3;
|
|
||||||
let e = (last_bin - first_bin) / (n_bins - 1) as f64;
|
|
||||||
let (bins, jpdf, _d_jpdf_m) =
|
|
||||||
cubic_bspline_joint_parzen_grad_b(&a, &a, first_bin, last_bin, n_bins, e);
|
|
||||||
println!("bins: {:?}, jpdf: {:?}", bins, jpdf);
|
|
||||||
println!("sum: {}", jpdf.sum());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reduction_filter() -> Result<(), Error> {
|
|
||||||
let _v = BSpline::<3, Ix1>::reduction_filter2(&[10]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn subsample2() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let shape = [600, 800];
|
|
||||||
let transform = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let center = shape
|
|
||||||
.iter()
|
|
||||||
.map(|&s| (s - 1) as f64 / 2.0)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let center = [center[0], center[1]];
|
|
||||||
let k = julia_image(&shape, &transform, ¢er, &[-0.4, 0.6]);
|
|
||||||
let b = BSpline::<3, _>::new(k.view());
|
|
||||||
let b2 = b.subsample2()?;
|
|
||||||
let shape = k.shape().iter().map(|i| i / 2).collect::<Vec<_>>();
|
|
||||||
let mut xy = Array3::zeros((2, shape[0], shape[1]));
|
|
||||||
for (i, mut x) in xy
|
|
||||||
.slice_mut(s![1, .., ..])
|
|
||||||
.columns_mut()
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
x.fill(i as f64);
|
|
||||||
}
|
|
||||||
for (i, mut y) in xy
|
|
||||||
.slice_mut(s![0, .., ..])
|
|
||||||
.rows_mut()
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
y.fill(i as f64);
|
|
||||||
}
|
|
||||||
let k2 = b2.evaluate(xy)?.mapv(|i| i.clamp(0.0, 255.0) as u8);
|
|
||||||
let mut t1 =
|
|
||||||
tiffwrite::IJTiffFile::new(std::env::home_dir().unwrap().join("tmp/subsample1.tif"))?;
|
|
||||||
// t1.save(b.coefficients.view(), 0, 0, 0)?;
|
|
||||||
t1.save(k.view(), 0, 0, 0)?;
|
|
||||||
|
|
||||||
let mut t2 =
|
|
||||||
tiffwrite::IJTiffFile::new(std::env::home_dir().unwrap().join("tmp/subsample2.tif"))?;
|
|
||||||
// t2.save(b2.coefficients.view(), 0, 0, 0)?;
|
|
||||||
t2.save(k2.view(), 0, 0, 0)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn derivative() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let b = BSpline::<3, _>::new(a.view());
|
|
||||||
let mut mem = b.get_mem();
|
|
||||||
let x = Array1::linspace(-0.5, 10.5, 500);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut d = Vec::new();
|
|
||||||
for i in &x {
|
|
||||||
let (vi, di) = b.evaluate_value_and_derivative_at_continuous_index(&[*i], &mut mem)?;
|
|
||||||
v.push(vi);
|
|
||||||
d.push(di[0]);
|
|
||||||
}
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
npz.add_array("x", &x)?;
|
|
||||||
npz.add_array("v", &Array1::from(v))?;
|
|
||||||
npz.add_array("d", &Array1::from(d))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ pub enum Error {
|
|||||||
SerdeYAML(#[from] serde_yaml::Error),
|
SerdeYAML(#[from] serde_yaml::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ShapeError(#[from] ndarray::ShapeError),
|
ShapeError(#[from] ndarray::ShapeError),
|
||||||
#[error(transparent)]
|
|
||||||
NpyError(#[from] ndarray_npy::WriteNpzError),
|
|
||||||
#[error("number of dimensions is not defined")]
|
#[error("number of dimensions is not defined")]
|
||||||
NumberOfDimensionsNotDefined,
|
NumberOfDimensionsNotDefined,
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-366
@@ -499,13 +499,9 @@ impl Sigma {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::filter::gaussian_smooth;
|
use crate::filter::gaussian_smooth;
|
||||||
use crate::julia_image;
|
|
||||||
use crate::transform::Transform;
|
|
||||||
use algos::OptimizationConfig;
|
use algos::OptimizationConfig;
|
||||||
use algos::optimization::{bfgs_minimize, gradient_descent_minimize};
|
use algos::optimization::{bfgs_minimize, gradient_descent_minimize};
|
||||||
use ndarray::{MeshIndex, array, meshgrid, stack};
|
use ndarray::array;
|
||||||
use ndarray_npy::NpzWriter;
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn derivative() -> Result<(), Box<dyn std::error::Error>> {
|
fn derivative() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -526,39 +522,6 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn metric() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let im_a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let im_b = array![1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0];
|
|
||||||
// let a = gaussian_smooth(im_a.view(), &[4.0])?;
|
|
||||||
// let b = gaussian_smooth(im_b.view(), &[4.0])?;
|
|
||||||
let a = im_a;
|
|
||||||
let b = im_b;
|
|
||||||
|
|
||||||
// let points = vec![vec![3.0], vec![4.0], vec![5.0], vec![6.0], vec![7.0], vec![8.0]];
|
|
||||||
// let m = MattesMetric::new_from_arrays(a.view(), b.view(), SamplingArg::FixedAt(points))?
|
|
||||||
// .with_fixed_mu(vec![Some(1.0), None]);
|
|
||||||
let m = MattesMetric::new_from_arrays(a.view(), b.view(), SamplingArg::Fixed(20), 5, 0.05)?
|
|
||||||
.with_fixed_mu(vec![Some(1.0), None]);
|
|
||||||
// let m = MattesMetric::new_all_from_arrays(im_a.view(), im_b.view())?.with_fixed_mu(vec![Some(1.0), None]);
|
|
||||||
let mu = Array1::linspace(-4.0, 2.0, 601);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut d = Vec::new();
|
|
||||||
for x in &mu {
|
|
||||||
v.push(m.evaluate(&[*x]));
|
|
||||||
d.push(m.gradient(&[*x]).unwrap()[0]);
|
|
||||||
}
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
npz.add_array("mu", &mu)?;
|
|
||||||
npz.add_array("v", &Array1::from_vec(v))?;
|
|
||||||
npz.add_array("d", &Array1::from_vec(d))?;
|
|
||||||
npz.add_array("a", &a)?;
|
|
||||||
npz.add_array("b", &b)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn metric2() -> Result<(), Box<dyn std::error::Error>> {
|
fn metric2() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let im_a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
let im_a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
||||||
@@ -597,135 +560,6 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn metric1() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let im_a = julia_image(
|
|
||||||
&[60, 80],
|
|
||||||
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
|
||||||
&[29.5, 39.5],
|
|
||||||
&[-0.8, 0.156],
|
|
||||||
)
|
|
||||||
.mapv(|i| i as f64);
|
|
||||||
let im_b = julia_image(
|
|
||||||
&[60, 80],
|
|
||||||
&[1.0, 0.0, 0.0, 1.0, 10.0, -20.0],
|
|
||||||
&[29.5, 39.5],
|
|
||||||
&[-0.8, 0.156],
|
|
||||||
)
|
|
||||||
.mapv(|i| i as f64);
|
|
||||||
let a = gaussian_smooth(im_a.view(), &[30.0, 40.0])?;
|
|
||||||
let b = gaussian_smooth(im_b.view(), &[30.0, 40.0])?;
|
|
||||||
let m = MattesMetric::new_from_arrays(a.view(), b.view(), SamplingArg::Fixed(250), 3, 0.2)?
|
|
||||||
.with_fixed_mu(vec![Some(1.0), Some(0.0), Some(0.0), Some(1.0), None, None]);
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let s = 200;
|
|
||||||
let mu = Array1::linspace(-50.0, 50.0, s);
|
|
||||||
let (mux, muy) = meshgrid((&mu, &mu), MeshIndex::XY);
|
|
||||||
let mu = stack(Axis(0), &[mux, muy])?;
|
|
||||||
let mut v = Array2::zeros([s, s]);
|
|
||||||
let mut d = Array3::zeros([2, s, s]);
|
|
||||||
for (mui, (vi, mut di)) in mu
|
|
||||||
.lanes(Axis(0))
|
|
||||||
.into_iter()
|
|
||||||
.zip_eq(v.iter_mut().zip_eq(d.lanes_mut(Axis(0))))
|
|
||||||
{
|
|
||||||
*vi = m.evaluate(mui.to_vec().as_slice());
|
|
||||||
di.assign(&Array1::from_vec(
|
|
||||||
m.gradient(mui.to_vec().as_slice()).unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
npz.add_array("a", &a)?;
|
|
||||||
npz.add_array("b", &b)?;
|
|
||||||
npz.add_array("mu", &mu)?;
|
|
||||||
npz.add_array("v", &v)?;
|
|
||||||
npz.add_array("d", &d)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn metric3() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let im_a = julia_image(
|
|
||||||
&[100, 1],
|
|
||||||
&[1.0, 0.0, 0.0, 0.01, 0.0, 0.0],
|
|
||||||
&[99.5, 0.5],
|
|
||||||
&[-0.8, 0.156],
|
|
||||||
)
|
|
||||||
.slice(s![.., 0])
|
|
||||||
.mapv(|i| i as f64);
|
|
||||||
let im_b =
|
|
||||||
Transform::new(vec![1.0, 0.0], vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
|
||||||
let a = gaussian_smooth(im_a.view(), &[25.0])?;
|
|
||||||
let b = gaussian_smooth(im_b.view(), &[25.0])?;
|
|
||||||
let m = MattesMetric::new_from_arrays(a.view(), b.view(), SamplingArg::Fixed(100), 6, 0.1)?
|
|
||||||
.with_fixed_mu(vec![None, None]);
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let s = 200;
|
|
||||||
let translate = Array1::linspace(-50.0, 50.0, s);
|
|
||||||
let scale = Array1::logspace(10.0, -1.0, 1.0, s);
|
|
||||||
let (mux, muy) = meshgrid((&scale, &translate), MeshIndex::XY);
|
|
||||||
let mu = stack(Axis(0), &[mux, muy])?;
|
|
||||||
let mut v = Array2::zeros([s, s]);
|
|
||||||
let mut d = Array3::zeros([2, s, s]);
|
|
||||||
for (mui, (vi, mut di)) in mu
|
|
||||||
.lanes(Axis(0))
|
|
||||||
.into_iter()
|
|
||||||
.zip_eq(v.iter_mut().zip_eq(d.lanes_mut(Axis(0))))
|
|
||||||
{
|
|
||||||
*vi = m.evaluate(mui.to_vec().as_slice());
|
|
||||||
di.assign(&Array1::from_vec(
|
|
||||||
m.gradient(mui.to_vec().as_slice()).unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
npz.add_array("a", &a)?;
|
|
||||||
npz.add_array("b", &b)?;
|
|
||||||
npz.add_array("mu", &mu)?;
|
|
||||||
npz.add_array("v", &v)?;
|
|
||||||
npz.add_array("d", &d)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn metric4() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let im_a = julia_image(
|
|
||||||
&[100, 1],
|
|
||||||
&[1.0, 0.0, 0.0, 0.01, 0.0, 0.0],
|
|
||||||
&[99.5, 0.5],
|
|
||||||
&[-0.8, 0.156],
|
|
||||||
)
|
|
||||||
.slice(s![.., 0])
|
|
||||||
.mapv(|i| i as f64);
|
|
||||||
let im_b =
|
|
||||||
Transform::new(vec![1.0, 0.0], vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
|
||||||
let a = gaussian_smooth(im_a.view(), &[50.0])?;
|
|
||||||
let b = gaussian_smooth(im_b.view(), &[50.0])?;
|
|
||||||
let m =
|
|
||||||
MattesMetric::new_from_arrays(a.view(), b.view(), SamplingArg::Fixed(1000), 3, 0.05)?
|
|
||||||
.with_fixed_mu(vec![None, Some(0.0)]);
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let mu = Array1::logspace(2.0, -1.0, 1.0, 1000);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut d = Vec::new();
|
|
||||||
for mui in &mu {
|
|
||||||
v.push(m.evaluate(&[*mui]));
|
|
||||||
d.push(m.gradient(&[*mui]).unwrap()[0]);
|
|
||||||
}
|
|
||||||
npz.add_array("a", &a)?;
|
|
||||||
npz.add_array("b", &b)?;
|
|
||||||
npz.add_array("mu", &mu)?;
|
|
||||||
npz.add_array("v", &Array1::from(v))?;
|
|
||||||
npz.add_array("d", &Array1::from(d))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn optimize() -> Result<(), Box<dyn std::error::Error>> {
|
fn optimize() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let im_a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
let im_a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
||||||
@@ -749,205 +583,6 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parzen_test() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let b = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let fixed = BSpline::<0, _>::new(a.view());
|
|
||||||
let moving = BSpline::<3, _>::new(b.view());
|
|
||||||
|
|
||||||
let mus = [0.0, 0.001];
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
let s = a.len() as f64;
|
|
||||||
// let center = (s - 1.0) / 2.0;
|
|
||||||
let mut rng = rand::rng();
|
|
||||||
let idx = (&mut rng)
|
|
||||||
.random_iter::<f64>()
|
|
||||||
.take(25)
|
|
||||||
.map(|i| i * s - 0.5)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for (index, mu) in mus.iter().enumerate() {
|
|
||||||
let mut mem_f = fixed.get_mem();
|
|
||||||
let mut mem_m = moving.get_mem();
|
|
||||||
let mut w = Vec::new();
|
|
||||||
let mut dw = Vec::new();
|
|
||||||
let mut a = Vec::new();
|
|
||||||
let mut b = Vec::new();
|
|
||||||
let mut db = Vec::new();
|
|
||||||
|
|
||||||
for u in idx.clone() {
|
|
||||||
let v = u + mu;
|
|
||||||
if (-0.5 < v) && (v <= s - 0.5) {
|
|
||||||
let f = fixed.evaluate_at_continuous_index(&[u], &mut mem_f)?;
|
|
||||||
let (m, d) = moving
|
|
||||||
.evaluate_value_and_derivative_at_continuous_index(&[v], &mut mem_m)?;
|
|
||||||
// let j = image_jacobian(&[v], &[center], &d);
|
|
||||||
a.push(f);
|
|
||||||
b.push(m);
|
|
||||||
db.push(d);
|
|
||||||
// the metric wouldn't be smooth if we'd drop a point abruptly,
|
|
||||||
// so apply a 0 <= weight <= 1 to points within 5% of the edge
|
|
||||||
if 20.0 * (v + 0.5) < s {
|
|
||||||
let k = 60.0 * (v + 0.5) / s - 1.5;
|
|
||||||
w.push(square_bspline_integral(k));
|
|
||||||
dw.push(vec![square_bspline(k) * 60.0 / s]);
|
|
||||||
} else if 20.0 * (v + 0.5) > 19.0 * s {
|
|
||||||
let k = 60.0 * (s - v - 0.5) / s - 1.5;
|
|
||||||
w.push(square_bspline_integral(k));
|
|
||||||
dw.push(vec![-square_bspline(k) * 60.0 / s]);
|
|
||||||
} else {
|
|
||||||
w.push(1.0);
|
|
||||||
dw.push(vec![0.0]);
|
|
||||||
}
|
|
||||||
// let dw = image_jacobian(&[v], &[center], &dw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let alpha = w.iter().sum::<f64>();
|
|
||||||
let mut dalpha = [0.0];
|
|
||||||
for i in dw.iter() {
|
|
||||||
for (j, a) in i.iter().zip_eq(dalpha.iter_mut()) {
|
|
||||||
*a += j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (j, dj) = parzen(alpha, &dalpha, &a, &b, &db, &w, &dw, 0.0, 2.0, 5);
|
|
||||||
let db = db.iter().map(|i| i[0]).collect::<Vec<_>>();
|
|
||||||
let dw = dw.iter().map(|i| i[0]).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
npz.add_array(format!("idx{}", index), &Array1::from_vec(idx.clone()))?;
|
|
||||||
npz.add_array(format!("a{}", index), &Array1::from_vec(a))?;
|
|
||||||
npz.add_array(format!("b{}", index), &Array1::from_vec(b))?;
|
|
||||||
npz.add_array(format!("db{}", index), &Array1::from_vec(db))?;
|
|
||||||
npz.add_array(format!("w{}", index), &Array1::from_vec(w))?;
|
|
||||||
npz.add_array(format!("dw{}", index), &Array1::from_vec(dw))?;
|
|
||||||
npz.add_array(format!("j{}", index), &j)?;
|
|
||||||
npz.add_array(format!("dj{}", index), &dj)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn metric_test() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let a = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let b = array![0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0];
|
|
||||||
let fixed = BSpline::<0, _>::new(a.view());
|
|
||||||
let moving = BSpline::<3, _>::new(b.view());
|
|
||||||
|
|
||||||
let mus = Array1::linspace(-10.0, 10.0, 500);
|
|
||||||
let s = a.len() as f64;
|
|
||||||
let mut rng = rand::rng();
|
|
||||||
let idx = (&mut rng)
|
|
||||||
.random_iter::<f64>()
|
|
||||||
.take(25)
|
|
||||||
.map(|i| i * s - 0.5)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut metrics = Vec::new();
|
|
||||||
let mut dmetrics = Vec::new();
|
|
||||||
|
|
||||||
for mu in &mus {
|
|
||||||
let mut mem_f = fixed.get_mem();
|
|
||||||
let mut mem_m = moving.get_mem();
|
|
||||||
let mut w = Vec::new();
|
|
||||||
let mut dw = Vec::new();
|
|
||||||
let mut a = Vec::new();
|
|
||||||
let mut b = Vec::new();
|
|
||||||
let mut db = Vec::new();
|
|
||||||
|
|
||||||
for u in idx.clone() {
|
|
||||||
let v = u + mu;
|
|
||||||
if (-0.5 < v) && (v <= s - 0.5) {
|
|
||||||
let f = fixed.evaluate_at_continuous_index(&[u], &mut mem_f)?;
|
|
||||||
let (m, d) = moving
|
|
||||||
.evaluate_value_and_derivative_at_continuous_index(&[v], &mut mem_m)?;
|
|
||||||
// let j = image_jacobian(&[v], &[center], &d);
|
|
||||||
a.push(f);
|
|
||||||
b.push(m);
|
|
||||||
db.push(d);
|
|
||||||
// the metric wouldn't be smooth if we'd drop a point abruptly,
|
|
||||||
// so apply a 0 <= weight <= 1 to points within 5% of the edge
|
|
||||||
if 20.0 * (v + 0.5) < s {
|
|
||||||
let k = 60.0 * (v + 0.5) / s - 1.5;
|
|
||||||
w.push(square_bspline_integral(k));
|
|
||||||
dw.push(vec![square_bspline(k) * 60.0 / s]);
|
|
||||||
} else if 20.0 * (v + 0.5) > 19.0 * s {
|
|
||||||
let k = 60.0 * (s - v - 0.5) / s - 1.5;
|
|
||||||
w.push(square_bspline_integral(k));
|
|
||||||
dw.push(vec![-square_bspline(k) * 60.0 / s]);
|
|
||||||
} else {
|
|
||||||
w.push(1.0);
|
|
||||||
dw.push(vec![0.0]);
|
|
||||||
}
|
|
||||||
// let dw = image_jacobian(&[v], &[center], &dw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let alpha = w.iter().sum::<f64>();
|
|
||||||
let mut dalpha = [0.0];
|
|
||||||
for i in dw.iter() {
|
|
||||||
for (j, a) in i.iter().zip_eq(dalpha.iter_mut()) {
|
|
||||||
*a += j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (jpdf, d_jpdf_m) = parzen(alpha, &dalpha, &a, &b, &db, &w, &dw, 0.0, 2.0, 5);
|
|
||||||
|
|
||||||
let pdf_f = jpdf.axis_iter(Axis(0)).map(|i| i.sum()).collect::<Vec<_>>();
|
|
||||||
let pdf_m = jpdf.axis_iter(Axis(1)).map(|i| i.sum()).collect::<Vec<_>>();
|
|
||||||
let d_pdf_m = d_jpdf_m
|
|
||||||
.axis_iter(Axis(1))
|
|
||||||
.map(|i| i.sum())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut metric = 0.0;
|
|
||||||
let mut dmetric = vec![0.0];
|
|
||||||
let mut n;
|
|
||||||
let mut m;
|
|
||||||
|
|
||||||
for ((row, d_row), k) in jpdf
|
|
||||||
.axis_iter(Axis(0))
|
|
||||||
.zip_eq(d_jpdf_m.axis_iter(Axis(0)))
|
|
||||||
.zip_eq(pdf_f)
|
|
||||||
{
|
|
||||||
debug_assert_eq!(row.len(), d_row.shape()[0]);
|
|
||||||
debug_assert_eq!(row.len(), pdf_m.len());
|
|
||||||
|
|
||||||
for ((&j, dj), (i, di)) in row
|
|
||||||
.into_iter()
|
|
||||||
.zip_eq(d_row.axis_iter(Axis(0)))
|
|
||||||
.zip_eq(pdf_m.iter().zip(d_pdf_m.iter()))
|
|
||||||
{
|
|
||||||
// check for non-zero bin contribution
|
|
||||||
if (j > 1e-16) && (i * k > 1e-16) {
|
|
||||||
metric -= j * (j / (i * k)).ln();
|
|
||||||
n = (j / (i * k)).ln() + 1.0;
|
|
||||||
m = di * j / i;
|
|
||||||
for (d, g) in dmetric.iter_mut().zip_eq(dj) {
|
|
||||||
*d += m - n * g; // eq 23 of Thevenaz & Unser paper [3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.push(metric);
|
|
||||||
dmetrics.push(dmetric);
|
|
||||||
}
|
|
||||||
let mut npz = NpzWriter::new(File::create(
|
|
||||||
std::env::home_dir().unwrap().join("tmp/metric.npz"),
|
|
||||||
)?);
|
|
||||||
npz.add_array("mu", &mus)?;
|
|
||||||
npz.add_array("metric", &Array1::from_vec(metrics))?;
|
|
||||||
npz.add_array(
|
|
||||||
"dmetric",
|
|
||||||
&Array1::from_vec(dmetrics.iter().map(|d| d[0]).collect()),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_square_bspline_integral() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_square_bspline_integral() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let x = Array1::linspace(-2.0, 2.0, 500);
|
let x = Array1::linspace(-2.0, 2.0, 500);
|
||||||
|
|||||||
Reference in New Issue
Block a user