def test_affine_transform_type_consistency(): image = da.ones((3, 3)) image_t = da_ndinterp.affine_transform(image, np.eye(2), [0, 0]) assert isinstance(image, type(image_t)) assert isinstance(image[0, 0].compute(), type(image_t[0, 0].compute()))
def test_affine_transform_no_output_shape_or_chunks_specified(): image = da.ones((3, 3)) image_t = da_ndinterp.affine_transform(image, np.eye(2), [0, 0]) assert image_t.shape == image.shape assert image_t.chunks == tuple([(s, ) for s in image.shape])
def test_affine_transform_numpy_input(): image = np.ones((3, 3)) image_t = da_ndinterp.affine_transform(image, np.eye(2), [0, 0]) assert image_t.shape == image.shape assert (image == image_t).min()
def test_affine_transform_type_consistency_gpu(): cupy = pytest.importorskip("cupy", minversion="6.0.0") image = da.ones((3, 3)) image_t = da_ndinterp.affine_transform(image, np.eye(2), [0, 0]) image.map_blocks(cupy.asarray) assert isinstance(image, type(image_t)) assert isinstance(image[0, 0].compute(), type(image_t[0, 0].compute()))
def test_affine_transform_parameter_formats(n): # define reference parameters scale_factors = np.ones(n, dtype=np.float) * 2. matrix_n = np.diag(scale_factors) offset = -np.ones(n) # convert into different formats matrix_only_scaling = scale_factors matrix_pre_homogeneous = np.hstack((matrix_n, offset[:, None])) matrix_homogeneous = np.vstack((matrix_pre_homogeneous, [0] * n + [1])) np.random.seed(0) image = da.random.random([5] * n) # reference run image_t_0 = da_ndinterp.affine_transform(image, matrix_n, offset).compute() # assert that the different parameter formats # lead to the same output image_t_scale = da_ndinterp.affine_transform(image, matrix_only_scaling, offset).compute() assert (np.allclose(image_t_0, image_t_scale)) for matrix in [matrix_pre_homogeneous, matrix_homogeneous]: image_t = da_ndinterp.affine_transform( image, matrix, offset + 10., # ignored ).compute() assert (np.allclose(image_t_0, image_t)) # catch matrices that are not homogeneous transformation matrices with pytest.raises(ValueError): matrix_not_homogeneous = np.vstack( (matrix_pre_homogeneous, [-1] * n + [1])) da_ndinterp.affine_transform(image, matrix_not_homogeneous, offset)
def test_affine_transform_large_input_small_output_cpu(): """ Make sure input array does not need to be computed entirely """ # fully computed, this array would occupy 8TB image = da.random.random([10000] * 3, chunks=(200, 200, 200)) image_t = da_ndinterp.affine_transform(image, np.eye(3), [0, 0, 0], output_chunks=[1, 1, 1], output_shape=[1, 1, 1]) # if more than the needed chunks should be computed, # this would take long and eventually raise a MemoryError image_t[0, 0, 0].compute()
def test_affine_transform_large_input_small_output_gpu(): """ Make sure input array does not need to be computed entirely """ cupy = pytest.importorskip("cupy", minversion="6.0.0") # this array would occupy more than 24GB on a GPU image = da.random.random([2000] * 3, chunks=(50, 50, 50)) image.map_blocks(cupy.asarray) image_t = da_ndinterp.affine_transform(image, np.eye(3), [0, 0, 0], output_chunks=[1, 1, 1], output_shape=[1, 1, 1]) # if more than the needed chunks should be computed, # this would take long and eventually raise a MemoryError image_t[0, 0, 0].compute()
def test_affine_transform_prefilter_warning(): with pytest.warns(UserWarning): da_ndinterp.affine_transform(da.ones(3), [1], [0], order=3, prefilter=True)
def test_affine_transform_minimal_input(): image = np.ones((3, 3)) image_t = da_ndinterp.affine_transform(np.ones((3, 3)), np.eye(2)) assert image_t.shape == image.shape
def validate_affine_transform( n=2, matrix=None, offset=None, input_output_shape_per_dim=(16, 16), interp_order=1, interp_mode='constant', input_output_chunksize_per_dim=(6, 6), random_seed=0, use_cupy=False, ): """ Compare the outputs of `ndimage.affine_transformation` and `dask_image.ndinterp.affine_transformation`. Notes ----- Currently, prefilter is disabled and therefore the output of `dask_image.ndinterp.affine_transformation` is compared to `prefilter=False`. """ # define test image a = input_output_shape_per_dim[0] np.random.seed(random_seed) image = np.random.random([a] * n) # transform into dask array chunksize = [input_output_chunksize_per_dim[0]] * n image_da = da.from_array(image, chunks=chunksize) if use_cupy: import cupy as cp image_da = image_da.map_blocks(cp.asarray) # define (random) transformation if matrix is None: # make sure to substantially deviate from unity matrix matrix = np.eye(n) + (np.random.random((n, n)) - 0.5) * 5. if offset is None: offset = (np.random.random(n) - 0.5) / 5. * np.array(image.shape) # define resampling options output_shape = [input_output_shape_per_dim[1]] * n output_chunks = [input_output_chunksize_per_dim[1]] * n # transform with scipy image_t_scipy = ndimage.affine_transform(image, matrix, offset, output_shape=output_shape, order=interp_order, mode=interp_mode, prefilter=False) # transform with dask-image image_t_dask = da_ndinterp.affine_transform(image_da, matrix, offset, output_shape=output_shape, output_chunks=output_chunks, order=interp_order, mode=interp_mode) image_t_dask_computed = image_t_dask.compute() assert np.allclose(image_t_scipy, image_t_dask_computed)
def _transform_array(image: da.Array, scale: Tuple[float, ...], offset: Tuple[float, ...], shape: Tuple[int, ...], chunks: Optional[Tuple[int, ...]], spline_order: int, recover_nan: bool) -> da.Array: """ Apply affine transformation to ND-image. :param image: ND-image with shape (..., size_y, size_x) :param scale: Scaling factors (1, ..., 1, sy, sx) :param offset: Offset values (0, ..., 0, oy, ox) :param shape: (..., size_y, size_x) :param chunks: (..., chunk_size_y, chunk_size_x) :param spline_order: 0 ... 5 :param recover_nan: True/False :return: Transformed ND-image. """ assert_true(len(scale) == image.ndim, 'invalid scale') assert_true(len(offset) == image.ndim, 'invalid offset') assert_true(len(shape) == image.ndim, 'invalid shape') assert_true(chunks is None or len(chunks) == image.ndim, 'invalid chunks') if _is_no_op(image, scale, offset, shape): return image # As of scipy 0.18, matrix = scale is no longer supported. # Therefore we use the diagonal matrix form here, # where scale is the diagonal. matrix = np.diag(scale) at_kwargs = dict( offset=offset, order=spline_order, output_shape=shape, output_chunks=chunks, mode='constant', ) if recover_nan and spline_order > 0: # We can "recover" values that are neighbours to NaN values # that would otherwise become NaN too. mask = da.isnan(image) # First check if there are NaN values ar all if da.any(mask): # Yes, then # 1. replace NaN by zero filled_im = da.where(mask, 0.0, image) # 2. transform the zeo-filled image scaled_im = ndinterp.affine_transform(filled_im, matrix, **at_kwargs, cval=0.0) # 3. transform the inverted mask scaled_norm = ndinterp.affine_transform(1.0 - mask, matrix, **at_kwargs, cval=0.0) # 4. put back NaN where there was zero, # otherwise decode using scaled mask return da.where(da.isclose(scaled_norm, 0.0), np.nan, scaled_im / scaled_norm) # No dealing with NaN required return ndinterp.affine_transform(image, matrix, **at_kwargs, cval=np.nan)