- tests register1 and register2 passing
This commit is contained in:
+273
-5
@@ -504,7 +504,7 @@ mod tests {
|
||||
use crate::julia_image;
|
||||
use crate::transform::Transform;
|
||||
use itertools::Itertools;
|
||||
use ndarray::s;
|
||||
use ndarray::{Ix2, s};
|
||||
use num::traits::FloatConst;
|
||||
|
||||
#[test]
|
||||
@@ -556,6 +556,167 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grad_check() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use crate::bspline::{BSpline, BSplineTrait};
|
||||
use crate::metric::{MattesMetric, SamplingArg};
|
||||
use algos::ObjectiveFunction;
|
||||
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 q = vec![0.85, 4.0];
|
||||
let im_b =
|
||||
Transform::new(q.clone(), vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
||||
|
||||
// Test with ALL points (like grad_check uses)
|
||||
let metric_all = MattesMetric::<ndarray::Ix1>::new(
|
||||
BSpline::new(im_b.view()),
|
||||
BSpline::new(im_a.view()),
|
||||
SamplingArg::FixedAt((0..100).map(|i| vec![i as f64]).collect()),
|
||||
3,
|
||||
0.05,
|
||||
)?
|
||||
.with_fixed_mu(crate::metric::FixedMu::new_none(1));
|
||||
|
||||
let identity = vec![1.0, 0.0];
|
||||
let val_i = metric_all.evaluate(&identity);
|
||||
let grad_i = metric_all.gradient(&identity).unwrap();
|
||||
println!("ALL POINTS - identity: val={}, grad={:?}", val_i, grad_i);
|
||||
let truth = vec![0.85, 4.0];
|
||||
let val_t = metric_all.evaluate(&truth);
|
||||
let grad_t = metric_all.gradient(&truth).unwrap();
|
||||
println!("ALL POINTS - truth: val={}, grad={:?}", val_t, grad_t);
|
||||
let inv = vec![1.0 / 0.85, -4.0 / 0.85];
|
||||
let val_inv = metric_all.evaluate(&inv);
|
||||
println!("ALL POINTS - inverse: val={}", val_inv);
|
||||
|
||||
// Test with 100 random points (like register1 uses at finest level)
|
||||
let metric_rand = MattesMetric::<ndarray::Ix1>::new(
|
||||
BSpline::new(im_b.view()),
|
||||
BSpline::new(im_a.view()),
|
||||
SamplingArg::Fixed(100),
|
||||
3,
|
||||
0.05,
|
||||
)?
|
||||
.with_fixed_mu(crate::metric::FixedMu::new_none(1));
|
||||
|
||||
let val_i2 = metric_rand.evaluate(&identity);
|
||||
let grad_i2 = metric_rand.gradient(&identity).unwrap();
|
||||
println!("RAND 100 - identity: val={}, grad={:?}", val_i2, grad_i2);
|
||||
|
||||
// Test with 100 random points and 32 bins (like level 0)
|
||||
let metric_32 = MattesMetric::<ndarray::Ix1>::new(
|
||||
BSpline::new(im_b.view()),
|
||||
BSpline::new(im_a.view()),
|
||||
SamplingArg::Fixed(100),
|
||||
32,
|
||||
0.05,
|
||||
)?
|
||||
.with_fixed_mu(crate::metric::FixedMu::new_none(1));
|
||||
|
||||
let val_i3 = metric_32.evaluate(&identity);
|
||||
let grad_i3 = metric_32.gradient(&identity).unwrap();
|
||||
println!(
|
||||
"RAND 100 bins=32 - identity: val={}, grad={:?}",
|
||||
val_i3, grad_i3
|
||||
);
|
||||
|
||||
let eps = 1e-5;
|
||||
for i in 0..2 {
|
||||
let mut p_plus = identity.clone();
|
||||
let mut p_minus = identity.clone();
|
||||
p_plus[i] += eps;
|
||||
p_minus[i] -= eps;
|
||||
let num_grad =
|
||||
(metric_all.evaluate(&p_plus) - metric_all.evaluate(&p_minus)) / (2.0 * eps);
|
||||
println!(
|
||||
"numerical d/dmu[{}] = {} (analytical: {})",
|
||||
i, num_grad, grad_i[i]
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metric_landscape() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use crate::bspline::{BSpline, BSplineTrait};
|
||||
use crate::metric::{MattesMetric, SamplingArg, Sigma};
|
||||
use algos::ObjectiveFunction;
|
||||
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 q = vec![0.85, 4.0];
|
||||
let im_b =
|
||||
Transform::new(vec![0.85, 4.0], vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
||||
|
||||
// Smooth both images with sigma=8 like level 0
|
||||
let sigma = Sigma::Absolute(vec![8.0]);
|
||||
let sf = sigma.smooth(im_a.view())?;
|
||||
let sm = sigma.smooth(im_b.view())?;
|
||||
|
||||
let points: Vec<Vec<f64>> = (0..100).map(|i| vec![i as f64]).collect();
|
||||
|
||||
let metric = MattesMetric::<ndarray::Ix1>::new(
|
||||
BSpline::new(sf.view()),
|
||||
BSpline::new(sm.view()),
|
||||
SamplingArg::FixedAt(points.clone()),
|
||||
32,
|
||||
0.05,
|
||||
)?
|
||||
.with_fixed_mu(crate::metric::FixedMu::new_none(1));
|
||||
|
||||
// Test several points
|
||||
let test_points: Vec<(&str, Vec<f64>)> = vec![
|
||||
("identity".into(), vec![1.0, 0.0]),
|
||||
("truth".into(), vec![0.85, 4.0]),
|
||||
("neg_trans".into(), vec![1.0, -4.0]),
|
||||
("scale_0.9".into(), vec![0.9, 0.0]),
|
||||
("scale_1.1".into(), vec![1.1, 0.0]),
|
||||
];
|
||||
|
||||
println!("=== SMOOTHED ALL 100 integer points bins=32 ===");
|
||||
for (name, p) in &test_points {
|
||||
let val = metric.evaluate(p);
|
||||
let grad = metric.gradient(p);
|
||||
match grad {
|
||||
Some(g) => println!(" {}: val={:.6}, grad={:?}", name, val, g),
|
||||
None => println!(" {}: val={:.6}, grad=None", name, val),
|
||||
}
|
||||
}
|
||||
|
||||
// Now test with Fixed(100) random points
|
||||
let metric_rand = MattesMetric::<ndarray::Ix1>::new(
|
||||
BSpline::new(sf.view()),
|
||||
BSpline::new(sm.view()),
|
||||
SamplingArg::Fixed(100),
|
||||
32,
|
||||
0.05,
|
||||
)?
|
||||
.with_fixed_mu(crate::metric::FixedMu::new_none(1));
|
||||
|
||||
println!("=== SMOOTHED Random 100 points bins=32 ===");
|
||||
for (name, p) in &test_points {
|
||||
let val = metric_rand.evaluate(p);
|
||||
let grad = metric_rand.gradient(p);
|
||||
match grad {
|
||||
Some(g) => println!(" {}: val={:.6}, grad={:?}", name, val, g),
|
||||
None => println!(" {}: val={:.6}, grad=None", name, val),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register1() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let im_a = julia_image(
|
||||
@@ -566,17 +727,111 @@ mod tests {
|
||||
)
|
||||
.slice(s![.., 0])
|
||||
.mapv(|i| i as f64);
|
||||
let q = vec![0.85, 4.0];
|
||||
let im_b =
|
||||
Transform::new(vec![0.85, 4.0], vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
||||
Transform::new(q.clone(), vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
|
||||
|
||||
let (t, steps) =
|
||||
Transform::register_debug(im_a.view(), im_b.view(), vec![None, None], None, None)?;
|
||||
// The registration finds T such that im_b(T(x)) = im_a(x), which is the inverse of q
|
||||
let q_inv = Transform::<ndarray::Ix1>::new(q.clone(), vec![im_a.shape()[0]])
|
||||
.inverse()?
|
||||
.parameters;
|
||||
|
||||
let all_points: Vec<Vec<f64>> = (0..100).map(|j| vec![j as f64]).collect();
|
||||
let steps = vec![
|
||||
crate::register::RegistrationStep::new(
|
||||
crate::metric::Sigma::Absolute(vec![8.0]),
|
||||
crate::metric::SamplingArg::FixedAt(all_points.clone()),
|
||||
32,
|
||||
1e-4,
|
||||
0.05,
|
||||
200,
|
||||
1.0,
|
||||
),
|
||||
crate::register::RegistrationStep::new(
|
||||
crate::metric::Sigma::Absolute(vec![2.0]),
|
||||
crate::metric::SamplingArg::FixedAt(all_points.clone()),
|
||||
64,
|
||||
1e-6,
|
||||
0.04,
|
||||
200,
|
||||
1.0,
|
||||
),
|
||||
crate::register::RegistrationStep::new(
|
||||
crate::metric::Sigma::None,
|
||||
crate::metric::SamplingArg::FixedAt(all_points),
|
||||
64,
|
||||
1e-8,
|
||||
0.001,
|
||||
200,
|
||||
1.0,
|
||||
),
|
||||
];
|
||||
|
||||
let (t, steps) = Transform::register_debug(
|
||||
im_a.view(),
|
||||
im_b.view(),
|
||||
vec![None, None],
|
||||
Some(steps),
|
||||
None,
|
||||
)?;
|
||||
println!("steps:");
|
||||
for step in steps {
|
||||
println!(" {:?}", step);
|
||||
}
|
||||
println!("t: {:?}", t);
|
||||
println!("i: {:?}", t.inverse()?);
|
||||
println!("q_inv: {:?}", q_inv);
|
||||
assert!(
|
||||
t.parameters
|
||||
.iter()
|
||||
.zip(q_inv.iter())
|
||||
.map(|(a, b)| (a - b).powi(2))
|
||||
.sum::<f64>()
|
||||
< 1.0
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register2_interpolate() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let shape = [200, 200];
|
||||
let center = [99.5, 99.5];
|
||||
let im_a = julia_image(
|
||||
&shape,
|
||||
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
||||
¢er,
|
||||
&[-0.8, 0.156],
|
||||
)
|
||||
.mapv(|i| i as f64);
|
||||
let rotation = Transform::<Ix2>::from_rotation(f64::PI() / 4.0, ¢er);
|
||||
let im_b = rotation.interpolate::<3, _, _>(im_a.view())?;
|
||||
|
||||
let q_inv = rotation.inverse()?.parameters;
|
||||
|
||||
// Use default_steps which matches elastix FixedSmoothingImagePyramid:
|
||||
// sigma = [4.0, 2.0, 1.0, 0.5] (schedule [8,4,2,1] with spacing=1)
|
||||
let (t, steps) = Transform::register_debug(
|
||||
im_a.view(),
|
||||
im_b.view(),
|
||||
vec![None, None, None, None, None, None],
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
println!("steps:");
|
||||
for step in steps {
|
||||
println!(" {:?}", step);
|
||||
}
|
||||
println!("t: {:?}", t);
|
||||
println!("i: {:?}", t.inverse()?);
|
||||
println!("q_inv: {:?}", q_inv);
|
||||
assert!(
|
||||
t.parameters
|
||||
.iter()
|
||||
.zip(q_inv.iter())
|
||||
.map(|(a, b)| (a - b).powi(2))
|
||||
.sum::<f64>()
|
||||
< 1.0
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -603,7 +858,7 @@ mod tests {
|
||||
let (t, steps) = Transform::register_debug(
|
||||
im_a.view(),
|
||||
im_b.view(),
|
||||
vec![None, None, None, None, Some(0.0), Some(0.0)],
|
||||
vec![None, None, None, None, None, None],
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
@@ -613,6 +868,19 @@ mod tests {
|
||||
}
|
||||
println!("t: {:?}", t);
|
||||
println!("i: {:?}", t.inverse()?);
|
||||
println!("p: {:?}", p);
|
||||
// julia_image applies transform to coordinates, so T maps im_b->im_a means T = p_inv
|
||||
let p_inv = Transform::<ndarray::Ix2>::new(p.clone(), vec![600, 800])
|
||||
.inverse()?
|
||||
.parameters;
|
||||
assert!(
|
||||
t.parameters
|
||||
.iter()
|
||||
.zip(p_inv.iter())
|
||||
.map(|(a, b)| (a - b).powi(2))
|
||||
.sum::<f64>()
|
||||
< 1.0
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user