Inheritance diagram for nipy.core.reference.coordinate_map:
This module describes two types of mappings:
inverse function.
necessarily of the same dimension, hence not always invertible.
Each of these objects is meant to encapsulate a tuple of (domain, range, function). Each of the mapping objects contain all the details about their domain CoordinateSystem, their range CoordinateSystem and the mapping between them.
They are separate classes, neither one inheriting from the other. They do, however, share some parts of an API, each having methods:
These methods are implemented by module level functions of the same name.
They also share some attributes:
and return their composition. If they are all AffineTransforms, an AffineTransform is returned. This checks to ensure that domains and ranges of the various mappings agree.
and return a new mapping that has domain and range given by the concatenation of their domains and ranges, and the mapping simply concatenates the output of each of the individual mappings. If they are all AffineTransforms, an AffineTransform is returned. If they are all AffineTransforms that are in fact linear (i.e. origin=0) then can is represented as a block matrix with the size of the blocks determined by
For mapping m, this is the same as product(AffineTransform.identity(‘concat’), m)
Bases: object
A class representing an affine transformation from a domain to a range.
This class has an affine attribute, which is a matrix representing the affine transformation in homogeneous coordinates. This matrix is used to evaluate the function, rather than having an explicit function.
>>> inp_cs = CoordinateSystem('ijk')
>>> out_cs = CoordinateSystem('xyz')
>>> cm = AffineTransform(inp_cs, out_cs, np.diag([1, 2, 3, 1]))
>>> cm
AffineTransform(
function_domain=CoordinateSystem(coord_names=('i', 'j', 'k'), name='', coord_dtype=float64),
function_range=CoordinateSystem(coord_names=('x', 'y', 'z'), name='', coord_dtype=float64),
affine=array([[ 1., 0., 0., 0.],
[ 0., 2., 0., 0.],
[ 0., 0., 3., 0.],
[ 0., 0., 0., 1.]])
)
>>> cm.affine
array([[ 1., 0., 0., 0.],
[ 0., 2., 0., 0.],
[ 0., 0., 3., 0.],
[ 0., 0., 0., 1.]])
>>> cm([1,1,1])
array([ 1., 2., 3.])
>>> icm = cm.inverse()
>>> icm([1,2,3])
array([ 1., 1., 1.])
Methods
| from_params | |
| from_start_step | |
| identity | |
| inverse | |
| renamed_domain | |
| renamed_range | |
| reordered_domain | |
| reordered_range |
Return an CoordinateMap specified by an affine transformation in homogeneous coordinates.
| Parameters: | affine : array-like
function_domain : CoordinateSystem
function_range : CoordinateSystem
|
|---|
Notes
The dtype of the resulting matrix is determined by finding a safe typecast for the function_domain, function_range and affine.
Create an AffineTransform instance from sequences of innames and outnames.
| Parameters: | innames : tuple of string
outnames : tuple of string
params : AffineTransform, ndarray or (ndarray, ndarray)
domain_name : string
range_name : string
|
|---|---|
| Returns: | aff : AffineTransform object instance |
Notes
| Precondition: | len(shape) == len(names) |
|---|---|
| Raises valueerror: | |
| if len(shape) != len(names) | |
Create an AffineTransform instance from sequences of names, start and step.
| Parameters: | innames : tuple of string
outnames : tuple of string
start : tuple of float
step : tuple of float
domain_name : string
range_name : string
|
|---|---|
| Returns: | cm : CoordinateMap |
Notes
len(names) == len(start) == len(step)
Examples
>>> cm = AffineTransform.from_start_step('ijk', 'xyz', [1, 2, 3], [4, 5, 6])
>>> cm.affine
array([[ 4., 0., 0., 1.],
[ 0., 5., 0., 2.],
[ 0., 0., 6., 3.],
[ 0., 0., 0., 1.]])
Return an identity coordmap of the given shape.
| Parameters: | coord_names : tuple of string
name : string
|
|---|---|
| Returns: | cm : CoordinateMap
|
Examples
>>> cm = AffineTransform.identity('ijk', 'somewhere')
>>> cm.affine
array([[ 1., 0., 0., 0.],
[ 0., 1., 0., 0.],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]])
>>> print cm.function_domain
CoordinateSystem(coord_names=('i', 'j', 'k'), name='somewhere', coord_dtype=float64)
>>> print cm.function_range
CoordinateSystem(coord_names=('i', 'j', 'k'), name='somewhere', coord_dtype=float64)
Create a new AffineTransform with the coordinates of function_domain reordered. Default behaviour is to reverse the order of the coordinates.
| Parameters: | order: sequence :
|
|---|
Create a new AffineTransform with the coordinates of function_range reordered. Defaults to reversing the coordinates of function_range.
| Parameters: | order: sequence :
|
|---|
Bases: object
A set of domain and range CoordinateSystems and a function between them.
For example, the function may represent the mapping of a voxel (the domain of the function) to real space (the range). The function may be an affine or non-affine transformation.
Examples
>>> function_domain = CoordinateSystem('ijk', 'voxels')
>>> function_range = CoordinateSystem('xyz', 'world')
>>> mni_orig = np.array([-90.0, -126.0, -72.0])
>>> function = lambda x: x + mni_orig
>>> inv_function = lambda x: x - mni_orig
>>> cm = CoordinateMap(function_domain, function_range, function, inv_function)
Map the first 3 voxel coordinates, along the x-axis, to mni space:
>>> x = np.array([[0,0,0], [1,0,0], [2,0,0]])
>>> cm.function(x)
array([[ -90., -126., -72.],
[ -89., -126., -72.],
[ -88., -126., -72.]])
>>> x = CoordinateSystem('x')
>>> y = CoordinateSystem('y')
>>> m = CoordinateMap(x, y, np.exp, np.log)
>>> m
CoordinateMap(
function_domain=CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64),
function_range=CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64),
function=<ufunc 'exp'>,
inverse_function=<ufunc 'log'>
)
>>> m.inverse()
CoordinateMap(
function_domain=CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64),
function_range=CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64),
function=<ufunc 'log'>,
inverse_function=<ufunc 'exp'>
)
>>>
Attributes
| function_domain | |
| function_range | |
| function | |
| inverse_function |
Methods
| function | |
| inverse | |
| inverse_function | |
| renamed_domain | |
| renamed_range | |
| reordered_domain | |
| reordered_range |
Create a CoordinateMap given the function and its domain and range.
| Parameters: | function : callable
function_domain : CoordinateSystem
function_range : CoordinateSystem
inverse_function : None or callable, optional
|
|---|---|
| Returns: | coordmap : CoordinateMap |
Create a new CoordinateMap with the coordinates of function_domain reordered. Default behaviour is to reverse the order of the coordinates.
| Parameters: | order: sequence :
|
|---|
Create a new CoordinateMap with the coordinates of function_range reordered. Defaults to reversing the coordinates of function_range.
| Parameters: | order: sequence :
|
|---|
Append input and output dimension to coordmap
| Parameters: | cm : Affine
in_name : str
out_name : str
start : float, optional
step : float, optional
|
|---|---|
| Returns: | cm_plus : Affine
|
Examples
Typical use is creating a 4D coordinate map from a 3D
>>> cm3d = AffineTransform.from_params('ijk', 'xyz', np.diag([1,2,3,1]))
>>> cm4d = append_io_dim(cm3d, 'l', 't', 9, 5)
>>> cm4d.affine
array([[ 1., 0., 0., 0., 0.],
[ 0., 2., 0., 0., 0.],
[ 0., 0., 3., 0., 0.],
[ 0., 0., 0., 5., 9.],
[ 0., 0., 0., 0., 1.]])
Return the composition of two or more CoordinateMaps.
| Parameters: | cmaps : sequence of CoordinateMaps |
|---|---|
| Returns: | cmap : CoordinateMap
>>> cmap = AffineTransform.from_params(‘i’, ‘x’, np.diag([2.,1.])) : >>> cmapi = cmap.inverse() : >>> id1 = compose(cmap,cmapi) : >>> print id1.affine : [[ 1. 0.] :
>>> id2 = compose(cmapi,cmap) : >>> id1.function_domain.coord_names : (‘x’,) : >>> id2.function_domain.coord_names : (‘i’,) : >>> : |
Drop dimension from coordinate map, if orthogonal to others
Drops both input and corresponding output dimension. Thus there should be a corresponding input dimension for a selected output dimension, or a corresponding output dimension for an input dimension.
| Parameters: | cm : Affine
name : str
|
|---|---|
| Returns: | cm_redux : Affine
|
Examples
Typical use is in getting a 3D coordinate map from 4D
>>> cm4d = AffineTranform.from_params('ijkl', 'xyzt', np.diag([1,2,3,4,1]))
>>> cm3d = drop_io_dim(cm4d, 't')
>>> cm3d.affine
array([[ 1., 0., 0., 0.],
[ 0., 2., 0., 0.],
[ 0., 0., 3., 0.],
[ 0., 0., 0., 1.]])
A test to see if mapping1 is equal to mapping2 after possibly reordering the domain and range of mapping.
| Parameters: | mapping1 : CoordinateMap or AffineTransform mapping2 : CoordinateMap or AffineTransform |
|---|---|
| Returns: | are_they_equal : bool |
Examples
>>> ijk = CoordinateSystem('ijk')
>>> xyz = CoordinateSystem('xyz')
>>> T = np.random.standard_normal((4,4))
>>> T[-1] = [0,0,0,1] # otherwise AffineTransform raises
... # an exception because
... # it's supposed to represent an
... # affine transform in homogeneous
... # coordinates
>>> A = AffineTransform(ijk, xyz, T)
>>> B = A.reordered_domain('ikj').reordered_range('xzy')
>>> C = B.renamed_domain({'i':'slice'})
>>> equivalent(A, B)
True
>>> equivalent(A, C)
False
>>> equivalent(B, C)
False
>>>
>>> D = CoordinateMap(ijk, xyz, np.exp)
>>> equivalent(D, D)
True
>>> E = D.reordered_domain('kij').reordered_range('xzy')
>>> # no non-AffineTransform will ever be
>>> # equivalent to a reordered version of itself,
>>> # because their functions don't evaluate as equal
>>> equivalent(D, E)
False
>>> equivalent(E, E)
True
>>>
>>> # This has not changed the order
>>> # of the axes, so the function is still the same
>>>
>>> F = D.reordered_range('xyz').reordered_domain('ijk')
>>> equivalent(F, D)
True
>>> id(F) == id(D)
False
>>>
Return the “topological” product of two or more mappings, which can be either AffineTransforms or CoordinateMaps.
If they are all AffineTransforms, the result is an AffineTransform, else it is a CoordinateMap.
| Parameters: | cmaps : sequence of CoordinateMaps or AffineTransforms |
|---|---|
| Returns: | cmap : CoordinateMap >>> inc1 = AffineTransform.from_params(‘i’, ‘x’, np.diag([2,1])) : >>> inc2 = AffineTransform.from_params(‘j’, ‘y’, np.diag([3,1])) : >>> inc3 = AffineTransform.from_params(‘k’, ‘z’, np.diag([4,1])) : >>> cmap = product(inc1, inc3, inc2) : >>> cmap.function_domain.coord_names : (‘i’, ‘k’, ‘j’) : >>> cmap.function_range.coord_names : (‘x’, ‘z’, ‘y’) : >>> cmap.affine : array([[ 2., 0., 0., 0.], :
>>> A1 = AffineTransform.from_params(‘ij’, ‘xyz’, np.array([[2,3,1,0],[3,4,5,0],[7,9,3,1]]).T) : >>> A2 = AffineTransform.from_params(‘xyz’, ‘de’, np.array([[8,6,7,4],[1,-1,13,3],[0,0,0,1]])) : >>> A1.affine : array([[ 2., 3., 7.], :
>>> A2.affine : array([[ 8., 6., 7., 4.], :
>>> p=product(A1, A2) : >>> p.affine : array([[ 2., 3., 0., 0., 0., 7.], :
>>> print np.allclose(p.affine[:3,:2], A1.affine[:3,:2]) : True : >>> print np.allclose(p.affine[:3,-1], A1.affine[:3,-1]) : True : >>> print np.allclose(p.affine[3:5,2:5], A2.affine[:2,:3]) : True : >>> print np.allclose(p.affine[3:5,-1], A2.affine[:2,-1]) : True : >>> : >>> A1([3,4]) : array([ 25., 34., 26.]) : >>> A2([5,6,7]) : array([ 129., 93.]) : >>> p([3,4,5,6,7]) : array([ 25., 34., 26., 129., 93.]) : |
Create a new coordmap with the coordinates of function_range renamed.
| Parameters: | newnames: dictionary :
|
|---|
Create a new coordmap with the coordinates of function_domain reordered.
Default behaviour is to reverse the order of the coordinates.
| Parameters: | order: sequence :
|
|---|
Notes
If no reordering is to be performed, it returns a copy of mapping.
Create a new coordmap with the coordinates of function_range reordered. Defaults to reversing the coordinates of function_range.
| Parameters: | order: sequence :
|
|---|
Notes
If no reordering is to be performed, it returns a copy of mapping.
Shift the origin of the domain.
| Parameters: | difference_vector : ndarray
|
|---|
Examples
>>> A = np.random.standard_normal((5,6))
>>> A[-1] = [0,0,0,0,0,1]
>>> affine_transform = AffineTransform(CS('ijklm', 'oldorigin'), CS('xyzt'), A)
>>> print affine_transform.function_domain
CoordinateSystem(coord_names=('i', 'j', 'k', 'l', 'm'), name='oldorigin', coord_dtype=float64)
# A random change of origin >>> difference = np.random.standard_normal(5)
# The same affine transforation with a different origin for its domain
>>> shifted_affine_transform = shifted_domain_origin(affine_transform, difference, 'neworigin')
>>> print shifted_affine_transform.function_domain
CoordinateSystem(coord_names=('i', 'j', 'k', 'l', 'm'), name='neworigin', coord_dtype=float64)
# Let’s check that things work
>>> point_in_old_basis = np.random.standard_normal(5)
# This is the relation ship between coordinates in old and new origins
>>> print np.allclose(shifted_affine_transform(point_in_old_basis), affine_transform(point_in_old_basis+difference))
True
>>> print np.allclose(shifted_affine_transform(point_in_old_basis-difference), affine_transform(point_in_old_basis))
True
>>>
Shift the origin of the range.
| Parameters: | difference_vector : ndarray
|
|---|
Examples
>>> A = np.random.standard_normal((5,6))
>>> A[-1] = [0,0,0,0,0,1]
>>> affine_transform = AffineTransform(CS('ijklm'), CS('xyzt', 'oldorigin'), A)
>>> print affine_transform.function_range
CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='oldorigin', coord_dtype=float64)
# Make a random shift of the origin in the range
>>> difference = np.random.standard_normal(4)
>>> shifted_affine_transform = shifted_range_origin(affine_transform, difference, 'neworigin')
>>> print shifted_affine_transform.function_range
CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='neworigin', coord_dtype=float64)
>>>
# Evaluate the transform and verify it does as expected
>>> point_in_domain = np.random.standard_normal(5)
# Check that things work
>>> np.allclose(shifted_affine_transform(point_in_domain), affine_transform(point_in_domain) - difference)
True
>>> np.allclose(shifted_affine_transform(point_in_domain) + difference, affine_transform(point_in_domain))
True
>>>