霍夫变换就是一个可以让计算机学会自己找图形的算法。是图形处理领域内从图像中检测几何形状的基本方法之一。经典霍夫变换用来检测图像中的直线,后来霍夫变换经过扩展可以进行任意型状物体的识别,例如圆和椭圆。
霍夫变换运用两个坐标空间之间的变换,将在一个空间具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测形状的问题转化成统计峰值问题
一,霍夫直线变换原理
如下图,将xy坐标系转换为kb的坐标系。由于一条直线的kb值是确定的,所以在kb坐标系下,一条直线就可以表示为一个点
在kb坐标系下,kb相当于变量,而xy相当于常量,所以,xy坐标系下的AB两点相当于霍夫空间的两条直线。在xy坐标系下,过A点有无数多条直线,对应霍夫空间中就是A点坐标对应直线上的无数多个点
读到这就可以发现,笛卡尔坐标系(xy坐标系)下的点 就代表了霍夫空间的一条直线,多个点就代表多条直线,如果这几个点共线,就代表这几个点对应在霍夫空间下的直线均交于一点,利用这个特点我们就可以检测图片中的直线
(错误更正:下图第一句多条曲线改为多条直线)在笛卡尔坐标系下,出现多个点,并且这多个点可以连成多条直线。将这些点均对应到霍夫空间中,可以看到交出了ABCD四个点,而AC点由三条直线交出,相比于BD更多,我们会选择尽可能多直线交出的点
将笛卡尔坐标系用极坐标表示出来。接下来将其转换到霍夫空间中,由于极坐标系变量为ρ和θ,所以霍夫空间的xy轴就是ρ和θ。如下图,根据上式xcosθ+ysinθ=ρ将笛卡尔坐标系中的点转换到霍夫空间中即可,可以发现最终也交于一点,这个点就代表了笛卡尔坐标系下的直线
再举一个例子
二,霍夫圆变换原理
霍夫圆变换有a,b,r三个参数,构成三维空间
以上霍夫圆变换仅为简单介绍,在OpenCV中是有优化的,这里不做介绍
三,霍夫变化在OpenCV中的应用
1,霍夫直线变换函数
OpenCV已经给我们封装好了函数,我们可以调用 cv2.HoughLines函数来使用霍夫直线变换检测图片中的直线。
rho就是上文提到的,如下蓝圈所示垂直距离。theta角度步长就是在选定一个θ值范围进行遍历代入时每次加几度,一般为1°。阈值可以简单的理解为直线的长度。返回值lines为极坐标表示的直线,如果我们需要利用这条直线,要把它转换为笛卡尔坐标下的直线
以下为将极坐标转换为笛卡尔坐标并画出直线的代码
lines = cv2.HoughLines(edges, 1, np.pi/180, 180)#通过调阈值来改变图像中找到直线的数量
#调完阈值还不行就调第二个参数1
for line in lines:
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 *a)
y2 = int(y0 - 1000 *a)
cv2.line(picture, (x1, y1), (x2, y2), (0, 0, 255), 2)
检测直线代码实例
def line_detect(picture):
gray = cv2.cvtColor(picture, cv2.COLOR_BGR2GRAY)
gaussion = cv2.GaussianBlur(gray, (9, 9), 0)
edges = cv2.Canny(gaussion, 50, 150)
lines = cv2.HoughLines(edges, 1, np.pi/180, 180)
for line in lines:
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 *a)
y2 = int(y0 - 1000 *a)
cv2.line(picture, (x1, y1), (x2, y2), (0, 0, 255), 2)
2,霍夫直线变换优化函数-概率霍夫检测
概率霍夫检测核心点是采取概率挑选机制,选取某些点进行检测,而不是像cv2.HoughLines一样选取所有点,这样可以减少计算压力
3,霍夫圆变换函数
circles为返回值,返回(圆横坐标。圆纵坐标,半径)
def circle_detect(picture):
gray = cv2.cvtColor(picture, cv2.COLOR_BGR2GRAY)
gaussion = cv2.GaussianBlur(gray, (9, 9), 0)
edges = cv2.Canny(gaussion, 50, 150)
#通过修改阈值param1和param2来调整检测的圆。从20向后都是比较重要的要调整的参数
circles = cv2.HoughCircles(edges,HOUGH_GRADIENT,1,20,param1=30,param2=20,minRadius=10,maxRadius=35)
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
cv2.circle(picture, (circle[0],circle[1]), circle[2], (0, 0, 255), 2)
cv2.circle(picture, (circle[0],circle[1]), 2, (0, 0, 255), 2)