- second commit
This commit is contained in:
@@ -0,0 +1,618 @@
|
||||
use crate::bspline::{BSpline, BSplineTrait};
|
||||
use crate::error::Error;
|
||||
use crate::metric::FixedMu;
|
||||
use crate::register::{Registration, RegistrationResult, RegistrationStep};
|
||||
use itertools::Itertools;
|
||||
use ndarray::{Array, Array2, ArrayD, AsArray, Dimension, Ix2, s};
|
||||
use ndarray_linalg::Inverse;
|
||||
use num::cast::AsPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::{from_reader, to_writer};
|
||||
use std::fs::File;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Mul;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// get coordinates resulting from transforming input coordinates, coordinate must have N
|
||||
/// columns: x, y, z, ...
|
||||
#[inline]
|
||||
pub fn transform_point<'a, T>(point: &'a [T], center: &'a [f64], parameters: &'a [f64]) -> Vec<f64>
|
||||
where
|
||||
T: AsPrimitive<f64>,
|
||||
{
|
||||
// [a, b, c] x = a*x + b*y + c
|
||||
// [d, e, f] y = d*x + e*y + f
|
||||
// [0, 0, 1] 1 = 1
|
||||
debug_assert_eq!(center.len(), point.len());
|
||||
debug_assert_eq!((center.len() + 1) * center.len(), parameters.len());
|
||||
|
||||
let m = center.len();
|
||||
let n = m * m;
|
||||
let point = point
|
||||
.iter()
|
||||
.zip_eq(center.iter())
|
||||
.map(|(p, c)| p.as_() - c)
|
||||
.collect::<Vec<_>>();
|
||||
parameters
|
||||
.iter()
|
||||
.take(n)
|
||||
.chunks(m)
|
||||
.into_iter()
|
||||
.zip_eq(parameters.iter().skip(n))
|
||||
.zip_eq(center.iter())
|
||||
.map(|((r, t), c)| r.zip_eq(point.iter()).map(|(i, p)| i * p).sum::<f64>() + t + c)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// a struct describing the transform
|
||||
/// generic parameter N = # image dimensions
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct Transform<D: Dimension = Ix2> {
|
||||
/// flattened NxN rotation matrix + N translation parameters
|
||||
pub parameters: Vec<f64>,
|
||||
/// error / significance on parameters
|
||||
pub dparameters: Vec<f64>,
|
||||
/// the point about which rotations are performed
|
||||
pub center: Vec<f64>,
|
||||
/// the shape of images for which this transform is meant
|
||||
pub shape: Vec<usize>,
|
||||
ndim: usize,
|
||||
dimension: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: Dimension> Mul for Transform<D> {
|
||||
type Output = Transform<D>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
&self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Mul<&Transform<D>> for Transform<D> {
|
||||
type Output = Transform<D>;
|
||||
|
||||
fn mul(self, rhs: &Transform<D>) -> Self::Output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Mul<Transform<D>> for &Transform<D> {
|
||||
type Output = Transform<D>;
|
||||
|
||||
fn mul(self, rhs: Transform<D>) -> Self::Output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Mul<&Transform<D>> for &Transform<D> {
|
||||
type Output = Transform<D>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn mul(self, rhs: &Transform<D>) -> Self::Output {
|
||||
let m = self.matrix().dot(&rhs.matrix());
|
||||
let dm = self.dmatrix().dot(&rhs.matrix()) + self.matrix().dot(&rhs.dmatrix());
|
||||
Self::Output {
|
||||
parameters: m
|
||||
.slice(s![..self.ndim, ..self.ndim])
|
||||
.flatten()
|
||||
.iter()
|
||||
.chain(m.slice(s![..self.ndim, self.ndim]).iter())
|
||||
.cloned()
|
||||
.collect(),
|
||||
dparameters: dm
|
||||
.slice(s![..self.ndim, ..])
|
||||
.flatten()
|
||||
.iter()
|
||||
.chain(dm.slice(s![..self.ndim, ..]).iter())
|
||||
.cloned()
|
||||
.collect(),
|
||||
center: self.center.clone(),
|
||||
shape: self.shape.clone(),
|
||||
ndim: self.ndim,
|
||||
dimension: self.dimension,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Eq for Transform<D> {}
|
||||
|
||||
impl<D: Dimension> Default for Transform<D> {
|
||||
/// the unit transform
|
||||
fn default() -> Self {
|
||||
let ndim = D::NDIM.expect("number of dimensions must be known to initialise transform");
|
||||
let mut parameters = vec![0.0; ndim * ndim + ndim];
|
||||
for i in 0..ndim {
|
||||
parameters[(ndim + 1) * i] = 1.0;
|
||||
}
|
||||
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: vec![0f64; ndim * ndim + ndim],
|
||||
center: vec![0f64; ndim],
|
||||
shape: vec![0usize; ndim],
|
||||
ndim,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Transform<D> {
|
||||
/// parameters: flat NxN part of matrix + translation; center: center of rotation
|
||||
pub fn new_with_center(parameters: Vec<f64>, center: Vec<f64>, shape: Vec<usize>) -> Self {
|
||||
let ndim = if let Some(ndim) = D::NDIM {
|
||||
ndim
|
||||
} else {
|
||||
center.len()
|
||||
};
|
||||
debug_assert_eq!(parameters.len(), ndim * ndim + ndim);
|
||||
debug_assert_eq!(center.len(), ndim);
|
||||
debug_assert_eq!(shape.len(), ndim);
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: vec![0.0; ndim * ndim + ndim],
|
||||
center,
|
||||
shape,
|
||||
ndim,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(parameters: Vec<f64>, shape: Vec<usize>) -> Self {
|
||||
let ndim = if let Some(ndim) = D::NDIM {
|
||||
ndim
|
||||
} else {
|
||||
shape.len()
|
||||
};
|
||||
debug_assert_eq!(parameters.len(), ndim * ndim + ndim);
|
||||
debug_assert_eq!(shape.len(), ndim);
|
||||
let center = shape.iter().map(|s| ((*s as f64) - 1.0) / 2.0).collect();
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: vec![0.0; ndim * ndim + ndim],
|
||||
center,
|
||||
shape,
|
||||
ndim,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// find the affine transform which transforms moving into fixed
|
||||
pub fn register_affine<'a, F, M, T>(fixed: F, moving: M) -> Result<Self, Error>
|
||||
where
|
||||
F: AsArray<'a, T, D>,
|
||||
M: AsArray<'a, T, D>,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
{
|
||||
Registration::new_affine()?.register(fixed, moving)
|
||||
}
|
||||
|
||||
/// find the translation which transforms moving into fixed
|
||||
pub fn register_translation<'a, F, M, T>(fixed: F, moving: M) -> Result<Self, Error>
|
||||
where
|
||||
F: AsArray<'a, T, D>,
|
||||
M: AsArray<'a, T, D>,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
{
|
||||
Registration::new_translation()?.register(fixed, moving)
|
||||
}
|
||||
|
||||
/// find the transform which transforms moving into fixed, using fixed_mu to specify which
|
||||
/// parameters to keep fixed
|
||||
pub fn register<'a, F, M, T, G>(fixed: F, moving: M, fixed_mu: G) -> Result<Self, Error>
|
||||
where
|
||||
F: AsArray<'a, T, D>,
|
||||
M: AsArray<'a, T, D>,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
G: Into<FixedMu>,
|
||||
{
|
||||
Registration::new(fixed_mu).register(fixed, moving)
|
||||
}
|
||||
|
||||
/// find the transform which transforms moving into fixed and return the results of each
|
||||
/// optimization step
|
||||
pub fn register_debug<'a, F, M, T, G>(
|
||||
fixed: F,
|
||||
moving: M,
|
||||
fixed_mu: G,
|
||||
steps: Option<Vec<RegistrationStep>>,
|
||||
initial_guess: Option<Vec<f64>>,
|
||||
) -> Result<(Self, Vec<RegistrationResult>), Error>
|
||||
where
|
||||
F: AsArray<'a, T, D>,
|
||||
M: AsArray<'a, T, D>,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
G: Into<FixedMu>,
|
||||
{
|
||||
let mut registration = Registration::new(fixed_mu);
|
||||
if let Some(steps) = steps {
|
||||
registration.set_steps(steps);
|
||||
}
|
||||
if let Some(initial_guess) = initial_guess {
|
||||
registration.set_initial_guess(initial_guess);
|
||||
}
|
||||
registration.register_debug(fixed, moving)
|
||||
}
|
||||
|
||||
/// create a transform from a translation
|
||||
pub fn from_translation(translation: &[f64]) -> Self {
|
||||
let ndim = if let Some(ndim) = D::NDIM {
|
||||
ndim
|
||||
} else {
|
||||
translation.len()
|
||||
};
|
||||
debug_assert_eq!(translation.len(), ndim);
|
||||
let mut parameters = vec![0.0; ndim * ndim + ndim];
|
||||
for i in 0..ndim {
|
||||
parameters[(ndim + 1) * i] = 1.0;
|
||||
}
|
||||
for (p, t) in parameters
|
||||
.iter_mut()
|
||||
.skip(ndim * ndim)
|
||||
.zip_eq(translation.iter())
|
||||
{
|
||||
*p = *t;
|
||||
}
|
||||
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: vec![0f64; ndim * ndim + ndim],
|
||||
center: vec![0f64; ndim],
|
||||
shape: vec![0usize; ndim],
|
||||
ndim,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// create a transform from a scaling
|
||||
pub fn from_scaling(scaling: &[f64]) -> Self {
|
||||
let ndim = if let Some(ndim) = D::NDIM {
|
||||
ndim
|
||||
} else {
|
||||
scaling.len()
|
||||
};
|
||||
debug_assert_eq!(scaling.len(), ndim);
|
||||
let mut parameters = vec![0.0; ndim * ndim + ndim];
|
||||
parameters[ndim * ndim + ndim - 1] = 1.0;
|
||||
for (p, s) in parameters
|
||||
.iter_mut()
|
||||
.step_by(ndim + 1)
|
||||
.zip_eq(scaling.iter())
|
||||
{
|
||||
*p = *s;
|
||||
}
|
||||
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: vec![0f64; ndim * ndim + ndim],
|
||||
center: vec![0f64; ndim],
|
||||
shape: vec![0usize; ndim],
|
||||
ndim,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// add a translation to self
|
||||
pub fn with_translation(&self, translation: &[f64]) -> Self {
|
||||
Self::from_translation(translation) * self
|
||||
}
|
||||
|
||||
/// scale self
|
||||
pub fn with_scaling(&self, scaling: &[f64]) -> Self {
|
||||
Self::from_scaling(scaling) * self
|
||||
}
|
||||
|
||||
/// read a transform from a file
|
||||
pub fn from_file(path: PathBuf) -> Result<Self, Error> {
|
||||
let file = File::open(path)?;
|
||||
Ok(from_reader(file)?)
|
||||
}
|
||||
|
||||
/// write a transform to a file
|
||||
pub fn to_file(&self, path: PathBuf) -> Result<(), Error> {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(path)?;
|
||||
to_writer(&mut file, self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// true if transform does nothing
|
||||
pub fn is_unity(&self) -> bool {
|
||||
let n = self.ndim * self.ndim;
|
||||
let m = self.ndim + 1;
|
||||
self.parameters
|
||||
.iter()
|
||||
.take(n)
|
||||
.enumerate()
|
||||
.all(|(i, &x)| if i % m == 0 { x == 1.0 } else { x == 0.0 })
|
||||
&& self.parameters.iter().skip(n).all(|&x| x == 0.0)
|
||||
}
|
||||
|
||||
/// get coordinates resulting from transforming input coordinates, coordinate must have N
|
||||
/// columns: x, y, z, ...
|
||||
pub fn transform_point<'a, T>(&'a self, point: &'a [T]) -> Vec<f64>
|
||||
where
|
||||
T: AsPrimitive<f64>,
|
||||
{
|
||||
transform_point(point, &self.center, &self.parameters)
|
||||
}
|
||||
|
||||
/// get coordinates resulting from transforming input coordinates, coordinates must have N
|
||||
/// columns: x, y, z, ...
|
||||
pub fn transform_points<'a, A, T>(&'a self, points: A) -> Result<Array2<f64>, Error>
|
||||
where
|
||||
T: AsPrimitive<f64>,
|
||||
A: AsArray<'a, T, Ix2>,
|
||||
{
|
||||
// [a, b, c] x = a*x + b*y + c
|
||||
// [d, e, f] y = d*x + e*y + f
|
||||
// [0, 0, 1] 1 = 1
|
||||
let points = points.into();
|
||||
let shape = points.shape();
|
||||
let a = points
|
||||
.rows()
|
||||
.into_iter()
|
||||
.flat_map(|p| {
|
||||
if let Some(q) = p.as_slice() {
|
||||
transform_point(q, &self.center, &self.parameters)
|
||||
} else {
|
||||
transform_point(&p.to_vec(), &self.center, &self.parameters)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(ArrayD::from_shape_vec(shape, a)?.into_dimensionality()?)
|
||||
}
|
||||
|
||||
/// get the matrix defining the transform
|
||||
pub fn matrix(&self) -> Array2<f64> {
|
||||
let n = self.ndim * self.ndim;
|
||||
let mut matrix = Array2::eye(self.ndim + 1);
|
||||
for (m, p) in matrix
|
||||
.slice_mut(s![..self.ndim, ..self.ndim])
|
||||
.iter_mut()
|
||||
.zip_eq(self.parameters.iter().take(n))
|
||||
{
|
||||
*m = *p;
|
||||
}
|
||||
for (m, p) in matrix
|
||||
.slice_mut(s![..self.ndim, self.ndim])
|
||||
.iter_mut()
|
||||
.zip_eq(self.parameters.iter().skip(n))
|
||||
{
|
||||
*m = *p;
|
||||
}
|
||||
matrix
|
||||
}
|
||||
|
||||
/// get the matrix describing the error of the transform
|
||||
pub fn dmatrix(&self) -> Array2<f64> {
|
||||
let n = self.ndim * self.ndim;
|
||||
let mut matrix = Array2::zeros([self.ndim + 1, self.ndim + 1]);
|
||||
for (m, p) in matrix
|
||||
.slice_mut(s![..self.ndim, ..self.ndim])
|
||||
.iter_mut()
|
||||
.zip_eq(self.dparameters.iter().take(n))
|
||||
{
|
||||
*m = *p;
|
||||
}
|
||||
for (m, p) in matrix
|
||||
.slice_mut(s![..self.ndim, self.ndim])
|
||||
.iter_mut()
|
||||
.zip_eq(self.dparameters.iter().skip(n))
|
||||
{
|
||||
*m = *p;
|
||||
}
|
||||
matrix
|
||||
}
|
||||
|
||||
/// get the inverse transform
|
||||
pub fn inverse(&self) -> Result<Self, Error> {
|
||||
let matrix = self.matrix();
|
||||
let inverse = matrix.inv()?;
|
||||
let parameters = inverse
|
||||
.slice(s![..self.ndim, ..self.ndim])
|
||||
.iter()
|
||||
.chain(inverse.slice(s![..self.ndim, self.ndim]))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
parameters,
|
||||
dparameters: vec![0f64; self.ndim * self.ndim + self.ndim],
|
||||
center: self.center.clone(),
|
||||
shape: self.shape.clone(),
|
||||
ndim: self.ndim,
|
||||
dimension: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// adapt the transform to a new center and shape
|
||||
pub fn adapt(&mut self, center: &[f64], shape: &[usize]) {
|
||||
self.center = self
|
||||
.shape
|
||||
.iter()
|
||||
.zip_eq(shape.iter())
|
||||
.zip_eq(center)
|
||||
.map(|((a, b), o)| o + (((a - b) as f64) / 2.0))
|
||||
.collect();
|
||||
self.shape = shape.to_vec();
|
||||
}
|
||||
|
||||
pub fn interpolate<'a, const B: usize, T, A>(&self, image: A) -> Result<Array<f64, D>, Error>
|
||||
where
|
||||
A: AsArray<'a, T, D>,
|
||||
D: Dimension,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
BSpline<B, D>: BSplineTrait<D>,
|
||||
{
|
||||
let image = image.into();
|
||||
let bspline = BSpline::<B, _>::new(&image);
|
||||
bspline.interpolate(self)
|
||||
}
|
||||
|
||||
pub fn interpolate_par<'a, const B: usize, T, A>(
|
||||
&self,
|
||||
image: A,
|
||||
) -> Result<Array<f64, D>, Error>
|
||||
where
|
||||
A: AsArray<'a, T, D>,
|
||||
D: Dimension,
|
||||
T: 'a + Clone + AsPrimitive<f64>,
|
||||
BSpline<B, D>: BSplineTrait<D>,
|
||||
{
|
||||
let image = image.into();
|
||||
let bspline = BSpline::<B, _>::new(&image);
|
||||
bspline.interpolate_par(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform<Ix2> {
|
||||
/// create a transform from a rotation in radians
|
||||
pub fn from_rotation(theta: f64, center: &[f64]) -> Self {
|
||||
Self {
|
||||
parameters: vec![
|
||||
theta.cos(),
|
||||
-theta.sin(),
|
||||
theta.sin(),
|
||||
theta.cos(),
|
||||
0.0,
|
||||
0.0,
|
||||
],
|
||||
dparameters: vec![0f64; 6],
|
||||
center: center.to_vec(),
|
||||
shape: vec![0usize; 2],
|
||||
ndim: 2,
|
||||
dimension: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// multiply self with a rotation matrix
|
||||
pub fn with_rotation(&self, theta: f64) -> Self {
|
||||
Self::from_rotation(theta, &self.center) * self
|
||||
}
|
||||
|
||||
/// multiply self with a rotation matrix
|
||||
pub fn with_rotation_around(&self, theta: f64, center: &[f64]) -> Self {
|
||||
Self::from_rotation(theta, center) * self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::julia_image;
|
||||
use crate::transform::Transform;
|
||||
use itertools::Itertools;
|
||||
use ndarray::s;
|
||||
use num::traits::FloatConst;
|
||||
|
||||
#[test]
|
||||
fn interpolate() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let image_a = julia_image(
|
||||
&[8000, 6000],
|
||||
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
||||
&[3999.5, 2999.5],
|
||||
&[-0.8, 0.156],
|
||||
);
|
||||
let transform = Transform::from_rotation(1.0, &[3999.5, 2999.5]);
|
||||
let image_b = transform.interpolate_par::<3, _, _>(image_a.view())?;
|
||||
let image_c = transform.interpolate::<3, _, _>(image_a.view())?;
|
||||
|
||||
assert!(
|
||||
image_b
|
||||
.into_iter()
|
||||
.zip_eq(image_c.into_iter())
|
||||
.all(|(a, b)| a == b)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpolate_serial() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let image_a = julia_image(
|
||||
&[8000, 6000],
|
||||
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
||||
&[3999.5, 2999.5],
|
||||
&[-0.8, 0.156],
|
||||
);
|
||||
let transform = Transform::from_rotation(1.0, &[3999.5, 2999.5]);
|
||||
let image_b = transform.interpolate::<3, _, _>(image_a.view())?;
|
||||
assert_ne!(image_b[[4000, 3000]], 0.0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpolate_par() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let image_a = julia_image(
|
||||
&[8000, 6000],
|
||||
&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
||||
&[3999.5, 2999.5],
|
||||
&[-0.8, 0.156],
|
||||
);
|
||||
let transform = Transform::from_rotation(1.0, &[3999.5, 2999.5]);
|
||||
let image_b = transform.interpolate_par::<3, _, _>(image_a.view())?;
|
||||
assert_ne!(image_b[[4000, 3000]], 0.0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register1() -> 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![0.85, 4.0], 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)?;
|
||||
println!("steps:");
|
||||
for step in steps {
|
||||
println!(" {:?}", step);
|
||||
}
|
||||
println!("t: {:?}", t);
|
||||
println!("i: {:?}", t.inverse()?);
|
||||
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, Some(0.0), Some(0.0)],
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
println!("steps:");
|
||||
for step in steps {
|
||||
println!(" {:?}", step);
|
||||
}
|
||||
println!("t: {:?}", t);
|
||||
println!("i: {:?}", t.inverse()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user