def test_compose(): value = np.array([[1.0, 2.0, 3.0]]).T aa = compose(E.a, E.a) yield assert_true, aa.inverse() is None yield assert_true, np.allclose(aa(value), 4 * value) ab = compose(E.a, E.b) yield assert_true, ab.inverse() is None assert_true, np.allclose(ab(value), 4 * value) ac = compose(E.a, E.c) yield assert_true, ac.inverse() is None yield assert_true, np.allclose(ac(value), value) bb = compose(E.b, E.b) # yield assert_true, bb.inverse() is not None aff1 = np.diag([1, 2, 3, 1]) affine1 = AffineTransform.from_params("ijk", "xyz", aff1) aff2 = np.diag([4, 5, 6, 1]) affine2 = AffineTransform.from_params("xyz", "abc", aff2) # compose mapping from 'ijk' to 'abc' compcm = compose(affine2, affine1) yield assert_equal, compcm.function_domain.coord_names, ("i", "j", "k") yield assert_equal, compcm.function_range.coord_names, ("a", "b", "c") yield assert_equal, compcm.affine, np.dot(aff2, aff1) # check invalid coordinate mappings yield assert_raises, ValueError, compose, affine1, affine2 yield assert_raises, ValueError, compose, affine1, "foo" cm1 = CoordinateMap(CoordinateSystem("ijk"), CoordinateSystem("xyz"), np.log) cm2 = CoordinateMap(CoordinateSystem("xyz"), CoordinateSystem("abc"), np.exp) yield assert_raises, ValueError, compose, cm1, cm2
def test_compose(): value = np.array([[1., 2., 3.]]).T aa = compose(E.a, E.a) yield assert_true, aa.inverse() is None yield assert_true, np.allclose(aa(value), 4*value) ab = compose(E.a,E.b) yield assert_true, ab.inverse() is None assert_true, np.allclose(ab(value), 4*value) ac = compose(E.a,E.c) yield assert_true, ac.inverse() is None yield assert_true, np.allclose(ac(value), value) bb = compose(E.b,E.b) # yield assert_true, bb.inverse() is not None aff1 = np.diag([1,2,3,1]) affine1 = AffineTransform.from_params('ijk', 'xyz', aff1) aff2 = np.diag([4,5,6,1]) affine2 = AffineTransform.from_params('xyz', 'abc', aff2) # compose mapping from 'ijk' to 'abc' compcm = compose(affine2, affine1) yield assert_equal, compcm.function_domain.coord_names, ('i', 'j', 'k') yield assert_equal, compcm.function_range.coord_names, ('a', 'b', 'c') yield assert_equal, compcm.affine, np.dot(aff2, aff1) # check invalid coordinate mappings yield assert_raises, ValueError, compose, affine1, affine2 yield assert_raises, ValueError, compose, affine1, 'foo' cm1 = CoordinateMap(CoordinateSystem('ijk'), CoordinateSystem('xyz'), np.log) cm2 = CoordinateMap(CoordinateSystem('xyz'), CoordinateSystem('abc'), np.exp) yield assert_raises, ValueError, compose, cm1, cm2
def test_inverse1(): inv = lambda a: a.inverse() yield assert_true, inv(E.a) is None yield assert_true, inv(E.c) is None inv_b = E.b.inverse() inv_d = E.d.inverse() ident_b = compose(inv_b,E.b) ident_d = compose(inv_d,E.d) value = np.array([[1., 2., 3.]]).T yield assert_true, np.allclose(ident_b(value), value) yield assert_true, np.allclose(ident_d(value), value)
def change_basis(element, bchange_linear): """ Matrices can be thought of as representations of linear mappings between two (coordinate-free) vector spaces represented in particular bases. Hence, a MatrixGroup instance with matrix.shape = (ndim, ndim) represents a linear transformation L on a vector space of dimension ndim, in a given coordinate system. If we change the basis in which we represent L, the matrix that represents L should also change. A change of basis is represented as a mapping between two coordinate systems and is also represented by a change of basis matrix. This is expressed in this function as bchange_linear.output_coords == element.coords This function expresses the same transformation L in a different basis. """ newcm = compose(bchange_linear.inverse, element, bchange_linear) matrix = newcm.affine[:-1,:-1] if bchange_linear.output_coords != element.coords: raise ValueError('expecting the basis change mapping to have the same output coords as element') return element.__class__(matrix, newcm.input_coords)
def change_basis(element, bchange_linear): """ Matrices can be thought of as representations of linear mappings between two (coordinate-free) vector spaces represented in particular bases. Hence, a MatrixGroup instance with matrix.shape = (ndim, ndim) represents a linear transformation L on a vector space of dimension ndim, in a given coordinate system. If we change the basis in which we represent L, the matrix that represents L should also change. A change of basis is represented as a mapping between two coordinate systems and is also represented by a change of basis matrix. This is expressed in this function as bchange_linear.function_range == element.coords This function expresses the same transformation L in a different basis. """ newcm = compose(bchange_linear.inverse(), element, bchange_linear) matrix = newcm.affine[:-1, :-1] if bchange_linear.function_range != element.coords: raise ValueError( 'expecting the basis change mapping to have the same output coords as element' ) return element.__class__(matrix, newcm.function_domain)
def product(*elements): """ Compute the group product of a set of elements """ notsame = filter(lambda x: type(x) != type(elements[0]), elements) if notsame: raise ValueError('all elements should be members of the same group') composed_mapping = compose(*elements) matrix = composed_mapping.affine[:-1,:-1] return elements[0].__class__(matrix, elements[0].coords)
def product(*elements): """ Compute the group product of a set of elements """ notsame = filter(lambda x: type(x) != type(elements[0]), elements) if notsame: raise ValueError('all elements should be members of the same group') composed_mapping = compose(*elements) matrix = composed_mapping.affine[:-1, :-1] return elements[0].__class__(matrix, elements[0].coords)
def product(*elements): """ Compute the group product of a set of elements """ type_e0 = type(elements[0]) notsame = [e for e in elements if not type(e) == type_e0] if notsame: raise ValueError('all elements should be members of the same group') composed_mapping = compose(*elements) matrix = composed_mapping.affine[:-1, :-1] return elements[0].__class__(matrix, elements[0].coords)
def product(*elements): """ Compute the group product of a set of elements """ type_e0 = type(elements[0]) notsame = [e for e in elements if not type(e) == type_e0] if notsame: raise ValueError('all elements should be members of the same group') composed_mapping = compose(*elements) matrix = composed_mapping.affine[:-1,:-1] return elements[0].__class__(matrix, elements[0].coords)
def to_ni_image(self, t, f, grid_shape=None, prior_mask=None): """Form a NIPY Image of the map of this object at (t,f). The new Image will have a CoordinateMap transforming the array indices into the target space of this Beam's coregistered MRI. """ print 'slicing tf beam at t,f =', (t,f) if (prior_mask is not None) and (prior_mask.shape == self.s.shape): prior_mask = prior_mask[:,t,f] m_arr = signal_array_to_masked_vol(self.s[:,t,f], self.voxel_indices, grid_shape=grid_shape, prior_mask=prior_mask) meg2mri = self.coreg.meg2mri return ni_api.Image(m_arr, compose(meg2mri, self.coordmap))
def test_compose(): value = np.array([[1., 2., 3.]]).T aa = compose(E.a, E.a) yield assert_true, aa.inverse is None yield assert_true, np.allclose(aa(value), 4*value) ab = compose(E.a,E.b) yield assert_true, ab.inverse is None assert_true, np.allclose(ab(value), 4*value) ac = compose(E.a,E.c) yield assert_true, ac.inverse is None yield assert_true, np.allclose(ac(value), value) bb = compose(E.b,E.b) yield assert_true, bb.inverse is not None aff1 = np.diag([1,2,3,1]) cm1 = Affine.from_params('ijk', 'xyz', aff1) aff2 = np.diag([4,5,6,1]) cm2 = Affine.from_params('xyz', 'abc', aff2) # compose mapping from 'ijk' to 'abc' compcm = compose(cm2, cm1) yield assert_equal, compcm.input_coords.coord_names, ('i', 'j', 'k') yield assert_equal, compcm.output_coords.coord_names, ('a', 'b', 'c') yield assert_equal, compcm.affine, np.dot(aff2, aff1) # check invalid coordinate mappings yield assert_raises, ValueError, compose, cm1, cm2
def test_subsample(): # This is how you would subsample with nipy.algorithms.resample # On the first axis, we'll take every 2nd, # on the second axis every 3rd, and on the 3rd every 4th im, xyz_im, ras = generate_im() subsample_matrix = np.array([[2,0,0,0], [0,3,0,0], [0,0,4,0], [0,0,0,1]]) subsampled_shape = np.array(xyz_im)[::2,::3,::4].shape subsample_coordmap = AffineTransform(xyz_im.xyz_transform.function_domain, xyz_im.xyz_transform.function_domain, subsample_matrix) target_coordmap = compose(xyz_im.xyz_transform, subsample_coordmap) # The images have the same output coordinates world_to_world_coordmap = AffineTransform(xyz_im.xyz_transform.function_range, xyz_im.xyz_transform.function_range, np.identity(4)) im_subsampled = resample(xyz_im, target_coordmap, world_to_world_coordmap, shape=subsampled_shape) xyz_im_subsampled = xyz_image.XYZImage(np.array(im_subsampled), im_subsampled.affine, im_subsampled.coordmap.function_domain.coord_names) yield assert_almost_equal, np.array(xyz_im_subsampled), np.array(xyz_im)[::2,::3,::4] # We can now do subsampling with these methods. xyz_im_subsampled2 = xyz_im.resampled_to_affine(target_coordmap, shape=subsampled_shape) yield assert_almost_equal, np.array(xyz_im_subsampled2), np.array(xyz_im_subsampled) yield assert_true, xyz_im_subsampled2 == xyz_im_subsampled
def from_image(klass, image, axis=0): # Now, reorder the axes and reference image = rollaxis(image, axis) imlist = [] coordmap = image.coordmap # We drop the first output coordinate of image's coordmap drop1st = np.identity(coordmap.ndims[1]+1)[1:] drop1st_domain = image.reference drop1st_range = CoordinateSystem(image.reference.coord_names[1:], name=image.reference.name, coord_dtype=image.reference.coord_dtype) drop1st_coordmap = AffineTransform(drop1st_domain, drop1st_range, drop1st) # And arbitrarily add a 0 for the first axis add0 = np.vstack([np.zeros(image.axes.ndim), np.identity(image.axes.ndim)]) add0_domain = CoordinateSystem(image.axes.coord_names[1:], name=image.axes.name, coord_dtype=image.axes.coord_dtype) add0_range = image.axes add0_coordmap = AffineTransform(add0_domain, add0_range, add0) coordmap = compose(drop1st_coordmap, image.coordmap, add0_coordmap) data = np.asarray(image) imlist = [Image(dataslice, copy(coordmap)) for dataslice in data] return klass(imlist)
def test_compose_cmap(): value = np.array([1., 2., 3.]) b = compose(E.e, E.e) assert_true(np.allclose(b(value), value))
def ni_affine_pixdim_from_affine(affine_transform, strict=False): """ Given a square affine_transform, return a new 3-dimensional AffineTransform and the pixel dimensions in dimensions greater than 3. If strict is True, then an exception is raised if the affine matrix is not diagonal with positive entries in dimensions greater than 3. If strict is True, then the names of the range coordinates must be LPS:('x+LR','y+PA','z+SI') or RAS:('x+RL','y+AP','z+SI'). If strict is False, and the names are not either of these, LPS:('x+LR','y+PA','z+SI') are used. If the names are RAS:('x+RL','y+AA','z+SI'), then the affine is flipped so the result is in LPS:('x+LR','y+PA','z+SI'). NIFTI images have the first 3 dimensions as spatial, and the remaining as non-spatial, with the 4th typically being time. Parameters ---------- affine_transform : `AffineTransform` Returns ------- nifti_transform: `AffineTransform` A 3-dimensional or less AffineTransform pixdim : ndarray(np.float) The pixel dimensions greater than 3. >>> outnames = CS(('x+LR','y+PA','z+SI') + ('t',)) >>> innames = CS(['phase', 'j', 'frequency', 't']) >>> af_tr = AT(outnames, innames, np.diag([2,-2,3,3.5,1])) >>> print af_tr AffineTransform( function_domain=CoordinateSystem(coord_names=('x+LR', 'y+PA', 'z+SI', 't'), name='', coord_dtype=float64), function_range=CoordinateSystem(coord_names=('phase', 'j', 'frequency', 't'), name='', coord_dtype=float64), affine=array([[ 2. , 0. , 0. , 0. , 0. ], [ 0. , -2. , 0. , 0. , 0. ], [ 0. , 0. , 3. , 0. , 0. ], [ 0. , 0. , 0. , 3.5, 0. ], [ 0. , 0. , 0. , 0. , 1. ]]) ) >>> af_tr3dorless, p = ni_affine_pixdim_from_affine(af_tr) >>> print af_tr3dorless AffineTransform( function_domain=CoordinateSystem(coord_names=('x+LR', 'y+PA', 'z+SI'), name='', coord_dtype=float64), function_range=CoordinateSystem(coord_names=('x+LR', 'y+PA', 'z+SI'), name='', coord_dtype=float64), affine=array([[ 2., 0., 0., 0.], [ 0., -2., 0., 0.], [ 0., 0., 3., 0.], [ 0., 0., 0., 1.]]) ) >>> print p [ 3.5] """ if ((not isinstance(affine_transform, AT)) or (affine_transform.ndims[0] != affine_transform.ndims[1])): raise ValueError('affine_transform must be a square AffineTransform' + ' to save as a NIFTI file') ndim = affine_transform.ndims[0] ndim3 = min(ndim, 3) range_names = affine_transform.function_range.coord_names if range_names[:ndim3] not in [lps_output_coordnames[:ndim3], ras_output_coordnames[:ndim3]]: if strict: raise ValueError('strict is true and the range is not LPS or RAS, assuming LPS') warnings.warn('range is not LPS or RAS, assuming LPS') range_names = list(range_names) range_names[:ndim3] = lps_output_coordnames[:ndim3] range_names = tuple(range_names) ndim = affine_transform.ndims[0] nifti_indim = 'ijk'[:ndim] + 'tuvw'[ndim3:ndim] nifti_outdim = range_names[:ndim3] + \ ('t', 'u', 'v', 'w' )[ndim3:ndim] nifti_transform = AT(CS(nifti_indim), CS(nifti_outdim), affine_transform.affine) domain_names = affine_transform.function_domain.coord_names[:ndim3] nifti_transform = nifti_transform.renamed_domain(dict(zip('ijk'[:ndim3], domain_names))) # now find the pixdims A = nifti_transform.affine[3:,3:] if (not np.allclose(np.diag(np.diag(A)), A) or not np.all(np.diag(A) > 0)): msg = "affine transformation matrix is not diagonal " + \ " with positive entries on diagonal, some information lost" if strict: raise ValueError('strict is true and %s' % msg) warnings.warn(msg) pixdim = np.fabs(np.diag(A)[:-1]) # find the 4x4 (or smaller) A3d = np.identity(ndim3+1) A3d[:ndim3,:ndim3] = nifti_transform.affine[:ndim3, :ndim3] A3d[:ndim3,-1] = nifti_transform.affine[:ndim3, -1] range_names = nifti_transform.function_range.coord_names[:ndim3] nifti_3dorless_transform = AT(CS(domain_names), CS(range_names), A3d) # if RAS, we flip, with a warning if range_names[:ndim3] == ras_output_coordnames[:ndim3]: signs = [-1,-1,1,1][:(ndim3+1)] # silly, but 1d case is handled for consistency if signs == [-1,-1]: signs = [-1,1] ras_to_lps = AT(CS(ras_output_coordnames[:ndim3]), CS(lps_output_coordnames[:ndim3]), np.diag(signs)) warnings.warn('affine_transform has RAS output_range, flipping to LPS') nifti_3dorless_transform = compose(ras_to_lps, nifti_3dorless_transform) return nifti_3dorless_transform, pixdim
def to_ni_image(self, t_idx, f_idx, grid_shape=None): m_arr = self.to_masked_array(t_idx, f_idx, grid_shape=grid_shape, fill_value=self.fill_value) meg2mri = self.beam.coreg.meg2mri return ni_api.Image(m_arr.filled(), compose(meg2mri, self.beam.coordmap))
def to_overlay(self, t_idx, f_idx, grid_spacing=None): m_arr = self.to_masked_array(t_idx, f_idx) meg2mri = self.beam.coreg.meg2mri img = ni_api.Image(m_arr, compose(meg2mri, self.beam.coordmap)) return img