霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。霍夫变换于1962年由Paul Hough 首次提出[53],后于1972年由Richard Duda和Peter Hart推广使用[54],经典霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆.
一、Hough变换原理
- 在图像中检测直线的问题,其实质是在找到构成直线的所有的像素点。那么问题就是从找到直线,变换成找到符合y = kx + b的所有(x, y)的点的额问题。
- 进行坐标系变化 y = kx + b :变成 b = -xk +y。这样表示为过点(k, b)的直线束。
- x-y空间的直线上每一个点在k-b坐标系中都变现为经过点(k, b)的直线。找到所有点的问题,转变为寻找直线的问题。
- 对于图像的每一个点,在k-b坐标系中对应着很多的直线。找到直线的交点,就对应着找到图像中的直线。
目前,OpenCV霍夫检测直线常用的方法是,将平面中任一条直线用极坐标方法表示:ρ=xcosθ+ysinθ 。
其中ρ表示直角坐标系中原点到直线的距离,θ表示x轴与ρ的夹角,这样图像平面中的一个点就对应到ρ-θ平面上的一条曲线。
如果对位于同一直线上的n个点进行变换,原图像空间的n个点在参数空间对应得到n条正弦曲线,并且这些曲线相交于一点。
二、实现霍夫变换检测直线的步骤
- 灰度化:cv.cvtColor(img, cv.COLOR_BGR2GRAY)
- 二值化:cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
- canny边缘检测:cv.Canny(binary, 50, 150, apertureSize=3)
- 霍夫直线检测:cv.HoughLines(edges, 1, np.pi/180, 200)
OpenCV支持两种不同的霍夫变换:标准霍夫变换(SHT)和累计概率霍夫变换(PPHT)。
三、具体代码
- 标准霍夫变换:
def line_detection(img):
# 高斯模糊 去除噪音
# dst = gaussian_noise(img)
#灰度化
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# #二值化
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
#canny边缘检测
edges = cv.Canny(binary, 50, 150, apertureSize=3)
#霍夫直线检测
lines = cv.HoughLines(edges, 1, np.pi/180, 200)
#在原图上标记
for line in lines:
print(line)
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0+1000*(-b))
y1 = int(y0+1000*(a))
x2 = int(x0-1000*(-b))
y2 = int(y0-1000*(a))
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv.imshow('image_lines', img)
2.累积概率霍夫变换
def line_detection(img):
# 高斯模糊 去除噪音
# dst = gaussian_noise(img)
#灰度化
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# #二值化
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
#canny边缘检测
edges = cv.Canny(binary, 50, 150, apertureSize=3)
#霍夫直线检测
lines = cv.HoughLines(edges, 1, np.pi/180, 200)
#在原图上标记
for line in lines:
print(line)
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0+1000*(-b))
y1 = int(y0+1000*(a))
x2 = int(x0-1000*(-b))
y2 = int(y0-1000*(a))
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv.imshow('image_lines', img)
四、效果图: