卷积滤波算子可视化

卷积滤波

许多2d的图像滤波算法都是基于图像空域滤波,此类滤波方法基本思想是先创建一个滤波模板作为卷积核,然后以滑动窗口的方式遍历图像进行卷积运算。所以基于空域的滤波算法(如高斯滤波、均值滤波)在运算上的区别就是卷积核的不同,不同的卷积核决定了计算的不同结果从而决定了不同的滤波效果。所以,接下来将这些卷积核绘制出来,直观地从卷积核方面来看看几种滤波算法之间的差异所在。

卷积核可视化

高斯滤波

高斯滤波的卷积核元素呈现高斯分布(正态分布),所以可以通过高斯分布公式将卷积核创建出来。首先,一维的高斯分布数学描述是这样子的:

$$f(x) = \frac{1}{\sqrt{2\pi}\sigma} exp(-\frac{(x - \mu)^2}{2\sigma^2})$$

其中:

(1) $\mu$ 为位置参数,为分布图的对称轴所在 (2) $\sigma$ 为尺度参数(标准差),描述分布元素向对称中心聚拢的程度,$\sigma$越大,元素分布越散,反之越聚拢。用python将一维的高斯分布绘制如下:

gaussian-1d.png

接下来再看卷积滤波中所用到的二维的高斯分布,其数学描述为: 在x、y独立的前提下,二维的高斯分布可以表示为

$$f(x,y) = f(x)f(y)$$

可以推导出

$$f(x, y) = \frac{1}{2\pi\sigma_1\sigma_2} exp(-\frac{1}{2}( \frac{(x-\mu_1)^2}{\sigma_1^2} + \frac{(y - \mu_2)^2}{\sigma_2^2}))$$

令$\sigma_1 = \sigma_2, \mu_1 = \mu_2 = 0$,公式可以进一步简化为

$$f(x, y) = \frac{1}{2\pi\sigma^2}exp(- \frac{x^2 + y^2}{2\sigma^2})$$

使用python绘制出的二维的高斯分布图如下:

gaussian-2d.png

在图像空域滤波中用的是离散的高斯分布,绘制离散的高斯分布图如下:

gaussian-2d-scatter.png

均值滤波

均值滤波则是非常简单,卷积核的元素都是相等的值,

$$\frac{1}{mn}\begin{bmatrix} 1&1&..1_{1n}\ 1&1&..1_{2n}\…&…&…\ 1&..1_{m(n-1)}&..1_{mn} \end{bmatrix}$$

绘制出来的图表如下:

average.png

拉普拉斯算子

拉普拉斯算子定义:

$$\nabla^2f = \frac{\partial^2f}{\partial x^2} + \frac{\partial^2f}{\partial y^2}$$

x方向上有:

$$\frac{\partial^2f}{\partial x^2} = f(x+1,y) + f(x-1,y) - 2f(x,y)$$

y方向上有:

$$\frac{\partial^2f}{\partial y^2} = f(x,y+1) + f(x,y-1) - 2f(x,y)$$

所以拉普拉斯算子在离散的情况下,可以近似为

$$\nabla^2f = f(x+1,y) + f(x-1,y) + f(x,y+1) + f(x,y-1) -4f(x,y)$$

用矩阵的形式就可以表示为:

$$\begin{bmatrix} 0&-1&0\ -1&4&-1\ 0&-1&0 \end{bmatrix}$$ laplace.png

散点图可能看得不够明显,换成柱状图会好一些 laplace-bar.png

Sobel算子

$$Gx =\begin{bmatrix} -1&0&1\ -2&0&2\ -1&0&1 \end{bmatrix}$$

$$Gy = \begin{bmatrix} 1&2&1\ 0&0&0\ -1&-2&-1 \end{bmatrix}$$

sobel.png sobel-bar.png

Roberts算子

Roberts算子求解的是对角线方向的梯度

$$g_x = f(x+1,y+1) -f(x,y) $$

$$g_y = f(x, y+1) - f(x+1, y) $$

$$g_x =\begin{bmatrix} -1&0\ 0&1 \end{bmatrix}$$

$$g_y =\begin{bmatrix} 0&-1\ 1&0 \end{bmatrix}$$

robert.png robert-bar.png

Robinson算子

Robinson算子8个模板,对应8个方向的梯度

$$K_E = \begin{bmatrix} -3&-3&5\ -3&0&5\ -3&-3&5 \end{bmatrix}$$

$$K_{SE} = \begin{bmatrix} -3&-3&-3\ -3&0&5\ -3&5&5 \end{bmatrix}$$

$$K_S = \begin{bmatrix} -3&-3&-3\ -3&0&-3\ 5&5&5 \end{bmatrix}$$

$$K_{SW} = \begin{bmatrix} -3&-3&-3\ 5&0&-3\ 5&5&-3 \end{bmatrix}$$

$$K_W = \begin{bmatrix} 5&-3&-3\ 5&0&-3\ 5&-3&-3 \end{bmatrix}$$

$$K_{NW} = \begin{bmatrix} 5&5&-3\ 5&0&-3\ -3&-3&-3 \end{bmatrix}$$

$$K_N = \begin{bmatrix} 5&5&5\ -3&0&-3\ -3&-3&-3 \end{bmatrix}$$

$$K_{NE} = \begin{bmatrix} -3&5&5\ -3&0&5\ -3&-3&-3 \end{bmatrix}$$

这里给出x、y方向与对角线方向的各一个图表绘制:

robinson.png robinson-bar.png

Kirsch算子

与Robinson算子相同,Kirsch算子拥有8个模板,对应8个方向的梯度。

$$K_E = \begin{bmatrix} -1&0&1\ -2&0&2\ -1&0&1 \end{bmatrix}$$

$$K_{SE} = \begin{bmatrix} -2&-1&0\ -1&0&1\ 0&1&2 \end{bmatrix}$$

$$K_S = \begin{bmatrix} -1&-2&-1\ 0&0&0\1&2&1 \end{bmatrix}$$

$$K_{SW} = \begin{bmatrix} 0&-1&-2\ 1&0&-1\ 2&1&0 \end{bmatrix}$$

$$K_W = \begin{bmatrix} 1&0&-1\ 2&0&-2\1&0&-1 \end{bmatrix}$$

$$K_{NW} = \begin{bmatrix} 2&1&0\ 1&0&-1\ 0&-1&-2 \end{bmatrix}$$

$$K_N = \begin{bmatrix} 1&2&1\ 0&0&0\ -1&-2&-1 \end{bmatrix}$$

$$K_{NE} = \begin{bmatrix} 0&1&2\ -1&0&1\ -2&-1&0 \end{bmatrix}$$

可以发现Kirsch算子包含了sobel算子,只取南北方向的模板就是sobel算子的模板描述,所以这里只绘制一个对角线方式与东西方向,结果如下图所示: kirsch.png kirsch-bar.png

Prewitt算子

与sobel算子类似

x方向:

$$Px =\begin{bmatrix} -1&0&1\ -1&0&1\ -1&0&1 \end{bmatrix}$$

y方向:

$$Py = \begin{bmatrix} 1&1&1\ 0&0&0\ -1&-1&-1 \end{bmatrix}$$

prewitt.png prewitt-bar.png

代码

最后给出本文的绘制代码

  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
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# visualize convolution kernel
# author:mango
# copyright:https://mangoroom.cn

def gaussian1d():
    mu = 0
    sigma = 1
    x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
    y = np.exp(-((x - mu)**2 / (2 * sigma**2)) / (np.sqrt(2*np.pi) * sigma))
    plt.plot(x, y)
    plt.title('Gaussian-1d Distribution: $\mu = %.2f, $sigma=%.2f'%(mu,sigma))
    plt.show()

def gaussian2d():
    mu1 = 0
    mu2 = 0
    sigma1 = 1
    sigma2 = 1
    x = np.linspace(mu1 - 3*sigma1, mu1 + 3*sigma1, 100)
    y = np.linspace(mu2 - 3 * sigma2, mu2 + 3 * sigma2, 100)
    x, y = np.meshgrid(x, y)
    z = np.exp(-0.5 * ((x - mu1)**2 / sigma1**2 + (y - mu2)**2 / sigma2**2)) / (2*np.pi * sigma1 * sigma2)
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap='rainbow')
    plt.title('Gaussian-2d Distribution: $\mu = %.2f, $sigma=%.2f' % (mu1, sigma1))
    plt.show()

def gaussian2d_scatter():
    mu1 = 0
    mu2 = 0
    sigma1 = 1
    sigma2 = 1
    x = np.linspace(mu1 - 3*sigma1, mu1 + 3*sigma1, 15)
    y = np.linspace(mu2 - 3 * sigma2, mu2 + 3 * sigma2, 15)
    x, y = np.meshgrid(x, y)
    z = np.exp(-0.5 * ((x - mu1)**2 / sigma1**2 + (y - mu2)**2 / sigma2**2)) / (2*np.pi * sigma1 * sigma2)
    ax = plt.subplot(111, projection='3d')
    ax.scatter(x, y, z, c='g')
    plt.title('Gaussian-2d Distribution: $\mu = %.2f, $sigma=%.2f' % (mu1, sigma1))
    plt.show()

def average_distribution():
    x = np.linspace(-3, 3, 15)
    y = np.linspace(-3, 3, 15)
    x, y = np.meshgrid(x, y)
    z = (x + y) - (x + y) + 1
    ax = plt.subplot(111, projection='3d')
    ax.scatter(x, y, z, c='g')
    plt.title('Average Distribution:')
    plt.show()

def laplace():
    ax = plt.subplot(111, projection='3d')
    # ax.scatter(x, y, z, c='g')
    ax.scatter([-1, 1, -1, 1], [-1, -1, 1, 1], [0, 0, 0, 0], c='g')
    ax.scatter([0, -1, 1,  0], [-1, 0, 0, 1], [-1, -1, -1, -1], c='r')
    ax.scatter([0], [0], [4], c='y')

    plt.title('Laplace:')
    plt.show()

def laplace_bar():
    ax = plt.subplot(111, projection='3d')
    # ax.scatter(x, y, z, c='g')
    ax.bar3d([-1, 1, -1, 1], [-1, -1, 1, 1], 0, 0.2, 0.2, [0, 0, 0, 0])
    ax.bar3d([0, -1, 1,  0], [-1, 0, 0, 1], 0, 0.2, 0.2, [-1, -1, -1, -1])
    ax.bar3d([0], [0], 0, 0.2, 0.2, [4])
    plt.title('Laplace:')
    plt.show()


def sobel():
    ax = plt.subplot(121, projection='3d')
    ax.scatter([-1, -1, -1], [-1, 0, 1], [-1, -2, -1], c='r')
    ax.scatter([0, 0, 0], [-1, 0, 1], [0, 0, 0], c='g')
    ax.scatter([1, 1, 1], [-1, 0, 1], [1, 2, 1], c='b')
    plt.title('Solel Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.scatter([-1, 0, 1], [-1, -1, -1], [-1, -2, -1], c='r')
    bx.scatter([-1, 0, 1], [0, 0, 0], [0, 0, 0], c='g')
    bx.scatter([-1, 0, 1], [1, 1, 1], [1, 2, 1], c='b')
    plt.title('Solel Gy:')
    plt.show()

def sobel_bar():
    ax = plt.subplot(121, projection='3d')
    ax.bar3d([-1, -1, -1], [-1, 0, 1], 0, 0.2, 0.2, [-1, -2, -1])
    ax.bar3d([0, 0, 0], [-1, 0, 1], 0, 0.2, 0.2, [0, 0, 0])
    ax.bar3d([1, 1, 1], [-1, 0, 1], 0, 0.2, 0.2, [1, 2, 1])
    plt.title('Solel Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.bar3d([-1, 0, 1], [-1, -1, -1], 0, 0.2, 0.2, [-1, -2, -1])
    bx.bar3d([-1, 0, 1], [0, 0, 0], 0, 0.2, 0.2, [0, 0, 0])
    bx.bar3d([-1, 0, 1], [1, 1, 1], 0, 0.2, 0.2, [1, 2, 1])
    plt.title('Solel Gy:')
    plt.show()

def robinson():
    ax = plt.subplot(121, projection='3d')
    ax.scatter([-1, -1, -1], [-1, 0, 1], [-3, -3, -3], c='r')
    ax.scatter([0, 0, 0], [-1, 0, 1], [-3, 0, -3], c='g')
    ax.scatter([1, 1, 1], [-1, 0, 1], [5, 5, 5], c='b')
    plt.title('Robinson Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.scatter([-1, 0, 1], [-1, -1, -1], [-3, -3, -3], c='r')
    bx.scatter([-1, 0, 1], [0, 0, 0], [-3, 0, 5], c='g')
    bx.scatter([-1, 0, 1], [1, 1, 1], [-3, 5, 5], c='b')
    plt.title('Robinson diagonal line:')
    plt.show()

def robinson_bar():
    ax = plt.subplot(121, projection='3d')
    ax.bar3d([-1, -1, -1], [-1, 0, 1],0, 0.2, 0.2, [-3, -3, -3])
    ax.bar3d([0, 0, 0], [-1, 0, 1],0, 0.2, 0.2, [-3, 0, -3])
    ax.bar3d([1, 1, 1], [-1, 0, 1],0, 0.2, 0.2, [5, 5, 5])
    plt.title('Robinson Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.bar3d([-1, 0, 1], [-1, -1, -1],0, 0.2, 0.2, [-3, -3, -3])
    bx.bar3d([-1, 0, 1], [0, 0, 0],0, 0.2, 0.2, [-3, 0, 5])
    bx.bar3d([-1, 0, 1], [1, 1, 1],0, 0.2, 0.2, [-3, 5, 5])
    plt.title('Robinson diagonal line:')
    plt.show()

def kirsch():
    ax = plt.subplot(121, projection='3d')
    ax.scatter([-1, -1, -1], [-1, 0, 1], [-1, 0, 1], c='r')
    ax.scatter([0, 0, 0], [-1, 0, 1], [-2, 0, 2], c='g')
    ax.scatter([1, 1, 1], [-1, 0, 1], [-1, 0, 1], c='b')
    plt.title('kirsch KN:')
    bx = plt.subplot(122, projection='3d')
    bx.scatter([-1, 0, 1], [-1, -1, -1], [-2, -1, 0], c='r')
    bx.scatter([-1, 0, 1], [0, 0, 0], [-1, 0, 1], c='g')
    bx.scatter([-1, 0, 1], [1, 1, 1], [0, 1, 2], c='b')
    plt.title('kirsch KES:')
    plt.show()

def kirsch_bar():
    ax = plt.subplot(121, projection='3d')
    ax.bar3d([-1, -1, -1], [-1, 0, 1], 0, 0.2, 0.2, [-1, 0, 1])
    ax.bar3d([0, 0, 0], [-1, 0, 1], 0, 0.2, 0.2, [-2, 0, 2])
    ax.bar3d([1, 1, 1], [-1, 0, 1], 0, 0.2, 0.2, [-1, 0, 1])
    plt.title('kirsch KN:')
    bx = plt.subplot(122, projection='3d')
    bx.bar3d([-1, 0, 1], [-1, -1, -1], 0, 0.2, 0.2, [-2, -1, 0])
    bx.bar3d([-1, 0, 1], [0, 0, 0], 0, 0.2, 0.2, [-1, 0, 1])
    bx.bar3d([-1, 0, 1], [1, 1, 1], 0, 0.2, 0.2, [0, 1, 2])
    plt.title('kirsch KES:')
    plt.show()
def prewitt():
    ax = plt.subplot(121, projection='3d')
    ax.scatter([-1, -1, -1], [-1, 0, 1], [-1, -1, -1], c='r')
    ax.scatter([0, 0, 0], [-1, 0, 1], [0, 0, 0], c='g')
    ax.scatter([1, 1, 1], [-1, 0, 1], [1, 1, 1], c='b')
    plt.title('Prewitt Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.scatter([-1, 0, 1], [-1, -1, -1], [-1, -1, -1], c='r')
    bx.scatter([-1, 0, 1], [0, 0, 0], [0, 0, 0], c='g')
    bx.scatter([-1, 0, 1], [1, 1, 1], [1, 1, 1], c='b')
    plt.title('Prewitt Gy:')
    plt.show()

def prewitt_bar():
    ax = plt.subplot(121, projection='3d')
    ax.bar3d([-1, -1, -1], [-1, 0, 1], 0, 0.2, 0.2, [-1, -1, -1])
    ax.bar3d([0, 0, 0], [-1, 0, 1], 0, 0.2, 0.2, [0, 0, 0])
    ax.bar3d([1, 1, 1], [-1, 0, 1], 0, 0.2, 0.2, [1, 1, 1])
    plt.title('Prewitt Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.bar3d([-1, 0, 1], [-1, -1, -1], 0, 0.2, 0.2, [-1, -1, -1])
    bx.bar3d([-1, 0, 1], [0, 0, 0], 0, 0.2, 0.2, [0, 0, 0])
    bx.bar3d([-1, 0, 1], [1, 1, 1], 0, 0.2, 0.2, [1, 1, 1])
    plt.title('Prewitt Gy:')
    plt.show()
def robert():
    ax = plt.subplot(121, projection='3d')
    ax.scatter([0, 1], [0, 1], [1, -1], c='r')
    ax.scatter([1, 0], [0, 1], [0, 0], c='g')

    plt.title('Robert Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.scatter([0, 1], [0, 1], [0, 0], c='r')
    bx.scatter([1, 0], [0, 1], [1, -1], c='g')
    plt.title('Robert Gy:')
    plt.show()

def robert_bar():
    ax = plt.subplot(121, projection='3d')
    ax.bar3d([0, 1], [0, 1], 0, 0.2, 0.2, [1, -1])
    ax.bar3d([1, 0], [0, 1], 0, 0.2, 0.2, [0, 0])

    plt.title('Robert Gx:')
    bx = plt.subplot(122, projection='3d')
    bx.bar3d([0, 1], [0, 1], 0, 0.2, 0.2, [0, 0])
    bx.bar3d([1, 0], [0, 1], 0, 0.2, 0.2, [1, -1])
    plt.title('Robert Gy:')
    plt.show()



if __name__ == '__main__':
    # gaussian1d()
    # gaussian2d()
    # gaussian2d_scatter()
    # average_distribution()
    # laplace()
    # sobel()
    # laplace_bar()
    # sobel_bar()
    # robert()
    # robert_bar()
    # prewitt()
    # prewitt_bar()
    # kirsch()
    # kirsch_bar()
    robinson()
    robinson_bar()

本文由芒果浩明发布,转载请注明出处。 本文链接:https://blog.mangoeffect.net/python/visualize-convolution-kernel.html


微信公众号