I am studying image-processing using Numpy and facing a problem with filtering with convolution.
I would like to convolve a gray-scale image. (convolve a 2d Array with a smaller 2d Array)
Does anyone have an idea to refine my method ?
I know that scipy supports convolve2d but I want to make a convolve2d only by using Numpy.
First, I made a 2d array the submatrices.
a = np.arange(25).reshape(5,5) # original matrix submatrices = np.array([ [a[:-2,:-2], a[:-2,1:-1], a[:-2,2:]], [a[1:-1,:-2], a[1:-1,1:-1], a[1:-1,2:]], [a[2:,:-2], a[2:,1:-1], a[2:,2:]]])
the submatrices seems complicated but what I am doing is shown in the following drawing.
Next, I multiplied each submatrices with a filter.
conv_filter = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]]) multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices)
and summed them.
np.sum(np.sum(multiplied_subs, axis = -3), axis = -3) #array([[ 6, 7, 8], # [11, 12, 13], # [16, 17, 18]])
Thus this procudure can be called my convolve2d.
def my_convolve2d(a, conv_filter): submatrices = np.array([ [a[:-2,:-2], a[:-2,1:-1], a[:-2,2:]], [a[1:-1,:-2], a[1:-1,1:-1], a[1:-1,2:]], [a[2:,:-2], a[2:,1:-1], a[2:,2:]]]) multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices) return np.sum(np.sum(multiplied_subs, axis = -3), axis = -3)
However, I find this my_convolve2d troublesome for 3 reasons.
Thank you for reading this far.
Kind of update. I wrote a conv3d for myself. I will leave this as a public domain.
def convolve3d(img, kernel): # calc the size of the array of submatracies sub_shape = tuple(np.subtract(img.shape, kernel.shape) + 1) # alias for the function strd = np.lib.stride_tricks.as_strided # make an array of submatracies submatrices = strd(img,kernel.shape + sub_shape,img.strides * 2) # sum the submatraces and kernel convolved_matrix = np.einsum('hij,hijklm->klm', kernel, submatrices) return convolved_matrix
numpy. convolve(a, v, mode='full')[source] Returns the discrete, linear convolution of two one-dimensional sequences. The convolution operator is often seen in signal processing, where it models the effect of a linear time-invariant system on a signal [1].
The 2D convolution is a fairly simple operation at heart: you start with a kernel, which is simply a small matrix of weights. This kernel “slides” over the 2D input data, performing an elementwise multiplication with the part of the input it is currently on, and then summing up the results into a single output pixel.
You could generate the subarrays using as_strided
:
import numpy as np a = 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]]) sub_shape = (3,3) view_shape = tuple(np.subtract(a.shape, sub_shape) + 1) + sub_shape strides = a.strides + a.strides sub_matrices = np.lib.stride_tricks.as_strided(a,view_shape,strides)
To get rid of your second "ugly" sum, alter your einsum
so that the output array only has j
and k
. This implies your second summation.
conv_filter = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]]) m = np.einsum('ij,ijkl->kl',conv_filter,sub_matrices) # [[ 6 7 8] # [11 12 13] # [16 17 18]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With