无填充卷积

2024-05-16 12:19:12 发布

您现在位置:Python中文网/ 问答频道 /正文

opencvpython中有没有函数可以用内核卷积图像,而不需要任何填充?基本上,我想要一个只在内核和图像部分完全重叠的区域进行卷积的图像。在


Tags: 函数图像区域内核卷积opencvpython
2条回答

我将在仔细阅读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的尺寸

所以替换:

^{pr2}$

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

OpenCV只支持对返回的输出与输入图像大小相同的图像进行卷积。因此,您仍然可以使用OpenCV的过滤函数,但只需忽略那些边缘上的像素,这些像素是内核没有完全封装在图像中的。假设您的图像内核是奇数,您可以简单地将每个维度除以一半,取地板(或向下取整)并使用这些来删除无效的信息并返回剩余的内容。正如Divakar所提到的,这与使用'valid'选项的^{}'s 2D convolution method相同。在

因此,假设您的图像存储在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度旋转。您只需执行以下操作即可:

^{pr2}$

为了进行比较,我们可以证明上面的代码等价于使用scipyconvolve2D函数。下面是一个可复制的IPython会话,它向我们展示了:

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)

这个例子很容易理解。我声明一个7×7的伪矩阵,其中值从0增加到48行。我还为每个元素声明一个(1/25)的5x5内核,因此这实际上将实现一个5x5平均值过滤器。因此,我们使用cv2.filter2D和{}来提取卷积结果的有效部分。就精度而言,Ccv2.filter2D的输出,C2是{}的输出。不仅要特别注意实际内容,还要注意两个输出数组的形状。在

但是,如果您希望保持原始图像的大小并用过滤后的结果替换受影响的像素,只需制作原始图像的副本,并使用用于删除无效信息的索引逻辑,即用卷积结果替换副本中的这些像素:

C_copy = A.copy()
C_copy[H[0]:-H[0],H[1]:-H[1]] = C 

相关问题 更多 >