=========================
 Registration API Design
=========================

This contains design ideas for the end-user api when registering images in nipy.

We want to provide a simple api, but with enough flexibility to allow
users to changes various components of the pipeline.  We will also
provide various **Standard** scripts that perform typical pipelines.

The pluggable script::

  func_img = load_image(filename)
  anat_img = load_image(filename)
  interpolator = SplineInterpolator(order=3)
  metric = NormalizedMutualInformation()
  optimizer = Powell()
  strategy = RegistrationStrategy(interpolator, metric, optimizer)
  w2w = strategy.apply(img_fixed, img_moving)

To apply the transform and resample the image::

  new_img = resample(img_moving, w2w, interp=interpolator)

Or::

  new_img = Image(img_moving, w2w*img_moving.coordmap)

Transform Multiplication
------------------------

The multiplication order is important and coordinate systems must
*make sense*.  The *output coordinates* of the mapping on the
right-hand of the operator, must match the *input coordinates* of the
mapping on the left-hand side of the operator.

For example, imageA has a mapping from voxels-to-world (v2w), imageB
has a mapping from world-to-world (w2w).  So the output of imageA,
*world*, maps to the input of imageB, *world*.  We would compose a new
mapping (transform) from these mappings like this::

  new_coordmap = imageB.coordmap * imageA.coordmap

If one tried to compose a mapping in the other order, an error should
be raised as the code would detect a mismatch of trying to map output
coordinates from imageB, *world* to the input coordinates of imageA,
*voxels*::

  new_coordmap = imageA.coordmap * imageB.coordmap
  raise ValueError!!!

Note: We should consider a meaningful error message to help people
quickly correct this mistake.

One way to remember this ordering is to think of composing functions.
If these were functions, the output of the first function to evaluate
(imageA.coordmap) is passed as input to the second function
(imageB.coordmap).  And therefore they must match::

  new_coordmap = imageB.coordmap(imageA.coordmap())

Matching Coordinate Systems
---------------------------

We need to make sure we can detect mismatched coordinate mappings.
The CoordinateSystem class has a check for equality (__eq__ method)
based on the axis and name attributes.  Long-term this may not be
robust enough, but it's a starting place.  We should write tests for
failing cases of this, if they don't already exists.

CoordinateMap
-------------

Recall the CoordinateMap defines a mapping between two coordinate
systems, an input coordinate system and an output coordinate system.
One example of this would be a mapping from voxel space to scanner
space.  In a Nifti1 header we would have an affine transform to apply
this mapping.  The *input coordinates* would be voxel space, the
*output coordinates* would be world space, and the affine transform
provides the mapping between them.
