Skip to content

pyhiperta.utils.convolve

Convolution utilities for hexagonal lattices.

Functions:

Name Description
convolve_view

Read-only view into a that has stencil_shape extra dimensions for each part of a that a stencil

convolve_view

convolve_view(a, stencil_shape)

Read-only view into a that has stencil_shape extra dimensions for each part of a that a stencil of shape stencil_shape would operate on.

Directly taken from https://stackoverflow.com/questions/43086557/convolve2d-just-by-using-numpy It allows to view the sub-parts of a of shape stencil_shape without copying or duplicating a's data.

Parameters:

Name Type Description Default
a ndarray

The array to get the view in.

required
stencil_shape Tuple[int, ...]

The shape of the stencil that we would want to operate on a. It must have the same number of dimension than a.

required

Returns:

Type Description
ndarray

Read-only view in a with stencil_shape extra dimension for each stencil sub-parts. Shape: [(a.shape - (stencil_shape - 1)), stencil_shape]. The shape first dimensions are the same than a's, subtracting the stencil elements that would fall outside of a in the boundaries. The shape last dimensions are the same as stencil_shape. Example: a.shape=(55, 55), stencil_shape=(3, 3): result's shape: (53, 53, 3, 3)

Examples:

>>> a = np.arange(55 * 55).reshape((55, 55))
>>> stencil = np.array([[0, 1], [1, 0]])
>>> convolve_view(a, stencil.shape).shape
(54, 54, 2, 2)
>>> convolve_view(a, stencil.shape)[0, 0, :, :]  # left/high-most 2x2 part of `a`.
...
array([[ 0,  1],
       [55, 56]])
>>> convolve_view(a, stencil.shape)[1, 1, :, :]
...
array([[ 56,  57],
       [111, 112]])

Raises:

Type Description
ValueError

If stencil_shape and the array's shape are not compatible, or if the stencil shape is invalid.

Source code in src/pyhiperta/utils/convolve.py
def convolve_view(a: np.ndarray, stencil_shape: tuple[int, ...]) -> np.ndarray:
    """Read-only view into `a` that has `stencil_shape` extra dimensions for each part of `a` that a stencil
       of shape `stencil_shape` would operate on.

    Directly taken from https://stackoverflow.com/questions/43086557/convolve2d-just-by-using-numpy
    It allows to view the sub-parts of `a` of shape `stencil_shape` without copying or duplicating `a`'s data.

    Parameters
    ----------
    a : np.ndarray
        The array to get the view in.
    stencil_shape : Tuple[int, ...]
        The shape of the stencil that we would want to operate on `a`. It must have the same number of dimension
        than `a`.

    Returns
    -------
    np.ndarray
        Read-only view in `a` with `stencil_shape` extra dimension for each stencil sub-parts.
        Shape: [*(a.shape - (stencil_shape - 1)), *stencil_shape]. The shape first dimensions are the same than `a`'s,
        subtracting the stencil elements that would fall outside of `a` in the boundaries. The shape last dimensions are
        the same as `stencil_shape`. Example: a.shape=(55, 55), stencil_shape=(3, 3): result's shape: (53, 53, 3, 3)

    Examples
    --------
    >>> a = np.arange(55 * 55).reshape((55, 55))
    >>> stencil = np.array([[0, 1], [1, 0]])
    >>> convolve_view(a, stencil.shape).shape
    (54, 54, 2, 2)
    >>> convolve_view(a, stencil.shape)[0, 0, :, :]  # left/high-most 2x2 part of `a`.
    ... # doctest: +NORMALIZE_WHITESPACE
    array([[ 0,  1],
           [55, 56]])
    >>> convolve_view(a, stencil.shape)[1, 1, :, :]
    ... # doctest: +NORMALIZE_WHITESPACE
    array([[ 56,  57],
           [111, 112]])

    Raises
    ------
    ValueError
        If `stencil_shape` and the array's shape are not compatible, or if the stencil shape is invalid.
    """
    if len(a.shape) != len(stencil_shape):
        raise ValueError(
            f"Stencil shape {stencil_shape} and array shape {a.shape} must have the same number of dimensions"
        )
    if not all([0 < s <= a.shape[i] for i, s in enumerate(stencil_shape)]):
        raise ValueError(
            "Stencil shape must be strictly positive and smaller or equal than a.shape in all dimensions. "
            f"Got stencil shape {stencil_shape} and a's shape: {a.shape}"
        )

    # The output shape is a.shape - (stencil_shape - 1)
    # The minus 1 is because the stencil center element is applied on each pixel of a, so it
    # doesn't reduce the shape.
    # Stencil of shape [3, 3] reduces each axis shape by 2: 1 element on each end of each axis for instance
    convolve_view_shape = tuple(np.subtract(a.shape, stencil_shape) + 1) + stencil_shape
    # strides of the view's extra dimension are the same than of the input array: we index subparts of it!
    convolve_view_strides = a.strides + a.strides

    return np.lib.stride_tricks.as_strided(
        a, shape=convolve_view_shape, strides=convolve_view_strides, writeable=False
    )