bundles / scipy 1.17.1 / scipy / ndimage / _filters / vectorized_filter
function
scipy.ndimage._filters:vectorized_filter
source: /scipy/ndimage/_filters.py :205
Signature
def vectorized_filter ( input , function , * , size = None , footprint = None , output = None , mode = reflect , cval = None , origin = None , axes = None , batch_memory = 1073741824 ) Summary
Filter an array with a vectorized Python callable as the kernel
Parameters
input: array_likeThe input array.
function: callableKernel to apply over a window centered at each element of
input. Callable must have signaturefunction(window: ndarray, *, axis: int | tuple) -> scalarwhere
axisspecifies the axis (or axes) ofwindowalong which the filter function is evaluated.size: scalar or tuple, optionalSee
footprintbelow. Ignored iffootprintis given.footprint: array, optionalEither
sizeorfootprintmust be defined.sizegives the shape that is taken from the input array, at every element position, to define the input to the filter function.footprintis a boolean array that specifies (implicitly) a shape, but also which of the elements within this shape will get passed to the filter function. Thussize=(n, m)is equivalent tofootprint=np.ones((n, m)). We adjustsizeto the number of dimensions indicated byaxes. For instance, ifaxesis(0, 2, 1)andnis passed forsize, then the effectivesizeis(n, n, n).output: array, optionalThe array in which to place the output. By default, an array of the dtype returned by
functionwill be created.mode: {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optionalThe
modeparameter determines how the input array is extended beyond its boundaries. Default is 'reflect'. Behavior for each valid value is as follows:'reflect' (
d c b a | a b c d | d c b a)The input is extended by reflecting about the edge of the last pixel. This mode is also sometimes referred to as half-sample symmetric.
'constant' (
k k k k | a b c d | k k k k)The input is extended by filling all values beyond the edge with the same constant value, defined by the
cvalparameter.'nearest' (
a a a a | a b c d | d d d d)The input is extended by replicating the last pixel.
'mirror' (
d c b | a b c d | c b a)The input is extended by reflecting about the center of the last pixel. This mode is also sometimes referred to as whole-sample symmetric.
'wrap' (
a b c d | a b c d | a b c d)The input is extended by wrapping around to the opposite edge.
'valid' (
| a b c d |)The input is not extended; rather, the output shape is reduced depending on the window size according to the following calculation
window_size = np.asarray(size if size is not None else footprint.shape) output_shape = np.asarray(input.shape) output_shape[np.asarray(axes)] -= (window_size - 1)
cval: scalar, optionalValue to fill past edges of input if
modeis 'constant'. Default is 0.0.origin: int or sequence, optionalControls the placement of the filter on the input array's pixels. A value of 0 (the default) centers the filter over the pixel, with positive values shifting the filter to the left, and negative ones to the right. By passing a sequence of origins with length equal to the number of dimensions of the input array, different shifts can be specified along each axis.
axes: tuple of int, optionalIf None,
inputis filtered along all axes. Otherwise,inputis filtered along the specified axes. Whenaxesis specified, the dimensionality offootprintand the length of any tuples used forsizeororiginmust match the length ofaxes. The ith axis offootprintand the ith element in these tuples corresponds to the ith element ofaxes.batch_memory: int, default: 2**30The maximum number of bytes occupied by data in the
windowarray passed tofunction.
Returns
output: ndarrayFiltered array. The dtype is the output dtype of
function. Iffunctionis scalar-valued when applied to a single window, the shape of the output is that ofinput(unlessmode=='valid'; seemodedocumentation). Iffunctionis multi-valued when applied to a single window, the placement of the corresponding dimensions within the output shape depends entirely on the behavior offunction; see Examples.
Notes
This function works by padding input according to mode, then calling the provided function on chunks of a sliding window view over the padded array. This approach is very simple and flexible, and so the function has many features not offered by some other filter functions (e.g. memory control, float16 and complex dtype support, and any NaN-handling features provided by the function argument).
However, this brute-force approach may perform considerable redundant work. Use a specialized filter (e.g. minimum_filter instead of this function with numpy.min as the callable; uniform_filter instead of this function with numpy.mean as the callable) when possible, as it may use a more efficient algorithm.
When a specialized filter is not available, this function is ideal when function is a vectorized, pure-Python callable. Even better performance may be possible by passing a scipy.LowLevelCallable to generic_filter. generic_filter may also be preferred for expensive callables with large filter footprints and callables that are not vectorized (i.e. those without axis support).
This function does not provide the extra_arguments or extra_keywords arguments provided by some ndimage functions. There are two reasons:
The passthrough functionality can be achieved by the user: simply wrap the original callable in another function that provides the required arguments; e.g.,
function=lambda input, axis: function(input, *extra_arguments, axis=axis, **extra_keywords).There are use cases for
functionto be passed additional sliding-window data tofunctionbesidesinput. This is not yet implemented, but we reserve these argument names for such a feature, which would add capability rather than providing a duplicate interface to existing capability.
Array API Standard Support
vectorized_filter has experimental support for Python Array API Standard compatible backends in addition to NumPy. Please consider testing these features by setting an environment variable SCIPY_ARRAY_API=1 and providing CuPy, PyTorch, JAX, or Dask arrays as array arguments. The following combinations of backend and device (or other capability) are supported.
==================== ==================== ==================== Library CPU GPU ==================== ==================== ==================== NumPy ✅ n/a CuPy n/a ⛔ PyTorch ✅ ⛔ JAX ⚠️ no JIT ⛔ Dask ⚠️ computes graph n/a ==================== ==================== ====================
See
dev-arrayapifor more information.
Examples
Suppose we wish to perform a median filter with even window size on a ``float16`` image. Furthermore, the image has NaNs that we wish to be ignored (and effectively removed by the filter). `median_filter` does not support ``float16`` data, its behavior when NaNs are present is not defined, and for even window sizes, it does not return the usual sample median - the average of the two middle elements. This would be an excellent use case for `vectorized_filter` with ``function=np.nanmedian``, which supports the required interface: it accepts a data array of any shape as the first positional argument, and tuple of axes as keyword argument ``axis``.import numpy as np from scipy import datasets, ndimage from scipy.ndimage import vectorized_filter import matplotlib.pyplot as plt✓
ascent = ndimage.zoom(datasets.ascent(), 0.5).astype(np.float16) ascent[::16, ::16] = np.nan result = vectorized_filter(ascent, function=np.nanmedian, size=4)⚠
fig = plt.figure() plt.gray() # show the filtered result in grayscale ax1 = fig.add_subplot(121) # left side ax2 = fig.add_subplot(122) # right side✓
ax1.imshow(ascent) ax2.imshow(result)⚠
fig.tight_layout() plt.show()✓

ascent = ndimage.zoom(datasets.ascent(), 0.5)
⚠def get_quantile_fun(p): return lambda x, axis: np.quantile(x, p, axis=axis)✓
ref1 = vectorized_filter(ascent, get_quantile_fun(0.25), size=4) ref2 = vectorized_filter(ascent, get_quantile_fun(0.50), size=4) ref3 = vectorized_filter(ascent, get_quantile_fun(0.75), size=4) ref = np.stack([ref1, ref2, ref3])⚠
def quartiles(x, axis): return np.quantile(x, [0.25, 0.50, 0.75], axis=axis)✓
res = vectorized_filter(ascent, quartiles, size=4, batch_memory=np.inf) np.all(np.isclose(res, ref))⚠
res.shape == (3,) + ascent.shape
⚠def quartiles(x, axis): return np.moveaxis(np.quantile(x, [0.25, 0.50, 0.75], axis=axis), 0, -1)✓
res = vectorized_filter(ascent, quartiles, size=4, batch_memory=np.inf) res.shape == ascent.shape + (3,)⚠
from scipy import stats rng = np.random.default_rng(3195824598724609246) input = rng.integers(255, size=(50, 50)).astype(np.uint8) def simple_mode(input): return stats.mode(input, axis=None).mode ref = ndimage.generic_filter(input, simple_mode, size=5)✓
def vectorized_mode(x, axis=(-1,)): n_axes = 1 if np.isscalar(axis) else len(axis) x = np.moveaxis(x, axis, tuple(range(-n_axes, 0))) x = np.reshape(x, x.shape[:-n_axes] + (-1,)) y = np.sort(x, axis=-1) i = np.concatenate([np.ones(y.shape[:-1] + (1,), dtype=bool), y[..., :-1] != y[..., 1:]], axis=-1) indices = np.arange(y.size)[i.ravel()] counts = np.diff(indices, append=y.size) counts = np.reshape(np.repeat(counts, counts), y.shape) k = np.argmax(counts, axis=-1, keepdims=True) return np.take_along_axis(y, k, axis=-1)[..., 0] res = vectorized_filter(input, vectorized_mode, size=5) np.all(res == ref)✓
See also
Aliases
-
scipy.ndimage.vectorized_filter