fsleyes.displaycontext.niftiopts

This module defines the NiftiOpts class.

An important note on coordinate systems

FSLeyes displays all overlays in a single coordinate system, referred throughout as the display coordinate system. However, Nifti overlays can potentially be transformed into the display coordinate system in one of several ways:

voxels

(a.k.a. id) The image data voxel coordinates map to the display coordinates.

scaled voxels

(a.k.a. pixdim) The image data voxel coordinates are scaled by the pixdim values stored in the NIFTI header. The origin is fixed at the centre of voxel (0, 0, 0).

radioloigcal scaled voxels

(a.k.a. pixdim-flip) The image data voxel coordinates are scaled by the pixdim values stored in the NIFTI header and, if the image appears to be stored in neurological order, the X (left-right) axis is inverted. The origin is fixed at the centre of voxel (0, 0, 0) (or (X-1, 0, 0) for inverted images).

world

(a.k.a. affine) The image data voxel coordinates are transformed by the transformation matrix stored in the NIFTI header - see the Nifti class for more details.

reference

(a.k.a. reference) The image data voxel coordinates are transformed into the pixdim-flip coordinate system of another, reference, NIFTI image. The reference overlay is specified by the DisplayContext.displaySpace attribute.

The NiftiOpts.transform property controls how the image data is transformed into the display coordinate system. It allows any of the above spaces to be specified (as id, pixdim, pixdim-flip, affine`, or reference respectively).

Pixdim flip

The pixdim-flip transform is the coordinate system used internally by many of the FSL tools. For instance, this is the coordinate system used by FSLView, by flirt, and in the VTK sub-cortical segmentation model files output by first.

Furthermore, the vectors in eigenvector images images output by dtifit are oriented according to this space, so if the input data is in neurological orientation, these vectors need to be inverted along the x axis.

https://fsl.fmrib.ox.ac.uk/fsl/docs/#/registration/flirt/faq?id=what-is-the-format-of-the-matrix-used-by-flirt-and-how-does-it-relate-to-the-transformation-parameters

What is a voxel?

Regardless of the space in which the Nifti is displayed , the voxel-to-display space transformation (and in general, all of FSLeyes) assumes that integer voxel coordinates correspond to the centre of the voxel in the display coordinate system. In other words, a voxel at location:

[x, y, z]

is assumed to occupy the space that corresponds to:

[x-0.5 - x+0.5, y-0.5 - y+0.5, z-0.5 - z+0.5]

For example, if the NiftiOpts.transform property is set to id, the voxel:

[2, 3, 4]

is drawn such that it occupies the space:

[1.5 - 2.5, 2.5 - 3.5, 3.5 - 4.5]

This convention is in line with the convention defined by the NIFTI specification: it assumes that the voxel coordinates [x, y, z] correspond to the centre of a voxel.

class fsleyes.displaycontext.niftiopts.NiftiOpts(*args, **kwargs)[source]

Bases: DisplayOpts

The NiftiOpts class describes how a Nifti overlay should be displayed.

NiftiOpts is the base class for a number of DisplayOpts sub-classes - it contains display options which are common to all overlay types that represent a NIFTI image.

volume

If the Image has more than 3 dimensions, the current volume to display. The volume dimension is controlled by the volumeDim property.

volumeDim

For images with more than three dimensions, this property controls the dimension that the volume property indexes into. When the volumeDim changes, the volume for the previous volumeDim is fixed at its last value, and used for subsequent lookups.

transform

This property defines how the overlay should be transformd into the display coordinate system. See the note on coordinate systems for important information regarding this property.

displayXform

A custom transformation matrix which is concatenated on to the voxel -> world transformation of the Nifti overlay.

This transform is intended for temporary changes to the overlay display (when DisplayContext.displaySpace == 'world') - changes to it will not result in the :DisplayContext.bounds being updated.

If you change the displayXform, make sure to change it back to an identity matrix when you are done.

enableOverrideDataRange

By default, the Image.dataRange property is used to set display and clipping ranges. However, if this property is True, the overrideDataRange is used instead.

..note:: The point of this property is to make it easier to display images

with a very large data range driven by outliers. On platforms which do not support floating point textures, these images are impossible to display unless they are normalised according to a smaller data range. See the Texture3D.__determineTextureType() method for some more details.

overrideDataRange

Data range used in place of the Image.dataRange if the enableOverrideDataRange property is True.

__init__(*args, **kwargs)[source]

Create a NiftiOpts instance.

All arguments are passed through to the DisplayOpts constructor.

destroy()[source]

Calls the DisplayOpts.destroy() method.

__toggleSiblingListeners(enable=True)

Enables/disables the volumeDim listeners of sibling NiftiOpts instances. This is used by the __volumeDimChanged() method to avoid nastiness.

__volumeDimChanged(*a)

Called when the volumeDim changes. Saves the value of volume for the last volumeDim, and restores the previous value of volume for the new volumeDim.

__overlayTransformChanged(*a)

Called when the Nifti overlay sends a notification on the 'transform' topic, indicating that its voxel->world transformation matrix has been updated.

__displaySpaceTransformChanged(*a)

Called when the DisplayContext.displaySpace is a Nifti overlay, and its Nifti.voxToWorldMat changes. Updates the transformation matrices for this image.

__transformChanged(*a)

Called when the transform property changes.

Calculates the min/max values of a 3D bounding box, in the display coordinate system, which is big enough to contain the image. Sets the DisplayOpts.bounds property accordingly.

__displaySpaceChanged(*a, **kwa)

Called when the DisplayContext.displaySpace property changes. Re-generates transformation matrices, and re-calculates the display bounds (via calls to __setupTransforms() and __transformChanged()).

__displayXformChanged(*a)

Called when the displayXform property changes. Updates the transformation matrices and bounds accordingly.

Critically, when the displayXform property changes, the DisplayContext is not notified. This is because the displayXform is intended for temporary changes.

__setupTransforms()

Calculates transformation matrices between all of the possible spaces in which the overlay may be displayed.

These matrices are accessible via the getTransform() method.

classmethod getVolumeProps()[source]

Overrides DisplayOpts.getVolumeProps(). Returns a list of property names which control the displayed volume/timepoint.

__annotations__ = {}
__module__ = 'fsleyes.displaycontext.niftiopts'
getTransform(from_, to)[source]

Return a matrix which may be used to transform coordinates from from_ to to. Valid values for from_ and to are:

id

Voxel coordinates

voxel

Equivalent to id.

pixdim

Voxel coordinates, scaled by voxel dimensions

pixdim-flip

Voxel coordinates, scaled by voxel dimensions, and with the X axis flipped if the affine matrix has a positivie determinant. If the affine matrix does not have a positive determinant, this is equivalent to pixdim.

pixflip

Equivalent to pixdim-flip.

affine

World coordinates, as defined by the NIFTI qform/sform. See Image.voxToWorldMat.

world

Equivalent to affine.

reference

pixdim-flip coordinates of the reference image specified by the DisplayContext.displaySpace attribute. If the displaySpace is set to 'world', this is equivalent to affine.

ref

Equivalent to reference.

display

Equivalent to the current value of transform.

texture

Voxel coordinates scaled to lie between 0.0 and 1.0, suitable for looking up voxel values when stored as an OpenGL texture.

roundVoxels(voxels, daxes=None, roundOther=False)[source]

Round the given voxel coordinates to integers. This is a surprisingly complicated operation.

FSLeyes and the NIFTI standard map integer voxel coordinates to the voxel centre. For example, a voxel [3, 4, 5] fills the space:

[2.5-3.5, 3.5-4.5, 4.5-5.5].

So all we need to do is round to the nearest integer. But there are a few problems with breaking ties when rounding…

The numpy.round function breaks ties (e.g. 7.5) by rounding to the nearest even integer, which can cause funky behaviour. So instead of using numpy.round, we take floor(x+0.5), to force consistent behaviour (i.e. always rounding central values up).

The next problem is that we have to round the voxel coordaintes carefully, depending on the orientation of the voxel axis w.r.t. the display axis. We want to round in the same direction in the display coordinate system, regardless of the voxel orientation. So we need to check the orientation of the voxel axis, and round down or up accordingly.

This is to handle scenarios where we have two anatomically aligned images, but with opposing storage orders (e.g. one stored neurologically, and one stored radiologically). If we have such images, and the display location is on a voxel boundary, we want the voxel coordinates for one image to be rounded in the same anatomical direction (i.e. the same direction in the display coordinate system). Otherwise the same display location will map to mis-aligned voxels in the two images, because the voxel coordinate rounding will move in anatomically opposing directions.

This method also prevents coordinates that are close to 0 from being set to -1, and coordinates that are close to the axis size from being set to (size + 1). In other words, voxel coordinates which are on the low or high boundaries will be rounded so as to be valid voxel coordinates.

Parameters:
  • voxels – A (N, 3) numpy array containing the voxel coordinates to be rounded.

  • daxes – Display coordinate system axes along which to round the coordinates (defaults to all axes).

  • roundOther – If True, any voxel axes which are not in daxes will still be rounded, but not with an orientation-specific rounding convention.

Returns:

The voxels, rounded appropriately.

transformCoords(coords, from_, to_, vround=False, vector=False, pre=None, post=None)[source]

Transforms the given coordinates from from_ to to_.

The from_ and to_ parameters must be those accepted by the getTransform() method.

Parameters:
  • coords – Coordinates to transform

  • from – Space to transform from

  • to – Space to transform to

  • vround – If True, and to_ in ('voxel', 'id), the transformed coordinates are rounded to the nearest integer.

  • vector – Defaults to False. If True, the coordinates are treated as vectors.

  • pre – Transformation to apply before the from_-to-to transformation.

  • post – Transformation to apply after the from_-to-to transformation.

getVoxel(xyz=None, clip=True, vround=True)[source]

Calculates and returns the voxel coordinates corresponding to the given location (assumed to be in the display coordinate system) for the Nifti associated with this NiftiOpts instance..

Parameters:
  • xyz – Display space location to convert to voxels. If not provided, the current DisplayContext.location is used.

  • clip – If False, and the transformed coordinates are out of the voxel coordinate bounds, the coordinates returned anyway. Defaults to True.

  • vround – If True, the returned voxel coordinates are rounded to the nearest integer. Otherwise they may be fractional.

Returns:

None if the location is outside of the image bounds, unless clip=False.

setIndex(indices)[source]

Sets the indexes of all non-spatial dimensions. The volume property is also updated.

index(slc=None, atVolume=True)[source]

Given a slice object slc, which indexes into the X, Y, and Z dimensions, fills it to slice every dimension of the image, using the current volume and volumeDim, and saved values for the other volume dimensions.

Parameters:
  • slc – Something which can slice the first three dimensions of the image. If None, defaults to [:, :, :].

  • atVolume – If True, the returned slice will index the current volume of the current volumeDim. Otherwise the returned slice will index across the whole volumeDim.