霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。
霍夫变换于1962年由PaulHough首次提出,最初的Hough变换是设计用来检测直线和曲线,起初的方法要求知道物体边界线的解析方程,但不需要有关区域位置的先验知识。这种方法的一个突出优点是分割结果的Robustness,即对数据的不完全或噪声不是非常敏感。然而,要获得描述边界的解析表达常常是不可能的。后于1972年由Richard Duda & Peter Hart推广使用,经典霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。霍夫变换运用两个坐标空间之间的变换将在一个空间中具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题。
霍夫变换在OpenCV中分为霍夫线变换和霍夫圆变换两种。
Hough直线检测的基本原理在于利用点与线的对偶性,在我们的直线检测任务中,即图像空间中的直线与参数空间中的点是一一对应的,参数空间中的直线与图像空间中的点也是一一对应的。这意味着我们可以得出两个非常有用的结论:
1)图像空间中的每条直线在参数空间中都对应着单独一个点来表示;
2)图像空间中的直线上任何一部分线段在参数空间对应的是同一个点。
因此Hough直线检测算法就是把在图像空间中的直线检测问题转换到参数空间中对点的检测问题,通过在参数空间里寻找峰值来完成直线检测任务。
优点:Hough直线检测的优点是抗干扰能力强,对图像中直线的残缺部分、噪声以及其它共存的非直线结构不敏感,能容忍特征边界描述中的间隙,并且相对不受图像噪声的影响。
缺点:Hough变换算法的特点导致其时间复杂度和空间复杂度都很高,并且在检测过程中只能确定直线方向,丢失了线段的长度信息。由于霍夫检测过程中进行了离散化,因此检测精度受参数离散间隔制约。
OpenCV的中用函数HoughLines(标准)和HoughLinesP(基于统计)来检测图像中的直线,函数原型如下:
lines = cv.HoughLines( image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]] )
参数说明:
lines:返回值(ρ,θ),ρ以像素为单位,θ以弧度为单位;
rho:累加器的距离分辨率(以像素为单位);
theta:累加器的角度分辨率(弧度);
threshold:阈值,仅大于的才可以被返回;
srn:对于多尺度Hough变换,它是距离分辨率rho的除数。粗累加器距离分辨率为rho,精确累加器分辨率为rho/srn。如果srn=0和stn=0,则使用经典Hough变换。否则,这两个参数都应为正值;
stn:对于多尺度Hough变换,它是距离分辨率θ的除数;
min_theta:对于标准和多尺度Hough变换,检查直线的最小角度。必须介于0和最大θ之间;
max_theta:对于标准和多尺度Hough变换,检查直线的最大角度。必须介于min_theta和CV_PI之间。
lines = cv.HoughLinesP( image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]] )
参数说明:
lines:返回两个端点的坐标;
rho:累加器的距离分辨率(以像素为单位);
theta:累加器的角度分辨率(弧度);
threshold:阈值,仅大于的才可以被返回;
minLineLength:最小行长度,小于该长度的线段将被拒绝;
maxLineGap:同一直线上连接点的最大允许间距。
测试代码如下:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
src = cv.imread("demo-line.jpg")
img = src.copy()
# 二值化图像(Canny边缘检测)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
dst_img = cv.Canny(gray_img, 50, 150)
# 霍夫线变换
lines = cv.HoughLines(dst_img, 0.5, np.pi / 180, 300)
# 将检测的线绘制在原图上(注意是极坐标)
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
# 找两个点
x0 = rho * a
y0 = rho * b
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), (255, 0, 0), 3)
# 显示图像
plt.subplot(311), plt.imshow(src, 'gray'), plt.title('src_img'), plt.axis('off')
plt.subplot(312), plt.imshow(dst_img, 'gray'), plt.title('canny_img'), plt.axis('off')
plt.subplot(313), plt.imshow(img, 'gray'), plt.title('HoughLines_img'), plt.axis('off')
plt.show()
效果如下:
霍夫圆变换的基本思路是认为图像上每一个非零像素点都有可能是一个潜在的圆上的一点,跟霍夫线变换一样,也是通过投票,生成累积坐标平面,设置一个累积权重来定位圆。
OpenCV中使用cv2.HoughCircles函数来实现霍夫圆检测,其函数原型如下:
cv2.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) → circles
参数说明:
image:8位单通道图像,如果使用彩色图像,需要先转换成灰度图像;
method:定义检测图像中圆的方法,目前唯一实现的方法是cv2.HOUGH_GRADIENT;
dp:图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值,它把参数空间认为是一个累加器,毕竟里面存储的都是经过的像素点的数量),dp=1,则参数空间与图像像素空间(分辨率)一样大,dp=2,参数空间的分辨率只有像素空间的一半大;
minDist:检测到的圆的中心,(x,y)坐标之间的最小距离。如果minDist太小,则可能导致检测到多个相邻的圆。如果minDist太大,则可能导致很多圆检测不到;
param1:用于处理边缘检测的梯度值方法;
param2:cv2.HOUGH_GRADIENT方法的累加器阈值。阈值越小,检测到的圆越多;
minRadius:半径的最小大小(以像素为单位);
maxRadius:半径的最大大小(以像素为单位)。
测试代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('demo-circle.jpg',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,200,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
plt.subplot(121), plt.imshow(img, 'gray'), plt.title('src_img'), plt.axis('off')
plt.subplot(122), plt.imshow(cimg, 'gray'), plt.title('HoughCircles_img'), plt.axis('off')
plt.show()
效果如下:
图像处理、机器学习的常用算法汇总