Skip to content

Commit

Permalink
transform update for generic transforms (#60)
Browse files Browse the repository at this point in the history
Co-authored-by: Todd Keeler <tdk@meta.com>
  • Loading branch information
tdk-meta and Todd Keeler authored Jul 11, 2023
1 parent 5223a69 commit bf7c834
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 95 deletions.
7 changes: 3 additions & 4 deletions examples/transforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ fn main() {
// Create transformation matrix
let x = 0.5 * (cameraman.cols() as f64) - 0.5;
let y = 0.5 * (cameraman.rows() as f64) - 0.5;
let trans = rotate_around_centre(FRAC_PI_4, (x, y)).dot(&scale(0.7, 0.7));
let trans =
transform_from_2dmatrix(rotate_around_centre(FRAC_PI_4, (x, y)).dot(&scale(0.7, 0.7)));

let transformed = cameraman
.transform(trans.view(), None)
.expect("Transform failed");
let transformed = cameraman.transform(&trans, None).expect("Transform failed");

// save
let path = Path::new("transformed_cameraman.png");
Expand Down
74 changes: 74 additions & 0 deletions src/transform/affine.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,94 @@
use super::Transform;
use ndarray::{array, prelude::*};
use ndarray_linalg::Inverse;

/// converts a matrix into an equivalent `AffineTransform`
pub fn transform_from_2dmatrix(in_array: Array2<f64>) -> AffineTransform {
let transform = match in_array.inv() {
Ok(inv) => AffineTransform {
matrix2d_transform: in_array.clone(),
matrix2d_transform_inverse: inv,
inverse_exists: true,
},
Err(e) => AffineTransform {
matrix2d_transform: in_array.clone(),
matrix2d_transform_inverse: Array2::zeros((2, 2)),
inverse_exists: false,
},
};
return transform;
}

/// a linear transform of an image represented by either size 2x2
/// or 3x3 ( right column is a translation and projection ) matrix applied to the image index
/// coordinates
pub struct AffineTransform {
matrix2d_transform: Array2<f64>,
matrix2d_transform_inverse: Array2<f64>,
inverse_exists: bool,
}

fn source_coordinate(p: (f64, f64), trans: ArrayView2<f64>) -> (f64, f64) {
let p = match trans.shape()[0] {
2 => array![[p.0], [p.1]],
3 => array![[p.0], [p.1], [1.0]],
_ => unreachable!(),
};

let result = trans.dot(&p);
let x = result[[0, 0]];
let y = result[[1, 0]];
let w = match trans.shape()[0] {
2 => 1.0,
3 => result[[2, 0]],
_ => unreachable!(),
};
if (w - 1.0).abs() > std::f64::EPSILON {
(x / w, y / w)
} else {
(x, y)
}
}

impl Transform for AffineTransform {
fn apply(&self, p: (f64, f64)) -> (f64, f64) {
return source_coordinate(p, self.matrix2d_transform.view());
}

fn apply_inverse(&self, p: (f64, f64)) -> (f64, f64) {
return source_coordinate(p, self.matrix2d_transform_inverse.view());
}

fn inverse_exists(&self) -> bool {
return self.inverse_exists;
}
}

/// describes the Axes to use in rotation_3d
/// X and Y correspond to the image index coordinates and
/// Z is perpendicular out of the image plane
pub enum Axes {
X,
Y,
Z,
}

/// generates a 2d matrix describing a rotation around a 2d coordinate
pub fn rotate_around_centre(radians: f64, centre: (f64, f64)) -> Array2<f64> {
translation(centre.0, centre.1)
.dot(&rotation_3d(radians, Axes::Z))
.dot(&translation(-centre.0, -centre.1))
}

/// generates a matrix describing 2d rotation around origin
pub fn rotation_2d(radians: f64) -> Array2<f64> {
let s = radians.sin();
let c = radians.cos();
array![[c, -s], [s, c]]
}

/// generates a 3x3 matrix describing a rotation around either the index coordinate axes
/// (X,Y) or in the perpendicular axes to the image (Z)
pub fn rotation_3d(radians: f64, ax: Axes) -> Array2<f64> {
let s = radians.sin();
let c = radians.cos();
Expand All @@ -29,14 +100,17 @@ pub fn rotation_3d(radians: f64, ax: Axes) -> Array2<f64> {
}
}

/// generates a matrix describing translation in the image index space
pub fn translation(x: f64, y: f64) -> Array2<f64> {
array![[1.0, 0.0, x], [0.0, 1.0, y], [0.0, 0.0, 1.0]]
}

/// generates a matrix describing scaling in image index space
pub fn scale(x: f64, y: f64) -> Array2<f64> {
array![[x, 0.0, 0.0], [0.0, y, 0.0], [0.0, 0.0, 1.0]]
}

/// generates a matrix describing shear in image index space
pub fn shear(x: f64, y: f64) -> Array2<f64> {
array![[1.0, x, 0.0], [y, 1.0, 0.0], [0.0, 0.0, 1.0]]
}
Loading

0 comments on commit bf7c834

Please sign in to comment.