def test_nonaffine(): # resamples an image along a curve through the image. # # FIXME: use the reference.evaluate.Grid to perform this nicer # FIXME: Remove pylab references def curve(x): # function accept N by 1, returns N by 2 return np.vstack([5 * np.sin(x.T), 5 * np.cos(x.T)]).T + [52, 47] for names in (("xy", "ij", "t", "u"), ("ij", "xy", "t", "s")): in_names, out_names, tin_names, tout_names = names g = AffineTransform.from_params(in_names, out_names, np.identity(3)) img = Image(np.ones((100, 90)), g) img[50:55, 40:55] = 3.0 tcoordmap = AffineTransform.from_start_step(tin_names, tout_names, [0], [np.pi * 1.8 / 100]) ir = resample(img, tcoordmap, curve, (100,)) if gui_review: import pylab pylab.figure(num=3) pylab.imshow(img, interpolation="nearest") d = curve(np.linspace(0, 1.8 * np.pi, 100)) pylab.plot(d[0], d[1]) pylab.gca().set_ylim([0, 99]) pylab.gca().set_xlim([0, 89]) pylab.figure(num=4) pylab.plot(np.asarray(ir))
def test_rotate2d(): # Rotate an image in 2d on a square grid, should result in transposed image g = AffineTransform.from_params('ij', 'xy', np.diag([0.7,0.5,1])) g2 = AffineTransform.from_params('ij', 'xy', np.diag([0.5,0.7,1])) i = Image(np.ones((100,100)), g) # This sets the image data by writing into the array i.get_data()[50:55,40:55] = 3. a = np.array([[0,1,0], [1,0,0], [0,0,1]], np.float) ir = resample(i, g2, a, (100, 100)) assert_array_almost_equal(ir.get_data().T, i.get_data())
def setUp(self): names = ['zspace', 'yspace', 'xspace'] shape = (10,20,30) self.img = Image(np.zeros(shape), AffineTransform.from_start_step(names, names, (0,)*3, (1,)*3)) self.img2 = Image(np.ones(shape), AffineTransform.from_start_step(names, names, (0,)*3, (1,)*3)) shape = (3,5,4) self.img3 = Image(np.zeros(shape), AffineTransform.from_start_step(names, names, (0,)*3, (1,)*3)) self.img4 = Image(np.zeros(shape), AffineTransform.from_start_step(names, names, (0,)*3, (1,)*3))
def test_rotate3d(): # Rotate / transpose a 3d image on a non-square grid g = AffineTransform.from_params('ijk', 'xyz', np.diag([0.5,0.6,0.7,1])) g2 = AffineTransform.from_params('ijk', 'xyz', np.diag([0.5,0.7,0.6,1])) shape = (100,90,80) i = Image(np.ones(shape), g) i.get_data()[50:55,40:55,30:33] = 3. a = np.array([[1,0,0,0], [0,0,1,0], [0,1,0,0], [0,0,0,1.]]) ir = resample(i, g2, a, (100,80,90)) assert_array_almost_equal(np.transpose(ir.get_data(), (0,2,1)), i.get_data())
def test_rotate3d(): # Rotate / transpose a 3d image on a non-square grid g = AffineTransform.from_params("ijk", "xyz", np.diag([0.5, 0.6, 0.7, 1])) g2 = AffineTransform.from_params("ijk", "xyz", np.diag([0.5, 0.7, 0.6, 1])) shape = (100, 90, 80) i = Image(np.ones(shape), g) i[50:55, 40:55, 30:33] = 3.0 a = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1.0]]) ir = resample(i, g2, a, (100, 80, 90)) yield assert_array_almost_equal, np.transpose(np.asarray(ir), (0, 2, 1)), i
def test_rotate2d2(): # Rotate an image in 2d on a non-square grid, # should result in transposed image g = AffineTransform.from_params("ij", "xy", np.diag([0.7, 0.5, 1])) g2 = AffineTransform.from_params("ij", "xy", np.diag([0.5, 0.7, 1])) i = Image(np.ones((100, 80)), g) i[50:55, 40:55] = 3.0 a = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 1]], np.float) ir = resample(i, g2, a, (80, 100)) yield assert_array_almost_equal, np.asarray(ir).T, i
def test_slice_from_3d(): # Resample a 3d image, returning a zslice, yslice and xslice # # This example creates a coordmap that coincides with # a given z, y, or x slice of an image, and checks that # resampling agrees with the data in the given slice. shape = (100,90,80) g = AffineTransform.from_params('ijk', 'xyz', np.diag([0.5,0.5,0.5,1])) img = Image(np.ones(shape), g) img.get_data()[50:55,40:55,30:33] = 3 I = np.identity(4) zsl = slices.zslice(26, ((0,49.5), 100), ((0,44.5), 90), img.reference) ir = resample(img, zsl, I, (100, 90)) assert_array_almost_equal(ir.get_data(), img[:,:,53].get_data()) ysl = slices.yslice(22, ((0,49.5), 100), ((0,39.5), 80), img.reference) ir = resample(img, ysl, I, (100, 80)) assert_array_almost_equal(ir.get_data(), img[:,45,:].get_data()) xsl = slices.xslice(15.5, ((0,44.5), 90), ((0,39.5), 80), img.reference) ir = resample(img, xsl, I, (90, 80)) assert_array_almost_equal(ir.get_data(), img[32,:,:].get_data())
def __init__(self, data, affine, axis_names, metadata={}, lps=True): """ Creates a new nipy image with an affine mapping. Parameters ---------- data : ndarray ndarray representing the data. affine : 4x4 ndarray affine transformation to the reference coordinate system axis_names : [string] names of the axes in the coordinate system. """ if len(axis_names) < 3: raise ValueError('XYZImage must have a minimum of 3 axes') # The first three axes are assumed to be the # spatial ones xyz_transform = XYZTransform(affine, axis_names[:3], lps) nonspatial_names = axis_names[3:] if nonspatial_names: nonspatial_affine_transform = AffineTransform.from_start_step(nonspatial_names, nonspatial_names, [0]*(data.ndim-3), [1]*(data.ndim-3)) full_dimensional_affine_transform = cmap_product(xyz_transform, nonspatial_affine_transform) else: full_dimensional_affine_transform = xyz_transform self._xyz_transform = xyz_transform Image.__init__(self, data, full_dimensional_affine_transform, metadata=metadata)
def __init__(self, data, affine, coord_sys, metadata=None): """ Creates a new nipy image with an affine mapping. Parameters ---------- data : ndarray ndarray representing the data. affine : 4x4 ndarray affine transformation to the reference coordinate system coord_system : string name of the reference coordinate system. """ function_domain = CoordinateSystem(['axis%d' % i for i in range(3)], name=coord_sys) function_range = CoordinateSystem(['x','y','z'], name='world') spatial_coordmap = AffineTransform(function_domain, function_range, affine) nonspatial_names = ['axis%d' % i for i in range(3, data.ndim)] if nonspatial_names: nonspatial_coordmap = AffineTransform.from_start_step(nonspatial_names, nonspatial_names, [0]*(data.ndim-3), [1]*(data.ndim-3)) full_coordmap = cmap_product(coordmap, nonspatial_coordmap) else: full_coordmap = spatial_coordmap self._spatial_coordmap = spatial_coordmap self.coord_sys = coord_sys Image.__init__(self, data, full_coordmap) if metadata is not None: self.metadata = metadata
def test_resample2d2(): g = AffineTransform.from_params("ij", "xy", np.diag([0.5, 0.5, 1])) i = Image(np.ones((100, 90)), g) i[50:55, 40:55] = 3.0 a = np.identity(3) a[:2, -1] = 4.0 A = np.identity(2) b = np.ones(2) * 4 ir = resample(i, i.coordmap, (A, b), (100, 90)) yield assert_array_almost_equal, ir[42:47, 32:47], 3.0
def test_resample2d3(): # Same as test_resample2d, only a different way of specifying # the transform: here it is an (A,b) pair g = AffineTransform.from_params('ij', 'xy', np.diag([0.5,0.5,1])) i = Image(np.ones((100,90)), g) i.get_data()[50:55,40:55] = 3. a = np.identity(3) a[:2,-1] = 4. ir = resample(i, i.coordmap, a, (100,90)) assert_array_almost_equal(ir.get_data()[42:47,32:47], 3.)
def test_resample2d2(): g = AffineTransform.from_params('ij', 'xy', np.diag([0.5,0.5,1])) i = Image(np.ones((100,90)), g) i.get_data()[50:55,40:55] = 3. a = np.identity(3) a[:2,-1] = 4. A = np.identity(2) b = np.ones(2)*4 ir = resample(i, i.coordmap, (A, b), (100,90)) assert_array_almost_equal(ir.get_data()[42:47,32:47], 3.)
def test_resample2d3(): # Same as test_resample2d, only a different way of specifying # the transform: here it is an (A,b) pair g = AffineTransform.from_params("ij", "xy", np.diag([0.5, 0.5, 1])) i = Image(np.ones((100, 90)), g) i[50:55, 40:55] = 3.0 a = np.identity(3) a[:2, -1] = 4.0 ir = resample(i, i.coordmap, a, (100, 90)) yield assert_array_almost_equal, ir[42:47, 32:47], 3.0
def test_rotate2d3(): # Another way to rotate/transpose the image, similar to # test_rotate2d2 and test_rotate2d, except the world of the # output coordmap is the same as the world of the # original image. That is, the data is transposed on disk, but the # output coordinates are still 'x,'y' order, not 'y', 'x' order as # above # this functionality may or may not be used a lot. if data is to # be transposed but one wanted to keep the NIFTI order of output # coords this would do the trick g = AffineTransform.from_params('xy', 'ij', np.diag([0.5,0.7,1])) i = Image(np.ones((100,80)), g) # This sets the image data by writing into the array i.get_data()[50:55,40:55] = 3. a = np.identity(3) g2 = AffineTransform.from_params('xy', 'ij', np.array([[0,0.5,0], [0.7,0,0], [0,0,1]])) ir = resample(i, g2, a, (80,100)) assert_array_almost_equal(ir.get_data().T, i.get_data())
def test_rotate2d3(): # Another way to rotate/transpose the image, similar to # test_rotate2d2 and test_rotate2d except the world of the # output coordmap are the same as the world of the # original image. That is, the data is transposed on disk, but the # output coordinates are still 'x,'y' order, not 'y', 'x' order as # above # this functionality may or may not be used a lot. if data is to # be transposed but one wanted to keep the NIFTI order of output # coords this would do the trick g = AffineTransform.from_params("xy", "ij", np.diag([0.5, 0.7, 1])) i = Image(np.ones((100, 80)), g) i[50:55, 40:55] = 3.0 a = np.identity(3) g2 = AffineTransform.from_params("xy", "ij", np.array([[0, 0.5, 0], [0.7, 0, 0], [0, 0, 1]])) ir = resample(i, g2, a, (80, 100)) v2v = compose(g.inverse(), g2) yield assert_array_almost_equal, np.asarray(ir).T, i
def test_2d_from_3d(): # Resample a 3d image on a 2d affine grid # This example creates a coordmap that coincides with # the 10th slice of an image, and checks that # resampling agrees with the data in the 10th slice. shape = (100, 90, 80) g = AffineTransform.from_params("ijk", "xyz", np.diag([0.5, 0.5, 0.5, 1])) i = Image(np.ones(shape), g) i[50:55, 40:55, 30:33] = 3.0 a = np.identity(4) g2 = ArrayCoordMap.from_shape(g, shape)[10] ir = resample(i, g2.coordmap, a, g2.shape) yield assert_array_almost_equal, np.asarray(ir), np.asarray(i[10])
def test_resample2d1(): # Tests the same as test_resample2d, only using a callable instead of # an AffineTransform instance g = AffineTransform.from_params('ij', 'xy', np.diag([0.5,0.5,1])) i = Image(np.ones((100,90)), g) i.get_data()[50:55,40:55] = 3. a = np.identity(3) a[:2,-1] = 4. A = np.identity(2) b = np.ones(2)*4 def mapper(x): return np.dot(x, A.T) + b ir = resample(i, i.coordmap, mapper, (100,90)) assert_array_almost_equal(ir.get_data()[42:47,32:47], 3.)
def __init__(self, affine, axis_names, lps=True): """ >>> xyz = XYZTransform(np.diag([3,4,5,1]), 'ijk') >>> xyz XYZTransform( function_domain=CoordinateSystem(coord_names=('i', 'j', 'k'), name='voxel', coord_dtype=float64), function_range=CoordinateSystem(coord_names=('x+LR', 'y+PA', 'z+SI'), name='world', coord_dtype=float64), affine=array([[ 3., 0., 0., 0.], [ 0., 4., 0., 0.], [ 0., 0., 5., 0.], [ 0., 0., 0., 1.]]) ) >>> """ if affine.shape != (4,4): raise ValueError('affine must be a 4x4 matrix representing an affine transformation in homogeneous coordinates') if lps: xyz = lps_output_coordnames else: xyz = ras_output_coordnames AffineTransform.__init__(self, CS(axis_names, name='voxel'), CS(xyz, name='world'), affine)
def test_resample2d(): g = AffineTransform.from_params('ij', 'xy', np.diag([0.5,0.5,1])) i = Image(np.ones((100,90)), g) i.get_data()[50:55,40:55] = 3. # This mapping describes a mapping from the "target" physical # coordinates to the "image" physical coordinates. The 3x3 matrix # below indicates that the "target" physical coordinates are related # to the "image" physical coordinates by a shift of -4 in each # coordinate. Or, to find the "image" physical coordinates, given # the "target" physical coordinates, we add 4 to each "target # coordinate". The resulting resampled image should show the # overall image shifted -8,-8 voxels towards the origin a = np.identity(3) a[:2,-1] = 4. ir = resample(i, i.coordmap, a, (100,90)) assert_array_almost_equal(ir.get_data()[42:47,32:47], 3.)
def output_resid(outfile, fmri_image, clobber=False): """ Create an output file of the residuals parameter from the OLS pass of fmristat. Uses affine part of the first image to output resids unless fmri_image is an Image. Parameters ---------- outfile : fmri_image : ``FmriImageList`` or 4D image If ``FmriImageList``, needs attributes ``volume_start_times``, supports len(), and object[0] has attributes ``affine``, ``coordmap`` and ``shape``, from which we create a new 4D coordmap and shape If 4D image, use the images coordmap and shape clobber : bool if True, overwrite previous output Returns ------- regression_output : """ if isinstance(fmri_image, FmriImageList): n = len(fmri_image.list) T = np.zeros((5,5)) g = fmri_image[0].coordmap T[1:,1:] = fmri_image[0].affine T[0,0] = (fmri_image.volume_start_times[1:] - fmri_image.volume_start_times[:-1]).mean() # FIXME: NIFTI specific naming here innames = ["l"] + list(g.input_coords.coord_names) outnames = ["t"] + list(g.output_coords.coord_names) cmap = AffineTransform.from_params(innames, outnames, T) shape = (n,) + fmri_image[0].shape elif isinstance(fmri_image, Image): cmap = fmri_image.coordmap shape = fmri_image.shape else: raise ValueError, "expecting FmriImageList or 4d Image" outim = ModelOutputImage(outfile, cmap, shape, clobber=clobber) return regression.RegressionOutput(outim, regression.output_resid)
def test_resample3d(): g = AffineTransform.from_params("ijk", "xyz", np.diag([0.5, 0.5, 0.5, 1])) shape = (100, 90, 80) i = Image(np.ones(shape), g) i[50:55, 40:55, 30:33] = 3.0 # This mapping describes a mapping from the "target" physical # coordinates to the "image" physical coordinates. The 4x4 matrix # below indicates that the "target" physical coordinates are related # to the "image" physical coordinates by a shift of -4 in each # coordinate. Or, to find the "image" physical coordinates, given # the "target" physical coordinates, we add 4 to each "target # coordinate". The resulting resampled image should show the # overall image shifted [-6,-8,-10] voxels towards the origin a = np.identity(4) a[:3, -1] = [3, 4, 5] ir = resample(i, i.coordmap, a, (100, 90, 80)) yield assert_array_almost_equal, ir[44:49, 32:47, 20:23], 3.0
def test_resample3d(): g = AffineTransform.from_params('ijk', 'xyz', np.diag([0.5, 0.5, 0.5, 1])) shape = (100, 90, 80) i = Image(np.ones(shape), g) i.get_data()[50:55, 40:55, 30:33] = 3. # This mapping describes a mapping from the "target" physical # coordinates to the "image" physical coordinates. The 4x4 matrix # below indicates that the "target" physical coordinates are related # to the "image" physical coordinates by a shift of -4 in each # coordinate. Or, to find the "image" physical coordinates, given # the "target" physical coordinates, we add 4 to each "target # coordinate". The resulting resampled image should show the # overall image shifted [-6,-8,-10] voxels towards the origin a = np.identity(4) a[:3, -1] = [3, 4, 5] ir = resample(i, i.coordmap, a, (100, 90, 80)) assert_array_almost_equal(ir.get_data()[44:49, 32:47, 20:23], 3.)
def output_resid(outfile, fmri_image, clobber=False): """ Create an output file of the residuals parameter from the OLS pass of fmristat. Uses affine part of the first image to output resids unless fmri_image is an Image. Parameters ---------- outfile : fmri_image : ``FmriImageList`` or 4D image If ``FmriImageList``, needs attributes ``volume_start_times``, supports len(), and object[0] has attributes ``affine``, ``coordmap`` and ``shape``, from which we create a new 4D coordmap and shape If 4D image, use the images coordmap and shape clobber : bool if True, overwrite previous output Returns ------- regression_output : """ if isinstance(fmri_image, FmriImageList): n = len(fmri_image.list) T = np.zeros((5, 5)) g = fmri_image[0].coordmap T[1:, 1:] = fmri_image[0].affine T[0, 0] = (fmri_image.volume_start_times[1:] - fmri_image.volume_start_times[:-1]).mean() # FIXME: NIFTI specific naming here innames = ["t"] + list(g.function_domain.coord_names) outnames = ["t"] + list(g.function_range.coord_names) cmap = AffineTransform.from_params(innames, outnames, T) shape = (n, ) + fmri_image[0].shape elif isinstance(fmri_image, Image): cmap = fmri_image.coordmap shape = fmri_image.shape else: raise ValueError, "expecting FmriImageList or 4d Image" outim = ModelOutputImage(outfile, cmap, shape, clobber=clobber) return outputters.RegressionOutput(outim, outputters.output_resid)
def test_slice_from_3d(): # Resample a 3d image, returning a zslice, yslice and xslice # # This example creates a coordmap that coincides with # the 10th slice of an image, and checks that # resampling agrees with the data in the 10th slice. shape = (100, 90, 80) g = AffineTransform.from_params("ijk", "xyz", np.diag([0.5, 0.5, 0.5, 1])) i = Image(np.ones(shape), g) i[50:55, 40:55, 30:33] = 3 a = np.identity(4) zsl = slices.zslice(26, ((0, 44.5), 90), ((0, 39.5), 80), i.reference) ir = resample(i, zsl, a, (90, 80)) yield assert_true(np.allclose(np.asarray(ir), np.asarray(i[53]))) ysl = slices.yslice(22, (0, 49.5), (0, 39.5), i.reference, (100, 80)) ir = resample(i, ysl.coordmap, a, ysl.shape) yield assert_true(np.allclose(np.asarray(ir), np.asarray(i[:, 45]))) xsl = slices.xslice(15.5, (0, 49.5), (0, 44.5), i.reference, (100, 90)) ir = resample(i, xsl.coordmap, a, xsl.shape) yield assert_true(np.allclose(np.asarray(ir), np.asarray(i[:, :, 32])))
def generateTestingPair(betaGT): betaGTRads = np.array(betaGT, dtype=np.float64) betaGTRads[0:3] = np.copy(np.pi * betaGTRads[0:3] / 180.0) ns = 181 nr = 217 nc = 181 left = np.fromfile('data/t2/t2_icbm_normal_1mm_pn0_rf0.rawb', dtype=np.ubyte).reshape(ns, nr, nc) left = left.astype(np.float64) right = np.fromfile('data/t1/t1_icbm_normal_1mm_pn0_rf0.rawb', dtype=np.ubyte).reshape(ns, nr, nc) right = right.astype(np.float64) right = rcommon.applyRigidTransformation3D(right, betaGTRads) affine_transform = AffineTransform( 'ijk', ['aligned-z=I->S', 'aligned-y=P->A', 'aligned-x=L->R'], np.eye(4)) left = Image(left, affine_transform) right = Image(right, affine_transform) nipy.save_image(left, 'moving.nii') nipy.save_image(right, 'fixed.nii')
def test_slice_from_3d(): # Resample a 3d image, returning a zslice, yslice and xslice # # This example creates a coordmap that coincides with # a given z, y, or x slice of an image, and checks that # resampling agrees with the data in the given slice. shape = (100, 90, 80) g = AffineTransform.from_params('ijk', 'xyz', np.diag([0.5, 0.5, 0.5, 1])) img = Image(np.ones(shape), g) img.get_data()[50:55, 40:55, 30:33] = 3 I = np.identity(4) zsl = slices.zslice(26, ((0, 49.5), 100), ((0, 44.5), 90), img.reference) ir = resample(img, zsl, I, (100, 90)) assert_array_almost_equal(ir.get_data(), img[:, :, 53].get_data()) ysl = slices.yslice(22, ((0, 49.5), 100), ((0, 39.5), 80), img.reference) ir = resample(img, ysl, I, (100, 80)) assert_array_almost_equal(ir.get_data(), img[:, 45, :].get_data()) xsl = slices.xslice(15.5, ((0, 44.5), 90), ((0, 39.5), 80), img.reference) ir = resample(img, xsl, I, (90, 80)) assert_array_almost_equal(ir.get_data(), img[32, :, :].get_data())
def changeCoords2(affine, scale, coords): domain = coords.function_domain outrange = coords.function_range # Create a new matrix mod_affine = np.zeros((4, 4)) # Set up the diagonals mod_affine[0, 0] = affine[0, 0] * scale mod_affine[1, 1] = affine[1, 1] * scale mod_affine[2, 2] = affine[2, 2] * scale mod_affine[ 3, 3] = 1 # This is the new temporal dimension: each vol ume is 2s apart # Pull the last column of the first 3 rows into the new matrix mod_affine[0, 3] = affine[0, 3] // scale mod_affine[1, 3] = affine[1, 3] // scale mod_affine[2, 3] = affine[2, 3] // scale modified = AffineTransform(domain, outrange, mod_affine) return modified
def peelTemplateBrain(): ns = 181 nr = 217 nc = 181 gt_template = np.fromfile('data/phantom_1.0mm_normal_crisp.rawb', dtype=np.ubyte).reshape((ns, nr, nc)) t1_template = np.fromfile('data/t1/t1_icbm_normal_1mm_pn0_rf0.rawb', dtype=np.ubyte).reshape((ns, nr, nc)) t2_template = np.fromfile('data/t2/t2_icbm_normal_1mm_pn0_rf0.rawb', dtype=np.ubyte).reshape((ns, nr, nc)) #t1_template*=((1<=gt_template)*(gt_template<=3)+(gt_template==8)) t1_template *= ((1 <= gt_template) * (gt_template <= 3)) t2_template *= ((1 <= gt_template) * (gt_template <= 3)) affine_transform = AffineTransform( 'ijk', ['aligned-z=I->S', 'aligned-y=P->A', 'aligned-x=L->R'], np.eye(4)) t1_template = Image(t1_template, affine_transform) t2_template = Image(t2_template, affine_transform) nipy.save_image(t1_template, 'data/t1/t1_icbm_normal_1mm_pn0_rf0_peeled.nii.gz') nipy.save_image(t2_template, 'data/t2/t2_icbm_normal_1mm_pn0_rf0_peeled.nii.gz')
def testIntersubjectRigidRegistration(fname0, fname1, level, outfname): nib_left = nib.load(fname0) nib_right = nib.load(fname1) left = nib_left.get_data().astype(np.double).squeeze() right = nib_right.get_data().astype(np.double).squeeze() leftPyramid = [i for i in rcommon.pyramid_gaussian_3D(left, level)] rightPyramid = [i for i in rcommon.pyramid_gaussian_3D(right, level)] plotSlicePyramidsAxial(leftPyramid, rightPyramid) print 'Estimation started.' beta = estimateRigidTransformationMultiscale3D(leftPyramid, rightPyramid) print 'Estimation finished.' rcommon.applyRigidTransformation3D(left, beta) sl = np.array(left.shape) // 2 sr = np.array(right.shape) // 2 rcommon.overlayImages(left[sl[0], :, :], leftPyramid[0][sr[0], :, :]) rcommon.overlayImages(left[sl[0], :, :], right[sr[0], :, :]) affine_transform = AffineTransform( 'ijk', ['aligned-z=I->S', 'aligned-y=P->A', 'aligned-x=L->R'], np.eye(4)) left = Image(left, affine_transform) nipy.save_image(left, outfname) return beta
def convert3DCoordsTo4D(orig): # Get the data from the original coordinates domain = orig.function_domain outrange = orig.function_range affine = orig.affine print(type(affine), affine) # Add the temporal dimension mod_domain = CoordinateSystem(domain.coord_names + ('t', ), domain.name, domain.coord_dtype) mod_range = CoordinateSystem(outrange.coord_names + ('t', ), outrange.name, outrange.coord_dtype) # Temporal is a little trickier in the affine matrix # Create a new matrix mod_affine = np.zeros((5, 5)) # Set up the diagonals mod_affine[0, 0] = affine[0, 0] mod_affine[1, 1] = affine[1, 1] mod_affine[2, 2] = affine[2, 2] mod_affine[ 3, 3] = 2 # This is the new temporal dimension: each volume is 2s apart mod_affine[ 4, 4] = 1 # This is the last row in the matrix and should be all 0 except this column # Pull the last column of the first 3 rows into the new matrix mod_affine[0, 4] = affine[0, 3] mod_affine[1, 4] = affine[1, 3] mod_affine[2, 4] = affine[2, 3] print(mod_affine) modified = AffineTransform(mod_domain, mod_range, mod_affine) return modified
def create(rootdir): out = None prev = None out_is_there = False dirs = sorted(os.listdir(rootdir)) for d in dirs: files = os.listdir(os.path.join(rootdir, d)) for f in files: input_image = tif.imread(os.path.join(rootdir, d, f)) # print input_image # print type(input_image) # print 'ccc',input_image.flatten() if out_is_there: #out = np.concatenate([out, input_image.flatten()]) out = np.dstack([out, input_image]) else: # out = input_image.flatten() out = input_image out_is_there = True # return # i = 0 # for root, dirs, files in os.walk(rootdir): # fullpaths = [(os.path.join(root, name)) for name in files] # for f in fullpaths: # print f # input_image = tif.imread(f) # # print input_image # # print type(input_image) # # print 'ccc',input_image.flatten() # if out_is_there: # #out = np.concatenate([out, input_image.flatten()]) # out = np.dstack([out, input_image]) # else: # # out = input_image.flatten() # out = input_image # out_is_there = True #>>> from nipy.io.api import save_image #>>> data = np.zeros((91,109,91), dtype=np.uint8) #>>> cmap = AffineTransform('kji', 'zxy', np.eye(4)) #>>> img = Image(data, cmap) #>>> fname1 = os.path.join(tmpdir, 'img1.nii.gz') #>>> saved_img1 = save_image(img, fname1) # image_data = PILImage.open(os.path.join(subdir,file)) # if image_data.mode != "RGB": # image_data = image_data.convert("RGB") # print image_data.toString() # break # shutil.copy(os.path.join(subdir, file), '/tmp/'+str(i)+'.tif') # i+=1 length = out.shape[0] print 'aaa' print out # out = out.reshape((512, 512, length/512/512)) print out cmap = AffineTransform('kji', 'zxy', np.eye(4)) img = Image(out, cmap) save_image(img, '/tmp/out.nii.gz')
def test_array_coord_map(): # array coord map recreates the affine when you slice an image. In # general, if you take an integer slice in some dimension, the # corresponding column of the affine will go, leaving a row for the # lost dimension, with all zeros, execpt for the translation in the # now-removed dimension, encoding the position of that particular # slice xz = 1.1; yz = 2.3; zz = 3.5 xt = 10.0; yt = 11; zt = 12 aff = np.diag([xz, yz, zz, 1]) aff[:3,3] = [xt, yt, zt] shape = (2,3,4) cmap = AffineTransform.from_params('ijk', 'xyz', aff) acm = acs.ArrayCoordMap(cmap, shape) # slice the coordinate map for the first axis sacm = acm[1] # The affine has lost the first column, but has a remaining row (the # first) encoding the translation to get to this slice assert_array_almost_equal(sacm.coordmap.affine, np.array([ [0, 0, xz+xt], [yz, 0, yt], [0, zz, zt], [0, 0, 1]])) sacm = acm[:,1] # lost second column, remaining second row with translation assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, 0, yz+yt], [0, zz, zt], [0, 0, 1]])) sacm = acm[:,:,2] # ditto third column and row assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # check ellipsis slicing is the same as [:,: ... sacm = acm[...,2] assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # that ellipsis can follow other slice types sacm = acm[:,...,2] assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # that there can be only one ellipsis assert_raises(ValueError, acm.__getitem__, ( (Ellipsis, Ellipsis,2))) # that you can integer slice in all three dimensions, leaving only # the translation column sacm = acm[1,0,2] assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz+xt], [yt], [2*zz+zt], [1]])) # that anything other than an int, slice or Ellipsis is an error assert_raises(ValueError, acm.__getitem__, ([0,2],)) assert_raises(ValueError, acm.__getitem__, (np.array([0,2]),))
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 affine_transform_from_array(affine, ijk, pixdim): """Generate a AffineTransform from an affine transform. This is a convenience function to create a AffineTransform from image attributes. It assumes that the first three axes in the image (and therefore affine) are spatial (in 'ijk' in input and equal to 'xyz' in output), and appends the standard names for further dimensions (e.g. 'l' as the 4th in input, 't' as the 4th in output). Parameters ---------- affine : array affine for affine_transform ijk : sequence sequence, some permutation of 'ijk', giving spatial axis ordering. These are the spatial input axis names pixdim : sequence of floats Pixdims for dimensions beyond 3. Returns ------- 3daffine_transform : ``AffineTransform`` affine transform corresponding to `affine` and `ijk` domain names with LPS range names full_transform: ``AffineTransform`` affine transform corresponding to `affine` and `ijk` domain names for first 3 coordinates, diagonal with pixdim values beyond Examples -------- >>> af_tr3d, af_tr = affine_transform_from_array(np.diag([2,3,4,1]), 'ijk', []) >>> af_tr.function_domain.coord_names ('i', 'j', 'k') >>> af_tr3d.function_domain.coord_names ('i', 'j', 'k') >>> af_tr.function_range.coord_names ('x+LR', 'y+PA', 'z+SI') >>> af_tr3d.function_range.coord_names ('x+LR', 'y+PA', 'z+SI') >>> af_tr3d, af_tr = affine_transform_from_array(np.diag([2,3,4,1]), 'kij', [3.5]) >>> af_tr.function_domain.coord_names ('k', 'i', 'j', 't') >>> af_tr.function_range.coord_names ('x+LR', 'y+PA', 'z+SI', 't') >>> af_tr3d.function_domain.coord_names ('k', 'i', 'j') >>> af_tr3d.function_range.coord_names ('x+LR', 'y+PA', 'z+SI') >>> print af_tr3d AffineTransform( function_domain=CoordinateSystem(coord_names=('k', 'i', 'j'), 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., 3., 0., 0.], [ 0., 0., 4., 0.], [ 0., 0., 0., 1.]]) ) >>> print af_tr AffineTransform( function_domain=CoordinateSystem(coord_names=('k', 'i', 'j', 't'), name='product', coord_dtype=float64), function_range=CoordinateSystem(coord_names=('x+LR', 'y+PA', 'z+SI', 't'), name='product', coord_dtype=float64), affine=array([[ 2. , 0. , 0. , 0. , 0. ], [ 0. , 3. , 0. , 0. , 0. ], [ 0. , 0. , 4. , 0. , 0. ], [ 0. , 0. , 0. , 3.5, 0. ], [ 0. , 0. , 0. , 0. , 1. ]]) ) FIXME: This is an internal function and should be revisited when the AffineTransform is refactored. """ if affine.shape != (4, 4) or len(ijk) != 3: raise ValueError('affine must be square, 4x4, ijk of length 3') innames = tuple(ijk) + tuple('tuvw'[:len(pixdim)]) incoords = CS(innames, 'voxel') outnames = lps_output_coordnames + tuple('tuvw'[:len(pixdim)]) outcoords = CS(outnames, 'world') transform3d = AT(CS(incoords.coord_names[:3]), CS(outcoords.coord_names[:3]), affine) if pixdim: nonspatial = AT.from_params(incoords.coord_names[3:], outcoords.coord_names[3:], np.diag(list(pixdim) + [1])) transform_full = mapping_product(transform3d, nonspatial) else: transform_full = transform3d return transform3d, transform_full
def smooth(self, inimage, clean=False, is_fft=False): """ :Parameters: inimage : `core.api.Image` The image to be smoothed clean : ``bool`` Should we call ``nan_to_num`` on the data before smoothing? is_fft : ``bool`` Has the data already been fft'd? :Returns: `Image` """ if inimage.ndim == 4: _out = np.zeros(inimage.shape) nslice = inimage.shape[0] elif inimage.ndim == 3: nslice = 1 else: raise NotImplementedError, 'expecting either 3 or 4-d image.' for _slice in range(nslice): if inimage.ndim == 4: data = inimage[_slice] elif inimage.ndim == 3: data = inimage[:] if clean: data = np.nan_to_num(data) if not is_fft: data = self._presmooth(data) data *= self.fkernel else: data *= self.fkernel data = fft.irfftn(data) / self.norms[self.normalization] gc.collect() _dslice = [slice(0, self.bshape[i], 1) for i in range(3)] if self.scale != 1: data = self.scale * data[_dslice] if self.location != 0.0: data += self.location gc.collect() # Write out data if inimage.ndim == 4: _out[_slice] = data else: _out = data _slice += 1 gc.collect() _out = _out[[slice(self._kernel.shape[i]/2, self.bshape[i] + self._kernel.shape[i]/2) for i in range(len(self.bshape))]] if inimage.ndim == 3: return Image(_out, coordmap=self.coordmap) else: concat_affine = AffineTransform.identity('concat') return Image(_out, coordmap=product(self.coordmap, concat_affine))
def __repr__(self): s_split = AffineTransform.__repr__(self).split('\n') s_split[0] = 'XYZTransform(' return '\n'.join(s_split)
def test_affine_transform_from_array(): X = np.random.standard_normal((4, 4)) X[-1] = [0, 0, 0, 1] function_domain = CoordinateSystem("ijk", "input") function_range = CoordinateSystem(lps, "output") cmap = AffineTransform(function_domain, function_range, X) A, _ = niref.affine_transform_from_array(X, "ijk", []) yield assert_almost_equal, X, A.affine yield assert_raises, ValueError, niref.affine_transform_from_array, X[:, 1:], "ijk", [] yield assert_raises, ValueError, niref.affine_transform_from_array, X, "ij", [] threed, fourd = niref.affine_transform_from_array(X, "ijk", [3.5]) yield assert_almost_equal, X, threed.affine yield assert_almost_equal, fourd.affine[3:, 3:], np.diag([3.5, 1]) # get the pixdim back out A, p = niref.ni_affine_pixdim_from_affine(fourd) yield assert_almost_equal, X, A.affine yield assert_almost_equal, p, 3.5 # try strict A, p = niref.ni_affine_pixdim_from_affine(fourd, strict=True) # try using RAS cmap = fourd.renamed_range(dict(zip(lps, ras_output_coordnames))) A, p = niref.ni_affine_pixdim_from_affine(cmap, strict=True) # will have been flipped to LPS yield assert_almost_equal, A.affine, np.dot(np.diag([-1, -1, 1, 1]), X) yield assert_equal, A.function_range.coord_names, lps # use coordinates that aren't OK and strict raises an exception cmap = fourd.renamed_range(dict(zip(lps, "xyz"))) yield assert_raises, ValueError, niref.ni_affine_pixdim_from_affine, cmap, True # use coordinates that aren't OK and not strict just guesses LPS cmap4 = fourd.renamed_range(dict(zip(lps, "xyz"))) A, p = niref.ni_affine_pixdim_from_affine(cmap4, False) yield assert_almost_equal, A.affine, X yield assert_equal, A.function_range.coord_names, lps yield assert_almost_equal, p, 3.5 # non-square affine fails Z = np.random.standard_normal((5, 4)) Z[-1] = [0, 0, 0, 1] affine = AffineTransform.from_params("ijk", "xyzt", Z) yield assert_raises, ValueError, niref.ni_affine_pixdim_from_affine, affine # CoordinateMap fails ijk = CoordinateSystem("ijk") xyz = CoordinateSystem("xzy") cmap = CoordinateMap(ijk, xyz, np.exp) yield assert_raises, ValueError, niref.ni_affine_pixdim_from_affine, cmap, True # non-diagonal above 3rd dimension, with strict True raises an exception cmap5 = cmap4.renamed_range(dict(zip("xyz", lps))) cmap5.affine[3, -1] = 4.0 yield assert_raises, ValueError, niref.ni_affine_pixdim_from_affine, cmap5, True B, p = niref.ni_affine_pixdim_from_affine(cmap5) yield assert_equal, p, 3.5
def smooth(self, inimage, clean=False, is_fft=False): """ Apply smoothing to `inimage` Parameters ---------- inimage : ``Image`` The image to be smoothed. Should be 3D. clean : bool, optional Should we call ``nan_to_num`` on the data before smoothing? is_fft : bool, optional Has the data already been fft'd? Returns ------- s_image : `Image` New image, with smoothing applied """ if inimage.ndim == 4: # we need to generalize which axis to iterate over. By # default it should probably be the last. raise NotImplementedError('Smoothing volumes in a 4D series ' 'is broken, pending a rethink') _out = np.zeros(inimage.shape) # iterate over the first (0) axis - this is confusing - see # above nslice = inimage.shape[0] elif inimage.ndim == 3: nslice = 1 else: raise NotImplementedError('expecting either 3 or 4-d image') in_data = inimage.get_data() for _slice in range(nslice): if in_data.ndim == 4: data = in_data[_slice] elif in_data.ndim == 3: data = in_data[:] if clean: data = np.nan_to_num(data) if not is_fft: data = self._presmooth(data) data *= self.fkernel data = fft.irfftn(data) / self.norms[self.normalization] gc.collect() _dslice = [slice(0, self.bshape[i], 1) for i in range(3)] if self.scale != 1: data = self.scale * data[_dslice] if self.location != 0.0: data += self.location gc.collect() # Write out data if in_data.ndim == 4: _out[_slice] = data else: _out = data _slice += 1 gc.collect() _out = _out[[slice(self._kernel.shape[i]/2, self.bshape[i] + self._kernel.shape[i]/2) for i in range(len(self.bshape))]] if inimage.ndim == 3: return Image(_out, coordmap=self.coordmap) else: # This does not work as written. See above concat_affine = AffineTransform.identity('concat') return Image(_out, coordmap=product(self.coordmap, concat_affine))
def __init__(self, function_domain, function_range, matrix): ndim = matrix.shape[0] T = np.identity(ndim + 1, dtype=matrix.dtype) T[:-1, :-1] = matrix AffineTransform.__init__(self, function_domain, function_range, T)
def test_array_coord_map(): # array coord map recreates the affine when you slice an image. In # general, if you take an integer slice in some dimension, the # corresponding column of the affine will go, leaving a row for the # lost dimension, with all zeros, execpt for the translation in the # now-removed dimension, encoding the position of that particular # slice xz = 1.1; yz = 2.3; zz = 3.5 xt = 10.0; yt = 11; zt = 12 aff = np.diag([xz, yz, zz, 1]) aff[:3,3] = [xt, yt, zt] shape = (2,3,4) cmap = AffineTransform.from_params('ijk', 'xyz', aff) acm = acs.ArrayCoordMap(cmap, shape) # slice the coordinate map for the first axis sacm = acm[1] # The affine has lost the first column, but has a remaining row (the # first) encoding the translation to get to this slice yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [0, 0, xz+xt], [yz, 0, yt], [0, zz, zt], [0, 0, 1]])) sacm = acm[:,1] # lost second column, remaining second row with translation yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, 0, yz+yt], [0, zz, zt], [0, 0, 1]])) sacm = acm[:,:,2] # ditto third column and row yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # check ellipsis slicing is the same as [:,: ... sacm = acm[...,2] yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # that ellipsis can follow other slice types sacm = acm[:,...,2] yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz, 0, xt], [0, yz, yt], [0, 0, 2*zz+zt], [0, 0, 1]])) # that there can be only one ellipsis yield assert_raises(ValueError, acm.__getitem__, ( (Ellipsis,Ellipsis,2))) # that you can integer slice in all three dimensions, leaving only # the translation column sacm = acm[1,0,2] yield assert_array_almost_equal(sacm.coordmap.affine, np.array([ [xz+xt], [yt], [2*zz+zt], [1]])) # that anything other than an int, slice or Ellipsis is an error yield assert_raises(ValueError, acm.__getitem__, ([0,2],)) yield assert_raises(ValueError, acm.__getitem__, (np.array([0,2]),))
def smooth(self, inimage, clean=False, is_fft=False): """ Apply smoothing to `inimage` Parameters ---------- inimage : ``Image`` The image to be smoothed. Should be 3D. clean : bool, optional Should we call ``nan_to_num`` on the data before smoothing? is_fft : bool, optional Has the data already been fft'd? Returns ------- s_image : `Image` New image, with smoothing applied """ if inimage.ndim == 4: # we need to generalize which axis to iterate over. By # default it should probably be the last. raise NotImplementedError('Smoothing volumes in a 4D series ' 'is broken, pending a rethink') _out = np.zeros(inimage.shape) # iterate over the first (0) axis - this is confusing - see # above nslice = inimage.shape[0] elif inimage.ndim == 3: nslice = 1 else: raise NotImplementedError('expecting either 3 or 4-d image') in_data = inimage.get_data() for _slice in range(nslice): if in_data.ndim == 4: data = in_data[_slice] elif in_data.ndim == 3: data = in_data[:] if clean: data = np.nan_to_num(data) if not is_fft: data = self._presmooth(data) data *= self.fkernel data = fft.irfftn(data) / self.norms[self.normalization] gc.collect() _dslice = [slice(0, self.bshape[i], 1) for i in range(3)] if self.scale != 1: data = self.scale * data[_dslice] if self.location != 0.0: data += self.location gc.collect() # Write out data if in_data.ndim == 4: _out[_slice] = data else: _out = data _slice += 1 gc.collect() _out = _out[[ slice(self._kernel.shape[i] / 2, self.bshape[i] + self._kernel.shape[i] / 2) for i in range(len(self.bshape)) ]] if inimage.ndim == 3: return Image(_out, coordmap=self.coordmap) else: # This does not work as written. See above concat_affine = AffineTransform.identity('concat') return Image(_out, coordmap=product(self.coordmap, concat_affine))
def __init__(self, function_domain, function_range, matrix): ndim = matrix.shape[0] T = np.identity(ndim+1, dtype=matrix.dtype) T[:-1,:-1] = matrix AffineTransform.__init__(self, function_domain, function_range, T)