Skip to content

Commit bf7c834

Browse files
tdk-metaTodd Keeler
and
Todd Keeler
authored
transform update for generic transforms (#60)
Co-authored-by: Todd Keeler <[email protected]>
1 parent 5223a69 commit bf7c834

File tree

3 files changed

+181
-95
lines changed

3 files changed

+181
-95
lines changed

examples/transforms.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ fn main() {
3434
// Create transformation matrix
3535
let x = 0.5 * (cameraman.cols() as f64) - 0.5;
3636
let y = 0.5 * (cameraman.rows() as f64) - 0.5;
37-
let trans = rotate_around_centre(FRAC_PI_4, (x, y)).dot(&scale(0.7, 0.7));
37+
let trans =
38+
transform_from_2dmatrix(rotate_around_centre(FRAC_PI_4, (x, y)).dot(&scale(0.7, 0.7)));
3839

39-
let transformed = cameraman
40-
.transform(trans.view(), None)
41-
.expect("Transform failed");
40+
let transformed = cameraman.transform(&trans, None).expect("Transform failed");
4241

4342
// save
4443
let path = Path::new("transformed_cameraman.png");

src/transform/affine.rs

+74
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,94 @@
1+
use super::Transform;
12
use ndarray::{array, prelude::*};
3+
use ndarray_linalg::Inverse;
24

5+
/// converts a matrix into an equivalent `AffineTransform`
6+
pub fn transform_from_2dmatrix(in_array: Array2<f64>) -> AffineTransform {
7+
let transform = match in_array.inv() {
8+
Ok(inv) => AffineTransform {
9+
matrix2d_transform: in_array.clone(),
10+
matrix2d_transform_inverse: inv,
11+
inverse_exists: true,
12+
},
13+
Err(e) => AffineTransform {
14+
matrix2d_transform: in_array.clone(),
15+
matrix2d_transform_inverse: Array2::zeros((2, 2)),
16+
inverse_exists: false,
17+
},
18+
};
19+
return transform;
20+
}
21+
22+
/// a linear transform of an image represented by either size 2x2
23+
/// or 3x3 ( right column is a translation and projection ) matrix applied to the image index
24+
/// coordinates
25+
pub struct AffineTransform {
26+
matrix2d_transform: Array2<f64>,
27+
matrix2d_transform_inverse: Array2<f64>,
28+
inverse_exists: bool,
29+
}
30+
31+
fn source_coordinate(p: (f64, f64), trans: ArrayView2<f64>) -> (f64, f64) {
32+
let p = match trans.shape()[0] {
33+
2 => array![[p.0], [p.1]],
34+
3 => array![[p.0], [p.1], [1.0]],
35+
_ => unreachable!(),
36+
};
37+
38+
let result = trans.dot(&p);
39+
let x = result[[0, 0]];
40+
let y = result[[1, 0]];
41+
let w = match trans.shape()[0] {
42+
2 => 1.0,
43+
3 => result[[2, 0]],
44+
_ => unreachable!(),
45+
};
46+
if (w - 1.0).abs() > std::f64::EPSILON {
47+
(x / w, y / w)
48+
} else {
49+
(x, y)
50+
}
51+
}
52+
53+
impl Transform for AffineTransform {
54+
fn apply(&self, p: (f64, f64)) -> (f64, f64) {
55+
return source_coordinate(p, self.matrix2d_transform.view());
56+
}
57+
58+
fn apply_inverse(&self, p: (f64, f64)) -> (f64, f64) {
59+
return source_coordinate(p, self.matrix2d_transform_inverse.view());
60+
}
61+
62+
fn inverse_exists(&self) -> bool {
63+
return self.inverse_exists;
64+
}
65+
}
66+
67+
/// describes the Axes to use in rotation_3d
68+
/// X and Y correspond to the image index coordinates and
69+
/// Z is perpendicular out of the image plane
370
pub enum Axes {
471
X,
572
Y,
673
Z,
774
}
875

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

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

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

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

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

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

0 commit comments

Comments
 (0)