Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convolution without any padding-opencv Python

Is there any function in Opencv-python that can convolve an image with a kernel without any padding ? Basically, I want an image in which convolution takes place only in the regions where the kernel and the portion of the image fully overlaps.

like image 346
Harshit Rajgadia Avatar asked Sep 12 '16 19:09

Harshit Rajgadia


1 Answers

OpenCV only supports convolving an image where the output returned is the same size as the input image. As such, you can still use OpenCV's filter functions, but simply ignore those pixels along the edges where the kernel didn't fully encapsulate itself inside the image. Assuming that your image kernel is odd, you can simply divide each of the dimensions by half, take the floor (or round down) and use these to cut away the information that isn't valid and return what is left. As Divakar mentioned, this is the same method as using scipy's 2D convolution method with the 'valid' option.

As such, assuming that your image is stored in A and your kernel is stored in B, you would simply do the following to get the filtered image where the kernel was fully encapsulated inside the image. Take note that we're going to assume that the kernel is odd and the output is stored in C.

import cv2
import numpy as np

A = cv2.imread('...') # Load in image here
B = (1.0/25.0)*np.ones((5,5)) # Specify kernel here
C = cv2.filter2D(A, -1, B) # Convolve

H = np.floor(np.array(B.shape)/2).astype(np.int) # Find half dims of kernel
C = C[H[0]:-H[0],H[1]:-H[1]] # Cut away unwanted information

Take note that cv2.filter2D performs correlation, not convolution. However if the kernel is symmetric (that is if you take the transpose and it equals itself), correlation and convolution are equivalent. If this is not the case, you will need to perform a 180 degree rotation of the kernel before using cv2.filter2D. You can do that by simply doing:

B = B[::-1,::-1]

In order to compare, we can show that the above code is equivalent to use scipy's convolve2D function. Here's a reproducible IPython session that shows us this:

In [41]: import cv2

In [42]: import numpy as np

In [43]: from scipy.signal import convolve2d

In [44]: A = np.reshape(np.arange(49), (7,7)).astype(np.float32)

In [45]: A
Out[45]:
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.]], dtype=float32)

In [46]: B = (1.0/25.0)*np.ones((5,5), dtype=np.float32)

In [47]: B
Out[47]:
array([[ 0.04,  0.04,  0.04,  0.04,  0.04],
       [ 0.04,  0.04,  0.04,  0.04,  0.04],
       [ 0.04,  0.04,  0.04,  0.04,  0.04],
       [ 0.04,  0.04,  0.04,  0.04,  0.04],
       [ 0.04,  0.04,  0.04,  0.04,  0.04]], dtype=float32)

In [48]: C = cv2.filter2D(A, -1, B)

In [49]: H = np.floor(np.array(B.shape)/2).astype(np.int)

In [50]: C = C[H[0]:-H[0],H[1]:-H[1]]

In [51]: C
Out[51]:
array([[ 15.99999809,  16.99999809,  18.        ],
       [ 22.99999809,  24.        ,  24.99999809],
       [ 29.99999809,  30.99999809,  31.99999809]], dtype=float32)

In [52]: C2 = convolve2d(A, B, mode='valid')

In [53]: C2
Out[53]:
array([[ 15.99999905,  17.00000191,  18.00000191],
       [ 22.99999809,  23.99999809,  24.99999809],
       [ 29.99999809,  30.99999809,  31.99999809]], dtype=float32)

The example is quite simple to understand. I declare a dummy matrix of 7 x 7 where the values increase from 0 to 48 row-wise. I also declare a 5 x 5 kernel of (1/25) for each element so this would essentially implement a 5 x 5 average filter. We thus use cv2.filter2D and scipy.signal.convolve2d to extract out only the valid portions of the convolution result. As far as precision goes, C which is the output of cv2.filter2D and C2 which is the output of convolve2d are both equivalent. Take special note of not only the actual contents but the shape of both output arrays.

However, if you wish to keep the size of the original image and replace the affected pixels by the filtered results, simply make a copy of the original image and use the same indexing logic that was used to cut away the information that was invalid with replacing those pixels in the copy with the convolved result:

C_copy = A.copy()
C_copy[H[0]:-H[0],H[1]:-H[1]] = C 
like image 167
rayryeng Avatar answered Oct 20 '22 01:10

rayryeng