@@ -282,6 +282,80 @@ True
282282See :doc: `images_and_memory ` for more details on managing image memory and
283283controlling the image cache.
284284
285+ .. _image-slicing :
286+
287+ Image slicing
288+ =============
289+
290+ At times it is useful to manipulate an image's shape while keeping it in the
291+ same coordinate system.
292+ The ``slicer `` attribute provides an array-slicing interface to produce new
293+ images with an appropriately adjusted header, such that the data at a given
294+ RAS+ location is unchanged.
295+
296+ >>> cropped_img = img.slicer[32 :- 32 , ... ]
297+ >>> cropped_img.shape
298+ (64, 96, 24, 2)
299+
300+ The data is identical to cropping the data block directly:
301+
302+ >>> np.array_equal(cropped_img.get_fdata(), img.get_fdata()[32 :- 32 , ... ])
303+ True
304+
305+ However, unused data did not need to be loaded into memory or scaled.
306+ Additionally, the image affine was adjusted so that the X-translation is
307+ 32 voxels (64mm) less:
308+
309+ >>> cropped_img.affine
310+ array([[ -2. , 0. , 0. , 53.86],
311+ [ -0. , 1.97, -0.36, -35.72],
312+ [ 0. , 0.32, 2.17, -7.25],
313+ [ 0. , 0. , 0. , 1. ]])
314+
315+ >>> img.affine - cropped_img.affine
316+ array([[ 0., 0., 0., 64.],
317+ [ 0., 0., 0., 0.],
318+ [ 0., 0., 0., 0.],
319+ [ 0., 0., 0., 0.]])
320+
321+ Another use for the slicer object is to choose specific volumes from a
322+ time series:
323+
324+ >>> vol0 = img.slicer[... , 0 ]
325+ >>> vol0.shape
326+ (128, 96, 24)
327+
328+ Or a selection of volumes:
329+
330+ >>> img.slicer[... , :1 ].shape
331+ (128, 96, 24, 1)
332+ >>> img.slicer[... , :2 ].shape
333+ (128, 96, 24, 2)
334+
335+ It is also possible to use an integer step when slicing, downsampling
336+ the image without filtering.
337+ Note that this *will induce artifacts * in the frequency spectrum
338+ (`aliasing <wikipedia aliasing >`_) along any axis that is down-sampled.
339+
340+ >>> downsampled = vol0.slicer[::2 , ::2 , ::2 ]
341+ >>> downsampled.header.get_zooms()
342+ (4.0, 4.0, 4.399998)
343+
344+ Finally, an image can be flipped along an axis, maintaining an appropriate
345+ affine matrix:
346+
347+ >>> nib.orientations.aff2axcodes(img.affine)
348+ ('L', 'A', 'S')
349+ >>> ras = img.slicer[::- 1 ]
350+ >>> nib.orientations.aff2axcodes(ras.affine)
351+ ('R', 'A', 'S')
352+ >>> ras.affine
353+ array([[ 2. , 0. , 0. , 117.86],
354+ [ 0. , 1.97, -0.36, -35.72],
355+ [ -0. , 0.32, 2.17, -7.25],
356+ [ 0. , 0. , 0. , 1. ]])
357+
358+
285359******************
286360Loading and saving
287361******************
0 commit comments