@@ -19,42 +19,43 @@ class OrientationError(Exception):
1919def io_orientation (affine , tol = None ):
2020 ''' Orientation of input axes in terms of output axes for `affine`
2121
22- Valid for an affine transformation from ``m `` dimensions to ``n ``
23- dimensions (``affine.shape == (n+ 1, m+ 1)``).
22+ Valid for an affine transformation from ``p `` dimensions to ``q ``
23+ dimensions (``affine.shape == (q + 1, p + 1)``).
2424
2525 The calculated orientations can be used to transform associated
26- arrays to best match the output orientations. If ``n `` > ``m ``, then
26+ arrays to best match the output orientations. If ``p `` > ``q ``, then
2727 some of the output axes should be considered dropped in this
2828 orientation.
2929
3030 Parameters
3131 ----------
32- affine : (n +1,m +1) ndarray-like
33- Transformation affine from ``m `` inputs to ``n `` outputs.
34- Usually this will be a shape (4,4) matrix, transforming 3 inputs
35- to 3 outputs, but the code also handles the more general case
32+ affine : (q +1, p +1) ndarray-like
33+ Transformation affine from ``p `` inputs to ``q `` outputs. Usually this
34+ will be a shape (4,4) matrix, transforming 3 inputs to 3 outputs, but the
35+ code also handles the more general case
3636 tol : {None, float}, optional
37- threshold below which SVD values of the affine are considered
38- zero. If `tol` is None, and ``S`` is an array with singular
39- values for `affine`, and ``eps`` is the epsilon value for
40- datatype of ``S``, then `tol` set to ``S.max() * eps``.
37+ threshold below which SVD values of the affine are considered zero. If
38+ `tol` is None, and ``S`` is an array with singular values for `affine`,
39+ and ``eps`` is the epsilon value for datatype of ``S``, then `tol` set to
40+ ``S.max() * eps``.
4141
4242 Returns
4343 -------
44- orientations : (n,2) ndarray
45- one row per input axis, where the first value in each row is the
46- closest corresponding output axis, and the second value is 1 if
47- the input axis is in the same direction as the corresponding
48- output axis, , and -1 if it is in the opposite direction, to the
49- corresponding output axis. If a row is [np.nan, np.nan], which
50- can happen when n > m, then this row should be considered
51- dropped.
44+ orientations : (p, 2) ndarray
45+ one row per input axis, where the first value in each row is the closest
46+ corresponding output axis. The second value in each row is 1 if the input
47+ axis is in the same direction as the corresponding output axis and -1 if
48+ it is in the opposite direction. If a row is [np.nan, np.nan], which can
49+ happen when p > q, then this row should be considered dropped.
5250 '''
5351 affine = np .asarray (affine )
54- n , m = affine .shape [0 ]- 1 , affine .shape [1 ]- 1
55- # extract the underlying rotation matrix
56- RZS = affine [:n , :m ]
52+ q , p = affine .shape [0 ]- 1 , affine .shape [1 ]- 1
53+ # extract the underlying rotation, zoom, shear matrix
54+ RZS = affine [:q , :p ]
5755 zooms = np .sqrt (np .sum (RZS * RZS , axis = 0 ))
56+ # Zooms can be zero, in which case all elements in the column are zero, and
57+ # we can leave them as they are
58+ zooms [zooms == 0 ] = 1
5859 RS = RZS / zooms
5960 # Transform below is polar decomposition, returning the closest
6061 # shearless matrix R to RS
@@ -66,13 +67,13 @@ def io_orientation(affine, tol=None):
6667 R = np .dot (P [:, keep ], Qs [keep ])
6768 # the matrix R is such that np.dot(R,R.T) is projection onto the
6869 # columns of P[:,keep] and np.dot(R.T,R) is projection onto the rows
69- # of Qs[keep]. R (== np.dot(R, np.eye(m ))) gives rotation of the
70+ # of Qs[keep]. R (== np.dot(R, np.eye(p ))) gives rotation of the
7071 # unit input vectors to output coordinates. Therefore, the row
7172 # index of abs max R[:,N], is the output axis changing most as input
7273 # axis N changes. In case there are ties, we choose the axes
7374 # iteratively, removing used axes from consideration as we go
74- ornt = np .ones ((n , 2 ), dtype = np .int8 ) * np .nan
75- for in_ax in range (m ):
75+ ornt = np .ones ((p , 2 ), dtype = np .int8 ) * np .nan
76+ for in_ax in range (p ):
7677 col = R [:, in_ax ]
7778 if not np .alltrue (np .equal (col , 0 )):
7879 out_ax = np .argmax (np .abs (col ))
@@ -96,38 +97,37 @@ def _ornt_to_affine(orientations):
9697
9798 Parameters
9899 ----------
99- orientations : (n,2) ndarray
100- one row per input axis, where the first value in each row is the
101- closest corresponding output axis, and the second value is 1 if
102- the input axis is in the same direction as the corresponding
103- output axis, and -1 if it is in the opposite direction, to the
104- corresponding output axis. If a row has first entry np.nan, then
105- this axis is dropped from the output.
100+ orientations : (p, 2) ndarray
101+ one row per input axis, where the first value in each row is the closest
102+ corresponding output axis. The second value in each row is 1 if the input
103+ axis is in the same direction as the corresponding output axis and -1 if
104+ it is in the opposite direction. If a row is [np.nan, np.nan], which can
105+ happen when p > q, then this row should be considered dropped.
106106
107107 Returns
108108 -------
109- affine : (m+1,n+ 1) ndarray
110- matrix representing flipping / dropping axes. m is equal to the
111- number of rows of orientations[:,0] that are not np.nan
109+ affine : (q + 1, p + 1) ndarray
110+ matrix representing flipping / dropping axes. q is equal to the number of
111+ rows of `` orientations[:, 0]`` that are not np.nan
112112 '''
113113 ornt = np .asarray (orientations )
114- n = ornt .shape [0 ]
114+ p = ornt .shape [0 ]
115115 keep = ~ np .isnan (ornt [:, 1 ])
116116 # These are the input coordinate axes that do have a matching output
117117 # column in the orientation. That is, if the 2nd row is [np.nan,
118118 # np.nan] then the orientation indicates that no output axes of an
119119 # affine with this orientation matches the 2nd input coordinate
120120 # axis. This would happen if the 2nd row of the affine that
121121 # generated ornt was [0,0,0,*]
122- axes_kept = np .arange (n )[keep ]
123- m = keep .sum (0 )
122+ axes_kept = np .arange (p )[keep ]
123+ q = keep .sum (0 )
124124 # the matrix P represents the affine transform impled by ornt. If
125125 # all entries of ornt are not np.nan, then P is square otherwise it
126126 # has more columns than rows indicating some coordinates were
127127 # dropped
128- P = np .zeros ((m + 1 , n + 1 ))
128+ P = np .zeros ((q + 1 , p + 1 ))
129129 P [- 1 , - 1 ] = 1
130- for idx in range (m ):
130+ for idx in range (q ):
131131 axs , flip = ornt [axes_kept [idx ]]
132132 P [idx , axs ] = flip
133133 return P
@@ -164,7 +164,6 @@ def apply_orientation(arr, ornt):
164164 if np .any (np .isnan (ornt [:, 0 ])):
165165 raise OrientationError ('Cannot drop coordinates when '
166166 'applying orientation to data' )
167- shape = t_arr .shape
168167 # apply ornt transformations
169168 for ax , flip in enumerate (ornt [:, 1 ]):
170169 if flip == - 1 :
0 commit comments