| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- import math
- import numpy as np
- from .._shared.filters import gaussian
- from .._shared.utils import convert_to_float
- from ._warps import resize
- def _smooth(image, sigma, mode, cval, channel_axis):
- """Return image with each channel smoothed by the Gaussian filter."""
- smoothed = np.empty_like(image)
- # apply Gaussian filter to all channels independently
- if channel_axis is not None:
- # can rely on gaussian to insert a 0 entry at channel_axis
- channel_axis = channel_axis % image.ndim
- sigma = (sigma,) * (image.ndim - 1)
- else:
- channel_axis = None
- gaussian(
- image,
- sigma=sigma,
- out=smoothed,
- mode=mode,
- cval=cval,
- channel_axis=channel_axis,
- )
- return smoothed
- def _check_factor(factor):
- if factor <= 1:
- raise ValueError('scale factor must be greater than 1')
- def pyramid_reduce(
- image,
- downscale=2,
- sigma=None,
- order=1,
- mode='reflect',
- cval=0,
- preserve_range=False,
- *,
- channel_axis=None,
- ):
- """Smooth and then downsample image.
- Parameters
- ----------
- image : ndarray
- Input image.
- downscale : float, optional
- Downscale factor.
- sigma : float, optional
- Sigma for Gaussian filter. Default is `2 * downscale / 6.0` which
- corresponds to a filter mask twice the size of the scale factor that
- covers more than 99% of the Gaussian distribution.
- order : int, optional
- Order of splines used in interpolation of downsampling. See
- `skimage.transform.warp` for detail.
- mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional
- The mode parameter determines how the array borders are handled, where
- cval is the value when mode is equal to 'constant'.
- cval : float, optional
- Value to fill past edges of input if mode is 'constant'.
- preserve_range : bool, optional
- Whether to keep the original range of values. Otherwise, the input
- image is converted according to the conventions of `img_as_float`.
- Also see https://scikit-image.org/docs/dev/user_guide/data_types.html
- channel_axis : int or None, optional
- If None, the image is assumed to be a grayscale (single channel) image.
- Otherwise, this parameter indicates which axis of the array corresponds
- to channels.
- .. versionadded:: 0.19
- ``channel_axis`` was added in 0.19.
- Returns
- -------
- out : array
- Smoothed and downsampled float image.
- References
- ----------
- .. [1] http://persci.mit.edu/pub_pdfs/pyramid83.pdf
- """
- _check_factor(downscale)
- image = convert_to_float(image, preserve_range)
- if channel_axis is not None:
- channel_axis = channel_axis % image.ndim
- out_shape = tuple(
- math.ceil(d / float(downscale)) if ax != channel_axis else d
- for ax, d in enumerate(image.shape)
- )
- else:
- out_shape = tuple(math.ceil(d / float(downscale)) for d in image.shape)
- if sigma is None:
- # automatically determine sigma which covers > 99% of distribution
- sigma = 2 * downscale / 6.0
- smoothed = _smooth(image, sigma, mode, cval, channel_axis)
- out = resize(
- smoothed, out_shape, order=order, mode=mode, cval=cval, anti_aliasing=False
- )
- return out
- def pyramid_expand(
- image,
- upscale=2,
- sigma=None,
- order=1,
- mode='reflect',
- cval=0,
- preserve_range=False,
- *,
- channel_axis=None,
- ):
- """Upsample and then smooth image.
- Parameters
- ----------
- image : ndarray
- Input image.
- upscale : float, optional
- Upscale factor.
- sigma : float, optional
- Sigma for Gaussian filter. Default is `2 * upscale / 6.0` which
- corresponds to a filter mask twice the size of the scale factor that
- covers more than 99% of the Gaussian distribution.
- order : int, optional
- Order of splines used in interpolation of upsampling. See
- `skimage.transform.warp` for detail.
- mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional
- The mode parameter determines how the array borders are handled, where
- cval is the value when mode is equal to 'constant'.
- cval : float, optional
- Value to fill past edges of input if mode is 'constant'.
- preserve_range : bool, optional
- Whether to keep the original range of values. Otherwise, the input
- image is converted according to the conventions of `img_as_float`.
- Also see https://scikit-image.org/docs/dev/user_guide/data_types.html
- channel_axis : int or None, optional
- If None, the image is assumed to be a grayscale (single channel) image.
- Otherwise, this parameter indicates which axis of the array corresponds
- to channels.
- .. versionadded:: 0.19
- ``channel_axis`` was added in 0.19.
- Returns
- -------
- out : array
- Upsampled and smoothed float image.
- References
- ----------
- .. [1] http://persci.mit.edu/pub_pdfs/pyramid83.pdf
- """
- _check_factor(upscale)
- image = convert_to_float(image, preserve_range)
- if channel_axis is not None:
- channel_axis = channel_axis % image.ndim
- out_shape = tuple(
- math.ceil(upscale * d) if ax != channel_axis else d
- for ax, d in enumerate(image.shape)
- )
- else:
- out_shape = tuple(math.ceil(upscale * d) for d in image.shape)
- if sigma is None:
- # automatically determine sigma which covers > 99% of distribution
- sigma = 2 * upscale / 6.0
- resized = resize(
- image, out_shape, order=order, mode=mode, cval=cval, anti_aliasing=False
- )
- out = _smooth(resized, sigma, mode, cval, channel_axis)
- return out
- def pyramid_gaussian(
- image,
- max_layer=-1,
- downscale=2,
- sigma=None,
- order=1,
- mode='reflect',
- cval=0,
- preserve_range=False,
- *,
- channel_axis=None,
- ):
- """Yield images of the Gaussian pyramid formed by the input image.
- Recursively applies the `pyramid_reduce` function to the image, and yields
- the downscaled images.
- Note that the first image of the pyramid will be the original, unscaled
- image. The total number of images is `max_layer + 1`. In case all layers
- are computed, the last image is either a one-pixel image or the image where
- the reduction does not change its shape.
- Parameters
- ----------
- image : ndarray
- Input image.
- max_layer : int, optional
- Number of layers for the pyramid. 0th layer is the original image.
- Default is -1 which builds all possible layers.
- downscale : float, optional
- Downscale factor.
- sigma : float, optional
- Sigma for Gaussian filter. Default is `2 * downscale / 6.0` which
- corresponds to a filter mask twice the size of the scale factor that
- covers more than 99% of the Gaussian distribution.
- order : int, optional
- Order of splines used in interpolation of downsampling. See
- `skimage.transform.warp` for detail.
- mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional
- The mode parameter determines how the array borders are handled, where
- cval is the value when mode is equal to 'constant'.
- cval : float, optional
- Value to fill past edges of input if mode is 'constant'.
- preserve_range : bool, optional
- Whether to keep the original range of values. Otherwise, the input
- image is converted according to the conventions of `img_as_float`.
- Also see https://scikit-image.org/docs/dev/user_guide/data_types.html
- channel_axis : int or None, optional
- If None, the image is assumed to be a grayscale (single channel) image.
- Otherwise, this parameter indicates which axis of the array corresponds
- to channels.
- .. versionadded:: 0.19
- ``channel_axis`` was added in 0.19.
- Returns
- -------
- pyramid : generator
- Generator yielding pyramid layers as float images.
- References
- ----------
- .. [1] http://persci.mit.edu/pub_pdfs/pyramid83.pdf
- """
- _check_factor(downscale)
- # cast to float for consistent data type in pyramid
- image = convert_to_float(image, preserve_range)
- layer = 0
- current_shape = image.shape
- prev_layer_image = image
- yield image
- # build downsampled images until max_layer is reached or downscale process
- # does not change image size
- while layer != max_layer:
- layer += 1
- layer_image = pyramid_reduce(
- prev_layer_image,
- downscale,
- sigma,
- order,
- mode,
- cval,
- channel_axis=channel_axis,
- )
- prev_shape = current_shape
- prev_layer_image = layer_image
- current_shape = layer_image.shape
- # no change to previous pyramid layer
- if current_shape == prev_shape:
- break
- yield layer_image
- def pyramid_laplacian(
- image,
- max_layer=-1,
- downscale=2,
- sigma=None,
- order=1,
- mode='reflect',
- cval=0,
- preserve_range=False,
- *,
- channel_axis=None,
- ):
- """Yield images of the laplacian pyramid formed by the input image.
- Each layer contains the difference between the downsampled and the
- downsampled, smoothed image::
- layer = resize(prev_layer) - smooth(resize(prev_layer))
- Note that the first image of the pyramid will be the difference between the
- original, unscaled image and its smoothed version. The total number of
- images is `max_layer + 1`. In case all layers are computed, the last image
- is either a one-pixel image or the image where the reduction does not
- change its shape.
- Parameters
- ----------
- image : ndarray
- Input image.
- max_layer : int, optional
- Number of layers for the pyramid. 0th layer is the original image.
- Default is -1 which builds all possible layers.
- downscale : float, optional
- Downscale factor.
- sigma : float, optional
- Sigma for Gaussian filter. Default is `2 * downscale / 6.0` which
- corresponds to a filter mask twice the size of the scale factor that
- covers more than 99% of the Gaussian distribution.
- order : int, optional
- Order of splines used in interpolation of downsampling. See
- `skimage.transform.warp` for detail.
- mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional
- The mode parameter determines how the array borders are handled, where
- cval is the value when mode is equal to 'constant'.
- cval : float, optional
- Value to fill past edges of input if mode is 'constant'.
- preserve_range : bool, optional
- Whether to keep the original range of values. Otherwise, the input
- image is converted according to the conventions of `img_as_float`.
- Also see https://scikit-image.org/docs/dev/user_guide/data_types.html
- channel_axis : int or None, optional
- If None, the image is assumed to be a grayscale (single channel) image.
- Otherwise, this parameter indicates which axis of the array corresponds
- to channels.
- .. versionadded:: 0.19
- ``channel_axis`` was added in 0.19.
- Returns
- -------
- pyramid : generator
- Generator yielding pyramid layers as float images.
- References
- ----------
- .. [1] http://persci.mit.edu/pub_pdfs/pyramid83.pdf
- .. [2] http://sepwww.stanford.edu/data/media/public/sep/morgan/texturematch/paper_html/node3.html
- """
- _check_factor(downscale)
- # cast to float for consistent data type in pyramid
- image = convert_to_float(image, preserve_range)
- if sigma is None:
- # automatically determine sigma which covers > 99% of distribution
- sigma = 2 * downscale / 6.0
- current_shape = image.shape
- smoothed_image = _smooth(image, sigma, mode, cval, channel_axis)
- yield image - smoothed_image
- if channel_axis is not None:
- channel_axis = channel_axis % image.ndim
- shape_without_channels = list(current_shape)
- shape_without_channels.pop(channel_axis)
- shape_without_channels = tuple(shape_without_channels)
- else:
- shape_without_channels = current_shape
- # build downsampled images until max_layer is reached or downscale process
- # does not change image size
- if max_layer == -1:
- max_layer = math.ceil(math.log(max(shape_without_channels), downscale))
- for layer in range(max_layer):
- if channel_axis is not None:
- out_shape = tuple(
- math.ceil(d / float(downscale)) if ax != channel_axis else d
- for ax, d in enumerate(current_shape)
- )
- else:
- out_shape = tuple(math.ceil(d / float(downscale)) for d in current_shape)
- resized_image = resize(
- smoothed_image,
- out_shape,
- order=order,
- mode=mode,
- cval=cval,
- anti_aliasing=False,
- )
- smoothed_image = _smooth(resized_image, sigma, mode, cval, channel_axis)
- current_shape = resized_image.shape
- yield resized_image - smoothed_image
|