Skip to content

Commit 1e7c257

Browse files
committed
DOC: Discuss slicer interface
1 parent 71ec964 commit 1e7c257

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

doc/source/links_names.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@
223223
.. _`wikipedia shear matrix`: https://en.wikipedia.org/wiki/Shear_matrix
224224
.. _`wikipedia reflection`: https://en.wikipedia.org/wiki/Reflection_(mathematics)
225225
.. _`wikipedia direction cosine`: https://en.wikipedia.org/wiki/Direction_cosine
226+
.. _`wikipedia aliasing`: https://en.wikipedia.org/wiki/Aliasing
226227

227228
.. Programming ideas
228229
.. _proxy: https://en.wikipedia.org/wiki/Proxy_pattern

doc/source/nibabel_images.rst

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,79 @@ True
282282
See :doc:`images_and_memory` for more details on managing image memory and
283283
controlling 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+
304+
However, unused data did not need to be loaded into memory or scaled.
305+
Additionally, the image affine was adjusted so that the X-translation is
306+
32 voxels (64mm) less:
307+
308+
>>> cropped_img.affine
309+
array([[ -2. , 0. , 0. , 53.86],
310+
[ -0. , 1.97, -0.36, -35.72],
311+
[ 0. , 0.32, 2.17, -7.25],
312+
[ 0. , 0. , 0. , 1. ]])
313+
314+
>>> img.affine - cropped_img.affine
315+
array([[ 0., 0., 0., 64.],
316+
[ 0., 0., 0., 0.],
317+
[ 0., 0., 0., 0.],
318+
[ 0., 0., 0., 0.]])
319+
320+
Another use for the slicer object is to choose specific volumes from a
321+
time series:
322+
323+
>>> vol0 = img.slicer[..., 0]
324+
>>> vol0.shape
325+
(128, 96, 24)
326+
327+
Or a selection of volumes:
328+
329+
>>> img.slicer[..., :1].shape
330+
(128, 96, 24, 1)
331+
>>> img.slicer[..., :2].shape
332+
(128, 96, 24, 2)
333+
334+
It is also possible to use an integer step when slicing, downsampling
335+
the image without filtering.
336+
Note that this *will induce artifacts* in the frequency spectrum
337+
(`aliasing <wikipedia aliasing>`_) along any axis that is down-sampled.
338+
339+
>>> downsampled = vol0.slicer[::2, ::2, ::2]
340+
>>> downsampled.header.get_zooms()
341+
(4.0, 4.0, 4.399998)
342+
343+
Finally, an image can be flipped along an axis, maintaining an appropriate
344+
affine matrix:
345+
346+
>>> nib.orientations.aff2axcodes(img.affine)
347+
('L', 'A', 'S')
348+
>>> ras = img.slicer[::-1]
349+
>>> nib.orientations.aff2axcodes(ras.affine)
350+
('R', 'A', 'S')
351+
>>> ras.affine
352+
array([[ 2. , 0. , 0. , 117.86],
353+
[ 0. , 1.97, -0.36, -35.72],
354+
[ -0. , 0.32, 2.17, -7.25],
355+
[ 0. , 0. , 0. , 1. ]])
356+
357+
285358
******************
286359
Loading and saving
287360
******************

0 commit comments

Comments
 (0)