def test_3D_len_one_indexing(shape=None, img=None): # Testing ImageWrapper for a 3D image with # a third dimension of length 1 - it should # look like a 3D image, but should still # accept (valid) 2D slicing. if shape is None: shape = (20, 20, 1) elif len(shape) < 3: shape = tuple(list(shape) + [1]) if img is None: data = np.random.random(shape) nibImg = nib.Nifti1Image(data, np.eye(4)) img = imagewrap.ImageWrapper(nibImg, loadData=True) test_3D_indexing(shape, img) assert type(img[0, 0, :]) == np.ndarray assert type(img[0, 0]) == np.ndarray assert type(img[0, 0, 0]) == np.float64 mask = np.zeros(shape[:2], dtype=np.bool) mask[0, 0] = True assert type(img[mask]) == np.ndarray assert img[mask].shape == (1, ) mask = np.zeros(shape, dtype=np.bool) mask[0, 0, 0] = True assert type(img[mask]) == np.ndarray assert img[mask].shape == (1, )
def _test_ImageWrapper_read(niters, seed, threaded): for _ in range(niters): # Generate an image with a number of volumes ndims = random.choice((2, 3, 4)) - 1 shape = np.random.randint(5, 60, size=ndims + 1) shape[-1] = np.random.randint(5, 15) nvols = shape[-1] data = np.zeros(shape) # The data range of each volume # increases sequentially data[..., 0] = np.random.randint(-5, 6, shape[:-1]) volRanges = [(np.min(data[..., 0]), np.max(data[..., 0]))] for vol in range(1, nvols): data[..., vol] = data[..., 0] * (vol + 1) volRanges.append((np.min(data[..., vol]), np.max(data[..., vol]))) img = nib.Nifti1Image(data, np.eye(4)) wrapper = imagewrap.ImageWrapper(img, loadData=False, threaded=threaded) # We're going to access data volumes # through the image wrapper with a # bunch of random volume orderings. for _ in range(niters): ordering = list(range(nvols)) random.shuffle(ordering) ranges = [volRanges[o] for o in ordering] wrapper.reset() assert wrapper.dataRange == (0.0, 0.0) for j, (vol, r) in enumerate(zip(ordering, ranges)): # Access the volume if len(data.shape) >= 3: wrapper[..., vol] else: wrapper[:, vol, 0] _ImageWraper_busy_wait(wrapper, vol) # The current known data range # should be the min/max of # all acccessed volumes so far expMin = min([r[0] for r in ranges[:j + 1]]) expMax = max([r[1] for r in ranges[:j + 1]]) assert wrapper.dataRange == (expMin, expMax) if j < nvols - 1: assert not wrapper.covered else: assert wrapper.covered
def test_4D_indexing(shape=None, img=None): if shape is None: shape = (20, 21, 22, 23) if img is None: data = np.random.random(shape) nibImg = nib.Nifti1Image(data, affine=np.eye(4)) img = imagewrap.ImageWrapper(nibImg, loadData=True) assert tuple(img[:].shape) == tuple(shape) assert tuple(img[:, :].shape) == tuple(shape) assert tuple(img[:, :, :].shape) == tuple(shape) assert tuple(img[:, :, :, :].shape) == tuple(shape) assert tuple(img[:, 0, 0, 0].shape) == (shape[0], ) assert tuple(img[0, :, 0, 0].shape) == (shape[1], ) assert tuple(img[0, 0, :, 0].shape) == (shape[2], ) assert tuple(img[0, 0, 0, :].shape) == (shape[3], ) assert tuple(img[0, :, :, :].shape) == (shape[1], shape[2], shape[3]) assert tuple(img[:, 0, :, :].shape) == (shape[0], shape[2], shape[3]) assert tuple(img[:, :, 0, :].shape) == (shape[0], shape[1], shape[3]) assert tuple(img[:, :, :, 0].shape) == (shape[0], shape[1], shape[2]) assert type(img[0, 0, 0, 0]) == np.float64 mask1 = np.zeros(shape, dtype=np.bool) mask1[0, 0, 0, 0] = True mask1[1, 0, 0, 0] = True assert tuple(img[mask1].shape) == (2, ) img[0, 0, 0, 0] = 999 img[:, 0, 0, 0] = [999] * shape[0] img[0, :, 0, 0] = [999] * shape[1] img[0, 0, :, 0] = [999] * shape[2] img[0, 0, 0, :] = [999] * shape[3] img[:, 0, 0, 0] = np.array([999] * shape[0]) img[0, :, 0, 0] = np.array([999] * shape[1]) img[0, 0, :, 0] = np.array([999] * shape[2]) img[0, 0, 0, :] = np.array([999] * shape[3]) img[0, :, :, :] = np.ones((shape[1], shape[2], shape[3])) img[:, 0, :, :] = np.ones((shape[0], shape[2], shape[3])) img[:, :, 0, :] = np.ones((shape[0], shape[1], shape[3])) img[:, :, :, 0] = np.ones((shape[0], shape[1], shape[2])) img[0, :, :, :] = [[[999] * shape[1]] * shape[2]] * shape[3] img[:, 0, :, :] = [[[999] * shape[0]] * shape[2]] * shape[3] img[:, :, 0, :] = [[[999] * shape[0]] * shape[1]] * shape[3] img[:, :, :, 0] = [[[999] * shape[0]] * shape[1]] * shape[2]
def test_2D_indexing(): # Testing ImageWrapper for a 2D image - # it should look just like a 3D image # (the same as is tested above). shape = (20, 20) data = np.random.random(shape[:2]) nibImg = nib.Nifti1Image(data, np.eye(4)) img = imagewrap.ImageWrapper(nibImg, loadData=True) test_3D_len_one_indexing(shape, img)
def test_3D_indexing(shape=None, img=None): # Test that a 3D image looks like a 3D image if shape is None: shape = (21, 22, 23) elif len(shape) == 2: shape = tuple(list(shape) + [1]) if img is None: data = np.random.random(shape) nibImg = nib.Nifti1Image(data, np.eye(4)) img = imagewrap.ImageWrapper(nibImg, loadData=True) assert tuple(img[:].shape) == tuple(shape) assert tuple(img[:, :].shape) == tuple(shape) assert tuple(img[:, :, :].shape) == tuple(shape) assert tuple(img[:, 0, 0].shape) == (shape[0], ) assert tuple(img[0, :, 0].shape) == (shape[1], ) assert tuple(img[0, 0, :].shape) == (shape[2], ) assert tuple(img[0, :, :].shape) == (shape[1], shape[2]) assert tuple(img[:, 0, :].shape) == (shape[0], shape[2]) assert tuple(img[:, :, 0].shape) == (shape[0], shape[1]) assert type(img[0, 0, 0]) == np.float64 mask1 = np.zeros(shape, dtype=np.bool) mask1[0, 0, 0] = True mask1[1, 0, 0] = True assert tuple(img[mask1].shape) == (2, ) img[0, 0, 0] = 999 img[:, 0, 0] = [999] * shape[0] img[0, :, 0] = [999] * shape[1] img[0, 0, :] = [999] * shape[2] img[:, 0, 0] = np.array([999] * shape[0]) img[0, :, 0] = np.array([999] * shape[1]) img[0, 0, :] = np.array([999] * shape[2]) img[0, :, :] = np.ones((shape[1], shape[2])) img[:, 0, :] = np.ones((shape[0], shape[2])) img[:, :, 0] = np.ones((shape[0], shape[1])) img[0, :, :] = [[999] * shape[1]] * shape[2] img[:, 0, :] = [[999] * shape[0]] * shape[2] img[:, :, 0] = [[999] * shape[0]] * shape[1]
def test_3D_4D_indexing(): # Testing ImageWrapper for an image with a # trailing fourth dimension of length 1 - # it should look like a 3D image, but # should still accept (valid) 4D slicing. # __getitem__ and __setitem__ on # - 3D index # - 4D index # - 3D boolean array # - 4D boolean array # padShape = (21, 22, 23, 1) shape = padShape[:3] data = np.random.random(shape) nibImg = nib.Nifti1Image(data, np.eye(4)) img = imagewrap.ImageWrapper(nibImg, loadData=True) test_3D_indexing(shape, img) assert tuple(img[:, :, :, :].shape) == tuple(shape) assert tuple(img[:, 0, 0, 0].shape) == (shape[0], ) assert tuple(img[:, 0, 0, :].shape) == (shape[0], ) assert tuple(img[:, :, 0, 0].shape) == (shape[0], shape[1]) assert tuple(img[:, :, 0, :].shape) == (shape[0], shape[1]) assert type(img[0, 0, 0, 0]) == np.float64 assert type(img[0, 0, 0, :]) == np.float64 mask = np.zeros(padShape, dtype=np.bool) mask[0, 0, 0, 0] = True assert type(img[mask]) == np.ndarray assert img[mask].shape == (1, )
def _test_ImageWrapper_write_different_volume(niters, seed, threaded): for _ in range(niters): # Generate an image with several volumes. ndims = random.choice((2, 3, 4)) - 1 nvols = np.random.randint(10, 40) shape = np.random.randint(5, 60, size=ndims + 1) shape[-1] = nvols data = np.zeros(shape) # The data range of each volume # increases sequentially data[..., 0] = np.random.randint(-5, 6, shape[:-1]) for vol in range(1, nvols): data[..., vol] = data[..., 0] * (vol + 1) # Generate a random coverage fullcov = random_coverage(shape) cov = np.copy(fullcov) # Choose some consecutive volumes # to limit that coverage to. while True: covvlo = np.random.randint(0, nvols - 1) covvhi = np.random.randint(covvlo + 1, nvols + 1) # Only include up to 4 # volumes in the coverage if covvhi - covvlo <= 3: break for v in range(nvols): if v < covvlo or v >= covvhi: cov[..., v] = np.nan covlo, covhi = coverageDataRange(data, cov) print('Coverage: {} [{} - {}]'.format( [(lo, hi) for lo, hi in cov[:, :, covvlo].T], covvlo, covvhi)) # Now, we'll simulate some writes # on volumes that are not in the # coverage for _ in range(niters): # Generate a slice, making # sure that the slice covers # at least one element while True: slices = random_slices(fullcov, shape, random.choice(('out', 'in', 'overlap'))) # print(slices) # Clobber the slice volume range # so it does not overlap with the # coverage volume range while True: vlo = np.random.randint(0, nvols) vhi = np.random.randint(vlo + 1, nvols + 1) if vhi < covvlo or vlo > covvhi: break slices[-1] = vlo, vhi sliceshape = [hi - lo for lo, hi in slices] if np.prod(sliceshape) == 0: continue sliceobjs = imagewrap.sliceTupleToSliceObj(slices) break # Calculate what we expect the # coverage to be after the write expCov = np.copy(cov) for vol in range(slices[-1][0], slices[-1][1]): expCov[..., vol] = imagewrap.adjustCoverage(expCov[..., vol], slices) # Test all data range possibilities: # - inside the existing range (clo < rlo < rhi < chi) # - encompassing the existing range (rlo < clo < chi < rhi) # - Overlapping the existing range (rlo < clo < rhi < chi) # (clo < rlo < chi < rhi) # - Outside of the existing range (clo < chi < rlo < rhi) # (rlo < rhi < clo < chi) loRanges = [ rfloat(covlo, covhi), rfloat(covlo - 100, covlo), rfloat(covlo - 100, covlo), rfloat(covlo, covhi), rfloat(covhi, covhi + 100), rfloat(covlo - 100, covlo) ] hiRanges = [ rfloat(loRanges[0], covhi), rfloat(covhi, covhi + 100), rfloat(covlo, covhi), rfloat(covhi, covhi + 100), rfloat(loRanges[4], covhi + 100), rfloat(loRanges[5], covlo) ] # What we expect the range to # be after the data write expected = [(covlo, covhi), (loRanges[1], hiRanges[1]), (loRanges[2], covhi), (covlo, hiRanges[3]), (covlo, hiRanges[4]), (loRanges[5], covhi)] for rlo, rhi, (elo, ehi) in zip(loRanges, hiRanges, expected): img = nib.Nifti1Image(np.copy(data), np.eye(4)) wrapper = imagewrap.ImageWrapper(img, threaded=threaded) applyCoverage(wrapper, cov) oldLo, oldHi = wrapper.dataRange newData = np.linspace(rlo, rhi, np.prod(sliceshape)) newData = newData.reshape(sliceshape) if np.prod(sliceshape) == 1: ehi = max(newData.max(), oldHi) wrapper[tuple(sliceobjs)] = newData _ImageWraper_busy_wait(wrapper) newLo, newHi = wrapper.dataRange for vol in range(nvols): np.all(wrapper.coverage(vol) == expCov[..., vol]) print('Old range: {} - {}'.format(oldLo, oldHi)) print('Newdata range: {} - {}'.format(newData.min(), newData.max())) print('Expected range: {} - {}'.format(elo, ehi)) print('New range: {} - {}'.format(newLo, newHi)) assert np.isclose(newLo, elo) assert np.isclose(newHi, ehi)
def _test_ImageWrapper_write_in_overlap(niters, seed, threaded): # Generate a bunch of random coverages for _ in range(niters): # Generate an image with just two volumes. We're only # testing within-volume modifications here. ndims = random.choice((2, 3, 4)) - 1 shape = np.random.randint(5, 60, size=ndims + 1) shape[-1] = np.random.randint(2, 3) nvols = shape[-1] data = np.zeros(shape) # The data range of each volume # increases sequentially data[..., 0] = np.random.randint(-5, 6, shape[:-1]) for vol in range(1, nvols): data[..., vol] = data[..., 0] * (vol + 1) # Generate a random coverage cov = random_coverage(shape, vol_limit=1) print('Shape: {}'.format(shape)) print('Coverage: {}'.format(cov)) print('Data: {}'.format(data)) # Now, we'll simulate some writes # which are contained within, or # overlap with, the initial coverage for _ in range(niters): # Generate some slices outside # of the coverage area, making # sure that the slice covers # at least one element while True: slices = random_slices(cov, shape, random.choice(('in', 'overlap'))) slices[-1] = [0, 1] sliceshape = [hi - lo for lo, hi in slices] if np.prod(sliceshape) == 0: continue sliceobjs = imagewrap.sliceTupleToSliceObj(slices) sliceobjs = tuple(list(sliceobjs[:-1]) + [0]) sliceshape = sliceshape[:-1] break # Expected wrapper coverage after the # write is the union of the original # coverage and the write slice. expCov = imagewrap.adjustCoverage(cov[..., 0], slices) for _ in range(10): rlo = rfloat(data.min() - 100, data.max() + 100) rhi = rfloat(rlo, data.max() + 100) if np.prod(sliceshape) == 1: rhi = rlo img = nib.Nifti1Image(np.copy(data), np.eye(4)) wrapper = imagewrap.ImageWrapper(img, threaded=threaded) applyCoverage(wrapper, cov) newData = np.linspace(rlo, rhi, np.prod(sliceshape)) newData = newData.reshape(sliceshape) print('Old coverage: {}'.format(cov[..., 0])) print('Slice: {}'.format(sliceobjs[:-1])) print('Expected coverage: {}'.format(expCov)) print('Old range: {} - {}'.format(*wrapper.dataRange)) print('New data range: {} - {}'.format( newData.min(), newData.max())) # We figure out the expected data # range by creating a copy of the # data, and doing the same write expData = np.copy(data[..., 0]) expData[sliceobjs[:-1]] = newData # Then calcultaing the min/max # on this copy expCovSlice = [slice(int(lo), int(hi)) for lo, hi in expCov.T] expLo = expData[expCovSlice].min() expHi = expData[expCovSlice].max() wrapper[tuple(sliceobjs)] = newData _ImageWraper_busy_wait(wrapper) newCov = wrapper.coverage(0) newLo, newHi = wrapper.dataRange print('Expected range: {} - {}'.format(expLo, expHi)) print('New range: {} - {}'.format(newLo, newHi)) print('Slice min/max: {} - {}'.format( img.get_data()[tuple(sliceobjs)].min(), img.get_data()[tuple(sliceobjs)].max())) print('Data min/max: {} - {}'.format( img.get_data().min(), img.get_data().max())) assert np.all(newCov == expCov) assert np.isclose(newLo, expLo) assert np.isclose(newHi, expHi)
def _test_ImageWrapper_write_out(niters, seed, threaded): # This is HORRIBLE loop = 0 # Generate a bunch of random coverages for _ in range(niters): # Generate an image with just two volumes. We're only # testing within-volume modifications here. ndims = random.choice((2, 3, 4)) - 1 shape = np.random.randint(5, 60, size=ndims + 1) shape[-1] = np.random.randint(2, 3) nvols = shape[-1] data = np.zeros(shape) # The data range of each volume # increases sequentially data[..., 0] = np.random.randint(-5, 6, shape[:-1]) for vol in range(1, nvols): data[..., vol] = data[..., 0] * (vol + 1) # Generate a random coverage cov = random_coverage(shape, vol_limit=1) img = nib.Nifti1Image(data, np.eye(4)) wrapper = imagewrap.ImageWrapper(img, threaded=threaded) applyCoverage(wrapper, cov) clo, chi = wrapper.dataRange loop += 1 # Now, we'll simulate some writes # outside of the coverage area. for _ in range(niters): # Generate some slices outside # of the coverage area, making # sure that the slice covers # at least one element while True: slices = random_slices(cov, shape, 'out') slices[-1] = [0, 1] sliceshape = [hi - lo for lo, hi in slices] if np.prod(sliceshape) == 0: continue sliceobjs = imagewrap.sliceTupleToSliceObj(slices) sliceobjs = tuple(list(sliceobjs[:-1]) + [0]) sliceshape = sliceshape[:-1] break # print('---------------') # print('Slice {}'.format(slices)) # Expected wrapper coverage after the write expCov = imagewrap.adjustCoverage(cov[..., 0], slices) # Figure out the data range of the # expanded coverage (the original # coverage expanded to include this # slice). elo, ehi = coverageDataRange(data, cov, slices) # Test all data range possibilities: # - inside the existing range (clo < rlo < rhi < chi) # - encompassing the existing range (rlo < clo < chi < rhi) # - Overlapping the existing range (rlo < clo < rhi < chi) # (clo < rlo < chi < rhi) # - Outside of the existing range (clo < chi < rlo < rhi) # (rlo < rhi < clo < chi) loRanges = [ rfloat(clo, chi), rfloat(elo - 100, elo), rfloat(elo - 100, elo), rfloat(clo, chi), rfloat(ehi, ehi + 100), rfloat(elo - 100, elo) ] hiRanges = [ rfloat(loRanges[0], chi), rfloat(ehi, ehi + 100), rfloat(clo, chi), rfloat(ehi, ehi + 100), rfloat(loRanges[4], ehi + 100), rfloat(loRanges[5], elo) ] for rlo, rhi in zip(loRanges, hiRanges): img = nib.Nifti1Image(np.copy(data), np.eye(4)) wrapper = imagewrap.ImageWrapper(img) applyCoverage(wrapper, cov) # print('ndims', ndims) # print('sliceshape', sliceshape) # print('sliceobjs', sliceobjs) newData = np.linspace(rlo, rhi, np.prod(sliceshape)) newData = newData.reshape(sliceshape) # Make sure that the expected low/high values # are present in the data being written # print('Writing data (shape: {})'.format(newData.shape)) oldCov = wrapper.coverage(0) wrapper[tuple(sliceobjs)] = newData _ImageWraper_busy_wait(wrapper) expLo, expHi = coverageDataRange(img.get_data(), cov, slices) newLo, newHi = wrapper.dataRange # print('Old range: {} - {}'.format(clo, chi)) # print('Sim range: {} - {}'.format(rlo, rhi)) # print('Exp range: {} - {}'.format(expLo, expHi)) # print('NewDat range: {} - {}'.format(newData.min(), newData.max())) # print('Data range: {} - {}'.format(data.min(), data.max())) # print('Expand range: {} - {}'.format(elo, ehi)) # print('New range: {} - {}'.format(newLo, newHi)) newCov = wrapper.coverage(0) # print('Old coverage: {}'.format(oldCov)) # print('New coverage: {}'.format(newCov)) # print('Expected coverage: {}'.format(expCov)) # print() # print() assert np.all(newCov == expCov) assert np.isclose(newLo, expLo) assert np.isclose(newHi, expHi)