def lightPos(self): """Takes the values of :attr:`.Scene3DOpts.lightPos` and :attr:`.Scene3DOpts.lightDistance`, and converts it to a position in the display coordinate system. The ``Scene3DOpts.lightPos`` property contains rotations about the centre of the display bounding box, and the :attr:`.Scene3DOpts.lightDistance` property specifies the distance of the light from the bounding box centre. """ b = self.__displayCtx.bounds centre = np.array([ b.xlo + 0.5 * (b.xhi - b.xlo), b.ylo + 0.5 * (b.yhi - b.ylo), b.zlo + 0.5 * (b.zhi - b.zlo) ]) yaw, pitch, roll = self.opts.lightPos distance = self.opts.lightDistance yaw = yaw * np.pi / 180 pitch = pitch * np.pi / 180 roll = roll * np.pi / 180 rotmat = affine.axisAnglesToRotMat(pitch, roll, yaw) xform = affine.compose([1, 1, 1], [0, 0, 0], rotmat, origin=centre) lightPos = centre + [0, 0, distance * b.zlen] lightPos = affine.transform(lightPos, xform) return lightPos
def test_newImage(): shape = (10, 20, 30) pixdim = (1.3, -5.4, 3.2) dtype = np.uint8 affine = fslaffine.compose((2, 3, 4), (-20, 15, 25), (1.5, 1.2, -2.1)) img = newimage.newImage(shape, pixdim, dtype, affine) assert tuple( img.shape) == shape assert tuple( img.pixdim) == tuple(np.abs(pixdim)) assert img.dtype == dtype assert np.all(img.voxToWorldMat == affine) assert img.xyzUnits == constants.NIFTI_UNITS_MM assert img.timeUnits == constants.NIFTI_UNITS_SEC assert img.name == 'new' img = newimage.newImage(shape, pixdim, dtype, affine, xyzUnits=constants.NIFTI_UNITS_METER, timeUnits=constants.NIFTI_UNITS_MSEC, name='whaa') assert tuple( img.shape) == shape assert tuple( img.pixdim) == tuple(np.abs(pixdim)) assert img.dtype == dtype assert np.all(img.voxToWorldMat == affine) assert img.xyzUnits == constants.NIFTI_UNITS_METER assert img.timeUnits == constants.NIFTI_UNITS_MSEC assert img.name == 'whaa'
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_convert_flirt(): with tempdir.tempdir(): src = random_image() ref = random_image() src.save('src') ref.save('ref') xform = affine.compose(np.random.randint(1, 10, 3), np.random.randint(-100, 100, 3), (np.random.random(3) - 0.5) * np.pi) np.savetxt('src2ref.mat', xform) fsl_convert_x5.main('flirt -s src -r ref ' 'src2ref.mat src2ref.x5'.split()) expxform = affine.concat(ref.getAffine('fsl', 'world'), xform, src.getAffine('world', 'fsl')) gotxform, gotsrc, gotref = x5.readLinearX5('src2ref.x5') assert np.all(np.isclose(gotxform, expxform)) assert src.sameSpace(gotsrc) assert ref.sameSpace(gotref) fsl_convert_x5.main('flirt src2ref.x5 src2ref_copy.mat'.split()) gotxform = flirt.readFlirt('src2ref_copy.mat') assert np.all(np.isclose(gotxform, xform))
def _random_image(): vx, vy, vz = np.random.randint(10, 50, 3) dx, dy, dz = np.random.randint(1, 10, 3) data = (np.random.random((vx, vy, vz)) - 0.5) * 10 aff = affine.compose((dx, dy, dz), np.random.randint(1, 100, 3), np.random.random(3) * np.pi / 2) return fslimage.Image(data, xform=aff)
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_toFlirt(): src = affine.compose(np.random.randint(1, 5, 3), np.random.randint(-20, 20, 3), np.random.random(3) - 0.5) ref = affine.compose(np.random.randint(1, 5, 3), np.random.randint(-20, 20, 3), np.random.random(3) - 0.5) src = fslimage.Image(make_random_image(xform=src)) ref = fslimage.Image(make_random_image(xform=ref)) flirtmat = affine.concat(ref.getAffine('world', 'fsl'), src.getAffine('fsl', 'world')) spaces = it.permutations(('voxel', 'fsl', 'world'), 2) for from_, to in spaces: xform = affine.concat(ref.getAffine('world', to), src.getAffine(from_, 'world')) got = flirt.toFlirt(xform, src, ref, from_, to) assert np.all(np.isclose(got, flirtmat))
def oblique(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_oblique.nii.gz'.format(basename) img = fslimage.Image(infile) xform = img.getAffine('voxel', 'world') rot = affine.compose([1, 1, 1], [0, 0, 0], [0, 0, 1]) xform = affine.concat(rot, xform) img = fslimage.Image(img.data, xform=xform) img.save(outfile) return outfile
def __onPixdim(self, ev): """Called when any pixdim widget changes. If linking is enabled, reconstructs the affine with the new pixdim values. """ if not self.link: return # If we construct an affine with # scales == 0, we get explosions if any([p == 0 for p in self.pixdim]): return offsets, rotations = fslaffine.decompose(self.affine)[1:] self.affine = fslaffine.compose(self.pixdim, offsets, rotations)
def test_transform_vector(seed): # Some transform with a # translation component xform = affine.compose([1, 2, 3], [5, 10, 15], [np.pi / 2, np.pi / 2, 0]) vecs = np.random.random((20, 3)) for v in vecs: vecExpected = np.dot(xform, list(v) + [0])[:3] ptExpected = np.dot(xform, list(v) + [1])[:3] vecResult = affine.transform(v, xform, vector=True) vec33Result = affine.transform(v, xform[:3, :3], vector=True) ptResult = affine.transform(v, xform, vector=False) assert np.all(np.isclose(vecExpected, vecResult)) assert np.all(np.isclose(vecExpected, vec33Result)) assert np.all(np.isclose(ptExpected, ptResult))
def test_transformNormal(seed): normals = -100 + 200 * np.random.random((50, 3)) def tn(n, xform): xform = npla.inv(xform[:3, :3]).T return np.dot(xform, n) for n in normals: scales = -10 + np.random.random(3) * 10 offsets = -100 + np.random.random(3) * 200 rotations = -np.pi + np.random.random(3) * 2 * np.pi origin = -100 + np.random.random(3) * 200 xform = affine.compose(scales, offsets, rotations, origin) expected = tn(n, xform) result = affine.transformNormal(n, xform) assert np.all(np.isclose(expected, result))
def _test_NewImageAction_existing(panel, overlayList, displayCtx): act = panel.frame.menuActions[newimage.NewImageAction] img = newimage.newImage((20, 30, 40), (1.5, 1, 2.5), np.int32, fslaffine.compose((-1.5, 1, 2.5), (-20, 30, 40), (1.5, 1.2, -1.5))) overlayList.append(img) realYield() with mock.patch('fsleyes.actions.newimage.NewImageDialog', MockNewImageDialog): MockNewImageDialog.ShowModalRet = wx.ID_OK MockNewImageDialog.initOverride = True act() realYield() assert len(overlayList) == 2 old, new = overlayList assert old.sameSpace(new) assert new.dtype == np.int32
def test_readWriteLinearX5(): with tempdir.tempdir(): make_random_image('src.nii') make_random_image('ref.nii') xform = affine.compose(np.random.randint(1, 5, 3), np.random.randint(-10, 10, 3), -np.pi / 4 + np.random.random(3) * np.pi / 2) src = fslimage.Image('src.nii') ref = fslimage.Image('ref.nii') x5.writeLinearX5('linear.x5', xform, src, ref) gotxform, gotsrc, gotref = x5.readLinearX5('linear.x5') assert np.all(np.isclose(gotxform, xform)) assert gotsrc.sameSpace(src) assert gotref.sameSpace(ref) with h5py.File('linear.x5', 'r') as f: _check_metadata(f) assert f.attrs['Type'] == 'linear' _check_affine(f['/Transform'], xform) _check_space(f['/A'], src) _check_space(f['/B'], ref)
def _test_NewImageAction(panel, overlayList, displayCtx): act = panel.frame.menuActions[newimage.NewImageAction] def check(ovl, shape, pixdim, dtype, affine): assert tuple( ovl.shape) == tuple(shape) assert tuple( ovl.pixdim) == tuple(pixdim) assert ovl.dtype == dtype assert np.all(ovl.voxToWorldMat == affine) tests = [ ((100, 100, 100), (1, 1, 1), np.float32, np.eye(4)), (( 50, 50, 50), (2, 2, 2), np.uint8, np.diag([2, 2, 2, 1])), (( 20, 30, 40), (1.5, 1.2, 1.3), np.int32, fslaffine.scaleOffsetXform([2, 3, 4], [-4, -3, -2])), ((100, 100, 100), (1, 1, 1), np.float64, fslaffine.compose((2, 3, 1), (1, 2, 3), (1, 1.5, 2))), ] with mock.patch('fsleyes.actions.newimage.NewImageDialog', MockNewImageDialog): MockNewImageDialog.ShowModalRet = wx.ID_CANCEL MockNewImageDialog.initOverride = False act() realYield() assert len(overlayList) == 0 MockNewImageDialog.ShowModalRet = wx.ID_OK for shape, pixdim, dtype, affine in tests: MockNewImageDialog.shapeRet = shape MockNewImageDialog.pixdimRet = pixdim MockNewImageDialog.dtypeRet = dtype MockNewImageDialog.affineRet = affine act() realYield() assert len(overlayList) == 1 check(overlayList[0], shape, pixdim, dtype, affine) overlayList.clear() realYield()
def test_convert_flirt_sameformat(): with tempdir.tempdir(): src = random_image() ref = random_image() src.save('src') ref.save('ref') xform = affine.compose(np.random.randint(1, 10, 3), np.random.randint(-100, 100, 3), (np.random.random(3) - 0.5) * np.pi) np.savetxt('src2ref.mat', xform) # test both .mat and .x5 fsl_convert_x5.main('flirt -s src -r ref ' 'src2ref.mat src2ref.x5'.split()) # mat -> mat fsl_convert_x5.main('flirt -s src -r ref ' 'src2ref.mat copy.mat'.split()) # x5 -> x5 fsl_convert_x5.main('flirt -s src -r ref ' 'src2ref.x5 copy.x5'.split()) with open('src2ref.mat', 'rb') as f: origmat = hashlib.md5(f.read()).digest() with open('copy.mat', 'rb') as f: copymat = hashlib.md5(f.read()).digest() with open('src2ref.x5', 'rb') as f: origx5 = hashlib.md5(f.read()).digest() with open('copy.x5', 'rb') as f: copyx5 = hashlib.md5(f.read()).digest() assert origmat == copymat assert origx5 == copyx5
def random_affine(): return compose(np.random.randint(1, 10, 3), np.random.randint(-50, 50, 3), np.random.randint(-3, 3, 3))
def test_compose_and_decompose(): testfile = op.join(datadir, 'test_transform_test_compose.txt') lines = readlines(testfile) ntests = len(lines) // 4 for i in range(ntests): xform = lines[i * 4:i * 4 + 4] xform = np.genfromtxt(xform) scales, offsets, rotations, shears = affine.decompose(xform, shears=True) result = affine.compose(scales, offsets, rotations, shears=shears) assert np.all(np.isclose(xform, result, atol=1e-5)) # The decompose function does not support a # different rotation origin, but we test # explicitly passing the origin for # completeness scales, offsets, rotations, shears = affine.decompose(xform, shears=True) result = affine.compose(scales, offsets, rotations, origin=[0, 0, 0], shears=shears) assert np.all(np.isclose(xform, result, atol=1e-5)) # compose should also accept a rotation matrix rots = [np.pi / 5, np.pi / 4, np.pi / 3] rmat = affine.axisAnglesToRotMat(*rots) xform = affine.compose([1, 1, 1], [0, 0, 0], rmat) # And the angles flag should cause decompose # to return the rotation matrix, instead of # the axis angls sc, of, rot = affine.decompose(xform) scat, ofat, rotat = affine.decompose(xform, angles=True) scaf, ofaf, rotaf = affine.decompose(xform, angles=False) sc, of, rot = np.array(sc), np.array(of), np.array(rot) scat, ofat, rotat = np.array(scat), np.array(ofat), np.array(rotat) scaf, ofaf, rotaf = np.array(scaf), np.array(ofaf), np.array(rotaf) assert np.all(np.isclose(sc, [1, 1, 1])) assert np.all(np.isclose(of, [0, 0, 0])) assert np.all(np.isclose(scat, [1, 1, 1])) assert np.all(np.isclose(ofat, [0, 0, 0])) assert np.all(np.isclose(scaf, [1, 1, 1])) assert np.all(np.isclose(ofaf, [0, 0, 0])) assert np.all(np.isclose(rot, rots)) assert np.all(np.isclose(rotat, rots)) assert np.all(np.isclose(rotaf, rmat)) # decompose should accept a 3x3 # affine, and return translations of 0 affine.decompose(xform[:3, :3]) sc, of, rot = affine.decompose(xform[:3, :3]) sc, of, rot = np.array(sc), np.array(of), np.array(rot) assert np.all(np.isclose(sc, [1, 1, 1])) assert np.all(np.isclose(of, [0, 0, 0])) assert np.all(np.isclose(rot, rots))
def _random_affine(): return affine.compose(np.random.randint(2, 5, 3), np.random.randint(1, 10, 3), np.random.random(3))
def __drawLegend(self): """Draws a legend in the bottom left corner of the screen, showing anatomical orientation. """ copts = self.opts b = self.__displayCtx.bounds w, h = self.GetSize() xlen, ylen = glroutines.adjust(b.xlen, b.ylen, w, h) # A line for each axis vertices = np.zeros((6, 3), dtype=np.float32) vertices[0, :] = [-1, 0, 0] vertices[1, :] = [1, 0, 0] vertices[2, :] = [0, -1, 0] vertices[3, :] = [0, 1, 0] vertices[4, :] = [0, 0, -1] vertices[5, :] = [0, 0, 1] # Each axis line is scaled to # 60 pixels, and the legend is # offset from the bottom-left # corner by twice this amount. scale = [xlen * 30.0 / w] * 3 offset = [ -0.5 * xlen + 2.0 * scale[0], -0.5 * ylen + 2.0 * scale[1], 0 ] # Apply the current camera # angle and rotation settings # to the legend vertices. Offset # anatomical labels off each # axis line by a small amount. rotation = affine.decompose(self.__viewMat)[2] xform = affine.compose(scale, offset, rotation) labelPoses = affine.transform(vertices * 1.2, xform) vertices = affine.transform(vertices, xform) # Draw the legend lines gl.glDisable(gl.GL_DEPTH_TEST) gl.glColor3f(*copts.cursorColour[:3]) gl.glLineWidth(2) gl.glBegin(gl.GL_LINES) gl.glVertex3f(*vertices[0]) gl.glVertex3f(*vertices[1]) gl.glVertex3f(*vertices[2]) gl.glVertex3f(*vertices[3]) gl.glVertex3f(*vertices[4]) gl.glVertex3f(*vertices[5]) gl.glEnd() canvas = np.array([w, h]) view = np.array([xlen, ylen]) # Draw each label for i, label in enumerate(self.__legendLabels): # Calculate pixel x/y # location for this label xx, xy = canvas * (labelPoses[i, :2] + 0.5 * view) / view label.xpix = xx label.ypix = xy label.draw(w, h)
def __drawLegend(self): """Draws a legend in the bottom left corner of the screen, showing anatomical orientation. """ copts = self.opts b = self.__displayCtx.bounds w, h = self.GetSize() xlen, ylen = glroutines.adjust(b.xlen, b.ylen, w, h) # A line for each axis vertices = np.zeros((6, 3), dtype=np.float32) vertices[0, :] = [-1, 0, 0] vertices[1, :] = [ 1, 0, 0] vertices[2, :] = [ 0, -1, 0] vertices[3, :] = [ 0, 1, 0] vertices[4, :] = [ 0, 0, -1] vertices[5, :] = [ 0, 0, 1] # Each axis line is scaled to # 60 pixels, and the legend is # offset from the bottom-left # corner by twice this amount. scale = [xlen * 30.0 / w] * 3 offset = [-0.5 * xlen + 2.0 * scale[0], -0.5 * ylen + 2.0 * scale[1], 0] # Apply the current camera # angle and rotation settings # to the legend vertices. Offset # anatomical labels off each # axis line by a small amount. rotation = affine.decompose(self.__viewMat)[2] xform = affine.compose(scale, offset, rotation) labelPoses = affine.transform(vertices * 1.2, xform) vertices = affine.transform(vertices, xform) # Draw the legend lines gl.glDisable(gl.GL_DEPTH_TEST) gl.glColor3f(*copts.cursorColour[:3]) gl.glLineWidth(2) gl.glBegin(gl.GL_LINES) gl.glVertex3f(*vertices[0]) gl.glVertex3f(*vertices[1]) gl.glVertex3f(*vertices[2]) gl.glVertex3f(*vertices[3]) gl.glVertex3f(*vertices[4]) gl.glVertex3f(*vertices[5]) gl.glEnd() # Figure out the anatomical # labels for each axis. overlay = self.__displayCtx.getSelectedOverlay() dopts = self.__displayCtx.getOpts(overlay) labels = dopts.getLabels()[0] # getLabels returns (xlo, ylo, zlo, xhi, yhi, zhi) - # - rearrange them to (xlo, xhi, ylo, yhi, zlo, zhi) labels = [labels[0], labels[3], labels[1], labels[4], labels[2], labels[5]] canvas = np.array([w, h]) view = np.array([xlen, ylen]) # Draw each label for i in range(6): # Calculate pixel x/y # location for this label xx, xy = canvas * (labelPoses[i, :2] + 0.5 * view) / view # Calculate the size of the label # in pixels, so we can centre the # label tw, th = glroutines.text2D(labels[i], (xx, xy), 10, (w, h), calcSize=True) # Draw the text xx -= 0.5 * tw xy -= 0.5 * th gl.glColor3f(*copts.legendColour[:3]) glroutines.text2D(labels[i], (xx, xy), 10, (w, h))