Example #1
0
def _validate_xyz_resolution(ndim, xyz_resolution):
    """Validate xyz_resolution to assure its length matches the dimensionality of image."""

    xyz_resolution = _validate_scalar_to_multi(xyz_resolution, size=ndim)

    if np.any(xyz_resolution <= 0):
        raise ValueError(f"All elements of xyz_resolution must be positive.\n"
                         f"np.min(xyz_resolution): {np.min(xyz_resolution)}.")

    return xyz_resolution
Example #2
0
def downsample_image(image, scale_factors, truncate=False):
    """
    Downsample an image by averaging.
    
    Arguments:
        image {np.ndarray} -- The image to be downsampled.
        scale_factors {int, sequence} -- The per-axis factor by which to reduce the image size.
    
    Keyword Arguments:
        truncate {bool} -- If True, evenly truncates the image down to the nearest multiple of the scale_factor for each axis. (default: {False})
    
    Raises:
        ValueError: Raised if any of scale_factors is less than 1.
    
    Returns:
        np.ndarray -- A downsampled copy of <image>.
    """

    # Validate arguments.

    # Validate image.
    image = _validate_ndarray(image)

    # Validate scale_factors.
    scale_factors = _validate_scalar_to_multi(scale_factors, image.ndim, int)
    # Verify that all scale_factors are at least 1.
    if np.any(scale_factors < 1):
        raise ValueError(
            f"Every element of scale_factors must be at least 1.\n"
            f"np.min(scale_factors): {np.min(scale_factors)}.")

    # Downsample a copy of image by averaging.

    scaled_image = np.copy(
        image)  # Not necessary since _downsample_along_axis does not mutate.

    # Downsample image along each dimension.
    for dim, scale_factor in enumerate(scale_factors):
        scaled_image = _downsample_along_axis(
            scaled_image, dim, scale_factor,
            truncate)  # Side effect: breaks alias.

    return scaled_image
    """
Example #3
0
def test__validate_scalar_to_multi():

    # Test proper use.

    kwargs = dict(value=1, size=1, dtype=float)
    correct_output = np.array([1], float)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=1, size=0, dtype=int)
    correct_output = np.array([], int)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=9.5, size=4, dtype=int)
    correct_output = np.full(4, 9, int)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=[1, 2, 3.5], size=3, dtype=float)
    correct_output = np.array([1, 2, 3.5], float)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=[1, 2, 3.5], size=3, dtype=int)
    correct_output = np.array([1, 2, 3], int)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=(1, 2, 3), size=3, dtype=int)
    correct_output = np.array([1, 2, 3], int)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    kwargs = dict(value=np.array([1, 2, 3], float), size=3, dtype=int)
    correct_output = np.array([1, 2, 3], int)
    assert np.array_equal(_validate_scalar_to_multi(**kwargs), correct_output)

    # Test improper use.

    kwargs = dict(value=[1, 2, 3, 4], size='size: not an int', dtype=float)
    expected_exception = TypeError
    match = "size must be interpretable as an integer."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)

    kwargs = dict(value=[], size=-1, dtype=float)
    expected_exception = ValueError
    match = "size must be non-negative."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)

    kwargs = dict(value=[1, 2, 3, 4], size=3, dtype=int)
    expected_exception = ValueError
    match = "The length of value must either be 1 or it must match size."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)

    kwargs = dict(value=np.arange(3 * 4, dtype=int).reshape(3, 4),
                  size=3,
                  dtype=float)
    expected_exception = ValueError
    match = "value must not have more than 1 dimension."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)

    kwargs = dict(value=[1, 2, 'c'], size=3, dtype=int)
    expected_exception = ValueError
    match = "value and dtype are incompatible with one another."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)

    kwargs = dict(value='c', size=3, dtype=int)
    expected_exception = ValueError
    match = "value and dtype are incompatible with one another."
    with pytest.raises(expected_exception, match=match):
        _validate_scalar_to_multi(**kwargs)
Example #4
0
def change_resolution_by(image,
                         xyz_scales,
                         xyz_resolution=1,
                         pad_to_match_res=True,
                         err_to_higher_res=True,
                         average_on_downsample=True,
                         truncate=False,
                         return_true_resolution=False,
                         **resample_kwargs):
    """
    Resample image such that its resolution is scaled by 1 / <xyz_scales>[dim] or abs(xyz_scales[dim]) if xyz_scales[dim] is negative, in each dimension dim.

    
    Arguments:
        image {np.ndarray} -- The image to be resampled, allowing arbitrary dimensions.
        xyz_scales {float, sequence} -- The per-axis factors by which to adjust the resolution of <image>. Negative values are treated as the reciprocal of their positive counterparts.
            xyz_scales[dim] > 1 implies upsampling - increasing resolution and image size.
            xyz_scales[dim] = 1 implies unity - no change in resolution for this dimension.
            xyz_scales[dim] < 1 implies downsampling - decreasing resolution and image size.
            xyz_scales[dim] < 0 implies downsampling by this factor - cast to -1 / xyz_scales[dim].

            Examples:
            xyz_scales[dim] = 2 --> upsample by 2
            xyz_scales[dim] = 1 --> do nothing
            xyz_scales[dim] = 1/2 --> downsample by 2
            xyz_scales[dim] = -3 --> downsample by 3
            xyz_scales[dim] = -1/5 --> upsample by 5
    
    Keyword Arguments:
        xyz_resolution {float, sequence} -- The per-axis resolution of <image>. (default: {1})
        pad_to_match_res {bool} -- If True, pads a copy of <image> to guarantee that <desired_xyz_resolution> is achieved. (default: {True})
        err_to_higher_res {bool} -- If True and <pad_to_match_res> is False, rounds the shape of the new image up rather than down. (default: {True})
        average_on_downsample {bool} -- If True, performs downsample_image on a copy of <image> before resampling to prevent aliasing. 
            It scales the image by the largest integer possible along each axis without reducing the resolution past the final resolution. (default: {True})
        truncate {bool} -- A kwarg passed to downsample_image. If true, evenly truncates the image down to the nearest multiple of the scale_factor for each axis. (default: {False})
        return_true_resolution {bool} -- If True, rather than just returning the resampled image, returns a tuple containing the resampled image and its actual resolution. (default: {False})
    
    Returns:
        np.ndarray, tuple -- A resampled copy of <image>. 
            If <return_true_resolution> was provided as True, then the return value is a tuple containing the resampled copy of <image> and its actual resolution.
    """

    # Validate arguments.

    # Validate image.
    image = _validate_ndarray(image)

    # Validate xyz_scales.
    xyz_scales = _validate_scalar_to_multi(xyz_scales, size=image.ndim)
    for dim, scale in enumerate(xyz_scales):
        if scale < 0:
            xyz_scales[dim] = -1 / xyz_scales[dim]

    # Validate xyz_resolution.
    xyz_resolution = _validate_xyz_resolution(image.ndim, xyz_resolution)

    # Compute desired_xyz_resolution.
    desired_xyz_resolution = xyz_resolution / xyz_scales

    change_resolution_to_kwargs = dict(
        image=image,
        xyz_resolution=xyz_resolution,
        desired_xyz_resolution=desired_xyz_resolution,
        pad_to_match_res=pad_to_match_res,
        err_to_higher_res=err_to_higher_res,
        average_on_downsample=average_on_downsample,
        truncate=truncate,
        return_true_resolution=return_true_resolution,
        **resample_kwargs)

    return change_resolution_to(**change_resolution_to_kwargs)


# TODO: reconcile use of scipy.interpolate.interpn vs scipy.misc.resize & skimage.transform.downscale_local_mean.

# TODO: isolate negative scale conversion into its own function.
# TODO: merge necessary new_shape transformations etc. into _resample.