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 prepareMask(self, mask): """Makes sure that the given mask has the same resolution as this atlas, so it can be used for querying. Used by the :meth:`.LabelAtlas.maskLabels` and :meth:`.StatisticAtlas.maskValues` methods. :arg mask: A :class:`.Image` :returns: A ``numpy`` array containing the resampled mask data. :raises: A :exc:`MaskError` if the mask is not in the same space as this atlas, or does not have three dimensions. """ # Make sure that the mask has the same # number of voxels as the atlas image. # Use nearest neighbour interpolation # for resampling, as it is most likely # that the mask is binary. try: mask, xform = resample.resample(mask, self.shape[:3], dtype=np.float32, order=0) except ValueError: raise MaskError('Mask has wrong number of dimensions') # TODO allow non-aligned mask - as long as it overlaps # in world coordinates, it should be allowed if not fslimage.Image(mask, xform=xform).sameSpace(self): raise MaskError('Mask is not in the same space as atlas') return mask
def test_prepareMask(): reg = atlases.registry reg.rescanAtlases() probatlas = reg.loadAtlas('harvardoxford-cortical') probsumatlas = reg.loadAtlas('harvardoxford-cortical', loadSummary=True) lblatlas = reg.loadAtlas('talairach') for atlas in [probatlas, probsumatlas, lblatlas]: ashape = list(atlas.shape[:3]) m2shape = [s * 1.5 for s in ashape] goodmask1 = fslimage.Image( np.array(np.random.random(ashape), dtype=np.float32), xform=atlas.voxToWorldMat) goodmask2, xf = resample.resample(goodmask1, m2shape) goodmask2 = fslimage.Image(goodmask2, xform=xf) wrongdims = fslimage.Image( np.random.random(list(ashape) + [2])) wrongspace = fslimage.Image( np.random.random((20, 20, 20)), xform=affine.concat(atlas.voxToWorldMat, np.diag([2, 2, 2, 1]))) with pytest.raises(atlases.MaskError): atlas.prepareMask(wrongdims) with pytest.raises(atlases.MaskError): atlas.prepareMask(wrongspace) assert list(atlas.prepareMask(goodmask1).shape) == ashape assert list(atlas.prepareMask(goodmask2).shape) == ashape
def test_linear_altsrc(seed): with tempdir.tempdir(): src2ref = _random_affine() src = _random_image(np.eye(4), (20, 20, 20)) ref = _random_image(src2ref) x5.writeLinearX5('xform.x5', src2ref, src, ref) src.save('src') ref.save('ref') srclo, xf = resample.resample(src, (10, 10, 10)) srclo = fslimage.Image(srclo, xform=xf) srchi, xf = resample.resample(src, (40, 40, 40)) 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 = resample.resampleToReference(src, ref, matrix=src2ref)[0] explo = resample.resampleToReference(srclo, ref, matrix=src2ref)[0] exphi = resample.resampleToReference(srchi, ref, matrix=src2ref)[0] expoff = resample.resampleToReference(srcoff, ref, matrix=src2ref)[0] assert out.sameSpace(ref) assert outlo.sameSpace(ref) assert outhi.sameSpace(ref) assert outoff.sameSpace(ref) assert np.all(np.isclose(out.data, exp)) assert np.all(np.isclose(outlo.data, explo)) assert np.all(np.isclose(outhi.data, exphi)) assert np.all(np.isclose(outoff.data, expoff))
def test_resample_4d(seed): # resample one volume img = fslimage.Image(make_random_image(dims=(10, 10, 10, 10))) slc = (slice(None), slice(None), slice(None), 3) resampled = resample.resample(img, img.shape[:3], slc)[0] assert np.all(resampled == img[..., 3]) # resample up resampled = resample.resample(img, (15, 15, 15), slc)[0] assert tuple(resampled.shape) == (15, 15, 15) # resample down resampled = resample.resample(img, (5, 5, 5), slc)[0] assert tuple(resampled.shape) == (5, 5, 5) # resample the entire image resampled = resample.resample(img, (15, 15, 15, 10), None)[0] assert tuple(resampled.shape) == (15, 15, 15, 10) resampled = resample.resample(img, (5, 5, 5, 10), None)[0] assert tuple(resampled.shape) == (5, 5, 5, 10) # resample along the fourth dim resampled = resample.resample(img, (15, 15, 15, 15), None)[0] assert tuple(resampled.shape) == (15, 15, 15, 15) resampled = resample.resample(img, (5, 5, 5, 15), None)[0] assert tuple(resampled.shape) == (5, 5, 5, 15)
def __resample(self): """Called when this ``ResampleAction`` is invoked. Shows a ``ResampleDialog``, and then resamples the currently selected overlay. """ ovl = self.displayCtx.getSelectedOverlay() opts = self.displayCtx.getOpts(ovl) def refCandidate(o): return (isinstance(o, fslimage.Nifti) and (o is not ovl) and not o.sameSpace(ovl)) refs = [o for o in self.overlayList if refCandidate(o)] dlg = ResampleDialog(self.__frame, title=ovl.name, shape=ovl.shape, pixdim=ovl.pixdim, refs=refs) if dlg.ShowModal() != wx.ID_OK: return newShape = dlg.GetVoxels() interp = dlg.GetInterpolation() origin = dlg.GetOrigin() dtype = dlg.GetDataType() smoothing = dlg.GetSmoothing() ref = dlg.GetReference() allvols = dlg.GetAllVolumes() interp = {'nearest': 0, 'linear': 1, 'cubic': 3}[interp] name = '{}_resampled'.format(ovl.name) if allvols or ovl.ndim == 3: slc = None else: slc = opts.index() if allvols and ovl.ndim > 3: newShape = list(newShape) + list(ovl.shape[3:]) kwargs = dict(sliceobj=slc, dtype=dtype, order=interp, origin=origin, smooth=smoothing) if ref is not None: resampled, xform = resample.resampleToReference(ovl, ref, **kwargs) else: resampled, xform = resample.resample(ovl, newShape, **kwargs) resampled = fslimage.Image(resampled, xform=xform, header=ovl.header, name=name) self.overlayList.append(resampled)
def test_resample_origin(seed): img = fslimage.Image(make_random_image(dims=(10, 10, 10))) # with origin='corner', image # bounding boxes should match for i in range(25): shape = np.random.randint(5, 50, 3) res = resample.resample(img, shape, origin='corner') res = fslimage.Image(res[0], xform=res[1]) imgb = affine.axisBounds(img.shape, img.voxToWorldMat) resb = affine.axisBounds(res.shape, res.voxToWorldMat) assert np.all(np.isclose(imgb, resb, rtol=1e-5, atol=1e-5)) # with origin='centre' image # bounding boxes should be offset # by (size_resampled - size_orig) / 2 for i in range(25): shape = np.random.randint(5, 50, 3) res = resample.resample(img, shape, origin='centre') res = fslimage.Image(res[0], xform=res[1]) off = (np.array(img.shape) / np.array(res.shape) - 1) / 2 imgb = np.array(affine.axisBounds(img.shape, img.voxToWorldMat)) resb = np.array(affine.axisBounds(res.shape, res.voxToWorldMat)) assert np.all(np.isclose(imgb, resb + off, rtol=1e-5, atol=1e-5)) # with origin='corner', using # linear interp, when we down- # sample an image to a shape # that divides evenly into the # original shape, a downsampled # voxel should equal the average # of the original voxels inside # it. res = resample.resample(img, (5, 5, 5), smooth=False, origin='corner')[0] for x, y, z in it.product(range(5), range(5), range(5)): block = img[x * 2:x * 2 + 2, y * 2:y * 2 + 2, z * 2:z * 2 + 2] assert np.isclose(res[x, y, z], block.mean())
def _gen_mask_query(atlas, use_label, q_type, q_in, res): maskres = int(q_type[-1]) maskfile = 'mask.nii.gz' a_img = _get_atlas(atlas, use_label, res) if q_in == 'out': make_random_mask(maskfile, (20, 20, 20), np.eye(4)) else: zmask = _get_zero_mask(a_img, atlas, use_label, res) if q_in == 'in': zmask = zmask == 0 mask = make_random_mask(maskfile, a_img.shape[:3], a_img.voxToWorldMat, zmask, minones=20) if maskres != res: zmask = ndi.binary_erosion(zmask, iterations=3) mask[zmask == 0] = 0 a = _get_atlas(atlas, True, maskres) # Make sure that when the mask gets # resampled into the atlas resolution, # it is still either in or out of the # atlas space mask, xform = resample.resample(mask, a.shape[:3], dtype=np.float32, order=1) thres = np.percentile(mask[mask > 0], 75) mask[mask >= thres] = 1 mask[mask < thres] = 0 mask = np.array(mask, dtype=np.uint8) mask = fslimage.Image(mask, xform=xform) mask.save(maskfile) return maskfile
def _gen_mask_query(atlas, qtype, qin, maskres): maskfile = 'mask.nii.gz' res = atlas.pixdim[0] if qin == 'out': make_random_mask(maskfile, (20, 20, 20), np.eye(4)) return maskfile zmask = _get_zero_mask(atlas) if qin == 'in': zmask = zmask == 0 mask = make_random_mask(maskfile, atlas.shape[:3], atlas.voxToWorldMat, zmask) # Make sure that when the mask gets # resampled into the atlas resolution, # it is still either in or out of the # atlas space if maskres != res: a = _get_atlas(atlas.desc.atlasID, maskres, True) a_zmask = _get_zero_mask(a) if qin == 'in': a_zmask = a_zmask == 0 # use linear interp and threshold # aggresively to make sure there # is no overlap between the different # resolutions mask, xform = resample.resample(mask, a.shape[:3], dtype=np.float32, order=1) mask[mask < 1.0] = 0 mask[a_zmask == 0] = 0 mask = np.array(mask, dtype=np.uint8) mask = fslimage.Image(mask, xform=xform) mask.save(maskfile) return maskfile
def _eval_mask_query(atlas, query, qtype, qin): mask = fslimage.Image(query) prob = atlas.desc.atlasType == 'probabilistic' maskres = mask.pixdim[0] res = atlas.pixdim[0] if maskres == res: rmask = mask[:] else: rmask = resample.resample(mask, atlas.shape[:3], dtype=np.float32, order=0)[0] rmask = np.array(rmask, dtype=np.bool) def evalLabel(): if qin == 'out': with pytest.raises(fslatlases.MaskError): atlas.maskLabel(mask) with pytest.raises(fslatlases.MaskError): atlas.label(mask) return if qin == 'in': voxels = np.array(np.where(rmask)).T maxval = int(atlas[:].max()) valcounts = np.zeros((maxval + 1, )) nvoxels = voxels.shape[0] for x, y, z in voxels: x, y, z = [int(v) for v in [x, y, z]] valcounts[int(atlas[x, y, z])] += 1.0 # make sure the values are sorted # according to their atlas ordering expvals = np.where(valcounts > 0)[0] explabels = [] # There may be more values in an image # than are listed in the atlas spec :( for v in expvals: try: explabels.append(atlas.find(value=int(v))) except KeyError: pass explabels = list(sorted(explabels)) expvals = [l.value for l in explabels] expprops = [100 * valcounts[v] / nvoxels for v in expvals] else: if prob: expvals = [] expprops = [] else: allvals = [l.value for l in atlas.desc.labels] if 0 in allvals: expvals = [0] expprops = [100] else: expvals = [] expprops = [] vals, props = atlas.label(mask) vals2, props2 = atlas.maskLabel(mask) assert np.all(np.isclose(vals, vals2)) assert np.all(np.isclose(props, props2)) assert np.all(np.isclose(vals, expvals)) assert np.all(np.isclose(props, expprops)) def evalProb(): if qin == 'out': with pytest.raises(fslatlases.MaskError): atlas.maskValues(mask) with pytest.raises(fslatlases.MaskError): atlas.values(mask) return props = atlas.values(mask) props2 = atlas.maskValues(mask) assert np.all(np.isclose(props, props2)) if isinstance(atlas, fslatlases.LabelAtlas): evalLabel() elif isinstance(atlas, fslatlases.ProbabilisticAtlas): evalProb()
def test_resample(seed): # Random base image shapes for i in range(25): shape = np.random.randint(5, 50, 3) img = fslimage.Image(make_random_image(dims=shape)) # bad shape with pytest.raises(ValueError): resample.resample(img, (10, 10)) with pytest.raises(ValueError): resample.resample(img, (10, 10, 10, 10)) # resampling to the same shape should be a no-op samei, samex = resample.resample(img, shape) assert np.all(samei == img[:]) assert np.all(samex == img.voxToWorldMat) # Random resampled image shapes for j in range(10): rshape = np.random.randint(5, 50, 3) resampled, xf = resample.resample(img, rshape, order=0) assert tuple(resampled.shape) == tuple(rshape) # We used nearest neighbour interp, so the # values in the resampled image should match # corresponding values in the original. Let's # check some whynot. restestcoords = np.array([ np.random.randint(0, rshape[0], 100), np.random.randint(0, rshape[1], 100), np.random.randint(0, rshape[2], 100) ]).T resx, resy, resz = restestcoords.T resvals = resampled[resx, resy, resz] res2orig = affine.concat(img.worldToVoxMat, xf) origtestcoords = affine.transform(restestcoords, res2orig) # remove any coordinates which are out of # bounds in the original image space, or # are right on a voxel boundary (where the # nn interp could have gone either way), or # have value == 0 in the resampled space. out = ((origtestcoords < 0) | (origtestcoords >= shape - 0.5) | (np.isclose(np.modf(origtestcoords)[0], 0.5))) out = np.any(out, axis=1) | (resvals == 0) origtestcoords = np.array(origtestcoords.round(), dtype=int) origtestcoords = origtestcoords[~out, :] restestcoords = restestcoords[~out, :] resx, resy, resz = restestcoords.T origx, origy, origz = origtestcoords.T origvals = img[:][origx, origy, origz] resvals = resampled[resx, resy, resz] assert np.all(np.isclose(resvals, origvals))
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))