边缘检测

边缘检测在图像的检测中是经常会用到的。图片的边缘会包含大量的信息,因此在图像的分割、识别、分析中通常可以取边缘作为图像特征。边缘检测最经典的应用就是图像的锐化了,想必大家都用过。

为了进行边缘检测,我们通常会用到以下的一些算子,即一阶算子(梯度算子)和二阶算子(拉普拉斯算子)。

拉普拉斯算子(Laplacian Operator)

拉普拉斯算子其实就是二阶微分算子,具有各向同性。他也会增强噪声,因此在使用拉普拉斯算子之前需要进行平滑处理。他的主要用处其实是判断一个像素是处于图像的亮区还是暗区。

拉普拉斯算子定义式
$$\begin{aligned}\nabla^2 f(x,y)&=\frac{\partial^2 f}{\partial x}+\frac{\partial^2 f}{\partial y}\\&=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)\end{aligned}$$

常用模板公式:(1)$\begin{bmatrix}0&1&0\\1&-4&1\\0&1&0\end{bmatrix}$(2)$\begin{bmatrix}1&1&1\\1&-8&1\\1&1&1\end{bmatrix}$

拉普拉斯模板有多个变种,上面是常用的两个,对于每一个像素,我们把他作为模板的中心元素,套用公式进行更新。需要注意的是,与梯度算子不同,他是各向同性的,因此不需要$G_x$,$G_y$两个模板,也就不能区分边缘的方向了。

还有一个常用的高斯-拉普拉斯算子(Gauss-Laplacian Operator),将平滑滤波器和拉普拉斯边缘检测算子结合起来的:

高斯-拉普拉斯算子模板:$\begin{bmatrix}-2&-4&-4&-4&-2\\-4&0&8&0&-4\\-4&8&24&8&-4\\-4&0&8&0&-4\\-2&-4&-4&-4&-2\end{bmatrix}$

另外,拉普拉斯算子有一个很实用的用途,就是对图像进行锐化,这时候只要将常用模板稍微改动一下即可得到锐化模板

(1)$\begin{bmatrix}0&-1&0\\-1&5&-1\\0&-1&0\end{bmatrix}$(2)$\begin{bmatrix}-1&-1&-1\\-1&9&-1\\-1&-1&-1\end{bmatrix}$

测试

OK,最后就是用代码实现一下了,总体还是很简单的,而且类似于OpenCV之类的库都有实现的函数。不过我还是用pil来简单实现一下:

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
import Image,matplotlib,sys,ImageEnhance,ImageFont,ImageDraw
import numpy as np

def gauss(im_arr):
mat=np.array([[-2,-4,-4,-4,-2],[-4,0,8,0,-4],[-4,8,24,8,-4],[-4,0,8,0,4],[-2,-4,-4,-4,-2]])
height,width=im_arr.shape
ans=np.zeros((height-4,width-4))
for i in range(height-4):
for j in range(width-4):
ans[i,j]=np.sum(im_arr[i:i+5,j:j+5]*mat)
return ans

def laplacian(im_arr):
mat=np.array([[1,1,1],[1,-8,1],[1,1,1]])
height,width=im_arr.shape
ans=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
ans[i,j]=np.sum(im_arr[i:i+3,j:j+3]*mat)
return ans

def sharpen(im_arr):
mat=np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]])
height,width=im_arr.shape
ans=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
ans[i,j]=np.sum(im_arr[i:i+3,j:j+3]*mat)
return ans

def robert(im_arr):
height,width=im_arr.shape
ans=np.zeros((height-1,width-1))
for i in range(height-1):
for j in range(width-1):
ans[i,j]=np.abs(im_arr[i,j]-im_arr[i+1,j+1])+np.abs(im_arr[i+1,j]-im_arr[i,j+1])
return ans

def sobel(im_arr):
matx=np.array([[-1,0,1],[-2,0,2],[-1,0,1]])
maty=np.array([[1,2,1],[0,0,0],[-1,-2,-1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans

def prewitt(im_arr):
matx=np.array([[1,0,-1],[1,0,-1],[1,0,-1]])
maty=np.array([[-1,-1,-1],[0,0,0],[1,1,1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans

def isotropic(im_arr):
matx=np.array([[1,0,-1],[1.414,0,-1.414],[1,0,-1]])
maty=np.array([[-1,-1.414,-1],[0,0,0],[1,1.414,1]])
height,width=im_arr.shape
gx=np.zeros((height-2,width-2))
gy=np.zeros((height-2,width-2))
for i in range(height-2):
for j in range(width-2):
gx[i][j]=np.sum(matx*im_arr[i:i+3,j:j+3])
gy[i][j]=np.sum(maty*im_arr[i:i+3,j:j+3])
ans=np.abs(gx)+np.abs(gy)
return ans

def show(im_arr,name):
im=Image.fromarray(im_arr)
draw=ImageDraw.Draw(im)
font = ImageFont.truetype('/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf', 18)
draw.text((0,0),name,font=font,fill='red')
del draw
im=im.convert('L')
im.save(name+'.png')

if __name__=='__main__':
im=Image.open(sys.argv[1])
im=im.convert('L')
im.save('source.png')
show(laplacian(np.array(im,'int64')),'Laplace')
show(sobel(np.array(im,'int64')),'Sobel')
show(robert(np.array(im,'int')),'Robert')
show(prewitt(np.array(im,'int64')),'Prewitt')
show(isotropic(np.array(im,'int64')),'Isotropic')
show(sharpen(np.array(im,'int64')),'sharpen')
show(gauss(np.array(im,'int64')),'gauss-laplace')

test
source
Sobel
sharpen
Robert
Prewitt
Isotropic
Laplace
gauss-laplace

最后用他来处理一下lena.png,会的到上面的结果。