- registration getting better

This commit is contained in:
Wim Pomp
2026-07-29 20:30:57 +02:00
parent e677e7400b
commit bb3d46cc9d
3 changed files with 136 additions and 304 deletions
+48 -97
View File
@@ -276,6 +276,11 @@ impl<D: Dimension> Transform<D> {
}
}
pub fn with_shape(mut self, shape: Vec<usize>) -> Self {
self.shape = shape;
self
}
/// create a transform from a scaling
pub fn from_scaling(scaling: &[f64]) -> Self {
let ndim = if let Some(ndim) = D::NDIM {
@@ -673,7 +678,6 @@ 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)?;
@@ -960,98 +964,6 @@ mod tests {
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],
&center,
&[-0.8, 0.156],
)
.mapv(|i| i as f64);
let rotation = Transform::<Ix2>::from_rotation(f64::PI() / 4.0, &center);
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(())
}
#[test]
fn register2() -> Result<(), Box<dyn std::error::Error>> {
let t = Transform::from_rotation(f64::PI() / 4.0, &[299.5, 399.5]);
let s = Transform::new(vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0], vec![600, 800]);
let r = t * s;
let p = r.parameters;
println!("{:?}", p);
let im_a = julia_image(
&[600, 800],
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
&[299.5, 399.5],
&[-0.8, 0.156],
);
let im_b = julia_image(
&[600, 800],
&[p[0], p[1], p[2], p[3], p[4], p[5]],
&[299.5, 399.5],
&[-0.8, 0.156],
);
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!("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(())
}
fn read_tiff<P: AsRef<Path>>(path: P) -> Result<Array2<f64>, Box<dyn std::error::Error>> {
let mut reader = Decoder::new(File::open(path)?)?;
reader.seek_to_image(0)?;
@@ -1064,7 +976,7 @@ mod tests {
DecodingResult::I16(data) => data.into_iter().map(|i| i as f64).collect::<Vec<_>>(),
DecodingResult::I32(data) => data.into_iter().map(|i| i as f64).collect::<Vec<_>>(),
DecodingResult::I64(data) => data.into_iter().map(|i| i as f64).collect::<Vec<_>>(),
DecodingResult::F16(data) => data.into_iter().map(|i| f64::from(i)).collect::<Vec<_>>(),
DecodingResult::F16(data) => data.into_iter().map(f64::from).collect::<Vec<_>>(),
DecodingResult::F32(data) => data.into_iter().map(|i| i as f64).collect::<Vec<_>>(),
DecodingResult::F64(data) => data,
};
@@ -1110,14 +1022,53 @@ mod tests {
.fold(0.0f64, f64::max);
println!("Our: {:?} max_err: {:.4} sse: {:.4}", t, max_err, sse);
assert!(max_err < 1.0);
assert!(sse < 1.0);
let mut tif = IJTiffFile::new(std::env::home_dir().unwrap().join("tmp/register_real_images.tif"))?;
tif.save(fixed.mapv(|i| i as u16), 0, 0, 0)?;
tif.save(t.interpolate_par::<1, _, _>(moving.view())?.mapv(|i| i as u16), 1, 0, 0)?;
tif.save(moving.mapv(|i| i as u16), 2, 0, 0)?;
assert!(max_err < 0.1);
assert!(sse < 0.1);
Ok(())
}
#[test]
fn register_real_images2() -> Result<(), Box<dyn std::error::Error>> {
let fixed = read_tiff("test_files/fixed.tif")?;
let e = Transform::<Ix2>::new(vec![0.8, 0.0, 0.0, 1.0, 0.0, 0.0], fixed.shape().to_vec()).inverse()?;
let moving = e.interpolate::<3, _, _>(fixed.view())?;
let t = Transform::<Ix2>::register(
fixed.view(),
moving.view(),
vec![None, None, None, None, None, None],
None,
None,
)?;
let sse = t
.parameters
.iter()
.zip(e.parameters.iter())
.map(|(a, b)| (a - b).powi(2))
.sum::<f64>();
let max_err = t
.parameters
.iter()
.zip(e.parameters.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f64, f64::max);
println!("Our: {:?} max_err: {:.4} sse: {:.4}", t, max_err, sse);
// let mut tif = IJTiffFile::new(std::env::home_dir().unwrap().join("tmp/register_real_images2.tif"))?;
// tif.save(fixed.mapv(|i| i as u16), 0, 0, 0)?;
// tif.save(t.interpolate_par::<1, _, _>(moving.view())?.mapv(|i| i as u16), 1, 0, 0)?;
// tif.save(moving.mapv(|i| i as u16), 2, 0, 0)?;
assert!(max_err < 0.1);
assert!(sse < 0.1);
Ok(())
}
}