dippykit.utilities module

Module of image processing utility functions

This module contains an assortment of general purpose or otherwise ill-categorized functions that are useful in image processing.

dippykit.utilities.block_process(im: numpy.ndarray, func: typing.Callable[numpy.ndarray, typing.Any], block_size: typing.Union[typing.Tuple[typing.Union[int, float], typing.Union[int, float]], int, float] = (8, 8), stride: typing.Union[typing.Tuple[typing.Union[int, float], typing.Union[int, float]], int, float] = None) → numpy.ndarray[source]

Performs an operation on an array/image in smaller blocks and concatenates the results

Given an array/image, a function, and a block size, this function will iterate through all non-overlapping blocks of size block_size and perform the specified function on each block. The return values of the specified function for each block are then concatenated together and returned as a new array.

Blocks begin with array index [0, 0] and continue until all values in the input array have been used. If a full block can’t be created from the area a block would occupy, a partial block will be evaluated (e.g. using a block size of (8, 8) on an image that is of size (9, 9) will result in a block of size (8, 8), a block of size (8,1), a block of size (1, 8), and a block of size (1, 1)).

The function argument, func, can return anything that numpy is able to convert to an ndarray. This value may be a scalar, vector, matrix, or anything that will consistently concatenate as blocks progress along each axis.

If a stride is specified, then blocks edges will be separated by the stride argument instead of by the block size itself. This can be used to process overlapping blocks or to skip over regions of an array/image.

Parameters:
  • im (numpy.ndarray) – The array or image to be processed.
  • func (Callable[[np.ndarray], Any]) – The function to apply to each block. The return value must be something that numpy can convert to an ndarray.
  • block_size (ShapeType) – (default=(8, 8)) The size of each block.
  • stride (ShapeType) – (default=block_size) The stride to take when moving to the next block.
Return type:

numpy.ndarray

Returns:

The concatenated output of the function func for each block.

Examples:

>>> import numpy as np
>>> im = np.array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
...                [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
...                [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
...                [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
...                [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
...                [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
...                [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
...                [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
...                [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
...                [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
>>> block_process(im, np.transpose, (2, 2))
array([[ 0, 10,  2, 12,  4, 14,  6, 16,  8, 18],
       [ 1, 11,  3, 13,  5, 15,  7, 17,  9, 19],
       [20, 30, 22, 32, 24, 34, 26, 36, 28, 38],
       [21, 31, 23, 33, 25, 35, 27, 37, 29, 39],
       [40, 50, 42, 52, 44, 54, 46, 56, 48, 58],
       [41, 51, 43, 53, 45, 55, 47, 57, 49, 59],
       [60, 70, 62, 72, 64, 74, 66, 76, 68, 78],
       [61, 71, 63, 73, 65, 75, 67, 77, 69, 79],
       [80, 90, 82, 92, 84, 94, 86, 96, 88, 98],
       [81, 91, 83, 93, 85, 95, 87, 97, 89, 99]])
>>> block_process(im, np.flipud, (3, 5))
array([[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
>>> block_process(im, np.mean, (5, 5))
array([[22., 27.],
       [72., 77.]])
>>> block_process(im, np.mean, (2, 2), stride=(4, 4))
array([[16.5, 20.5, 23.5],
       [56.5, 60.5, 63.5],
       [86.5, 90.5, 93.5]])
dippykit.utilities.zigzag_indices(shape: typing.Union[typing.Tuple[int, int], int], length: int = -1) → typing.Tuple[numpy.ndarray, numpy.ndarray][source]

Generates indices for a zigzag pattern in a matrix

Given a matrix shape, this function will determine the indices in such a matrix of consecutive elements in a zigzag pattern. If an integer length is specified, the indices of only length elements will be returned. It is almost always preferable to specify a length if one does not need every element’s indices. This saves computation time.

The zigzag pattern used here is akin to the one used in JPEG.

Parameters:
  • shape (Tuple[int, int] or int) – The shape of the matrix for which the zigzag indices are applicable.
  • length (int) – (default=shape[0]*shape[1]) The number of indices to be returned; equivalently, the length of each ndarray returned.
Return type:

Tuple[numpy.ndarray, numpy.ndarray]

Returns:

A length 2 tuple of indices of zigzag values.

Examples:

>>> import numpy as np
>>> im = np.array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
...                [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
...                [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
...                [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
...                [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
...                [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
...                [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
...                [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
...                [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
...                [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
>>> im[zigzag_indices(im.shape, 10)]
array([ 0,  1, 10, 20, 11,  2,  3, 12, 21, 30])
>>> im_2 = np.array([[ 0,  1,  2],
...                  [10, 11, 12],
...                  [20, 21, 22]])
>>> ind = zigzag_indices(im_2.shape)
>>> ind
(array([0, 0, 1, 2, 1, 0, 1, 2, 2]), array([0, 1, 0, 0, 1, 2, 2, 1, 2]))
>>> im_2[ind]
array([ 0,  1, 10, 20, 11,  2, 12, 21, 22])
dippykit.utilities.conv2d_grid(f_lower: typing.Tuple[typing.Union[int, float], typing.Union[int, float]], g_lower: typing.Tuple[typing.Union[int, float], typing.Union[int, float]], f: numpy.ndarray, g: numpy.ndarray) → typing.Tuple[numpy.ndarray, numpy.ndarray][source]

Generates a meshgrid for the domain of a convolution result

Given the domain values of the [0, 0] index in the two-dimensional signals f and g and the signals f and g themselves, this function returns a meshgrid of the domain of f convolved with g.

A step size of 1 is assumed uniformly for both signal domains. For example, if the domain value of the [0, 0] index in f is [x=3, y=1], then this function assumes the domain value of the [1, 7] index in f is [x=(3+1)=4, y=(1+7)=8].

A bit of background:

A common point of obscurity in image processing is the treatment of arrays as actual arrays vs. two-dimensional signals. When dealing with the latter, an underlying domain is implied. For convolution operations on two-dimensional signals in particular, keeping track of the changes in domain can be annoying and tricky. This function exists to be a simple, reliable way to track the domains of your signals after convolution operations.

Parameters:
  • f_lower (Tuple[int or float, int or float]) – The domain value of the point f[0, 0].
  • g_lower (Tuple[int or float, int or float]) – The domain value of the point g[0, 0].
  • f (numpy.ndarray) – An ndarray interpreted as a two-dimensional signal.
  • g (numpy.ndarray) – An ndarray interpreted as a two-dimensional signal.
Return type:

Tuple[numpy.ndarray, numpy.ndarray]

Returns:

The domain of the convolution of f and g in meshgrid format.

Examples:

>>> import numpy as np
>>> import dippykit as dip
>>> f_domain_x, f_domain_y = np.meshgrid(np.arange(-5, 5), np.arange(0, 7))
>>> f = np.zeros(f_domain_x.shape)
>>> f[(0 == f_domain_x) & (0 == f_domain_y)] = 1
>>> f
array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
>>> g_domain_x, g_domain_y = np.meshgrid(np.arange(0, 5), np.arange(-5, 2))
>>> g = np.zeros(g_domain_x.shape)
>>> g[(0 == g_domain_x) & (0 == g_domain_y)] = 1
>>> g
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])
>>> fg = dip.convolve2d(f, g)
>>> fg
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
>>> f_lower = (f_domain_x[0, 0], f_domain_y[0, 0])
>>> g_lower = (g_domain_x[0, 0], g_domain_y[0, 0])
>>> fg_domain_x, fg_domain_y = conv2d_grid(f_lower, g_lower, f, g)
>>> fg[(0 == fg_domain_x) & (0 == fg_domain_y)]
array([1.])
dippykit.utilities.rgb2ycbcr(rgb_im: numpy.ndarray) → numpy.ndarray[source]

Converts an RGB image to YCbCr, in exactly the same manner as Matlab

This function will return the same output as Matlab’s rgb2ycbcr function for images with a dtype of uint8.

Parameters:rgb_im (numpy.ndarray) – The RGB image to be converted.
Return type:numpy.ndarray
Returns:A YCbCr image, matching the Matlab conversion metric for dtypes of numpy.uint8.

Examples:

>>> import numpy as np
>>> im = np.array([[[255, 255, 255], [128, 128, 128]],
...                [[  0,   0,   0], [ 64,  32, 224]]], dtype=np.uint8)
>>> rgb2ycbcr(im)
array([[[235, 128, 128],
        [126, 128, 128]],
       [[ 16, 128, 128],
        [ 70, 208, 128]]], dtype=uint8)
dippykit.utilities.rgb2gray(rgb_im: numpy.ndarray) → numpy.ndarray[source]

Converts a 3-channel RGB image to 1-channel gray image

Given a 3-channel RGB image, this function converts the image to a 1-channel gray image with particular weights for each channel. These weights are the same as those used in the Matlab rgb2gray function.

Parameters:rgb_im (numpy.ndarray) – The input 3-channel RGB image.
Return type:numpy.ndarray
Returns:The output 1-channel gray image.

Examples:

>>> import numpy as np
>>> im = np.array([[[255, 255, 255], [128, 128, 128]],
...                [[  0,   0,   0], [ 64,  32, 224]]], dtype=np.uint8)
>>> rgb2gray(im)
array([[255, 128],
       [  0,  63]], dtype=uint8)
dippykit.utilities.convolve2d(sig_1: numpy.ndarray, sig_2: numpy.ndarray, mode: str = 'full', boundary: str = 'fill', fillvalue: typing.Union[int, float] = 0, like_matlab: bool = False) → numpy.ndarray[source]

Convolves two arrays in two dimensions

Given two input arrays, this function computes their 2D convolution. The input arrays must be either 2-dimensional or 3-dimensional, and the second input array must have less than or equal dimensions to the first.

Convolution is always computed across the first and second dimensions of the input arrays. In the case that both input arrays are 3-dimensional, the result is the sum of 2D convolutions for each corresponding 2D slice of the input arrays. This result is 2-dimensional. In the case that only the first input array is 3-dimensional, the result is the stack of 2D convolutions for each 2D slice of the first input array with the second input array. This result is 3-dimensional.

In cases where either the first or second dimension of the second input array is an even integer, and where the mode is ‘same’, this function will prefer lesser indices in centering by default. Setting the ‘like_matlab’ keyword argument to True will alter this behavior to prefer greater indices in centering. This is better understood through demonstration in the examples below.

This function is essentially a wrapper for scipy.signal.convolve2d, so more detailed documentation may be found there.

Parameters:
  • sig_1 (numpy.ndarray) – The first input array.
  • sig_2 (numpy.ndarray) – The second input array.
  • mode (str) – (default=’full’) The mode to be used for the convolution. If set to ‘full’, a full convolution will be performed. If set to ‘same’, the output will be the same size (in the first two dimensions) as the first input array. If set to valid, only values generated without padding will be retained. For more information, see scipy.signal.convolve2d.
  • boundary (str) – (default=’fill’) A string representing how boundaries will be handled. If set to ‘fill’, the first input array is padded with fillvalue values. If set to ‘wrap’, the first input array is padded with copies of itself in a tile configuration. If set to ‘symm’, the first input array is padded with reflected copies of itself. For more information, see scipy.signal.convolve2d.
  • fillvalue (NumericType) – (default=0) The numeric value to pad the first input array with (assuming boundary=’fill’).
  • like_matlab (bool) – (default=False) If set to True, this function will return arrays in the same manner that Matlab would.
Return type:

numpy.ndarray

Returns:

The convolution of the two arrays.

Note

This function wraps around functions from other packages. Reading these functions’ documentations may be useful. See the See also section for more information.

See also

scipy.signal.convolve2d
Documentation of the convolve2d function from Scipy

Examples:

>>> a = np.array([[1, 0, 2],
...               [0, 3, 0],
...               [4, 0, 5]])
>>> b = np.array([[ 1,  1],
...               [-1, -1]])
>>> convolve2d(a, b)
array([[ 1,  1,  2,  2],
       [-1,  2,  1, -2],
       [ 4,  1,  2,  5],
       [-4, -4, -5, -5]])
>>> convolve2d(a, b, mode='same')
array([[ 1,  1,  2],
       [-1,  2,  1],
       [ 4,  1,  2]])
>>> convolve2d(a, b, mode='same', like_matlab=True)
array([[ 2,  1, -2],
       [ 1,  2,  5],
       [-4, -5, -5]])
>>> c = np.stack(([[1, 1],
...                [1, 1]],
...               [[2, 2],
...                [2, 2]],
...               [[3, 3],
...                [3, 3]]), axis=2)
>>> d = convolve2d(c, b)
>>> d[:, :, 0]
array([[ 1,  2,  1],
       [ 0,  0,  0],
       [-1, -2, -1]])
>>> d[:, :, 1]
array([[ 2,  4,  2],
       [ 0,  0,  0],
       [-2, -4, -2]])
>>> d[:, :, 2]
array([[ 3,  6,  3],
       [ 0,  0,  0],
       [-3, -6, -3]])