没有任何padding-opencv Python的卷积

时间:2016-09-12 19:30:46

标签: python opencv numpy image-processing

Opencv-python中是否有任何函数可以在没有任何填充的情况下将图像与内核进行卷积?基本上,我想要一个图像,其中卷积只发生在内核和图像部分完全重叠的区域。

2 个答案:

答案 0 :(得分:4)

OpenCV仅支持卷积图像,其中返回的输出与输入图像的大小相同。因此,您仍然可以使用OpenCV的过滤器函数,但只是忽略内核未在图像中完全封装自身的边缘上的像素。假设您的图像内核是奇数,您可以简单地将每个维度划分为一半,然后使用这些内容(或向下舍入)并使用这些来删除无效的信息并返回剩余的信息。正如Divakar所提到的,这与将scipy's 2D convolution method'valid'选项一起使用的方法相同。

因此,假设您的图像存储在A中并且您的内核存储在B中,您只需执行以下操作即可获取内核完全封装在图像内的已过滤图像。请注意,我们假设内核是奇数,输出存储在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

请注意cv2.filter2D执行关联,而不是卷积。但是,如果内核是对称的(即如果你采用转置并且它等于自身),则相关和卷积是等价的。如果不是这种情况,则需要在使用cv2.filter2D之前执行内核的180度旋转。你可以这样做:

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

为了进行比较,我们可以证明上述代码等同于使用scipy的{​​{1}}函数。这是一个可重现的IPython会话,向我们展示了这一点:

convolve2D

这个例子很容易理解。我声明了一个7 x 7的虚拟矩阵,其中值从0逐行增加到48行。我还为每个元素声明了一个5 x 5内核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) ,因此这基本上实现了一个5 x 5的平均过滤器。因此,我们使用(1/25)cv2.filter2D仅提取卷积结果的有效部分。就精确度而言,作为scipy.signal.convolve2d的输出的Ccv2.filter2D的输出C2都是等价的。不仅要注意实际内容,还要注意两个输出数组的形状。

但是,如果您希望保留原始图像的大小并通过过滤后的结果替换受影响的像素,只需复制原始图像并使用相同的索引逻辑来删除原始图像。使用卷积结果替换副本中的像素时无效:

convolve2d

答案 1 :(得分:0)

我会根据scipy来源的修改来修改接受的答案:

(见https://github.com/scipy/scipy/blob/2526df72e5d4ca8bad6e2f4b3cbdfbc33e805865/scipy/signal/firfilter.c#L137

将有效输出大小计算为

else if (outsize == VALID) {Os[0] = Ns[0]-Nwin[0]+1; Os[1] = Ns[1]-Nwin[1]+1;}

Ns对应A的尺寸,Nwin对应B&#。

所以替换:

H = np.floor(np.array(B.shape)/2).astype(np.int)
C = C[H[0]:-H[0],H[1]:-H[1]]

H = np.floor(np.array(B.shape)/2).astype(np.int)
outdims = (np.array(A.shape) - np.array(B.shape)) + 1
C = C[H[0]:H[0]+outdims[0], H[1]:H[1]+outdims[1]]

运行以下代码段说明了两种方法之间输出大小的差异:

import cv2
import numpy as np
from scipy.signal import convolve2d

A = np.reshape(np.arange(49), (7,7)).astype(np.float32)
B = np.random.rand(2, 2)  # convolve with an even kernel
C = cv2.filter2D(A, -1, B)
C2 = convolve2d(A, B, mode='valid')
H = np.floor(np.array(B.shape)/2).astype(np.int)
C = C[H[0]:-H[0],H[1]:-H[1]]
shapes_match = C.shape == C2.shape
print shapes_match #  prints False