Commit 5f67d58c authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

TEST: Expand resample tests

parent 4e0acb87
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import itertools as it import itertools as it
import os.path as op
import numpy as np import numpy as np
import pytest import pytest
...@@ -10,181 +9,217 @@ import pytest ...@@ -10,181 +9,217 @@ import pytest
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.utils.transform as transform import fsl.utils.transform as transform
import fsl.utils.image.resample as resample import fsl.utils.image.resample as resample
from fsl.utils.tempdir import tempdir
from . import make_random_image from . import make_random_image
def test_resample(seed): def test_resample(seed):
with tempdir() as td: # Random base image shapes
for i in range(25):
fname = op.join(td, 'test.nii') shape = np.random.randint(5, 50, 3)
img = fslimage.Image(make_random_image(dims=shape))
# Random base image shapes # bad shape
for i in range(25): with pytest.raises(ValueError):
resample.resample(img, (10, 10))
with pytest.raises(ValueError):
resample.resample(img, (10, 10, 10, 10))
shape = np.random.randint(5, 50, 3) # resampling to the same shape should be a no-op
make_random_image(fname, shape) samei, samex = resample.resample(img, shape)
img = fslimage.Image(fname, mmap=False) assert np.all(samei == img[:])
assert np.all(samex == img.voxToWorldMat)
# bad shape # Random resampled image shapes
with pytest.raises(ValueError): for j in range(10):
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 rshape = np.random.randint(5, 50, 3)
samei, samex = resample.resample(img, shape) resampled, xf = resample.resample(img, rshape, order=0)
assert np.all(samei == img[:])
assert np.all(samex == img.voxToWorldMat)
# Random resampled image shapes assert tuple(resampled.shape) == tuple(rshape)
for j in range(10):
rshape = np.random.randint(5, 50, 3) # We used nearest neighbour interp, so the
resampled, xf = resample.resample(img, rshape, order=0) # 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
img.save('base.nii.gz') resx, resy, resz = restestcoords.T
fslimage.Image(resampled, xform=xf, resvals = resampled[resx, resy, resz]
mmap=False).save('res.nii.gz')
assert tuple(resampled.shape) == tuple(rshape) res2orig = transform.concat(img.worldToVoxMat, xf)
# We used nearest neighbour interp, so the origtestcoords = transform.transform(restestcoords, res2orig)
# 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 # remove any coordinates which are out of
resvals = resampled[resx, resy, resz] # 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)
res2orig = transform.concat(img.worldToVoxMat, xf) origtestcoords = np.array(origtestcoords.round(), dtype=np.int)
origtestcoords = transform.transform(restestcoords, res2orig) origtestcoords = origtestcoords[~out, :]
restestcoords = restestcoords[ ~out, :]
# remove any coordinates which are out of resx, resy, resz = restestcoords.T
# bounds in the original image space, or origx, origy, origz = origtestcoords.T
# 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=np.int) origvals = img[:][origx, origy, origz]
resvals = resampled[resx, resy, resz]
origtestcoords = origtestcoords[~out, :] assert np.all(np.isclose(resvals, origvals))
restestcoords = restestcoords[ ~out, :]
resx, resy, resz = restestcoords.T
origx, origy, origz = origtestcoords.T
origvals = img[:][origx, origy, origz] def test_resample_4d(seed):
resvals = resampled[resx, resy, resz]
assert np.all(np.isclose(resvals, origvals)) # 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])
del img # resample up
img = None 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)
def test_resample_4d(seed): # resample the entire image
resampled = resample.resample(img, (15, 15, 15, 10), None)[0]
assert tuple(resampled.shape) == (15, 15, 15, 10)
fname = 'test.nii.gz' resampled = resample.resample(img, (5, 5, 5, 10), None)[0]
assert tuple(resampled.shape) == (5, 5, 5, 10)
with tempdir(): # resample along the fourth dim
resampled = resample.resample(img, (15, 15, 15, 15), None)[0]
assert tuple(resampled.shape) == (15, 15, 15, 15)
make_random_image(fname, (10, 10, 10, 10)) resampled = resample.resample(img, (5, 5, 5, 15), None)[0]
assert tuple(resampled.shape) == (5, 5, 5, 15)
# resample one volume
img = fslimage.Image(fname)
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 def test_resample_origin(seed):
resampled = resample.resample(img, (15, 15, 15), slc)[0]
assert tuple(resampled.shape) == (15, 15, 15)
# resample down img = fslimage.Image(make_random_image(dims=(10, 10, 10)))
resampled = resample.resample(img, (5, 5, 5), slc)[0]
assert tuple(resampled.shape) == (5, 5, 5) # 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 = transform.axisBounds(img.shape, img.voxToWorldMat)
resb = transform.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(transform.axisBounds(img.shape, img.voxToWorldMat))
resb = np.array(transform.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())
# 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] def test_resampleToPixdims():
assert tuple(resampled.shape) == (5, 5, 5, 10)
# resample along the fourth dim img = fslimage.Image(make_random_image(dims=(10, 10, 10)))
resampled = resample.resample(img, (15, 15, 15, 15), None)[0] imglo, imghi = transform.axisBounds(img.shape, img.voxToWorldMat)
assert tuple(resampled.shape) == (15, 15, 15, 15) oldpix = np.array(img.pixdim, dtype=np.float)
oldshape = np.array(img.shape, dtype=np.float)
resampled = resample.resample(img, (5, 5, 5, 15), None)[0] for i, origin in it.product(range(25), ('centre', 'corner')):
assert tuple(resampled.shape) == (5, 5, 5, 15)
del img # random pixdims in the range 0.1 - 5.0
del resampled newpix = 0.1 + 4.9 * np.random.random(3)
img = None expshape = np.round(oldshape * (oldpix / newpix))
resampled = None
res = resample.resampleToPixdims(img, newpix, origin=origin)
res = fslimage.Image(res[0], xform=res[1])
reslo, reshi = transform.axisBounds(res.shape, res.voxToWorldMat)
resfov = reshi - reslo
expfov = newpix * res.shape
def test_resample_origin(seed): assert np.all(np.isclose(res.shape, expshape))
with tempdir() as td: assert np.all(np.isclose(res.pixdim, newpix))
fname = op.join(td, 'test.nii') assert np.all(np.isclose(resfov, expfov, rtol=1e-2, atol=1e-2))
make_random_image(fname, (10, 10, 10))
img = fslimage.Image(fname)
# 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 = transform.axisBounds(img.shape, img.voxToWorldMat)
resb = transform.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(transform.axisBounds(img.shape, img.voxToWorldMat))
resb = np.array(transform.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())
if origin == 'corner':
assert np.all(np.isclose(imglo, reslo))
assert np.all(np.isclose(reshi, reslo + expfov,
rtol=1e-2, atol=1e-2))
def test_resampleToPixdims():
pass
def test_resampleToReference(): def test_resampleToReference():
pass
def random_v2w():
return transform.compose(
0.25 + 4.75 * np.random.random(3),
-50 + 100 * np.random.random(3),
-np.pi + 2 * np.pi * np.random.random(3))
# Basic test - output has same
# dimensions/space as reference
for i in range(25):
ishape = np.random.randint(5, 50, 3)
rshape = np.random.randint(5, 50, 3)
iv2w = random_v2w()
rv2w = random_v2w()
img = fslimage.Image(make_random_image(dims=ishape, xform=iv2w))
ref = fslimage.Image(make_random_image(dims=rshape, xform=rv2w))
res = resample.resampleToReference(img, ref)
res = fslimage.Image(res[0], header=ref.header)
assert res.sameSpace(ref)
# More specific test - output
# data gets transformed correctly
# into reference space
img = np.zeros((5, 5, 5), dtype=np.float)
img[1, 1, 1] = 1
img = fslimage.Image(img)
refv2w = transform.scaleOffsetXform([1, 1, 1], [-1, -1, -1])
ref = np.zeros((5, 5, 5), dtype=np.float)
ref = fslimage.Image(ref, xform=refv2w)
res = resample.resampleToReference(img, ref, order=0)
exp = np.zeros((5, 5, 5), dtype=np.float)
exp[2, 2, 2] = 1
assert np.all(np.isclose(res[0], exp))
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment