.. _coordinate_map:

#############################
 Basics of the Coordinate Map
#############################

When you load an image it will have an associated Coordinate Map

**Coordinate Map**

    The Coordinate Map contains information defining the input (domain) and
    output (range) Coordinate Systems of the image, and the mapping between the
    two Coordinate systems.

The *input* or *domain* in an image are voxel coordinates in the image array.
The *output* or *range* are the millimetre coordinates in some space, that
correspond to the input (voxel) coordinates.

>>> import nipy

Get a filename for an example file:

>>> from nipy.testing import anatfile

Get the coordinate map for the image:

>>> anat_img = nipy.load_image(anatfile)
>>> coordmap = anat_img.coordmap

For more on Coordinate Systems and their properties
:mod:`nipy.core.reference.coordinate_system`

You can inspect a coordinate map::

>>> coordmap.function_domain.coord_names
>>> ('i', 'j', 'k')

>>> coordmap.function_range.coord_names
('aligned-x=L->R', 'aligned-y=P->A', 'aligned-z=I->S')

>>> coordmap.function_domain.name
'voxels'
>>> coordmap.function_range.name
'aligned'

A Coordinate Map has a mapping from the *input* Coordinate System to the
*output* Coordinate System

Here we can see we have a voxel to millimeter mapping from the voxel
space (i,j,k) to the millimeter space (x,y,z)

We can also get the name of the respective Coordinate Systems that our
Coordinate Map maps between.

A Coordinate Map is two Coordinate Systems with a mapping between
them.  Formally the mapping is a function that takes points from the
input Coordinate System and returns points from the output Coordinate
System.  This is the same as saying that the mapping takes points in the mapping
function *domain* and transforms them to points in the mapping function *range*.

Often this is simple as applying an Affine transform. In that case the
Coordinate System may well have an affine property which returns the
affine matrix corresponding to the transform.

>>> coordmap.affine
array([[ -2.,   0.,   0.,  32.],
       [  0.,   2.,   0., -40.],
       [  0.,   0.,   2., -16.],
       [  0.,   0.,   0.,   1.]])

If you call the Coordinate Map you will apply the mapping function
between the two Coordinate Systems. In this case from (i,j,k) to (x,y,z):

>>> coordmap([1,2,3])
array([ 30., -36., -10.])

It can also be used to  get the inverse mapping, or in this example from (x,y,z)
back to (i,j,k):

>>> coordmap.inverse()([30.,-36.,-10.])
array([1., 2., 3.])

We can see how this works if we just apply the affine
ourselves using dot product.

.. Note::

    Notice the affine is using homogeneous coordinates so we need to add a 1 to
    our input. (And note how  a direct call to the coordinate map does this work
    for you)

>>> coordmap.affine
array([[ -2.,   0.,   0.,  32.],
       [  0.,   2.,   0., -40.],
       [  0.,   0.,   2., -16.],
       [  0.,   0.,   0.,   1.]])

>>> import numpy as np
>>> np.dot(coordmap.affine, np.transpose([1,2,3,1]))
array([ 30., -36., -10.,   1.])

.. Note::

   The answer is the same as above (except for the added 1)

.. _normalize-coordmap:

***************************************************
Use of the Coordinate Map for spatial normalization
***************************************************

The Coordinate Map can be used to describe the transformations needed to perform
spatial normalization. Suppose we have an anatomical Image from one subject
*subject_img* and we want to create an Image in a standard space like Tailarach
space. An affine registration algorithm will produce a 4-by-4 matrix
representing the affine transformation, *T*, that takes a point in the subject's
coordinates *subject_world* to a point in Tailarach space *tailarach_world*. The
subject's Image has its own Coordinate Map, *subject_cmap* and there is a
Coordinate Map for Tailarach space which we will call *tailarach_cmap*.

Having found the transformation matrix *T*, the next step in spatial
normalization is usually to resample the array of *subject_img* so that it has
the same shape as some atlas *atlas_img*. Note that because it is an atlas
Image, *tailarach_camp=atlas_img.coordmap*.

A resampling algorithm uses an interpolator which needs to know
which voxel of *subject_img* corresponds to which voxel of *atlas_img*.
This is therefore a function from *atlas_voxel* to *subject_voxel*.

This function, paired with the information that it is a map from atlas-voxel to
subject-voxel is another example of a Coordinate Map. The code to do this might
look something like the following:

>>> from nipy.testing import anatfile, funcfile
>>> from nipy.algorithms.registration import HistogramRegistration
>>> from nipy.algorithms.kernel_smooth import LinearFilter

We'll make a smoothed version of the anatomical example image, and pretend it's
the template

>>> smoother = LinearFilter(anat_img.coordmap, anat_img.shape)
>>> atlas_im = smoother.smooth(anat_img)
>>> subject_im = anat_img

We do an affine registration between the two.

>>> reggie = HistogramRegistration(subject_im, atlas_im)
>>> aff = reggie.optimize('affine').as_affine() #doctest: +ELLIPSIS
Initial guess...
...

Now we make a coordmap with this transformation

>>> from nipy.core.api import AffineTransform
>>> subject_cmap = subject_im.coordmap
>>> talairach_cmap = atlas_im.coordmap
>>> subject_world_to_talairach_world = AffineTransform(
...                                       subject_cmap.function_range,
...                                       talairach_cmap.function_range,
...                                       aff)
...

We resample the 'subject' image to the 'atlas image

>>> from nipy.algorithms.resample import resample
>>> normalized_subject_im = resample(subject_im, talairach_cmap,
...                                  subject_world_to_talairach_world,
...                                  atlas_im.shape)
>>> normalized_subject_im.shape == atlas_im.shape
True
>>> normalized_subject_im.coordmap == atlas_im.coordmap
True
>>> # Normalized image now has atlas affine.
>>> assert np.all(normalized_subject_im.affine == atlas_im.affine)

***********************
Mathematical definition
***********************

For a more formal mathematical description of the coordinate map, see
:ref:`math-coordmap`.
