# core.image.image_spaces

## Module: `core.image.image_spaces`

Utilities for working with Images and common neuroimaging spaces

Images are very general things, and don’t know anything about the kinds of spaces they refer to, via their coordinate map.

There are a set of common neuroimaging spaces. When we create neuroimaging Images, we want to place them in neuroimaging spaces, and return information about common neuroimaging spaces.

We do this by putting information about neuroimaging spaces in functions and variables in the `nipy.core.reference.spaces` module, and in this module.

This keeps the specific neuroimaging spaces out of our Image object.

    >>> from nipy.core.api import Image, vox2mni, rollimg, xyz_affine, as_xyz_image

Make a standard 4D xyzt image in MNI space.

First the data and affine:

    >>> data = np.arange(24, dtype=np.float32).reshape((1,2,3,4))
    >>> affine = np.diag([2,3,4,1]).astype(float)

We can add the TR (==2.0) to make the full 5x5 affine we need

    >>> img = Image(data, vox2mni(affine, 2.0))
    >>> img.affine
    array([[ 2.,  0.,  0.,  0.,  0.],
           [ 0.,  3.,  0.,  0.,  0.],
           [ 0.,  0.,  4.,  0.,  0.],
           [ 0.,  0.,  0.,  2.,  0.],
           [ 0.,  0.,  0.,  0.,  1.]])

In this case the neuroimaging ‘xyz_affine’ is just the 4x4 from the 5x5 in the image

    >>> xyz_affine(img)
    array([[ 2.,  0.,  0.,  0.],
           [ 0.,  3.,  0.,  0.],
           [ 0.,  0.,  4.,  0.],
           [ 0.,  0.,  0.,  1.]])

However, if we roll time first in the image array, we can’t any longer get an xyz_affine that makes sense in relationship to the voxel data:

    >>> img_t0 = rollimg(img, 't')
    >>> xyz_affine(img_t0)
    Traceback (most recent call last):
        ...
    AxesError: First 3 input axes must correspond to X, Y, Z

But we can fix this:

    >>> img_t0_affable = as_xyz_image(img_t0)
    >>> xyz_affine(img_t0_affable)
    array([[ 2.,  0.,  0.,  0.],
           [ 0.,  3.,  0.,  0.],
           [ 0.,  0.,  4.,  0.],
           [ 0.,  0.,  0.,  1.]])

It also works with nibabel images, which can only have xyz_affines:

    >>> import nibabel as nib
    >>> nimg = nib.Nifti1Image(data, affine)
    >>> xyz_affine(nimg)
    array([[ 2.,  0.,  0.,  0.],
           [ 0.,  3.,  0.,  0.],
           [ 0.,  0.,  4.,  0.],
           [ 0.,  0.,  0.,  1.]])

## Functions

nipy.core.image.image_spaces.as_xyz_image(img, name2xyz=None)

  Return version of img that has a valid xyz affine, or raise error

  Parameters:
    img `Image` instance or nibabel image

      It has a `coordmap` attribute (`Image`) or a `get_affine` method (nibabel image object)

    name2xyz None or mapping

      Object such that `name2xyz[ax_name]` returns ‘x’, or ‘y’ or ‘z’ or raises a KeyError for a str `ax_name`. None means use module default. Not used for nibabel img input.

  Returns:
    reo_img `Image` instance or nibabel image

      Returns image of same type as img input. If necessary, reo_img has its data and coordmap changed to allow it to return an xyz affine. If img is already xyz affable we return the input unchanged (`img is reo_img`).

  Raises:
    SpaceTypeError if img does not have an affine coordinate map
    AxesError if not all of x, y, z recognized in img `coordmap` range
    AffineError if axes dropped from the affine contribute to x, y, z
    coordinates

nipy.core.image.image_spaces.is_xyz_affable(img, name2xyz=None)

  Return True if the image img has an xyz affine

  Parameters:
    img `Image` or nibabel `SpatialImage`

      If `Image` test `img.coordmap`. If a nibabel image, return True

    name2xyz None or mapping

      Object such that `name2xyz[ax_name]` returns ‘x’, or ‘y’ or ‘z’ or raises a KeyError for a str `ax_name`. None means use module default. Not used for nibabel img input.

  Returns:
    tf bool

      True if img has an xyz affine, False otherwise

  Examples

      >>> from nipy.core.api import vox2mni, Image, rollimg
      >>> arr = np.arange(24, dtype=np.float32).reshape((2,3,4,1))
      >>> img = Image(arr, vox2mni(np.diag([2,3,4,5,1])))
      >>> img.coordmap
      AffineTransform(
         function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='voxels', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S', 't'), name='mni', coord_dtype=float64),
         affine=array([[ 2.,  0.,  0.,  0.,  0.],
                       [ 0.,  3.,  0.,  0.,  0.],
                       [ 0.,  0.,  4.,  0.,  0.],
                       [ 0.,  0.,  0.,  5.,  0.],
                       [ 0.,  0.,  0.,  0.,  1.]])
      )
      >>> is_xyz_affable(img)
      True
      >>> time0_img = rollimg(img, 't')
      >>> time0_img.coordmap
      AffineTransform(
         function_domain=CoordinateSystem(coord_names=('l', 'i', 'j', 'k'), name='voxels', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S', 't'), name='mni', coord_dtype=float64),
         affine=array([[ 0.,  2.,  0.,  0.,  0.],
                       [ 0.,  0.,  3.,  0.,  0.],
                       [ 0.,  0.,  0.,  4.,  0.],
                       [ 5.,  0.,  0.,  0.,  0.],
                       [ 0.,  0.,  0.,  0.,  1.]])
      )
      >>> is_xyz_affable(time0_img)
      False

  Nibabel images always have xyz affines

      >>> import nibabel as nib
      >>> nimg = nib.Nifti1Image(arr, np.diag([2,3,4,1]))
      >>> is_xyz_affable(nimg)
      True

nipy.core.image.image_spaces.make_xyz_image(data, xyz_affine, world, metadata=None)

  Create 3D+ image embedded in space named in world

  Parameters:
    data object

      Object returning array from `np.asarray(obj)`, and having `shape` attribute. Should have at least 3 dimensions (`len(shape) >= 3`), and these three first 3 dimensions should be spatial

    xyz_affine(4, 4) array-like or tuple

      if (4, 4) array-like (the usual case), then an affine relating spatial dimensions in data (dimensions 0:3) to mm in XYZ space given in world. If a tuple, then contains two values: the (4, 4) array-like, and a sequence of scalings for the dimensions greater than 3. See examples.

    world str or XYZSpace or CoordSysMaker or CoordinateSystem

      World 3D space to which affine refers. See `spaces.get_world_cs()`

    metadata None or mapping, optional

      metadata for created image. Defaults to None, giving empty metadata.

  Returns:
    img Image

      image containing data, with coordmap constructed from affine and world, and with default voxel input coordinates. If the data has more than 3 dimensions, and you didn’t specify the added zooms with a tuple xyz_affine parameter, the coordmap affine gets filled out with extra ones on the diagonal to give an (N+1, N+1) affine, with `N = len(data.shape)`

  Examples

      >>> data = np.arange(24).reshape((2, 3, 4))
      >>> aff = np.diag([4, 5, 6, 1])
      >>> img = make_xyz_image(data, aff, 'mni')
      >>> img
      Image(
        data=array([[[ 0,  1,  2,  3],
                     [ 4,  5,  6,  7],
                     [ 8,  9, 10, 11]],

                    [[12, 13, 14, 15],
                     [16, 17, 18, 19],
                     [20, 21, 22, 23]]]),
        coordmap=AffineTransform(
                  function_domain=CoordinateSystem(coord_names=('i', 'j', 'k'), name='voxels', coord_dtype=float64),
                  function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S'), name='mni', coord_dtype=float64),
                  affine=array([[ 4.,  0.,  0.,  0.],
                                [ 0.,  5.,  0.,  0.],
                                [ 0.,  0.,  6.,  0.],
                                [ 0.,  0.,  0.,  1.]])
               ))

  Now make data 4D; we just add 1. to the diagonal for the new dimension

      >>> data4 = data[..., None]
      >>> img = make_xyz_image(data4, aff, 'mni')
      >>> img.coordmap
      AffineTransform(
         function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='voxels', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S', 't'), name='mni', coord_dtype=float64),
         affine=array([[ 4.,  0.,  0.,  0.,  0.],
                       [ 0.,  5.,  0.,  0.,  0.],
                       [ 0.,  0.,  6.,  0.,  0.],
                       [ 0.,  0.,  0.,  1.,  0.],
                       [ 0.,  0.,  0.,  0.,  1.]])
      )

  We can pass in a scalar or tuple to specify scaling for the extra dimension

      >>> img = make_xyz_image(data4, (aff, 2.0), 'mni')
      >>> img.coordmap.affine
      array([[ 4.,  0.,  0.,  0.,  0.],
             [ 0.,  5.,  0.,  0.,  0.],
             [ 0.,  0.,  6.,  0.,  0.],
             [ 0.,  0.,  0.,  2.,  0.],
             [ 0.,  0.,  0.,  0.,  1.]])
      >>> data5 = data4[..., None]
      >>> img = make_xyz_image(data5, (aff, (2.0, 3.0)), 'mni')
      >>> img.coordmap.affine
      array([[ 4.,  0.,  0.,  0.,  0.,  0.],
             [ 0.,  5.,  0.,  0.,  0.,  0.],
             [ 0.,  0.,  6.,  0.,  0.,  0.],
             [ 0.,  0.,  0.,  2.,  0.,  0.],
             [ 0.,  0.,  0.,  0.,  3.,  0.],
             [ 0.,  0.,  0.,  0.,  0.,  1.]])

nipy.core.image.image_spaces.xyz_affine(img, name2xyz=None)

  Return xyz affine from image img if possible, or raise error

  Parameters:
    img `Image` instance or nibabel image

      It has a `coordmap` or attribute `affine` or method `get_affine`

    name2xyz None or mapping

      Object such that `name2xyz[ax_name]` returns ‘x’, or ‘y’ or ‘z’ or raises a KeyError for a str `ax_name`. None means use module default. Not used for nibabel img input.

  Returns:
    xyz_aff(4,4) array

      voxel to X, Y, Z affine mapping

  Raises:
    SpaceTypeError if img does not have an affine coordinate map
    AxesError if not all of x, y, z recognized in img `coordmap` range
    AffineError if axes dropped from the affine contribute to x, y, z
    coordinates

  Examples

      >>> from nipy.core.api import vox2mni, Image
      >>> arr = np.arange(24).reshape((2,3,4,1)).astype(float)
      >>> img = Image(arr, vox2mni(np.diag([2,3,4,5,1])))
      >>> img.coordmap
      AffineTransform(
         function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='voxels', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('mni-x=L->R', 'mni-y=P->A', 'mni-z=I->S', 't'), name='mni', coord_dtype=float64),
         affine=array([[ 2.,  0.,  0.,  0.,  0.],
                       [ 0.,  3.,  0.,  0.,  0.],
                       [ 0.,  0.,  4.,  0.,  0.],
                       [ 0.,  0.,  0.,  5.,  0.],
                       [ 0.,  0.,  0.,  0.,  1.]])
      )
      >>> xyz_affine(img)
      array([[ 2.,  0.,  0.,  0.],
             [ 0.,  3.,  0.,  0.],
             [ 0.,  0.,  4.,  0.],
             [ 0.,  0.,  0.,  1.]])

  Nibabel images always have xyz affines

      >>> import nibabel as nib
      >>> nimg = nib.Nifti1Image(arr, np.diag([2,3,4,1]))
      >>> xyz_affine(nimg)
      array([[ 2.,  0.,  0.,  0.],
             [ 0.,  3.,  0.,  0.],
             [ 0.,  0.,  4.,  0.],
             [ 0.,  0.,  0.,  1.]])