def test_MGHImage(): testfile = op.join(datadir, 'example.mgz') # Load from a file img = fslmgh.MGHImage(testfile) nbimg = nib.load(testfile) v2s = nbimg.header.get_vox2ras_tkr() w2s = affine.concat(v2s, affine.invert(nbimg.affine)) assert np.all(np.isclose(img[:], np.asanyarray(nbimg.dataobj))) assert np.all(np.isclose(img.voxToWorldMat, nbimg.affine)) assert np.all(np.isclose(img.voxToSurfMat, v2s)) assert np.all(np.isclose(img.surfToVoxMat, affine.invert(v2s))) assert np.all(np.isclose(img.worldToSurfMat, w2s)) assert np.all(np.isclose(img.surfToWorldMat, affine.invert(w2s))) assert img.name == op.basename(testfile) assert img.dataSource == testfile assert img.mghImageFile == testfile # Load from an in-memory nibabel object img = fslmgh.MGHImage(nbimg) assert np.all(np.isclose(img[:], np.asanyarray(nbimg.dataobj))) assert np.all(np.isclose(img.voxToWorldMat, nbimg.affine)) assert img.dataSource is None assert img.mghImageFile is None
def __init__(self, image, **kwargs): """Create a ``MGHImage``. :arg image: Name of MGH file, or a ``nibabel.freesurfer.mghformat.MGHImage`` instance. All other arguments are passed through to :meth:`Image.__init__` """ if isinstance(image, six.string_types): filename = op.abspath(image) name = op.basename(filename) image = nib.load(image) else: name = 'MGH image' filename = None data = np.asanyarray(image.dataobj) xform = image.affine pixdim = image.header.get_zooms() vox2surf = image.header.get_vox2ras_tkr() # the image may have an affine which # transforms the data into some space # with a scaling that is different to # the pixdims. So we create a header # object with both the affine and the # pixdims, so they are both preserved. # # Note that we have to set the zooms # after the s/qform, otherwise nibabel # will clobber them with zooms gleaned # fron the affine. header = nib.nifti1.Nifti1Header() header.set_data_shape(data.shape) header.set_sform(xform) header.set_qform(xform) header.set_zooms(pixdim) fslimage.Image.__init__(self, data, header=header, name=name, dataSource=filename, **kwargs) if filename is not None: self.setMeta('mghImageFile', filename) self.__voxToSurfMat = vox2surf self.__surfToVoxMat = affine.invert(vox2surf) self.__surfToWorldMat = affine.concat(xform, self.__surfToVoxMat) self.__worldToSurfMat = affine.invert(self.__surfToWorldMat)
def test_nonlinear_altref(seed): with tempdir.tempdir(): src2ref = affine.scaleOffsetXform([1, 1, 1], [5, 5, 5]) ref2src = affine.invert(src2ref) altv2w = affine.scaleOffsetXform([1, 1, 1], [10, 10, 10]) srcdata = np.random.randint(1, 65536, (10, 10, 10)) src = fslimage.Image(srcdata, xform=np.eye(4)) ref = fslimage.Image(src.data, xform=src2ref) altref = fslimage.Image(src.data, xform=altv2w) field = _affine_field(src, ref, ref2src, 'world', 'world') src.save('src') ref.save('ref') altref.save('altref') x5.writeNonLinearX5('xform.x5', field) fsl_apply_x5.main('src xform.x5 out -r altref'.split()) result = fslimage.Image('out') expect = np.zeros(srcdata.shape) expect[:5, :5, :5] = srcdata[5:, 5:, 5:] assert result.sameSpace(altref) assert np.all(result.data == expect)
def getTransform(self, from_, to): """Return a matrix which may be used to transform coordinates from ``from_`` to ``to``. The following values are accepted for the ``from_`` and ``to`` parameters: - ``'world'``: World coordinate system - ``'display'`` Display coordinate system - ``'mesh'`` The coordinate system of this mesh. """ nfrom_ = self.normaliseSpace(from_) nto = self.normaliseSpace(to) ref = self.refImage if ref is None: return np.eye(4) opts = self.displayCtx.getOpts(ref) xform = opts.getTransform(nfrom_, nto) if from_ == 'mesh' and self.coordSpace == 'torig': surfToVox = affine.invert(fslmgh.voxToSurfMat(ref)) xform = affine.concat(xform, ref.getAffine('voxel', 'world'), surfToVox) if to == 'mesh' and self.coordSpace == 'torig': voxToSurf = fslmgh.voxToSurfMat(ref) xform = affine.concat(voxToSurf, ref.getAffine('world', 'voxel'), xform) return xform
def test_applyDeformation_altref(): src2ref = affine.compose( np.random.randint(2, 5, 3), np.random.randint(1, 10, 3), np.random.random(3)) ref2src = affine.invert(src2ref) srcdata = np.random.randint(1, 65536, (10, 10, 10)) refdata = np.random.randint(1, 65536, (10, 10, 10)) src = fslimage.Image(srcdata) ref = fslimage.Image(refdata, xform=src2ref) field = _affine_field(src, ref, ref2src, 'world', 'world') altrefxform = affine.concat( src2ref, affine.scaleOffsetXform([1, 1, 1], [5, 0, 0])) altref = fslimage.Image(refdata, xform=altrefxform) expect, xf = resample.resampleToReference( src, altref, matrix=src2ref, order=1, mode='constant', cval=0) result = nonlinear.applyDeformation( src, field, ref=altref, order=1, mode='constant', cval=0) # boundary voxels can get truncated # (4 is the altref-ref overlap boundary) expect[4, :, :] = 0 result[4, :, :] = 0 expect = expect[1:-1, 1:-1, 1:-1] result = result[1:-1, 1:-1, 1:-1] assert np.all(np.isclose(expect, result))
def test_applyDeformation_worldAligned(): refv2w = affine.scaleOffsetXform([1, 1, 1], [10, 10, 10]) fieldv2w = affine.scaleOffsetXform([2, 2, 2], [10.5, 10.5, 10.5]) src2ref = refv2w ref2src = affine.invert(src2ref) srcdata = np.random.randint(1, 65536, (10, 10, 10)) src = fslimage.Image(srcdata) ref = fslimage.Image(srcdata, xform=src2ref) field = _affine_field(src, ref, ref2src, 'world', 'world', shape=(5, 5, 5), fv2w=fieldv2w) field = nonlinear.DeformationField( nonlinear.convertDeformationType(field, 'absolute'), header=field.header, src=src, ref=ref, srcSpace='world', refSpace='world', defType='absolute') expect, xf = resample.resampleToReference( src, ref, matrix=src2ref, order=1, mode='constant', cval=0) result = nonlinear.applyDeformation( src, field, order=1, mode='constant', cval=0) expect = expect[1:-1, 1:-1, 1:-1] result = result[1:-1, 1:-1, 1:-1] assert np.all(np.isclose(expect, result))
def updateVertices(self, *a): """Called by :meth:`__init__`, and when certain display properties change. (Re-)generates the mesh vertices, indices and normals (if being displayed in 3D). They are stored as attributes called ``vertices``, ``indices``, and ``normals`` respectively. """ overlay = self.overlay vertices = overlay.vertices indices = overlay.indices normals = self.overlay.vnormals xform = self.opts.getTransform('mesh', 'display') if not np.all(np.isclose(xform, np.eye(4))): vertices = affine.transform(vertices, xform) if self.threedee: nmat = affine.invert(xform).T normals = affine.transform(normals, nmat, vector=True) self.vertices = np.asarray(vertices, dtype=np.float32) self.indices = np.asarray(indices.flatten(), dtype=np.uint32) self.vertices = dutils.makeWriteable(self.vertices) self.indices = dutils.makeWriteable(self.indices) if self.threedee: self.normals = np.array(normals, dtype=np.float32)
def test_nonlinear(seed): with tempdir.tempdir(): src2ref = _random_affine() ref2src = affine.invert(src2ref) src = _random_image(np.eye(4)) ref = _random_image(src2ref) field = _affine_field(src, ref, ref2src, 'world', 'world') x5.writeNonLinearX5('xform.x5', field) src.save('src') fsl_apply_x5.main('src xform.x5 out'.split()) result = fslimage.Image('out') expect = resample.resampleToReference(src, ref, matrix=src2ref, smooth=False)[0] assert result.sameSpace(ref) # We might get truncation on the edges result = result.data[1:-1, 1:-1, 1:-1] expect = expect[1:-1, 1:-1, 1:-1] tol = dict(atol=1e-3, rtol=1e-3) assert np.all(np.isclose(result, expect, **tol))
def resampleToReference(image, reference, matrix=None, **kwargs): """Resample ``image`` into the space of the ``reference``. This is a wrapper around :func:`resample` - refer to its documenttion for details on the other arguments and the return values. When resampling to a reference image, resampling will only be applied along the spatial (first three) dimensions. :arg image: :class:`.Image` to resample :arg reference: :class:`.Nifti` defining the space to resample ``image`` into :arg matrix: Optional world-to-world affine alignment matrix """ oldShape = list(image.shape) newShape = list(reference.shape[:3]) if matrix is None: matrix = np.eye(4) # If the input image is >3D, pad the # new shape so that we only resample # along the first 3 dimensions. if len(newShape) < len(oldShape): newShape = newShape + oldShape[len(newShape):] # Align the two images together # via their vox-to-world affines, # and the world-to-world affine # if provided matrix = affine.concat(image.worldToVoxMat, affine.invert(matrix), reference.voxToWorldMat) # If the input image is >3D, we # have to adjust the resampling # matrix to take into account the # additional dimensions (see scipy. # ndimage.affine_transform) if len(newShape) > 3: rotmat = matrix[:3, :3] offsets = matrix[:3, 3] matrix = np.eye(len(newShape) + 1) matrix[:3, :3] = rotmat matrix[:3, -1] = offsets kwargs['mode'] = kwargs.get('mode', 'constant') kwargs['newShape'] = newShape kwargs['matrix'] = matrix data = resample(image, **kwargs)[0] # The image is now in the same space # as the reference, so it inherits # ref's voxel-to-world affine return data, reference.voxToWorldMat
def draw(self, glType, vertices, indices=None, normals=None, vdata=None, mdata=None): """Called for 3D meshes, and :attr:`.MeshOpts.vertexData` is not ``None``. Loads and runs the shader program. :arg glType: The OpenGL primitive type. :arg vertices: ``(n, 3)`` array containing the line vertices to draw. :arg indices: Indices into the ``vertices`` array. If not provided, ``glDrawArrays`` is used. :arg normals: Vertex normals. :arg vdata: ``(n, )`` array containing data for each vertex. :arg mdata: ``(n, )`` array containing alpha modulation data for each vertex. """ shader = self.activeShader if normals is not None: shader.setAtt('normal', normals) if vdata is not None: shader.setAtt('vertexData', vdata.reshape(-1, 1)) if mdata is not None: shader.setAtt('modulateData', mdata.reshape(-1, 1)) if normals is not None: # NOTE You are assuming here that the canvas # view matrix is the GL model view matrix. normalMatrix = self.canvas.viewMatrix normalMatrix = affine.invert(normalMatrix).T shader.setVertParam('normalMatrix', normalMatrix) shader.loadAtts() nvertices = vertices.shape[0] vertices = vertices.ravel('C') with glroutines.enabled((gl.GL_VERTEX_ARRAY)): gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices) if indices is None: gl.glDrawArrays(glType, 0, nvertices) else: gl.glDrawElements(glType, indices.shape[0], gl.GL_UNSIGNED_INT, indices.ravel('C'))
def test_applyDeformation_premat(): src2ref = affine.compose( np.random.randint(2, 5, 3), np.random.randint(1, 10, 3), [0, 0, 0]) ref2src = affine.invert(src2ref) srcdata = np.random.randint(1, 65536, (10, 10, 10)) refdata = np.random.randint(1, 65536, (10, 10, 10)) src = fslimage.Image(srcdata) ref = fslimage.Image(refdata, xform=src2ref) field = _affine_field(src, ref, ref2src, 'world', 'world') # First try a down-sampled version # of the original source image altsrc, xf = resample.resample(src, (5, 5, 5), origin='corner') altsrc = fslimage.Image(altsrc, xform=xf, header=src.header) expect, xf = resample.resampleToReference( altsrc, ref, matrix=src2ref, order=1, mode='nearest') premat = affine.concat(src .getAffine('world', 'voxel'), altsrc.getAffine('voxel', 'world')) result = nonlinear.applyDeformation( altsrc, field, order=1, mode='nearest', premat=premat) assert np.all(np.isclose(expect, result)) # Now try a down-sampled ROI # of the original source image altsrc = roi.roi(src, [(2, 9), (2, 9), (2, 9)]) altsrc, xf = resample.resample(altsrc, (4, 4, 4)) altsrc = fslimage.Image(altsrc, xform=xf, header=src.header) expect, xf = resample.resampleToReference( altsrc, ref, matrix=src2ref, order=1, mode='nearest') premat = affine.concat(src .getAffine('world', 'voxel'), altsrc.getAffine('voxel', 'world')) result = nonlinear.applyDeformation( altsrc, field, order=1, mode='nearest', premat=premat) assert np.all(np.isclose(expect, result)) # down-sampled and offset ROI # of the original source image altsrc = roi.roi(src, [(-5, 8), (-5, 8), (-5, 8)]) altsrc, xf = resample.resample(altsrc, (6, 6, 6)) altsrc = fslimage.Image(altsrc, xform=xf, header=src.header) expect, xf = resample.resampleToReference( altsrc, ref, matrix=src2ref, order=1, mode='nearest') premat = affine.concat(src .getAffine('world', 'voxel'), altsrc.getAffine('voxel', 'world')) result = nonlinear.applyDeformation( altsrc, field, order=1, mode='nearest', premat=premat) assert np.all(np.isclose(expect, result))
def test_invert(): testfile = op.join(datadir, 'test_transform_test_invert.txt') testdata = np.loadtxt(testfile) nmatrices = testdata.shape[0] // 4 for i in range(nmatrices): x = testdata[i * 4:i * 4 + 4, 0:4] invx = testdata[i * 4:i * 4 + 4, 4:8] result = affine.invert(x) assert np.all(np.isclose(invx, result))
def transformCoords(self, coords, from_, to, *args, **kwargs): """Transforms the given ``coords`` from ``from_`` to ``to``. :arg coords: Coordinates to transform. :arg from_: Space that the coordinates are in :arg to: Space to transform the coordinates to All other parameters are passed through to the :meth:`.NiftiOpts.transformCoords` method of the reference image ``DisplayOpts``. The following values are accepted for the ``from_`` and ``to`` parameters: - ``'world'``: World coordinate system - ``'display'`` Display coordinate system - ``'mesh'`` The coordinate system of this mesh. - ``'voxel'``: The voxel coordinate system of the reference image - ``'id'``: Equivalent to ``'voxel'``. """ nfrom_ = self.normaliseSpace(from_) nto = self.normaliseSpace(to) ref = self.refImage pre = None post = None if ref is None: return coords if from_ == 'mesh' and self.coordSpace == 'torig': pre = affine.concat(ref.getAffine('voxel', 'world'), affine.invert(fslmgh.voxToSurfMat(ref))) if to == 'mesh' and self.coordSpace == 'torig': post = affine.concat(fslmgh.voxToSurfMat(ref), ref.getAffine('world', 'voxel')) opts = self.displayCtx.getOpts(ref) return opts.transformCoords(coords, nfrom_, nto, pre=pre, post=post, **kwargs)
def updateVertices(self, *a): """Called by :meth:`__init__`, and when certain display properties change. (Re-)generates the mesh vertices, indices and normals (if being displayed in 3D). They are stored as attributes called ``vertices``, ``indices``, and ``normals`` respectively. """ overlay = self.overlay opts = self.opts threedee = self.threedee vertices = overlay.vertices indices = overlay.indices normals = self.overlay.vnormals vdata = opts.getVertexData('vertex') xform = opts.getTransform('mesh', 'display') if not np.all(np.isclose(xform, np.eye(4))): vertices = affine.transform(vertices, xform) if self.threedee: nmat = affine.invert(xform).T normals = affine.transform(normals, nmat, vector=True) self.origIndices = indices indices = np.asarray(indices.flatten(), dtype=np.uint32) # If flatShading is active, we cannot share # vertices between triangles, so we generate # a set of unique vertices for each triangle, # and then re-generate the triangle indices. # The original indices are saved above, as # they will be used by the getVertexData # method to duplicate the vertex data. if threedee and (vdata is not None) and opts.flatShading: self.vertices = vertices[indices].astype(np.float32) self.indices = np.arange(0, len(self.vertices), dtype=np.uint32) normals = normals[indices, :] else: self.vertices = np.asarray(vertices, dtype=np.float32) self.indices = indices self.vertices = dutils.makeWriteable(self.vertices) self.indices = dutils.makeWriteable(self.indices) if self.threedee: self.normals = np.array(normals, dtype=np.float32)
def test_applyDeformation(): src2ref = affine.compose(np.random.randint(2, 5, 3), np.random.randint(1, 10, 3), np.random.random(3)) ref2src = affine.invert(src2ref) srcdata = np.random.randint(1, 65536, (10, 10, 10)) refdata = np.random.randint(1, 65536, (10, 10, 10)) src = fslimage.Image(srcdata) ref = fslimage.Image(refdata, xform=src2ref) field = _affine_field(src, ref, ref2src, 'world', 'world') expect, xf = resample.resampleToReference(src, ref, matrix=src2ref, order=1, mode='nearest') result = nonlinear.applyDeformation(src, field, order=1, mode='nearest') assert np.all(np.isclose(expect, result))
def __setViewport(self): """Called by :meth:`_draw`. Configures the viewport and calculates the model-view trasformation matrix. :returns: ``True`` if the viewport was successfully configured, ``False`` otherwise. """ width, height = self.GetScaledSize() b = self.__displayCtx.bounds blo = [b.xlo, b.ylo, b.zlo] bhi = [b.xhi, b.yhi, b.zhi] zoom = self.opts.zoom / 100.0 if width == 0 or height == 0: return False # We allow one dimension to be # flat, so we can display 2D # meshes (e.g. flattened surfaces) if np.sum(np.isclose(blo, bhi)) > 1: return False # Generate the view and projection matrices self.__genViewMatrix(width, height) projmat, viewport = glroutines.ortho(blo, bhi, width, height, zoom) self.__projMat = projmat self.__viewport = viewport self.__invViewProjMat = affine.concat(self.__projMat, self.__viewMat) self.__invViewProjMat = affine.invert(self.__invViewProjMat) gl.glViewport(0, 0, width, height) gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadMatrixf(self.__projMat.ravel('F')) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glLoadIdentity() return True
def test_rescale(): with pytest.raises(ValueError): affine.rescale((5, 5), (10, 10, 10)) assert np.all(affine.rescale((5, 5), (5, 5)) == np.eye(3)) assert np.all(affine.rescale((5, 5, 5), (5, 5, 5)) == np.eye(4)) assert np.all(affine.rescale((5, 5, 5, 5), (5, 5, 5, 5)) == np.eye(5)) # (old shape, new shape, origin, expect) tests = [ ((5, 5), (10, 10), 'centre', np.array([[0.5, 0, 0], [0, 0.5, 0], [0, 0, 1]])), ((5, 5), (10, 10), 'corner', np.array([[0.5, 0, -0.25], [0, 0.5, -0.25], [0, 0, 1]])), ((5, 5, 5), (10, 10, 10), 'centre', np.array([[0.5, 0, 0, 0], [0, 0.5, 0, 0], [0, 0, 0.5, 0], [0, 0, 0, 1]])), ((5, 5, 5), (10, 10, 10), 'corner', np.array([[0.5, 0, 0, -0.25], [0, 0.5, 0, -0.25], [0, 0, 0.5, -0.25], [0, 0, 0, 1]])), ((5, 5, 5, 5), (10, 10, 10, 10), 'centre', np.array([[0.5, 0, 0, 0, 0], [0, 0.5, 0, 0, 0], [0, 0, 0.5, 0, 0], [0, 0, 0, 0.5, 0], [0, 0, 0, 0, 1]])), ((5, 5, 5, 5), (10, 10, 10, 10), 'corner', np.array([[0.5, 0, 0, 0, -0.25], [0, 0.5, 0, 0, -0.25], [0, 0, 0.5, 0, -0.25], [0, 0, 0, 0.5, -0.25], [0, 0, 0, 0, 1]])), ] for oldshape, newshape, origin, expect in tests: got = affine.rescale(oldshape, newshape, origin) assert np.all(np.isclose(got, expect)) got = affine.rescale(newshape, oldshape, origin) assert np.all(np.isclose(got, affine.invert(expect)))
def invTexCoordXform(self, origShape): """Returns the inverse of :meth:`texCoordXform`. """ return affine.invert(self.texCoordXform(origShape))
def draw3D(self, *args, **kwargs): """Calls the version dependent ``draw3D`` function. """ opts = self.opts w, h = self.canvas.GetScaledSize() res = self.opts.resolution / 100.0 sw = int(np.ceil(w * res)) sh = int(np.ceil(h * res)) # Initialise and resize # the offscreen textures for rt in [self.renderTexture1, self.renderTexture2]: if rt.shape != (sw, sh): rt.shape = sw, sh with rt.target(): gl.glClearColor(0, 0, 0, 0) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) if opts.resolution != 100: gl.glViewport(0, 0, sw, sh) # Do the render. Even though we're # drawing off-screen, we need to # enable depth-testing, otherwise # depth values will not get written # to the depth buffer! # # The glvolume_funcs.draw3D function # will put the final render into # renderTexture1 with glroutines.enabled((gl.GL_DEPTH_TEST, gl.GL_CULL_FACE)): gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) gl.glFrontFace(gl.GL_CCW) gl.glCullFace(gl.GL_BACK) fslgl.glvolume_funcs.draw3D(self, *args, **kwargs) # Apply smoothing if needed. If smoothing # is enabled, the final final render will # be in renderTexture2 if opts.smoothing > 0: self.smoothFilter.set(offsets=[1.0 / sw, 1.0 / sh]) self.smoothFilter.osApply(self.renderTexture1, self.renderTexture2) # We now have the final result # - draw it to the screen. verts = np.array([[-1, -1, 0], [-1, 1, 0], [ 1, -1, 0], [ 1, -1, 0], [-1, 1, 0], [ 1, 1, 0]], dtype=np.float32) invproj = affine.invert(self.canvas.projectionMatrix) verts = affine.transform(verts, invproj) if opts.resolution != 100: gl.glViewport(0, 0, w, h) with glroutines.enabled(gl.GL_DEPTH_TEST): # If smoothing was not applied, rt1 # contains the final render. Otherwise, # rt2 contains the final render, but rt1 # contains the depth information. So we # need to # temporarily replace rt2.depth # with rt1.depth. if opts.smoothing > 0: src = self.renderTexture2 olddep = self.renderTexture2.depthTexture dep = self.renderTexture1.depthTexture else: src = self.renderTexture1 olddep = self.renderTexture1.depthTexture dep = olddep src.depthTexture = dep src.draw(verts, useDepth=True) src.depthTexture = olddep
def __setupTransforms(self): """Calculates transformation matrices between all of the possible spaces in which the overlay may be displayed. These matrices are accessible via the :meth:`getTransform` method. """ image = self.overlay shape = np.array(image.shape[:3]) voxToIdMat = np.eye(4) voxToPixdimMat = np.diag(list(image.pixdim[:3]) + [1.0]) voxToPixFlipMat = image.voxToScaledVoxMat voxToWorldMat = image.voxToWorldMat voxToWorldMat = affine.concat(self.displayXform, voxToWorldMat) ds = self.displayCtx.displaySpace # The reference transforms depend # on the value of displaySpace if ds == 'world': voxToRefMat = voxToWorldMat elif ds is self.overlay: voxToRefMat = voxToPixFlipMat else: voxToRefMat = affine.concat(ds.voxToScaledVoxMat, ds.worldToVoxMat, voxToWorldMat) # When going from voxels to textures, # we add 0.5 to centre the voxel (see # the note on coordinate systems at # the top of this file). voxToTexMat = affine.scaleOffsetXform(tuple(1.0 / shape), tuple(0.5 / shape)) idToVoxMat = affine.invert(voxToIdMat) idToPixdimMat = affine.concat(voxToPixdimMat, idToVoxMat) idToPixFlipMat = affine.concat(voxToPixFlipMat, idToVoxMat) idToWorldMat = affine.concat(voxToWorldMat, idToVoxMat) idToRefMat = affine.concat(voxToRefMat, idToVoxMat) idToTexMat = affine.concat(voxToTexMat, idToVoxMat) pixdimToVoxMat = affine.invert(voxToPixdimMat) pixdimToIdMat = affine.concat(voxToIdMat, pixdimToVoxMat) pixdimToPixFlipMat = affine.concat(voxToPixFlipMat, pixdimToVoxMat) pixdimToWorldMat = affine.concat(voxToWorldMat, pixdimToVoxMat) pixdimToRefMat = affine.concat(voxToRefMat, pixdimToVoxMat) pixdimToTexMat = affine.concat(voxToTexMat, pixdimToVoxMat) pixFlipToVoxMat = affine.invert(voxToPixFlipMat) pixFlipToIdMat = affine.concat(voxToIdMat, pixFlipToVoxMat) pixFlipToPixdimMat = affine.concat(voxToPixdimMat, pixFlipToVoxMat) pixFlipToWorldMat = affine.concat(voxToWorldMat, pixFlipToVoxMat) pixFlipToRefMat = affine.concat(voxToRefMat, pixFlipToVoxMat) pixFlipToTexMat = affine.concat(voxToTexMat, pixFlipToVoxMat) worldToVoxMat = affine.invert(voxToWorldMat) worldToIdMat = affine.concat(voxToIdMat, worldToVoxMat) worldToPixdimMat = affine.concat(voxToPixdimMat, worldToVoxMat) worldToPixFlipMat = affine.concat(voxToPixFlipMat, worldToVoxMat) worldToRefMat = affine.concat(voxToRefMat, worldToVoxMat) worldToTexMat = affine.concat(voxToTexMat, worldToVoxMat) refToVoxMat = affine.invert(voxToRefMat) refToIdMat = affine.concat(voxToIdMat, refToVoxMat) refToPixdimMat = affine.concat(voxToPixdimMat, refToVoxMat) refToPixFlipMat = affine.concat(voxToPixFlipMat, refToVoxMat) refToWorldMat = affine.concat(voxToWorldMat, refToVoxMat) refToTexMat = affine.concat(voxToTexMat, refToVoxMat) texToVoxMat = affine.invert(voxToTexMat) texToIdMat = affine.concat(voxToIdMat, texToVoxMat) texToPixdimMat = affine.concat(voxToPixdimMat, texToVoxMat) texToPixFlipMat = affine.concat(voxToPixFlipMat, texToVoxMat) texToWorldMat = affine.concat(voxToWorldMat, texToVoxMat) texToRefMat = affine.concat(voxToRefMat, texToVoxMat) self.__xforms['id', 'id'] = np.eye(4) self.__xforms['id', 'pixdim'] = idToPixdimMat self.__xforms['id', 'pixdim-flip'] = idToPixFlipMat self.__xforms['id', 'affine'] = idToWorldMat self.__xforms['id', 'reference'] = idToRefMat self.__xforms['id', 'texture'] = idToTexMat self.__xforms['pixdim', 'pixdim'] = np.eye(4) self.__xforms['pixdim', 'id'] = pixdimToIdMat self.__xforms['pixdim', 'pixdim-flip'] = pixdimToPixFlipMat self.__xforms['pixdim', 'affine'] = pixdimToWorldMat self.__xforms['pixdim', 'reference'] = pixdimToRefMat self.__xforms['pixdim', 'texture'] = pixdimToTexMat self.__xforms['pixdim-flip', 'pixdim-flip'] = np.eye(4) self.__xforms['pixdim-flip', 'id'] = pixFlipToIdMat self.__xforms['pixdim-flip', 'pixdim'] = pixFlipToPixdimMat self.__xforms['pixdim-flip', 'affine'] = pixFlipToWorldMat self.__xforms['pixdim-flip', 'reference'] = pixFlipToRefMat self.__xforms['pixdim-flip', 'texture'] = pixFlipToTexMat self.__xforms['affine', 'affine'] = np.eye(4) self.__xforms['affine', 'id'] = worldToIdMat self.__xforms['affine', 'pixdim'] = worldToPixdimMat self.__xforms['affine', 'pixdim-flip'] = worldToPixFlipMat self.__xforms['affine', 'reference'] = worldToRefMat self.__xforms['affine', 'texture'] = worldToTexMat self.__xforms['reference', 'reference'] = np.eye(4) self.__xforms['reference', 'id'] = refToIdMat self.__xforms['reference', 'pixdim'] = refToPixdimMat self.__xforms['reference', 'pixdim-flip'] = refToPixFlipMat self.__xforms['reference', 'affine'] = refToWorldMat self.__xforms['reference', 'texture'] = refToTexMat self.__xforms['texture', 'texture'] = np.eye(4) self.__xforms['texture', 'id'] = texToIdMat self.__xforms['texture', 'pixdim'] = texToPixdimMat self.__xforms['texture', 'pixdim-flip'] = texToPixFlipMat self.__xforms['texture', 'affine'] = texToWorldMat self.__xforms['texture', 'reference'] = texToRefMat
def calculateRayCastSettings(self, view=None, proj=None): """Calculates various parameters required for 3D ray-cast rendering (see the :class:`.GLVolume` class). :arg view: Transformation matrix which transforms from model coordinates to view coordinates (i.e. the GL view matrix). :arg proj: Transformation matrix which transforms from view coordinates to normalised device coordinates (i.e. the GL projection matrix). Returns a tuple containing: - A vector defining the amount by which to move along a ray in a single iteration of the ray-casting algorithm. This can be added directly to the volume texture coordinates. - A transformation matrix which transforms from image texture coordinates into the display coordinate system. .. note:: This method will raise an error if called on a ``GLImageObject`` which is managing an overlay that is not associated with a :class:`.Volume3DOpts` instance. """ if view is None: view = np.eye(4) if proj is None: proj = np.eye(4) # In GL, the camera position # is initially pointing in # the -z direction. eye = [0, 0, -1] target = [0, 0, 1] # We take this initial camera # configuration, and transform # it by the inverse modelview # matrix t2dmat = self.getTransform('texture', 'display') xform = affine.concat(view, t2dmat) ixform = affine.invert(xform) eye = affine.transform(eye, ixform, vector=True) target = affine.transform(target, ixform, vector=True) # Direction that the 'camera' is # pointing, normalied to unit length cdir = affine.normalise(eye - target) # Calculate the length of one step # along the camera direction in a # single iteration of the ray-cast # loop. Multiply by sqrt(3) so that # the maximum number of steps will # be reached across the longest axis # of the image texture cube. rayStep = np.sqrt(3) * cdir / self.getNumSteps() # A transformation matrix which can # transform image texture coordinates # into the corresponding screen # (normalised device) coordinates. # This allows the fragment shader to # convert an image texture coordinate # into a relative depth value. # # The projection matrix puts depth into # [-1, 1], but we want it in [0, 1] zscale = affine.scaleOffsetXform([1, 1, 0.5], [0, 0, 0.5]) xform = affine.concat(zscale, proj, xform) return rayStep, xform
def test_nonlinear_altsrc(seed): with tempdir.tempdir(): src2ref = _random_affine() ref2src = affine.invert(src2ref) src = _random_image(np.eye(4), (20, 20, 20)) ref = _random_image(src2ref, (20, 20, 20)) field = _affine_field(src, ref, ref2src, 'world', 'world') x5.writeNonLinearX5('xform.x5', field) src.save('src') ref.save('ref') # use origin=corner so that the # resampled variants are exactly # aligned in the world coordinate # system srclo, xf = resample.resample(src, (10, 10, 10), origin='corner', smooth=False) srclo = fslimage.Image(srclo, xform=xf) srchi, xf = resample.resample(src, (40, 40, 40), origin='corner', smooth=False) srchi = fslimage.Image(srchi, xform=xf) srcoff = roi.roi(src, [(-10, 10), (-10, 10), (-10, 10)]) srclo.save('srclo') srchi.save('srchi') srcoff.save('srcoff') fsl_apply_x5.main('src xform.x5 out'.split()) fsl_apply_x5.main('srclo xform.x5 outlo'.split()) fsl_apply_x5.main('srchi xform.x5 outhi'.split()) fsl_apply_x5.main('srcoff xform.x5 outoff'.split()) out = fslimage.Image('out') outlo = fslimage.Image('outlo') outhi = fslimage.Image('outhi') outoff = fslimage.Image('outoff') exp, x1 = resample.resampleToReference(src, ref, matrix=src2ref, mode='constant', smooth=False) explo, x2 = resample.resampleToReference(srclo, ref, matrix=src2ref, mode='constant', smooth=False) exphi, x3 = resample.resampleToReference(srchi, ref, matrix=src2ref, mode='constant', smooth=False) expoff, x4 = resample.resampleToReference(srcoff, ref, matrix=src2ref, mode='constant', smooth=False) assert out.sameSpace(ref) assert outlo.sameSpace(ref) assert outhi.sameSpace(ref) assert outoff.sameSpace(ref) # We get boundary cropping, # so ignore edge slices out = out.data[1:-1, 1:-1, 1:-1] outlo = outlo.data[1:-1, 1:-1, 1:-1] outhi = outhi.data[1:-1, 1:-1, 1:-1] outoff = outoff.data[:9, :9, :9] exp = exp[1:-1, 1:-1, 1:-1] explo = explo[1:-1, 1:-1, 1:-1] exphi = exphi[1:-1, 1:-1, 1:-1] expoff = expoff[:9, :9, :9] tol = dict(atol=1e-3, rtol=1e-3) assert np.all(np.isclose(out, exp, **tol)) assert np.all(np.isclose(outlo, explo, **tol)) assert np.all(np.isclose(outhi, exphi, **tol)) assert np.all(np.isclose(outoff, expoff, **tol))