霍夫变换是一种特征提取技术,被广泛应用在图像分析、计算机视觉以及数字图像处理。

目录

  1. Hough(霍夫)直线检测
  2. Hough 变换的原理
  3. 算法实现
  4. Code[Python]

Hough(霍夫)直线检测

直线检测:对于边界上有 n 个点的点集,找出共线的点集和直线方程。

Hough 变换的原理

对于直线,从原点做其垂线,如图中的。如果将其看做极坐标系,则可以用表示。

怎么表达AB与CD的关系呢?

任取AB上一点E(x,y),做CD的垂线GH,AB的垂线GI。从而我们可以得到角度关系
所以,,从而,即。可以证明对于直线AB上的任意一点,都满足上式,即是直线AB的表达式。

再看,如果我们固定,将作为自变量,可以得到一个的一个函数关系式。当变化时(CD绕原点旋转时),AB也会随之变化(转动)。由于固定了,所以直线AB一定会经过点,所以它表达了经过点所有直线。

例如:,曲线上每一个点都可以表示一条经过点的直线。

下图分别是的图像,则分别表示所有经过点的所有直线。那么图中的交点A表示什么?
经过点的直线是同一条线。Amazing!这不就是说明三点共线吗,感觉忽然发现了新大陆。

现在,我们已经找到了证明多个点共线的方法了。让我们重新梳理一下:

  1. 设有一个点集,可得到中任意点对应的曲线。它表示了经过该点的所有直线。
  2.  ,如果有交点),就说明两点共线。
  3. 可得到所在的直线

刚才分析的都是基于连续的函数,如果要用算法实现,则需要将其离散化
因为,但是如果用程序实现只能将离散化,如令theta = range(0, 360) 取360个离散的值(具体的精度根据实际情况决定)。

算法实现

  1. 输入一幅RGB图
  2. 检测图中的边缘,可以使用Canny边缘检测得到一幅二值图像
  3. 开辟一个二维数组 array ,初始值全部为0,用于存储共线点的个数;确定的精度。
  4. 对于每一个边缘点,使用公式计算每一个对应的,将作为数组 array 的索引,将对应的数组元素值加 1 。
  5. 设定一个阈值,过滤出共线点较多的直线(也可以选取共线点最多的几条直线)。
  6. 将检测出的直线绘制到原图上。

Code[Python]

  • hough_without_canny 中直接使用的是经过Canny检测后的二值图像。
  • hough_with_canny 中调用了之前写过的Canny算法。(有时间会把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
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import canny
import math


def to_gray(img):
height, width, _ = img.shape
gray = np.zeros((height, width), dtype=np.uint8)
k = np.array([0.299, 0.587, 0.114])

for i in range(height):
for j in range(width):
gray[i][j] = (k * img[i][j]).sum()

return gray


def to_rgb(img):
height, width = img.shape
rgb = np.zeros((height, width, 3), dtype=np.uint8)

for i in range(height):
for j in range(width):
rgb[i][j] = [img[i][j]] * 3

return rgb


def plot_line(img, expression):
if len(img.shape) != 3 or img.shape[2] != 3:
print('The image shape must be (height, width, 3)')
return img

height = img.shape[0]
width = img.shape[1]

def func(x, a, b):
return int(a * x + b)

for exp in expression:
a, b = exp
point = [(x, func(x, a, b)) for x in range(width)
if 0 <= func(x, a, b) < height]

for x, y in point:
img[y][x] = [255, 0, 0]

return img


def hough_check(img, precision=360, threshold=50, number=-1):
height, width = img.shape
theta = range(0, precision)
factor = 360 / precision

tab_height = len(theta)
tab_width = (height + width) << 1
tab = [[0] * tab_width for i in range(tab_height)]

for i in range(height):
for j in range(width):
if img[i][j] == 255:
for k in theta:
arc = k * factor * math.pi / 180
rho = int(j * math.cos(arc) + i * math.sin(arc))
tab[k][rho + height + width] += 1

line = []
for i in range(tab_height):
for j in range(tab_width):
if tab[i][j] > threshold:
# append theta (0-360) and rho to line
line.append((i * factor, j - height - width))

if number == -1 or len(line) <= number:
return line
else:
line.sort(key=lambda x: tab[int(x[0] / factor)][x[1] + height + width])
return line[0:number]


def get_expression(pair):
theta, rho = pair
theta = theta * math.pi / 180
k = -math.cos(theta) / (math.sin(theta) + 1e-8)
b = rho / (math.sin(theta) + 1e-8)
return k, b


def hough_with_canny():
filename = 'hf.jpg'
rgb_img = Image.open(filename)
gray_img = rgb_img.convert('L')
gray_array = np.array(gray_img)
# img = to_gray(img_array)
gray_array = canny.canny(gray_array, (3, 3))

line = hough_check(gray_array, number=5)
print('The number of lines is {}'.format(len(line)))
expression = [get_expression(x) for x in line]
rgb_array = to_rgb(gray_array)
rgb_array = plot_line(rgb_array, expression)

plt.figure()
plt.imshow(rgb_array)
plt.show()


def hough_without_canny():
filename = 'gray.jpg'
rgb_img = Image.open(filename)
gray_img = rgb_img.convert('L')

gray_array = np.array(gray_img)
rgb_array = to_rgb(gray_array)

line = hough_check(gray_array, number=5)
print('The number of lines is {}'.format(len(line)))
expression = [get_expression(x) for x in line]
rgb_array = plot_line(rgb_array, expression)

plt.figure()
plt.imshow(rgb_array)
plt.show()


if __name__ == '__main__':
# main()
hough_without_canny()