Search code examples
pythonopencvconvolution

How to do convolution in OpenCV


I am trying to find convolution in OpenCV using filter2D method but the result is not correct

import cv2 as cv
import scipy.signal as sig
import numpy as np
b=np.asarray([[1,2,0,1,2],
              [2,3,1,1,2],
              [1,4,2,2,0],
              [3,2,3,3,0],
              [1,0,0,2,1]
              ],dtype=np.uint8)

w=np.asarray([[1,1,1],
              [1,1,2],
              [2,1,1]],dtype=np.uint8)
w_r=np.asarray([[1,1,1],
                [2,1,1],
                [1,1,1]
                ],dtype=np.uint8)
print(sig.convolve2d(b,w,mode="same"))
kernel_r=np.asarray([[1,1,1],[1,1,2],[2,1,1]])
print("-------")
print(cv.filter2D(b,-1,w_r))

First output is generated by scipy.signal.convolve2D that is correct. The second output is generated by OpenCV filter2D which is not correct. how can i get the correct results.

[[ 8 10 10  7  7]
 [15 18 20 14  9]
 [18 23 26 18 10]
 [15 21 22 16 11]
 [ 8 13 13  9  8]]
-------
[[23 16 15 11 13]
 [25 18 19 12 13]
 [28 22 25 16 16]
 [19 19 20 16 18]
 [15 18 18 15 19]]

Solution

  • I assume, you wanted to use some rotated kernel w_r in your cv.filter2d call as also mentioned in the filter2d documentation:

    If you need a real convolution, flip the kernel using flip and [...]

    So, first problem is, that your manually set w_r is not the correct, flipped version of w, you forgot a 2 there.

    Second problem comes from, how scipy.sig.convolve2d handles boundaries:

    boundary : str {‘fill’, ‘wrap’, ‘symm’}, optional

    A flag indicating how to handle boundaries:

    fill

    pad input arrays with fillvalue. (default)

    From the obtained values after convolution, it seems that the boundary is padded with 0. There's a similar option for OpenCV's filter2d, see the BorderTypes, specifically cv.BORDER_CONSTANT. From tests it seems, that 0 is the default value here!? (Couldn't find any documentation on that by now.)

    So, the corrected code could look like this (unnecessary stuff omitted here):

    import cv2 as cv
    import scipy.signal as sig
    import numpy as np
    
    b=np.asarray([[1,2,0,1,2],
                  [2,3,1,1,2],
                  [1,4,2,2,0],
                  [3,2,3,3,0],
                  [1,0,0,2,1]
                  ], dtype=np.uint8)
    
    w=np.asarray([[1,1,1],
                  [1,1,2],
                  [2,1,1]], dtype=np.uint8)
    
    print(sig.convolve2d(b, w, mode="same"))
    print("-------")
    print(cv.filter2D(b, -1, cv.flip(w, -1), borderType=cv.BORDER_CONSTANT))
    

    Now, both outputs show the same result:

    [[ 8 10 10  7  7]
     [15 18 20 14  9]
     [18 23 26 18 10]
     [15 21 22 16 11]
     [ 8 13 13  9  8]]
    -------
    [[ 8 10 10  7  7]
     [15 18 20 14  9]
     [18 23 26 18 10]
     [15 21 22 16 11]
     [ 8 13 13  9  8]]
    

    Hope that helps!