Python Convolve2d 仅使用 Numpy
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43086557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convolve2d just by using Numpy
提问by Allosteric
I am studying image-processing using Numpy and facing a problem with filtering with convolution.
我正在使用 Numpy 研究图像处理,并面临使用卷积过滤的问题。
I would like to convolve a gray-scale image. (convolve a 2d Array with a smaller 2d Array)
我想对灰度图像进行卷积。(将 2d 数组与较小的 2d 数组卷积)
Does anyone have an idea to refinemy method ?
有没有人有改进我的方法的想法?
I know that scipysupports convolve2d but I want to make a convolve2d only by using Numpy.
我知道scipy支持convolve2d,但我只想使用 Numpy 来制作convolve2d。
What I have done
我做了什么
First, I made a 2d array the submatrices.
首先,我制作了一个二维数组作为子矩阵。
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:]]])
the submatrices seems complicated but what I am doing is shown in the following drawing.
子矩阵看起来很复杂,但我正在做的事情如下图所示。
Next, I multiplied each submatrices with a filter.
接下来,我将每个子矩阵与过滤器相乘。
conv_filter = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]])
multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices)
and summed them.
并将它们相加。
np.sum(np.sum(multiplied_subs, axis = -3), axis = -3)
#array([[ 6, 7, 8],
# [11, 12, 13],
# [16, 17, 18]])
Thus this procudure can be called my convolve2d.
因此,这个程序可以称为我的 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)
However, I find this my_convolve2d troublesome for 3 reasons.
但是,我发现 my_convolve2d 很麻烦,原因有 3 个。
- Generation of the submatrices is too awkward that is difficult to read and can only be used when the filter is 3*3
- The size of the varient submatrices seems to be too big, since it is approximately 9 folds bigger than the original matrix.
- The summing seems a little non intuitive. Simply said, ugly.
- 子矩阵的生成太笨拙,难以阅读,只能在过滤器为3*3时使用
- 变体子矩阵的大小似乎太大了,因为它比原始矩阵大大约 9 倍。
- 总结似乎有点不直观。简单的说,丑。
Thank you for reading this far.
感谢您阅读到这里。
Kind of update. I wrote a conv3d for myself. I will leave this as a public domain.
一种更新。我为自己写了一个 conv3d。我将把它作为一个公共领域。
def convolve3d(img, kernel):
# calc the size of the array of submatracies
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 submatracies
submatrices = strd(img,kernel.shape + sub_shape,img.strides * 2)
# sum the submatraces and kernel
convolved_matrix = np.einsum('hij,hijklm->klm', kernel, submatrices)
return convolved_matrix
采纳答案by Crispin
You could generate the subarrays using as_strided
[1]:
您可以使用as_strided
[1]生成子数组:
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)
To get rid of your second "ugly" sum, alter your einsum
so that the output array only has j
and k
. This implies your second summation.
为了摆脱你的第二个“丑陋”的总和,改变你的,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]]
回答by Dan Erez
You can also use fft (one of the faster methods to perform convolutions)
您还可以使用 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
- https://gist.github.com/thearn/5424195
- you must pad the filter to be the same size as image ( place it in the middle of a zeros_like mat.)
- https://gist.github.com/thearn/5424195
- 您必须将过滤器填充为与图像相同的大小(将其放在 zeros_like 垫子的中间。)
cheers, Dan
干杯,丹
回答by Daniel F
Cleaned up using as_strided
and @Crispin 's einsum
trick from above. Enforces the filter size into the expanded shape. Should even allow non-square inputs if the indices are compatible.
从上面清理使用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)