def test_fractional_coord_to_oversampled_index_math(): """ Sanity check the calculations for oversampling of subpixel offsets """ ##NB Example edge case: oversampling = 7 subpix_offset = 0.5 # Returns index value greater than ``oversampling//2`` : assert np.around(subpix_offset * oversampling).astype(int) == 4 assert (calculate_oversampled_kernel_indices( subpixel_coord=subpix_offset, oversampling=oversampling) == 3).all() # OK, now demonstrate values with oversampling of 0.5, which has easy # numbers to calculate since 1/5 = 0.2 oversampling = 5 io_pairs = np.array([ [-0.5, -2], [-0.4999, -2], [-0.4, -2], [-0.35, -2], [-0.3, -2], # <-- numpy.around favours even roundings [-0.2999, -1], [-0.2, -1], [-0.11, -1], [-0.1, 0], # <-- numpy.around favours even roundings [-0.09, 0], [-0.01, 0], [0.0, 0], ]) outputs = calculate_oversampled_kernel_indices(io_pairs[:, 0], oversampling) assert (io_pairs[:, 1] == outputs).all() # symmetry: io_pairs *= -1. outputs = calculate_oversampled_kernel_indices(io_pairs[:, 0], oversampling) assert (io_pairs[:, 1] == outputs).all() ## Check it works as expected when the inputs are co-ordinate pairs: inputs = np.array([ (0.3, 0.3), ]) outputs = np.array([ (2, 2), ]) assert (calculate_oversampled_kernel_indices( inputs, oversampling) == outputs).all()
def test_fractional_coord_in_2d_case(): """ Sanity check everything works OK when input is 2d array (i.e. UV-coords) """ # OK, now demonstrate values with oversampling of 5, which has easy # numbers to calculate since 1/5 = 0.2 oversampling = 5 io_pairs = np.array([ [-0.5, -2], [-0.4999, -2], [-0.4, -2], [-0.35, -2], [-0.3, -2], # <-- numpy.around favours even roundings [-0.2999, -1], [-0.2, -1], [-0.11, -1], [-0.1, 0], # <-- numpy.around favours even roundings [-0.09, 0], [-0.01, 0], [0.0, 0], ]) input = io_pairs[:, 0] input = np.vstack((input, input)).T assert (input.shape == (len(io_pairs), 2)) output = calculate_oversampled_kernel_indices(input, oversampling) assert output.shape == input.shape assert (io_pairs[:, 1] == output[:, 0]).all() assert (io_pairs[:, 1] == output[:, 1]).all()
def test_fractional_coord_to_oversampled_index_math(): """ Sanity check the calculations for oversampling of subpixel offsets """ ##NB Example edge case: oversampling = 7 subpix_offset = 0.5 # Returns index value greater than ``oversampling//2`` : assert np.around(subpix_offset * oversampling).astype(int) == 4 assert (calculate_oversampled_kernel_indices( subpixel_coord=subpix_offset, oversampling=oversampling) == 3).all() # OK, now demonstrate values with oversampling of 5, which has easy # numbers to calculate since 1/5 = 0.2 oversampling = 5 io_pairs = np.array([ [-0.5, -2], [-0.4999, -2], [-0.4, -2], [-0.35, -2], [-0.3, -2], # <-- numpy.around favours even roundings [-0.2999, -1], [-0.2, -1], [-0.11, -1], [-0.1, 0], # <-- numpy.around favours even roundings [-0.09, 0], [-0.01, 0], [0.0, 0], ]) outputs = calculate_oversampled_kernel_indices(io_pairs[:, 0], oversampling) assert (io_pairs[:, 1] == outputs).all() # symmetry: io_pairs *= -1. outputs = calculate_oversampled_kernel_indices(io_pairs[:, 0], oversampling) assert (io_pairs[:, 1] == outputs).all() ## Check it works as expected when the inputs are co-ordinate pairs: inputs = np.array([(0.3, 0.3), ]) outputs = np.array([(2, 2), ]) assert (calculate_oversampled_kernel_indices(inputs, oversampling) == outputs).all()
def test_stepped_vs_exact_convolution(): # We use a triangle to compare, since even a tiny pixel offset should # result in differing values when using exact convolution, # this makes it easier to verify that the 'stepped' kernel is behaving # as expected. n_image = 8 support = 3 kernel_func = conv_funcs.Triangle(half_base_width=2.5) oversampling = 5 # Choose sub-pixel steps that align with oversampling grid: steps = np.array([-0.4, 0.2, 0.0, 0.2, 0.4]) substeps = np.linspace(-0.099999, 0.099999, num=50) kernel_cache = populate_kernel_cache(kernel_func=kernel_func, support=support, oversampling=oversampling) for x_offset in steps: offset = (x_offset, 0.0) aligned_exact_kernel = Kernel(kernel_func=kernel_func, support=support, offset=offset) aligned_cache_idx = calculate_oversampled_kernel_indices( offset, oversampling) cached_kernel = kernel_cache[tuple(aligned_cache_idx)] # Check that for oversampling-grid aligned positions, cache == exact assert (aligned_exact_kernel.array == cached_kernel.array).all() # Now check the results for non-aligned positions for sub_offset in substeps: offset = (x_offset + sub_offset, 0.0) if sub_offset != 0.0: unaligned_exact_kernel = Kernel(kernel_func=kernel_func, support=support, offset=offset) unaligned_cache_idx = calculate_oversampled_kernel_indices( offset, oversampling) # We expect to return the same kernel as nearest grid-point assert (unaligned_cache_idx == aligned_cache_idx).all()
def test_kernel_caching(): """ Test generation of cached (offset) kernels, and demonstrate correct usage. In this test, we assume an oversampling of 5, resulting in step-widths of 0.2 regular pixels. We then iterate through a bunch of possible sub-pixel offsets, checking that we pick the nearest (closest to exact-positioned) cached kernel correctly. """ # We use a triangle to compare, since even a tiny pixel offset should # result in differing values when using exact convolution, # this makes it easier to verify that the 'stepped' kernel is behaving # as expected. n_image = 8 support = 3 kernel_func = conv_funcs.Triangle(half_base_width=2.5) oversampling = 5 # Choose sub-pixel steps that align with oversampling grid: steps = np.array([-0.4, 0.2, 0.0, 0.2, 0.4]) substeps = np.linspace(-0.099999, 0.099999, num=15) kernel_cache = populate_kernel_cache( kernel_func=kernel_func, support=support, oversampling=oversampling) for x_offset in steps: offset = (x_offset, 0.0) aligned_exact_kernel = Kernel(kernel_func=kernel_func, support=support, offset=offset) # Generate an index into the kernel-cache at the precise offset # (i.e. a multiple of 0.2-regular-pixel-widths) aligned_cache_idx = calculate_oversampled_kernel_indices(offset, oversampling) cached_kernel = kernel_cache[tuple(aligned_cache_idx)] # Check that for oversampling-grid aligned positions, cache == exact assert (aligned_exact_kernel.array == cached_kernel.array).all() # Now check the results for non-aligned positions for sub_offset in substeps: offset = (x_offset + sub_offset, 0.0) if sub_offset != 0.0: unaligned_exact_kernel = Kernel(kernel_func=kernel_func, support=support, offset=offset) # Check that the irregular position resolves to the correct # nearby aligned position: unaligned_cache_idx = calculate_oversampled_kernel_indices( offset, oversampling) assert (unaligned_cache_idx == aligned_cache_idx).all() ## Demonstrate retrieval of the cached kernel: cached_kernel = kernel_cache[tuple(unaligned_cache_idx)] assert (aligned_exact_kernel.array == cached_kernel.array).all() ## Sanity check - we expect the exact-calculated kernel to ## be different by a small amount diff = ( aligned_exact_kernel.array - unaligned_exact_kernel.array) eps = 10e-9 assert not (np.fabs(diff) < eps).all()