@@ -254,3 +254,45 @@ def dot_reduce(*args):
254
254
... arg[N-2].dot(arg[N-1])))...``
255
255
"""
256
256
return reduce (lambda x , y : np .dot (y , x ), args [::- 1 ])
257
+
258
+
259
+ def voxel_sizes (affine ):
260
+ r""" Return voxel size for each input axis given `affine`
261
+
262
+ The `affine` is the mapping between array (voxel) coordinates and mm
263
+ (world) coordinates.
264
+
265
+ The voxel size for the first voxel (array) axis is the distance moved in
266
+ world coordinates when moving one unit along the first voxel (array) axis.
267
+ This is the distance between the world coordinate of voxel (0, 0, 0) and
268
+ the world coordinate of voxel (1, 0, 0). The world coordinate vector of
269
+ voxel coordinate vector (0, 0, 0) is given by ``v0 = affine.dot((0, 0, 0,
270
+ 1)[:3]``. The world coordinate vector of voxel vector (1, 0, 0) is
271
+ ``v1_ax1 = affine.dot((1, 0, 0, 1))[:3]``. The final 1 in the voxel
272
+ vectors and the ``[:3]`` at the end are because the affine works on
273
+ homogenous coodinates. The translations part of the affine is ``trans =
274
+ affine[:3, 3]``, and the rotations, zooms and shearing part of the affine
275
+ is ``rzs = affine[:3, :3]``. Because of the final 1 in the input voxel
276
+ vector, ``v0 == rzs.dot((0, 0, 0)) + trans``, and ``v1_ax1 == rzs.dot((1,
277
+ 0, 0)) + trans``, and the difference vector is ``rzs.dot((0, 0, 0)) -
278
+ rzs.dot((1, 0, 0)) == rzs.dot((1, 0, 0)) == rzs[:, 0]``. The distance
279
+ vectors in world coordinates between (0, 0, 0) and (1, 0, 0), (0, 1, 0),
280
+ (0, 0, 1) are given by ``rzs.dot(np.eye(3)) = rzs``. The voxel sizes are
281
+ the Euclidean lengths of the distance vectors. So, the voxel sizes are
282
+ the Euclidean lengths of the columns of the affine (excluding the last row
283
+ and column of the affine).
284
+
285
+ Parameters
286
+ ----------
287
+ affine : 2D array-like
288
+ Affine transformation array. Usually shape (4, 4), but can be any 2D
289
+ array.
290
+
291
+ Returns
292
+ -------
293
+ vox_sizes : 1D array
294
+ Voxel sizes for each input axis of affine. Usually 1D array length 3,
295
+ but in general has length (N-1) where input `affine` is shape (M, N).
296
+ """
297
+ top_left = affine [:- 1 , :- 1 ]
298
+ return np .sqrt (np .sum (top_left ** 2 , axis = 0 ))
0 commit comments