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_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_roi(): # inshape, bounds, expected outshape, expected affine offset tests = [ # 3D image, 3D roi ([10, 10, 10], [(0, 10), (0, 10), (0, 10)], [10, 10, 10], [0, 0, 0]), ([10, 10, 10], [(1, 10), (1, 10), (1, 10)], [9, 9, 9], [1, 1, 1]), ([10, 10, 10], [(1, 9), (1, 9), (1, 9)], [8, 8, 8], [1, 1, 1]), ([10, 10, 10], [(3, 5), (3, 5), (3, 5)], [2, 2, 2], [3, 3, 3]), ([10, 10, 10], [(4, 5), (4, 5), (4, 5)], [1, 1, 1], [4, 4, 4]), # 4D image, 3D roi ([10, 10, 10, 10], [(0, 10), (0, 10), (0, 10)], [10, 10, 10, 10], [0, 0, 0]), ([10, 10, 10, 10], [(1, 10), (1, 10), (1, 10)], [9, 9, 9, 10], [1, 1, 1]), ([10, 10, 10, 10], [(1, 9), (1, 9), (1, 9)], [8, 8, 8, 10], [1, 1, 1]), ([10, 10, 10, 10], [(3, 5), (3, 5), (3, 5)], [2, 2, 2, 10], [3, 3, 3]), ([10, 10, 10, 10], [(4, 5), (4, 5), (4, 5)], [1, 1, 1, 10], [4, 4, 4]), # 4D image, 4D roi ([10, 10, 10, 10], [(0, 10), (0, 10), (0, 10), (0, 10)], [10, 10, 10, 10], [0, 0, 0]), ([10, 10, 10, 10], [(1, 10), (1, 10), (1, 10), (1, 10)], [9, 9, 9, 9], [1, 1, 1]), ([10, 10, 10, 10], [(1, 9), (1, 9), (1, 9), (1, 9)], [8, 8, 8, 8], [1, 1, 1]), ([10, 10, 10, 10], [(3, 5), (3, 5), (3, 5), (3, 5)], [2, 2, 2, 2], [3, 3, 3]), ([10, 10, 10, 10], [(4, 5), (4, 5), (4, 5), (4, 5)], [1, 1, 1], [4, 4, 4]), # expanding FOV ([10, 10, 10], [(-5, 15), (0, 10), (0, 10)], [20, 10, 10], [-5, 0, 0]), ([10, 10, 10], [(-5, 15), (-5, 15), (0, 10)], [20, 20, 10], [-5, -5, 0]), ([10, 10, 10], [(-5, 15), (-5, 10), (-5, 15)], [20, 15, 20], [-5, -5, -5]), ([10, 10, 10], [(-5, 15), (3, 7), (0, 10)], [20, 4, 10], [-5, 3, 0]), ] for inshape, bounds, outshape, offset in tests: data = np.random.randint(1, 10, inshape) image = fslimage.Image(data, xform=np.eye(4)) result = roi.roi(image, bounds) expaff = np.eye(4) expaff[:3, 3] = offset assert np.all(list(result.shape) == list(outshape)) assert np.all(np.isclose(result.voxToWorldMat, expaff)) oldslc = [] newslc = [] for (lo, hi), oldlen in zip(bounds, inshape): oldslc.append(slice(max(lo, 0), min(hi, oldlen))) if len(oldslc) < len(inshape): for d in inshape[len(oldslc):]: oldslc.append(slice(0, d)) for (lo, hi), slc in zip(bounds, oldslc): if lo < 0: newlo = -lo else: newlo = 0 oldlen = slc.stop - slc.start newslc.append(slice(newlo, newlo + oldlen)) if len(newslc) > len(outshape): newslc = newslc[:len(outshape)] assert np.all(data[tuple(oldslc)] == result.data[tuple(newslc)]) # Error on: # - not enough bounds # - too many bounds # - hi >= lo data = np.random.randint(1, 10, (10, 10, 10)) image = fslimage.Image(data, xform=np.eye(4)) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (0, 10), (0, 10), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(5, 5), (0, 10), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(6, 5), (0, 10), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (5, 5), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (6, 5), (0, 10)]) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (0, 10), (5, 5)]) with pytest.raises(ValueError): roi.roi(image, [(0, 10), (0, 10), (6, 5)])
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))
def copyImage(overlayList, displayCtx, overlay, createMask=False, copy4D=True, copyDisplay=True, name=None, roi=None, channel=None, data=None): """Creates a copy of the given :class:`.Image` overlay, and inserts it into the :class:`.OverlayList`. :arg overlayList: The :class:`.OverlayList`. :arg displayCtx: The :class:`.DisplayContext`. :arg overlay: The :class:`.Image` to be copied. :arg createMask: If ``True``, the copy will be an empty ``Image`` the same shape as the ``overlay``. :arg copy4D: If ``True``, and the ``overlay`` is 4D, the copy will also be 4D. Otherwise, the current 3D voluem is copied. :arg copyDisplay: If ``True``, the copy will inherit the display settings of the ``overlay``. Otherwise, the copy will be initialised with default display settings. :arg name: If provided, will be used as the :attr:`.Display.name` of the copy. Otherwise the copy will be given a name. :arg roi: If provided, the copy will be cropped to the low/high voxel bounds specified in the image. Must be a sequence of tuples, containing the low/high bounds for each voxel dimension. For 4D images, the bounds for the fourth dimension are optional. If ``roi`` specifies more than three dimensions, but ``copy4D is False``, the additional dimensions are ignored. :arg channel: If provided, and if the image is complex or multi-valued (RGB(A)), only this channel is copied. Otherwise the image and data type are copied as-is. For complex images, valid values are ``'real'`` or ``'imag'``; for multi- valued images, valid values are ``'R'``, ``'G'``, ``'B'`` or ``'A'``. :arg data: If provided, is used as the image data for the new copy. Must match the shape dictated by the other arguments (i.e. ``copy4D`` and ``roi``). If ``data`` is provided, the ``createMask`` argument is ignored. :returns: The newly created :class:`.Image` object. Note that the ``roi`` and ``copy4D`` options do not support images with more than four dimensions. """ ovlIdx = overlayList.index(overlay) opts = displayCtx.getOpts(overlay) is4D = len(overlay.shape) > 3 isROI = roi is not None copy4D = copy4D and is4D createMask = createMask and (data is None) # Initialise the roi indices if one wasn't # provided - we will use the indices # regardless of whether an ROI was passed # in or not if roi is None: roi = [(0, s) for s in overlay.shape] # Adjust the roi to index a # specific volume if requested if not copy4D: roi = list(roi[:3]) + [(i, i + 1) for i in opts.index()[3:]] if name is None: name = '{}_copy'.format(overlay.name) # If an ROI is not specified, we slice # the image data, either including all # volumes, or the currently selected volume if not isROI: slc = tuple(slice(lo, hi) for lo, hi in roi) imgdata = overlay[slc] xform = overlay.voxToWorldMat # if an ROI is specified, we use the # fsl.utils.image.roi module to generate # an ROI and the adjusted voxel-to-world # affine else: roi = imgroi.roi(overlay, roi) imgdata = roi.data xform = roi.voxToWorldMat if channel is not None: if overlay.iscomplex: if channel == 'real': imgdata = imgdata.real elif channel == 'imag': imgdata = imgdata.imag else: raise ValueError('Invalid value for channel: ' '{}'.format(channel)) elif overlay.nvals > 1: if channel not in 'RGBA': raise ValueError('Invalid value for channel: ' '{}'.format(channel)) imgdata = imgdata[channel] if createMask: data = np.zeros(imgdata.shape, dtype=imgdata.dtype) elif data is None: data = np.copy(imgdata) copy = fslimage.Image(data, xform=xform, header=overlay.header, name=name) overlayList.insert(ovlIdx + 1, copy) # Copy the Display/DisplayOpts settings if copyDisplay: # Don't override the name # that we set above dispexcl = ('name', ) # And don't clobber the transform, and related, # properties, as it is (typically) automatically # controlled via the DisplayContext.displaySpace optexcl = ('transform', 'bounds') copyDisplayProperties(displayCtx, overlay, copy, displayExclude=dispexcl, optExclude=optexcl) return copy