仅使用 Numpy 即可进行 Convolve2d
- 2025-03-12 08:52:00
- admin 原创
- 28
问题描述:
我正在研究使用 NumPy 进行图像处理,并且面临卷积过滤的问题。
我想卷积灰度图像。(将二维数组与较小的二维数组卷积)
有人有想法改进我的方法吗?
我知道SciPy支持 convolve2d,但我只想使用 NumPy 来制作 convolve2d。
我做了什么
首先,我创建了一个二维数组子矩阵。
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:]]])
子矩阵看起来很复杂,但我所做的如下图所示。
接下来,我将每个子矩阵与一个过滤器相乘。
conv_filter = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]])
multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices)
并将它们相加。
np.sum(np.sum(multiplied_subs, axis = -3), axis = -3)
#array([[ 6, 7, 8],
# [11, 12, 13],
# [16, 17, 18]])
因此这个过程可以称为我的 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)
但是,我发现这个 my_convolve2d 很麻烦,原因有 3 个。
子矩阵的生成太笨拙,难以阅读,并且只能在滤波器为 3*3 时使用
变体子矩阵的尺寸似乎太大,因为它比原始矩阵大约大 9 倍。
总结似乎有点不直观。简而言之,很丑陋。
感谢您读到这里。
有点更新。我为自己写了一个 conv3d。我会将其保留为公共领域。
def convolve3d(img, kernel):
# calc the size of the array of submatrices
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 submatrices
submatrices = strd(img,kernel.shape + sub_shape,img.strides * 2)
# sum the submatrices and kernel
convolved_matrix = np.einsum('hij,hijklm->klm', kernel, submatrices)
return convolved_matrix
解决方案 1:
您可以使用以下方法生成子数组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)
要消除第二个“丑陋”的总和,请更改您的einsum
,以便输出数组仅包含j
和k
。这意味着您的第二个求和。
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]]
解决方案 2:
as_strided
使用上面的@Crispin 技巧进行清理einsum
。强制将过滤器大小调整为扩展形状。如果索引兼容,甚至应该允许非正方形输入。
def conv2d(a, f):
s = f.shape + tuple(np.subtract(a.shape, f.shape) + 1)
strd = numpy.lib.stride_tricks.as_strided
subM = strd(a, shape = s, strides = a.strides * 2)
return np.einsum('ij,ijkl->kl', f, subM)
解决方案 3:
您还可以使用 fft(执行卷积的更快方法之一)
from numpy.fft import fft2, ifft2
import numpy as np
def fft_convolve2d(x,y):
""" 2D convolution, using FFT"""
fr = fft2(x)
fr2 = fft2(np.flipud(np.fliplr(y)))
m,n = fr.shape
cc = np.real(ifft2(fr*fr2))
cc = np.roll(cc, -m/2+1,axis=0)
cc = np.roll(cc, -n/2+1,axis=1)
return cc
您必须将过滤器填充为与图像相同的大小(将其放置在 zeros_like 垫的中间。)
欢呼吧,丹
解决方案 4:
在此处查看所有卷积方法及其各自的性能。此外,我发现下面的代码片段更简单。
from numpy.fft import fft2, ifft2
def np_fftconvolve(A, B):
return np.real(ifft2(fft2(A)*fft2(B, s=A.shape)))
相关推荐
热门文章
项目管理软件有哪些?
热门标签
云禅道AD