1
+ use super :: Transform ;
1
2
use ndarray:: { array, prelude:: * } ;
3
+ use ndarray_linalg:: Inverse ;
2
4
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
3
70
pub enum Axes {
4
71
X ,
5
72
Y ,
6
73
Z ,
7
74
}
8
75
76
+ /// generates a 2d matrix describing a rotation around a 2d coordinate
9
77
pub fn rotate_around_centre ( radians : f64 , centre : ( f64 , f64 ) ) -> Array2 < f64 > {
10
78
translation ( centre. 0 , centre. 1 )
11
79
. dot ( & rotation_3d ( radians, Axes :: Z ) )
12
80
. dot ( & translation ( -centre. 0 , -centre. 1 ) )
13
81
}
14
82
83
+ /// generates a matrix describing 2d rotation around origin
15
84
pub fn rotation_2d ( radians : f64 ) -> Array2 < f64 > {
16
85
let s = radians. sin ( ) ;
17
86
let c = radians. cos ( ) ;
18
87
array ! [ [ c, -s] , [ s, c] ]
19
88
}
20
89
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)
21
92
pub fn rotation_3d ( radians : f64 , ax : Axes ) -> Array2 < f64 > {
22
93
let s = radians. sin ( ) ;
23
94
let c = radians. cos ( ) ;
@@ -29,14 +100,17 @@ pub fn rotation_3d(radians: f64, ax: Axes) -> Array2<f64> {
29
100
}
30
101
}
31
102
103
+ /// generates a matrix describing translation in the image index space
32
104
pub fn translation ( x : f64 , y : f64 ) -> Array2 < f64 > {
33
105
array ! [ [ 1.0 , 0.0 , x] , [ 0.0 , 1.0 , y] , [ 0.0 , 0.0 , 1.0 ] ]
34
106
}
35
107
108
+ /// generates a matrix describing scaling in image index space
36
109
pub fn scale ( x : f64 , y : f64 ) -> Array2 < f64 > {
37
110
array ! [ [ x, 0.0 , 0.0 ] , [ 0.0 , y, 0.0 ] , [ 0.0 , 0.0 , 1.0 ] ]
38
111
}
39
112
113
+ /// generates a matrix describing shear in image index space
40
114
pub fn shear ( x : f64 , y : f64 ) -> Array2 < f64 > {
41
115
array ! [ [ 1.0 , x, 0.0 ] , [ y, 1.0 , 0.0 ] , [ 0.0 , 0.0 , 1.0 ] ]
42
116
}
0 commit comments