前言
滤波分为线性滤波和非线性滤波两种,线性滤波中有方框滤波、均值滤波和高斯滤波三种,非线性滤波则有中值滤波和双边滤波两种。在介绍滤波方式之前先以二维滤波的形式介绍滤波的运算。
滤波操作
二维滤波(二维卷积)
用二维滤波的方法选取不同的卷积核可以实现各种不同的效果,虽然OpenCV中内置函数能实现不同的操作,但是通过自己构建卷积核矩阵能够使我们更好的理解不同操作背后的运算逻辑。
将源图像和内核卷积处理,内核kernel(卷积核)是一个浮点数的矩阵,一个5×5的均值滤波内核矩阵如下所示,将内核(卷积核)的中心 与图像的一个像素对齐,5×5的内核则需要将中心周围的25个像素取平均值,并以此值替换中心的像素值,依次对整个图像的其他所有像素操作,便得到了滤波后的图像。
K
=
1
9
[
1
1
1
1
1
1
1
1
1
]
K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1\\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix}
K=91⎣⎡111111111⎦⎤
首先介绍一下矩阵卷积的计算理论。
离散二维卷积公式:
B
(
i
,
j
)
=
∑
m
=
0
∑
n
=
0
K
(
m
.
n
)
×
A
(
i
−
m
,
h
−
m
)
B(i,j)=\sum_{m=0}\sum_{n=0}K(m.n)×A(i-m,h-m)
B(i,j)=m=0∑n=0∑K(m.n)×A(i−m,h−m)
其中A为被卷积矩阵、B为卷积结果、K为卷积内核。
A
=
[
1
2
3
4
5
6
7
8
9
]
K
=
[
1
2
3
4
5
6
7
8
9
]
A = \begin{bmatrix} 1 & 2 & 3\\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix} K = \begin{bmatrix} 1 & 2 & 3\\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix}
A=⎣⎡147258369⎦⎤K=⎣⎡147258369⎦⎤
将卷积内核K翻转180°
K
∗
=
[
9
8
7
6
5
4
3
2
1
]
K\\^* = \begin{bmatrix} 9 & 8 & 7\\ 6& 5 & 4 \\ 3 & 2& 1 \end{bmatrix}
K∗=⎣⎡963852741⎦⎤
被卷积矩阵A拓展为 (m+2(n%2))x(m+2(n%2)) 的矩阵,在外围无元素位置以0填补
A
∗
=
[
0
0
0
0
0
0
1
2
3
0
0
4
5
6
0
0
7
8
9
0
0
0
0
0
0
]
A\\^* = \begin{bmatrix} 0 & 0 & 0 & 0 & 0 \\ 0 &1 & 2 & 3 & 0\\ 0 & 4 & 5 & 6 & 0 \\ 0 & 7 & 8 & 9 & 0 \\ 0 & 0 &0 & 0 & 0\end{bmatrix}
A∗=⎣⎢⎢⎢⎢⎡0000001470025800369000000⎦⎥⎥⎥⎥⎤
将K*的中心依次对准A*的元素,相乘后相加
A
∗
=
[
0
×
9
0
×
8
0
×
7
0
0
0
×
6
1
×
5
2
×
4
3
0
0
×
3
4
×
2
5
×
1
6
0
0
7
8
9
0
0
0
0
0
0
]
A\\^* = \begin{bmatrix} \color{red}0×9 & \color{red}0×8 & \color{red}0×7 & 0 & 0 \\ \color{red}0×6 &\color{red}1×5 & \color{red}2×4 & 3 & 0\\ \color{red}0×3 & \color{red}4×2 & \color{red}5×1 & 6 & 0 \\ 0 & 7 & 8 & 9 & 0 \\ 0 & 0 &0 & 0 & 0\end{bmatrix}
A∗=⎣⎢⎢⎢⎢⎡0×90×60×3000×81×54×2700×72×45×1800369000000⎦⎥⎥⎥⎥⎤
红色部分得出:0+0+0+0+5+8+0+8+5=26 为B(1,1)的值,依次对所有的元素计算,最终得出一个3x3的矩阵,便完成了矩阵的卷积运算。
结果为
K
∗
=
[
26
56
54
84
155
144
134
258
186
]
K\\^* = \begin{bmatrix} 26 & 56 & 54\\ 84& 155 & 144 \\ 134 & 258 & 186 \end{bmatrix}
K∗=⎣⎡26841345615525854144186⎦⎤
下面介绍函数
dst = cv2.filter2D(src, ddepth, kernel, dst=..., anchor=..., delta=..., borderType=...)
参数 | 介绍 |
---|---|
src | 输入源图像 |
ddepth | 图像的深度,当值为-1时为和源图像相同 |
kernal | 卷积内核,多取边长为奇数(便于取中心像素点) |
dst | 计算结果图像有和dst相同的大小和通道数 |
anchor | 内核的锚点,默认为(-1,-1) |
delta | 卷积过程中会加在每个像素上,默认值为0 |
borderType | 卷积计算时边界的形式,默认为 BORDER_DEAFAULT |
anchor 的取值会影响中心像素的位置,默认值(-1,-1)表示锚点位于内核中心。
import cv2
import numpy as np
#读取图片
im1 = cv2.resize(cv2.imread('cat1.jpg',0), None, fx=0.4, fy=0.4, interpolation=cv2.INTER_AREA)
#用Numpy生成了三个内核
#内核1为一个均值滤波的5x5的核
#内核2为一个3x3的边缘检测的核
#内核3为一个对对角线敏感的3x3的核
kernel1 = np.ones((5,5), np.float32)/25
kernel2 = np.mat('-1 -1 -1; -1 8 -1; -1 -1 -1', np.float32)
kernel3 = np.mat('-2 -1 0; -1 0 1; 0 1 2', np.float32)
#分别进行卷积运算
dst1 = cv2.filter2D(im1, -1, kernel1)
dst2 = cv2.filter2D(im1, -1, kernel2)
dst3 = cv2.filter2D(im1, -1, kernel3)
img = [im1, dst1, dst2, dst3]
for i in range(len(img)):
cv2.imshow('Fig.'+str(i+1), img[i])
k = cv2.waitKey(0)
if k == ord('s'):
cv2.destroyAllWindows()
线性滤波
方框滤波/均值滤波
这是最简单的一种滤波方式,方框滤波/均值滤波的作用是模糊一张图片。
使用的是 cv2.boxFilter() 方框滤波和 cv2.blur() 均值滤波
卷积核如下所示
K
e
r
n
a
l
=
α
[
1
1
.
.
.
1
1
1
.
.
.
1
.
.
.
.
.
.
1
1
1
]
Kernal = \alpha \begin{bmatrix} 1 & 1 &... & 1\\ 1& 1 & .. .& 1\ \\ ... & ... & & \\ 1 & 1 & & 1 \end{bmatrix}
Kernal=α⎣⎢⎢⎡11...111...1......11 1⎦⎥⎥⎤
α
=
{
1
k
s
i
z
e
×
k
s
i
z
e
,
n
o
r
m
a
l
i
z
e
=
T
r
u
e
1
,
n
o
r
m
a
l
i
z
e
=
F
a
l
s
e
\alpha = \begin{cases} \frac{1}{ksize×ksize} &\text{, } normalize = True \\ 1 &\text{, } normalize = False \end{cases}
α={ksize×ksize11, normalize=True, normalize=False
当方框滤波的参数 normalize 为 True 时,方框滤波的内核和均值滤波的内核相同。
dst=cv.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]])
参数 | 介绍 |
---|---|
src | 输入源图像,可以有多种通道,不过不同通道之间是分开处理的 |
ddepth | 图像的深度,当值为-1时为和源图像相同 |
ksize | 卷积核的尺寸,如(5,5) |
dst | 计算结果图像有和dst相同的大小和通道数 |
anchor | 内核的锚点,默认为(-1,-1)表示锚点在卷积核中心 |
normalize | 如上公式所示 |
borderType | 卷积计算时边界的形式,默认为 BORDER_DEAFAULT 一般不管 |
dst=cv.blur(src, ksize[, dst[, anchor[, borderType]]])
参数 | 介绍 |
---|---|
src | 输入源图像,可以有多种通道,不过不同通道之间是分开处理的 |
ksize | 卷积核的尺寸,如(5,5) |
dst | 计算结果图像有和dst相同的大小和通道数 |
anchor | 内核的锚点,默认为(-1,-1)表示锚点在卷积核中心 |
borderType | 卷积计算时边界的形式,默认为 BORDER_DEAFAULT 一般不管 |
import cv2
import numpy as np
#读取图片
im1 = cv2.resize(cv2.imread('cat1.jpg',1), None, fx=0.4, fy=0.4, interpolation=cv2.INTER_AREA)
#生成内核卷积与OpenCV的均值模糊相对比
kernal_1 = np.ones((5,5), np.float32)/25
#分别进行卷积运算
dst1 = cv2.blur(im1, (5,5))
dst2 = cv2.boxFilter(im1, -1, (5,5))
dst3 = cv2.filter2D(im1, -1, kernal_1)
img = [im1, dst1, dst2, dst3]
for i in range(len(img)):
cv2.imshow('Fig.'+str(i+1), img[i])
k = cv2.waitKey(0)
if k == ord('s'):
cv2.destroyAllWindows()
高斯滤波
高斯滤波是一种线性平滑滤波,用来消除高斯噪声效果极佳,通常用于图像的降噪。高斯滤波的卷积运算是一个加权平均的计算过程,其权函数来自正态分布函数,其本质便是图像与正态分布做卷积运算,正态分布也叫高斯分布,因此得名高斯滤波/高斯模糊。
高斯滤波是图像处理中十分常用的一种滤波技术,处理的效果好但是效率并不高。由于高斯函数的傅里叶变换后任为高斯函数,只是幅度和方差发生了变化,所以高斯滤波对于图像来说也是一种低通滤波操作。
一维高斯函数如下:
G
(
x
)
=
α
e
−
(
i
−
(
k
s
i
z
e
−
1
2
)
)
2
2
σ
2
G(x) = \alpha e\\^{-\frac{(i-(\frac{ksize-1}{2}))\\^2}{2\sigma\\^2}}
G(x)=αe−2σ2(i−(2ksize−1))2
其中
i 为满足 0<=i<=ksize-1 的整数
α 为使得 ∑G = 1 的一个值
ksize 为卷积核的阶数(长度)
σ 为标准差,通常取1其值越大表示周围的像素点权重越大,也可由下式计算得
σ
=
0.3
×
(
k
s
i
z
e
−
1
2
−
1
)
+
0.8
\sigma =0.3×(\frac{ksize-1}{2}-1)+0.8
σ=0.3×(2ksize−1−1)+0.8
例如
当卷积核阶数为3时,即 ksize = 3 ,那么 i = 0,1,2
取 σ=1
则有 G’1=[0.60653 1 0.60653]
通过计算可得
a
=
1
Σ
G
i
=
1
2.21306
=
0.45186
a=\frac{1}{\Sigma G\\_i}=\frac{1}{2.21306}=0.45186
a=ΣGi1=2.213061=0.45186
最终得出 G1=[0.27410 0.45186 0.27410]
此时得出的为一维的高斯权值
计算二维得高斯模板可以通过 GTG 计算
得:
G
T
G
=
[
0.0751
0.1239
0.0751
0.1239
0.2047
0.1239
0.0751
0.1239
0.0751
]
G\\^TG =\begin{bmatrix} 0.0751 & 0.1239 & 0.0751\\ 0.1239& 0.2047 & 0.1239\ \\0.0751 & 0.1239 & 0.0751 \end{bmatrix}
GTG=⎣⎡0.07510.12390.07510.12390.20470.12390.07510.1239 0.0751⎦⎤
在OpenCV中也可以通过 cv2.getGaussianKernal() 来创造高斯卷积核。
dst=cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])
参数 | 介绍 |
---|---|
src | 输入源图像,可以有不同的通道,但是不同通道之间是分开处理的 |
ksize | 内核的尺寸,宽度和长度可以不同,但是必须为奇数 |
sigmaX | X方向上的标准差,与上面 的介绍相同,若取0则由ksize求 |
sigmaY | Y方向上的标准差,与上面 的介绍相同,若不取值则与sigmaX相等 |
borderType | 图像边缘像素的边界形式 |
import cv2
import numpy as np
#读取图片
im1 = cv2.resize(cv2.imread('cat1.jpg',1), None, fx=0.4, fy=0.4, interpolation=cv2.INTER_AREA)
#生成5x5的高斯内核与OpenCV的高斯模糊相对比
kernal_1 = np.mat('0.00297 0.01331 0.02194 0.01331 0.00297;\
0.01331 0.05963 0.09832 0.05963 0.01331;\
0.02194 0.09832 0.16210 0.09832 0.02194;\
0.01331 0.05963 0.09832 0.05963 0.01331;\
0.00297 0.01331 0.02194 0.01331 0.00297', np.float32)
#分别进行卷积运算
dst1 = cv2.GaussianBlur(im1, (5,5), 1)
#dst2 = cv2.GaussianBlur(im1, (5,5), 0.1)
#dst3 = cv2.GaussianBlur(im1, (5,5), 100)
dst2 = cv2.filter2D(im1, -1, kernal_1)
img = [im1, dst1, dst2]
for i in range(len(img)):
cv2.imshow('Fig.'+str(i+1), img[i])
k = cv2.waitKey(0)
if k == ord('s'):
cv2.destroyAllWindows()
代码及结果如下所示,完全相同!
最后祝大家学习顺利噢!