我想尝试编写一个简单的函数来平滑输入的图像。我试图使用Image和numpy库来做到这一点。我在想使用卷积掩码是解决这个问题的方法,我知道numpy有一个内置的卷积函数。
如何使用numpy.convolve平滑图像?
答案 0 :(得分:17)
您想查看ndimage
,scipy
中的模块。它有许多过滤器
所有设置为函数,以及用于卷积任意内核的好包装器。
例如,
img_gaus = ndimage.filters.gaussian_filter(img, 2, mode='nearest')
用一张西格玛2的guassian卷曲你的形象。
如果你想卷入任意内核,请说一个交叉
k = np.array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]])
img2 = ndimage.convolve(img, k, mode='constant')
这些函数也适用于更高的维度,因此您可以使用几乎相同的代码(只是扩展内核的维度)来平滑更高维度的数据。
mode
和cval
参数控制卷积如何处理图像边缘的像素(对于边缘上的像素,内核需要查看的区域的一半不会存在,所以你需要选择一些东西来填充你的图像。
答案 1 :(得分:17)
好问题! tcaswell 发布这里是一个很好的建议,但你不会这么学习,因为scipy正在为你做所有的工作!既然你的问题说你想尝试编写函数,我会向你展示一些粗略和基本的方式来手动完成这一切,希望你能更好地理解卷积背后的数学等等,然后你可以用自己的想法和努力来改进它!
注意:你会得到不同形状/大小的内核的不同结果,高斯是常用的方法,但你可以尝试其他一些有趣(余弦,三角等!)。我刚刚在现场制作了这个,我觉得它是一种金字塔形状。
import scipy.signal
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread('example.jpg')
im /= 255. # normalise to 0-1, it's easier to work in float space
# make some kind of kernel, there are many ways to do this...
t = 1 - np.abs(np.linspace(-1, 1, 21))
kernel = t.reshape(21, 1) * t.reshape(1, 21)
kernel /= kernel.sum() # kernel should sum to 1! :)
# convolve 2d the kernel with each channel
r = scipy.signal.convolve2d(im[:,:,0], kernel, mode='same')
g = scipy.signal.convolve2d(im[:,:,1], kernel, mode='same')
b = scipy.signal.convolve2d(im[:,:,2], kernel, mode='same')
# stack the channels back into a 8-bit colour depth image and plot it
im_out = np.dstack([r, g, b])
im_out = (im_out * 255).astype(np.uint8)
plt.subplot(2,1,1)
plt.imshow(im)
plt.subplot(2,1,2)
plt.imshow(im_out)
plt.show()
答案 2 :(得分:4)
如果你不想使用scipy,你有三个选择:
1)你可以使用卷积定理结合傅里叶变换,因为numpy有一个2D FFT。
2)你可以使用一个可分离的内核,然后你可以在平顶阵列上做两个1D卷积,一个在x方向,另一个在y方向(ravel the transpose),这将得到相同的结果作为2D卷积。
3)如果你有一个小内核,比如3x3,那么将卷积写成乘法和求和就很容易了。这听起来很麻烦,但并不是那么糟糕。
如果你想使用scipy,你可以使用ngimage,正如tcaswell建议的那样。 scipy也有convolve2d。