15

我想尝试编写一个简单的函数来平滑输入的图像。我试图使用 Image 和 numpy 库来做到这一点。我在想使用卷积掩码将是解决这个问题的一种方法,我知道 numpy 内置了一个卷积函数。

如何使用numpy.convolve平滑图像?

4

3 回答 3

19

好问题! 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()

在此处输入图像描述

于 2013-02-08T05:37:21.213 回答
17

你想看看ndimage,这是一个模块scipy。它有许多过滤器都设置为函数,以及用于卷积任意内核的漂亮包装器。

例如,

img_gaus = ndimage.filters.gaussian_filter(img, 2, mode='nearest')

将您的图像与 sigma 为 2 的高斯卷积。

如果你想卷积一个任意内核,比如说一个交叉

k = np.array([[0, 1, 0],
              [1, 1, 1],
              [0, 1, 0]])

img2 = ndimage.convolve(img, k, mode='constant')

这些函数也适用于更高维度,因此您可以使用几乎相同的代码(只是扩大内核的维度)来平滑更高维度的数据。

和参数控制卷积如何处理图像边缘的像素(对于边缘上的像素,内核需要查看的一半区域不存在,因此您需要选择一些东西来填充mode图像cval和)。

于 2013-02-08T05:04:29.613 回答
4

如果你不想使用 scipy,你有三个选择:

1)您可以将卷积定理与傅里叶变换结合使用,因为 numpy 具有 2D FFT。

2)您可以使用可分离的内核,然后您可以在展平的阵列上进行两个一维卷积,一个在 x 方向,另一个在 y 方向(拉开转置),这将得到与 2D 相同的结果卷积。

3)如果你有一个小内核,比如 3x3,只需将卷积写成乘法和求和就很容易了。这听起来很麻烦,但还不错。

如果你确实想使用 scipy,你可以使用 ngimage,就像 tcaswell 建议的那样。scipy 也有 convolve2d。

于 2013-02-08T05:23:56.533 回答