- cleanup

This commit is contained in:
w.pomp committed 2026-07-31 17:49:11 +02:00
1 parent f75f12db16
commit cc12a1a9b9
5 files changed
+253 -175

No files matched your search

+172 -22
View File
@@ -579,7 +579,7 @@ mod tests {
use crate::julia_image;
use crate::transform::Transform;
use itertools::Itertools;
use ndarray::{Array2, Ix2, s};
use ndarray::{Array2, Ix2, array, s};
use num::traits::FloatConst;
use std::fs::File;
use std::path::Path;
@@ -789,11 +789,11 @@ mod tests {
// 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]),
("identity", vec![1.0, 0.0]),
("truth", vec![0.85, 4.0]),
("neg_trans", vec![1.0, -4.0]),
("scale_0.9", vec![0.9, 0.0]),
("scale_1.1", vec![1.1, 0.0]),
];
println!("=== SMOOTHED ALL 100 integer points bins=32 ===");
@@ -831,6 +831,96 @@ mod tests {
#[test]
fn register1() -> Result<(), Box<dyn std::error::Error>> {
let fixed = array![
0.00133107, 0.00138621, 0.00141719, 0.00137673, 0.00133192, 0.00131586, 0.00129737,
0.00131649, 0.00132131, 0.0013368, 0.0013557, 0.00135557, 0.00135903, 0.00136225,
0.00136999, 0.0013847, 0.00137785, 0.00138041, 0.00140829, 0.00142085, 0.0014344,
0.00149646, 0.00156956, 0.00172352, 0.0019473, 0.00226109, 0.00269055, 0.00312467,
0.00343227, 0.00352425, 0.00335073, 0.00305988, 0.00276482, 0.0024797, 0.00223358,
0.00204837, 0.00191672, 0.00183362, 0.00179435, 0.00177568, 0.00177995, 0.00177444,
0.00178801, 0.00180613, 0.00181906, 0.00183161, 0.00185975, 0.00185648, 0.00187759,
0.00188436, 0.00185824, 0.00185461, 0.00188928, 0.00190146, 0.00185707, 0.00185682,
0.00185372, 0.00184588, 0.00183992, 0.0018354, 0.0018307, 0.00181903, 0.00182027,
0.00181646, 0.00180596, 0.00180369, 0.00180493, 0.0018042, 0.00180887, 0.00180575,
0.00180144, 0.00180303, 0.00180743, 0.00180482, 0.0017817, 0.00175849, 0.00174724,
0.00174177, 0.00174569, 0.00176198, 0.00178241, 0.00180581, 0.00184414, 0.00188772,
0.00192021, 0.00195737, 0.00201045, 0.00207885, 0.00216532, 0.00227495, 0.00240301,
0.00254983, 0.00273925, 0.00298603, 0.00329465, 0.00367626, 0.00415677, 0.0047952,
0.00565066, 0.006755, 0.00808893, 0.00958421, 0.0111741, 0.01279892, 0.01434347,
0.0156247, 0.01649142, 0.01690067, 0.01689863, 0.0165402, 0.0158715, 0.01497264,
0.0139421, 0.01286888, 0.01182453, 0.01084795, 0.00994079, 0.00908256, 0.00826134,
0.00748309, 0.00676281, 0.00611948, 0.00556657, 0.00510174, 0.00470918, 0.0043702,
0.00407155, 0.00381196, 0.00359745, 0.00342831, 0.00329565, 0.00318778, 0.00309373,
0.00300738, 0.00292954, 0.00286483, 0.00281634, 0.00278162, 0.00275487, 0.00273168,
0.0027102, 0.0026901, 0.00267163, 0.00265643, 0.00264636, 0.00264188, 0.00264216,
0.00264419, 0.00264316, 0.00263564, 0.00262231, 0.00260767, 0.00259683, 0.00259233,
0.00259301, 0.00259521, 0.0025956, 0.00259316, 0.00258932, 0.00258678, 0.00258754,
0.00259157, 0.00259742, 0.00260376, 0.00261015, 0.00261699, 0.00262534, 0.00263624,
0.00265, 0.00266603, 0.00268315, 0.00269997, 0.00271538, 0.0027293, 0.00274301,
0.00275855, 0.00277762, 0.00280069, 0.00282693, 0.00285474, 0.00288248, 0.00290876,
0.00293257, 0.00295365, 0.0029727, 0.00299094, 0.00300944, 0.0030288, 0.00304906,
0.00306975, 0.00309011, 0.00310917, 0.00312581, 0.00313895, 0.00314813, 0.00315378,
0.00315703, 0.0031592, 0.00316134, 0.00316407, 0.00316761, 0.00317182, 0.00317628,
0.00318021, 0.00318272, 0.00318301, 0.00318065, 0.00317563, 0.00316838, 0.00315962,
0.00315024, 0.00314115, 0.00313327, 0.00312741, 0.0031241, 0.00312351, 0.00312549,
0.00312965, 0.00313552, 0.00314259, 0.00315038, 0.00315846, 0.00316645, 0.00317408,
0.00318122, 0.00318795, 0.0031945, 0.00320115, 0.00320818, 0.0032158, 0.00322408,
0.00323293, 0.00324213, 0.00325147, 0.00326083, 0.00327028, 0.00328004, 0.00329046,
0.00330194, 0.00331489, 0.0033297, 0.00334668, 0.00336601, 0.00338771, 0.00341162,
0.0034374, 0.0034646, 0.00349265, 0.003521, 0.00354909, 0.00357647, 0.00360277,
0.00362773, 0.00365124, 0.00367333, 0.0036942, 0.00371413, 0.00373349, 0.00375265,
0.0037719, 0.00379146, 0.00381142, 0.00383177, 0.00385234, 0.0038729, 0.0038931,
0.00391258, 0.00393098, 0.00394801, 0.00396348, 0.00397735, 0.0039897, 0.00400073,
0.00401069, 0.0040199, 0.00402867, 0.00403726, 0.0040459, 0.00405473, 0.00406382,
0.00407317, 0.00408273, 0.00409241, 0.00410213, 0.00411181, 0.00412144, 0.00413098,
0.00414044, 0.00414982, 0.00415912, 0.00416834, 0.00417744, 0.00418636, 0.00419505,
0.00420339, 0.0042113, 0.00421866, 0.00422539, 0.00423144, 0.00423681, 0.00424153
];
let q = vec![0.85, 2.0];
let moving =
Transform::new(q.clone(), vec![fixed.shape()[0]]).interpolate::<3, _, _>(&fixed)?;
// 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![fixed.shape()[0]]).inverse()?;
// Use default steps with ASGD — the multi-resolution pyramid converges
// reliably for this 1D affine problem.
let t = Transform::register(fixed.view(), moving.view(), vec![None; 2], None, None)?;
// Compare transformed coordinates of all pixels
let mut sum_diff = 0.0;
let mut count = 0;
for point in 0..fixed.len() {
let t_point = t.transform_point(&[point]);
let e_point = q_inv.transform_point(&[point]);
let diff_sq = (t_point[0] - e_point[0]).powi(2);
sum_diff += diff_sq.sqrt();
count += 1;
}
let mean_diff = sum_diff / count as f64;
println!("Our: {:?}\nmean_coord_diff: {:.6}", t, mean_diff);
println!("t: {:?}", t);
println!("i: {:?}", t.inverse()?);
println!("q_inv: {:?}", q_inv.parameters);
assert!(
t.parameters
.iter()
.zip(q_inv.parameters.iter())
.map(|(a, b)| (a - b).powi(2))
.sum::<f64>()
< 1.0
);
assert!(mean_diff < 0.1);
Ok(())
}
#[test]
fn diag_register1_metric() -> Result<(), Box<dyn std::error::Error>> {
use crate::bspline::{BSpline, BSplineTrait};
use crate::metric::{FixedMu, 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],
@@ -843,32 +933,92 @@ mod tests {
let im_b =
Transform::new(q.clone(), vec![im_a.shape()[0]]).interpolate::<1, _, _>(&im_a)?;
// The registration finds T such that im_b(T(x)) = im_a(x), which is the inverse of q
let f = Sigma::Absolute(vec![1.0]).smooth(im_a.view())?;
let m = Sigma::Absolute(vec![1.0]).smooth(im_b.view())?;
let metric = MattesMetric::<ndarray::Ix1>::new(
BSpline::new(f.view()),
BSpline::new(m.view()),
SamplingArg::Random(100),
32,
0.05,
)?
.with_fixed_mu(FixedMu::new_none(1));
let q_inv = Transform::<ndarray::Ix1>::new(q.clone(), vec![im_a.shape()[0]])
.inverse()?
.parameters;
// Use default steps with ASGD — the multi-resolution pyramid converges
// reliably for this 1D affine problem.
let t = Transform::register(im_a.view(), im_b.view(), vec![None; 2], None, None)?;
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
);
// sweep scale at the optimal translation, and translation at scale 1.0
let mut best = (f64::MAX, vec![0.0, 0.0]);
let mut rows = Vec::new();
for s in (60..=360).map(|i| i as f64 / 100.0) {
for t in (-120..=80).map(|i| i as f64 / 40.0) {
let p = vec![s, t];
let v = metric.evaluate(&p);
if v < best.0 {
best = (v, p);
}
}
let v0 = metric.evaluate(&[s, -2.353]);
rows.push(format!("s={:.2} t=-2.353 val={:.4}", s, v0));
}
println!("BEST on grid: val={:.4} params={:?}", best.0, best.1);
println!("rows:");
// line from identity to q_inv and beyond
let mut prev = f64::NAN;
for (i, alpha) in (0..=40).map(|i| i as f64 / 10.0).enumerate() {
let p = vec![1.0 + (q_inv[0] - 1.0) * alpha, 0.0 + q_inv[1] * alpha];
let v = metric.evaluate(&p);
println!("line[{}] alpha={:.1} p={:?} val={:.4}", i, alpha, p, v);
if !prev.is_nan() && v > prev {
println!(" ^ INCREASING here — minimum is before this point");
}
prev = v;
}
// metric value along diagonal from q_inv to the registered optimum
for p in [q_inv.clone(), vec![2.0, -1.0], vec![2.5, -0.3]] {
println!("eval {:?}: val={:.4}", p, metric.evaluate(&p));
}
// numerical vs analytical gradient at identity and q_inv
let eps = 1e-5;
for p in [vec![1.0, 0.0], q_inv.clone()] {
let grad = metric.gradient(&p).unwrap();
let mut num = Vec::new();
for i in 0..2 {
let mut pp = p.clone();
let mut pm = p.clone();
pp[i] += eps;
pm[i] -= eps;
num.push((metric.evaluate(&pp) - metric.evaluate(&pm)) / (2.0 * eps));
}
println!("grad at {:?}: analytical={:?} numerical={:?}", p, grad, num);
}
let (t, results) = Transform::<ndarray::Ix1>::register_debug(
im_a.view(),
im_b.view(),
vec![None, None],
None,
None,
)?;
println!("register_debug t: {:?}", t);
for r in results {
println!(
" step: opt={:?} iters={} converged={} val={}",
r.optimal_point, r.iterations, r.converged, r.optimal_value
);
}
Ok(())
}
#[test]
fn register2_random_affine() -> Result<(), Box<dyn std::error::Error>> {
use rand::prelude::*;
let mut rng = rand::rngs::StdRng::seed_from_u64(1337);
let mut rng = StdRng::seed_from_u64(1337);
let shape = [200, 200];
let center = [99.5, 99.5];