Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convolve2d just by using Numpy

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.

What I have done

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.

submatrices

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) 

multiplied_subs

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.

  1. Generation of the submatrices is too awkward that is difficult to read and can only be used when the filter is 3*3
  2. The size of the varient submatrices seems to be too big, since it is approximately 9 folds bigger than the original matrix.
  3. The summing seems a little non intuitive. Simply said, ugly.

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 
like image 628
Allosteric Avatar asked Mar 29 '17 07:03

Allosteric


People also ask

What is Numpy convolve in Python?

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].

How do you do 2D convolution?

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.


1 Answers

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]] 
like image 106
Crispin Avatar answered Oct 09 '22 12:10

Crispin