卷积滤波
许多2d的图像滤波算法都是基于图像空域滤波,此类滤波方法基本思想是先创建一个滤波模板作为卷积核,然后以滑动窗口的方式遍历图像进行卷积运算。所以基于空域的滤波算法(如高斯滤波、均值滤波)在运算上的区别就是卷积核的不同,不同的卷积核决定了计算的不同结果从而决定了不同的滤波效果。所以,接下来将这些卷积核绘制出来,直观地从卷积核方面来看看几种滤波算法之间的差异所在。
卷积核可视化
高斯滤波
高斯滤波的卷积核元素呈现高斯分布(正态分布),所以可以通过高斯分布公式将卷积核创建出来。首先,一维的高斯分布数学描述是这样子的:
$$f(x) = \frac{1}{\sqrt{2\pi}\sigma} exp(-\frac{(x - \mu)^2}{2\sigma^2})$$
其中:
(1) $\mu$ 为位置参数,为分布图的对称轴所在
(2) $\sigma$ 为尺度参数(标准差),描述分布元素向对称中心聚拢的程度,$\sigma$越大,元素分布越散,反之越聚拢。用python将一维的高斯分布绘制如下:
接下来再看卷积滤波中所用到的二维的高斯分布,其数学描述为:
在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绘制出的二维的高斯分布图如下:
在图像空域滤波中用的是离散的高斯分布,绘制离散的高斯分布图如下:
均值滤波
均值滤波则是非常简单,卷积核的元素都是相等的值,
$$\frac{1}{mn}\begin{bmatrix} 1&1&..1_{1n}\\ 1&1&..1_{2n}\\...&...&...\\ 1&..1_{m(n-1)}&..1_{mn} \end{bmatrix}$$
绘制出来的图表如下:
拉普拉斯算子
拉普拉斯算子定义:
$$\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}$$
散点图可能看得不够明显,换成柱状图会好一些
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}$$
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}$$
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方向与对角线方向的各一个图表绘制:
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算子的模板描述,所以这里只绘制一个对角线方式与东西方向,结果如下图所示:
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}$$
代码
最后给出本文的绘制代码
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://mangoroom.cn/python/visualize-convolution-kernel.html