图像处理の各种滤波方法

前几天“衣 紧 还 乡”,今天还是得把滤波算法补一下了

高斯滤波

参考blog:

小学的时候看我妈搞PS,有个叫“高斯模糊”的东西,今天终于明白了,这个高斯模糊,其实学名叫“高斯滤波”,是一种图像滤波算法,好神奇诶~

原理

对于通常的二维图像,高斯滤波使用二维正态分布函数(高斯函数)作为权重分配,函数形式如下

其实这就是之前在学CNN时的图像卷积操作(所以滤波器也叫做卷积核),通过构造一个卷积核,不断对原图的每个像素点进行卷积计算,对于一个3*3的滤波器来说,就是将指定像素与周围8个像素点乘以3*3的高斯权重分布再求和得到该像素点的输出值

  • 求高斯权重分布的方法:将滤波器中心点视为原点(0,0),可以得到滤波器函数值的分布,最后一定要再除以矩阵所有值之和,因为需要满足权重和为1,这是归一化操作所必需要有的步骤

PS:由于要保持滤波后的图像尺寸与原图相同,需要有$zero-padding$,即在矩阵周围补零,经过简单的数学推导,容易得出,在滤波器步长为1且滤波器为方阵的情况下,$pad$大小与滤波器尺寸$size$须满足如下关系:

效果

image-20200809183530008

代码

code
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
import cv2
import numpy as np

def gauss_func(x,y,sigma): # 高斯函数计算
return np.exp(-(x*x+y*y)/(2*sigma*sigma))/(2*np.pi*sigma*sigma)

def gauss_filter(image,Size=3,sigma=1.3):
img = image.copy()
H,W,C = image.shape
pad = Size//2 # pad大小
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

Filter = np.zeros((Size,Size),dtype=np.float)
for i in range(-pad,-pad+Size):
for j in range(-pad,-pad+Size):
Filter[i+pad,j+pad] = gauss_func(j,i,sigma)
Filter /= Filter.sum() # 切记

out = np.zeros_like(img,dtype=np.float)
for i in range(H):
for j in range(W):
for c in range(C):
out[i,j,c] = np.sum(Filter * pad_img[i:i+Size,j:j+Size,c])

out = np.clip(out,0,255)
out = out.astype(np.uint8)

return out


img = cv2.imread("test.jpg")
img2 = gauss_filter(img,Size=13,sigma=3)

print(img.shape)
cv2.imshow("img2", img2)
cv2.imshow("img1", img)
cv2.waitKey(0)

中值滤波

其实就是利用滤波器大小区域的原图对应元素的中位数作为该像素点的输出值,那么代码只需要把高斯滤波模板稍微改改就行了(利用np.median())求中位数

效果

image-20200809183820910

代码

code
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
import cv2
import numpy as np

def mid_filter(image,Size=3):
img = image.copy()
H,W,C = image.shape
pad = Size//2
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

out = np.zeros_like(img,dtype=np.float)
for i in range(H):
for j in range(W):
for c in range(C):
out[i,j,c] = np.median(pad_img[i:i+Size,j:j+Size,c])

out = np.clip(out,0,255)
out = out.astype(np.uint8)

return out


img = cv2.imread("test.jpg")
img2 = mid_filter(img,Size=13)

print(img.shape)
cv2.imshow("img2", img2)
cv2.imshow("img1", img)
cv2.waitKey(0)

均值滤波

同上,不过这次是均值,代码就不放了

效果

image-20200809184308820

Motion 滤波

取对角线方向像素的平均值作为输出,即 滤波器矩阵为(以3阶为例):

效果

image-20200809184201428

代码

  • np.diag()方法生成对角阵,传入对角元素值作为参数即可
code
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
import cv2
import numpy as np

def motion_filter(image,Size=3):
img = image.copy()
H,W,C = image.shape
pad = Size//2
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

Filter = np.diag([1] * Size).astype(np.float)
Filter /= Size

out = np.zeros_like(img,dtype=np.float)
for i in range(H):
for j in range(W):
for c in range(C):
out[i,j,c] = np.sum(Filter * pad_img[i:i+Size,j:j+Size,c])

out = np.clip(out,0,255)
out = out.astype(np.uint8)

return out


img = cv2.imread("test.jpg")
img2 = motion_filter(img,Size=5)

print(img.shape)
cv2.imshow("img2", img2)
cv2.imshow("img1", img)
cv2.waitKey(0)

MIN-MAX 滤波

MAX-MIN滤波器使用网格内像素的最大值和最小值的差值对网格内像素重新赋值。通常用于边缘检测

边缘检测用于检测图像中的线。像这样提取图像中的信息的操作被称为特征提取

边缘检测通常在灰度图像上进行。

效果

这里用filter_size = 3的滤波器进行特征提取

image-20200809191738889

代码

code
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
import cv2
import numpy as np

def minmax_filter(image,Size=3):
img = image.copy()
H,W,C = image.shape
pad = Size//2
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

out = np.zeros_like(img,dtype=np.float)
for i in range(H):
for j in range(W):
for c in range(C):
out[i,j,c] = np.max(pad_img[i:i+Size,j:j+Size,c])-np.min(pad_img[i:i+Size,j:j+Size,c])

out = np.clip(out,0,255)
out = out.astype(np.uint8)

return out


img = cv2.imread("test.jpg")
img2 = minmax_filter(img,Size=5)

print(img.shape)
cv2.imshow("img2", img2)
cv2.imshow("img1", img)
cv2.waitKey(0)

差分滤波

差分滤波器对图像亮度急剧变化的边缘有提取效果,可以获得邻接像素的差值。

纵向: 横向:

因此,差分滤波有横向和纵向两种方式,分别能提取对应的边缘特征

效果

vout为纵向,hout为横向

image-20200809192454076

代码

code
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
import cv2
import numpy as np

def differential_filter(image,Size=3):
img = image.copy()
H,W,C = image.shape
pad = Size//2
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

vFilter = np.array([[0,-1,0],[0,1,0],[0,0,0]])
hFilter = np.array([[0,0,0],[-1,1,0],[0,0,0]])

vout = np.zeros_like(img,dtype=np.float)
hout = vout.copy()

for i in range(H):
for j in range(W):
for c in range(C):
vout[i,j,c] = np.sum(vFilter * pad_img[i:i+Size,j:j+Size,c])
hout[i,j,c] = np.sum(hFilter * pad_img[i:i+Size,j:j+Size,c])

vout = np.clip(vout,0,255)
vout = vout.astype(np.uint8)
hout = np.clip(hout,0,255)
hout = hout.astype(np.uint8)

return hout,vout


img = cv2.imread("test2.jpg")
hout,vout = differential_filter(img,Size=3)
out = hout+vout

cv2.imshow("vout", vout)
cv2.imshow("hout", hout)
cv2.imshow("out", out)
cv2.imshow("img", img)
cv2.waitKey(0)

Sobel 滤波

Sobel滤波器可以提取特定方向(纵向或横向)的边缘,滤波器按下式定义:

纵向: 横向:

PS:从矩阵就可以看出来,这其实就是类似于差分滤波的一种滤波方式,加剧图像差别大的边缘的差别,从而达到特征提取的效果。代码实现上,只要把差分滤波的滤波器矩阵换掉就行了

效果

image-20200809192939813

Prewitt 滤波

Prewitt滤波器是用于边缘检测的一种滤波器,使用下式定义:

纵向: 横向:

PS:也类似于上面两种

效果

image-20200809193112759

Laplacian 滤波

Laplacian滤波器是对图像亮度进行二次微分从而检测边缘的滤波器。由于数字图像是离散的,$x$方向和$y$方向的一次微分分别按照以下式子计算:

因此二次微分按照以下式子计算:

同理:

特此,Laplacian 表达式如下:

如果把这个式子表示为卷积核是下面这样的:

PS:代码其实也就是把上面的矩阵换掉即可

效果

image-20200809193417506

Emboss 滤波

Emboss滤波器可以使物体轮廓更加清晰,按照以下式子定义:

PS:类似PS里面的浮雕效果,

效果

image-20200809193610956

LoG滤波

LoG即高斯-拉普拉斯(Laplacian of Gaussian)的缩写,使用高斯滤波器使图像平滑化之后再使用拉普拉斯滤波器使图像的轮廓更加清晰。

为了防止拉普拉斯滤波器计算二次微分会使得图像噪声更加明显,所以我们首先使用高斯滤波器来抑制噪声。

LoG 滤波器使用以下式子定义:

PS:即 先降噪,再提取轮廓特征

效果

image-20200809194231934

代码

code
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
import cv2
import numpy as np

def LoG_func(x,y,sigma=1.3):
return (x*x+y*y-sigma*sigma)/(2*np.pi*sigma**6)*np.exp(-(x*x+y*y)/(2*sigma*sigma))

def LoG_filter(image,Size=3,sigma=1.3):
img = image.copy()
H,W,C = image.shape
pad = Size//2
pad_img = np.zeros((H+pad*2,W+pad*2,C),dtype=np.float)
pad_img[pad:pad+H,pad:pad+W] = img.copy().astype(np.float)

Filter = np.zeros((Size,Size), dtype=np.float)
for y in range(-pad,-pad+Size):
for x in range(-pad,-pad+Size):
Filter[y+pad,x+pad] = LoG_func(x,y,sigma)
Filter /= Filter.sum()

out = np.zeros_like(img,dtype=np.float)

for i in range(H):
for j in range(W):
for c in range(C):
out[i,j,c] = np.sum(Filter * pad_img[i:i+Size,j:j+Size,c])

out = np.clip(out,0,255)
out = out.astype(np.uint8)
return out


img = cv2.imread("test2.jpg")
out = LoG_filter(img)

cv2.imshow("out", out)
cv2.imshow("img", img)
cv2.waitKey(0)