数字图像处理之图像边缘

1.Prewitt、Roberts算子

Prewitt算子

检测水平或者竖直方向的梯度

特点:有正有负,和为0,有水平竖直两种

效果:均匀的部分接近零,变化大的部分数值大。可以描绘边缘

竖直与水平两种算子

效果

image.png

合成梯度为绝对值相加,或者平方和开根号,前者运算更快

锐化:将原图加上边缘,突出边缘

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
kx = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=np.float32)
ky = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]], dtype=np.float32) #两个算子

imgX = cv.filter2D(img, cv.CV_64F, kx)
imgY = cv.filter2D(img, cv.CV_64F, ky) #分别两个方向的梯度,内容有正有负

show(np.abs(imgX).clip(0,255)) #显示时取绝对值,剪到范围之内
#两种合成方式
imgXY = np.sqrt(imgX**2 + imgY**2) #平方和开根合成
imgXY2 = np.abs(imgX) + np.abs(imgY) #绝对值相加合成

show(np.hstack([imgXY.clip(0,255), imgXY2.clip(0, 255)]))

Roberts算子

检测斜方向的梯度

算子

效果

image.png

代码实现

1
2
3
4
5
6
7
8
9
10
kx = np.array([[-1, 0], [0, 1]], dtype=np.float32)
ky = np.array([[0, -1], [1, 0]], dtype=np.float32)

imgX = cv.filter2D(img, cv.CV_64F, kx)
imgY = cv.filter2D(img, cv.CV_64F, ky)

imgXY = np.sqrt(imgX**2 + imgY**2)
imgXY2 = np.abs(imgX) + np.abs(imgY)

show(np.hstack([imgXY.clip(0,255), imgXY2.clip(0, 255)]))

2.Sobel、Scharr算子

Sobel算子

算子

效果

image.png

代码实现

1
2
3
4
5
imgX = cv.Sobel(img, cv.CV_16S, 1, 0)	#(原图像,数据类型,dx,dy),dx、dy为偏导,x一阶偏导y零阶偏导为水平方向检测
imgY = cv.Sobel(img, cv.CV_16S, 0, 1) #x零阶偏导y一阶偏导为竖直方向检测

imgXY = np.abs(imgX) + np.abs(imgY) #合成
show(imgXY.clip(0, 255))

Scharr算子

几乎与Sobel算子一样

算子

代码实现

1
2
3
4
5
imgX = cv.Scharr(img, cv.CV_64F, 1, 0)
imgY = cv.Scharr(img, cv.CV_64F, 0, 1)

imgXY = np.sqrt(imgX**2 + imgY**2)
show(np.hstack([np.abs(imgX), np.abs(imgY), imgXY]))

3.Laplacian、LoG算子

效果

image.png

算子

image.png

LoG算子处理:先高斯模糊,再Laplacian。现实中使用很少

代码实现

1
2
3
4
5
6
7
8

img_lap = cv.Laplacian(img, cv.CV_64F)
show(np.abs(img_lap))

#LoG
img_blur = cv.GaussianBlur(img, (3,3), 1)
img_log = cv.Laplacian(img_blur, cv.CV_64F)
show(np.abs(img_log))

4.Canny边缘检测

效果好、经典、应用广泛的边缘检测算法

效果

image.png

步骤:

  1. 平滑:模糊削弱噪声。(通常为高斯滤波)
  2. 计算梯度:梯度包括大小和方向,有x和y方向合成。
  3. 细化边缘:利用非极大值抑制,只有局部最大值保留为边缘。
  4. 双阈值抑制:根据大小两个阈值,高于高于大阈值的为确定边缘(强边缘);低于小阈值的不是边缘;处于两者之间的像素点(弱边缘)下一步确定。
  5. 边缘追踪:对于若边缘,如果和强边缘相连,则确定为边缘,否则不是边缘。

代码实现

1
2
3
img_edge = cv.Canny(img, 20, 200)
#(图像,小阈值,大阈值)
show(img_edge)

Canny编程实现

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

img = cv.imread('pic/notebook500x333.jpg', 0)

# 1. 平滑
img_blur = cv.GaussianBlur(img, (5,5), 2)

# 2. 计算梯度
gradx = cv.Sobel(img_blur, cv.CV_64F, 1, 0)
grady = cv.Sobel(img_blur, cv.CV_64F, 0, 1)
R = np.abs(gradx) + np.abs(grady) #大小
T = np.arctan(grady / (gradx + 1e-3)) #方向 t=arctan(y/x+le-3)当x=0是可计算,在x上加一个很小的数

# 3. 细化边缘
h, w = R.shape
img_thin = np.zeros_like(R)

for i in range(1, h-1):
for j in range(1, w-1):
theta = T[i,j]
if -np.pi / 8 <= theta < np.pi / 8:
if R[i,j] == max([R[i,j], R[i,j-1], R[i, j+1]]):
img_thin[i,j] = R[i,j]
elif -3 * np.pi / 8 <= theta < -np.pi / 8:
if R[i,j] == max([R[i,j], R[i-1,j+1], R[i+1,j-1]]):
img_thin[i,j] = R[i,j]
elif np.pi / 8 <= theta < 3 * np.pi / 8:
if R[i,j] == max([R[i,j], R[i-1,j-1], R[i+1,j+1]]):
img_thin[i,j] = R[i,j]
else:
if R[i,j] == max([R[i,j], R[i-1,j], R[i+1,j]]):
img_thin[i,j] = R[i,j]

show(img_thin)


# 4. 双阈值抑制
th1 = 20
th2 = 300

h, w = img_thin.shape
img_edge = np.zeros_like(img_thin, dtype=np.uint8) #

for i in range(1, h-1):
for j in range(1, w-1):
if img_thin[i,j] >= th2:
img_edge[i,j] = 255 #强边缘
elif img_thin[i,j] > th1:
around = img_thin[i-1:i+2, j-1:j+2]
if around.max() >= th2: #判断周围是否有强边缘
img_edge[i,j] = 255

show(img_edge)