Skip to content

DOC: Discuss slicer interface #846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/links_names.txt
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@
.. _`wikipedia shear matrix`: https://en.wikipedia.org/wiki/Shear_matrix
.. _`wikipedia reflection`: https://en.wikipedia.org/wiki/Reflection_(mathematics)
.. _`wikipedia direction cosine`: https://en.wikipedia.org/wiki/Direction_cosine
.. _`wikipedia aliasing`: https://en.wikipedia.org/wiki/Aliasing

.. Programming ideas
.. _proxy: https://en.wikipedia.org/wiki/Proxy_pattern
Expand Down
74 changes: 74 additions & 0 deletions doc/source/nibabel_images.rst
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,80 @@ True
See :doc:`images_and_memory` for more details on managing image memory and
controlling the image cache.

.. _image-slicing:

Image slicing
=============

At times it is useful to manipulate an image's shape while keeping it in the
same coordinate system.
The ``slicer`` attribute provides an array-slicing interface to produce new
images with an appropriately adjusted header, such that the data at a given
RAS+ location is unchanged.

>>> cropped_img = img.slicer[32:-32, ...]
>>> cropped_img.shape
(64, 96, 24, 2)

The data is identical to cropping the data block directly:

>>> np.array_equal(cropped_img.get_fdata(), img.get_fdata()[32:-32, ...])
True

However, unused data did not need to be loaded into memory or scaled.
Additionally, the image affine was adjusted so that the X-translation is
32 voxels (64mm) less:

>>> cropped_img.affine
array([[ -2. , 0. , 0. , 53.86],
[ -0. , 1.97, -0.36, -35.72],
[ 0. , 0.32, 2.17, -7.25],
[ 0. , 0. , 0. , 1. ]])

>>> img.affine - cropped_img.affine
array([[ 0., 0., 0., 64.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])

Another use for the slicer object is to choose specific volumes from a
time series:

>>> vol0 = img.slicer[..., 0]
>>> vol0.shape
(128, 96, 24)

Or a selection of volumes:

>>> img.slicer[..., :1].shape
(128, 96, 24, 1)
>>> img.slicer[..., :2].shape
(128, 96, 24, 2)

It is also possible to use an integer step when slicing, downsampling
the image without filtering.
Note that this *will induce artifacts* in the frequency spectrum
(`aliasing <wikipedia aliasing>`_) along any axis that is down-sampled.

>>> downsampled = vol0.slicer[::2, ::2, ::2]
>>> downsampled.header.get_zooms()
(4.0, 4.0, 4.399998)

Finally, an image can be flipped along an axis, maintaining an appropriate
affine matrix:

>>> nib.orientations.aff2axcodes(img.affine)
('L', 'A', 'S')
>>> ras = img.slicer[::-1]
>>> nib.orientations.aff2axcodes(ras.affine)
('R', 'A', 'S')
>>> ras.affine
array([[ 2. , 0. , 0. , 117.86],
[ 0. , 1.97, -0.36, -35.72],
[ -0. , 0.32, 2.17, -7.25],
[ 0. , 0. , 0. , 1. ]])


******************
Loading and saving
******************
Expand Down