Skip to content

Commit 7d1badf

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

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,80 @@ 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+
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
******************
286360
Loading and saving
287361
******************

0 commit comments

Comments
 (0)