参考:
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。
该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。
霍夫变换于1962年由Paul Hough 首次提出[53],后于1972年由Richard Duda和Peter Hart推广使用[54],经典霍夫变换用来检测图像中的直线,
后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆.
经过几天的学习,发现各位大牛的理解方式之前都是有一些区别的,但是核心的思想没有变化,因此记录一下自己对霍夫变换直线检测的认识。
一、原理介绍:
1、对于直角坐标系中的任意一点A(x0,y0),经过点A的直线满足Y0=k*X0+b.(k是斜率,b是截距)
2、那么在X-Y平面过点A(x0,y0)的直线簇可以用Y0=k*X0+b表示,但对于垂直于X轴的直线斜率是无穷大的则无法表示。因此将直角坐标系转换到极坐标系就能解决该特殊情况。
3、在极坐标系中表示直线的方程为ρ=xCosθ+ySinθ(ρ为原点到直线的距离),如图所示:
4、如上图,假定在一个8*8的平面像素中有一条直线,并且从左上角(1,8)像素点开始分别计算θ为0°、45°、90°、135°、180°时的ρ,
图中可以看出ρ分别为1、(9√2)/2、8、(7√2)/2、-1,并给这5个值分别记一票,同理计算像素点(3,6)点θ为0°、45°、90°、135°、180°时的ρ,
再给计算出来的5个ρ值分别记一票,此时就会发现ρ = (9√2)/2的这个值已经记了两票了,
以此类推,遍历完整个8*8的像素空间的时候ρ = (9√2)/2就记了5票, 别的ρ值的票数均小于5票,
所以得到该直线在这个8*8的像素坐标中的极坐标方程为 (9√2)/2=x*Cos45°+y*Sin45°,到此该直线方程就求出来了。
(PS:但实际中θ的取值不会跨度这么大,一般是PI/180)。
cvHoughLines()直线检测中代码的疑惑
rho :(0,0)到直线的距离分辨率,rho的正负判定:Y轴截距大于0的均为正,Y轴截距小于0则rho为负。theta为直线与Y轴负方向的夹角,以Y轴负轴为起始轴,逆时针旋转到直线的角度。
代码如下:
import cv2 as cv
import numpy as np
def line_detection(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
edges=cv.Canny(gray,50,150,apertureSize=3)
# rho=1,半径的步长??
# theta:偏转角度,threshold:寻找canny中幅值>200的点
# 出现的是霍夫空间的图r,theta
lines=cv.HoughLines(edges,rho=1,theta=np.pi/180,threshold=150) #返回列表
for line in lines:
r,theta=line[0]
x0=r*np.cos(theta)
y0=r*np.sin(theta)
x1 = int(x0 - 1000 * np.sin(theta))
y1 = int(y0 + 1000 * np.cos(theta))
x2 = int(x0 + 1000 * np.sin(theta))
y2 = int(y0 - 1000 * np.cos(theta))
cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)
cv.imshow('line_detection',image)
def line_possible_detection(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)
#minLineLength=50 ,最小线长度,50个pixel
# maxLineGap :线断了,最大能容忍多少gap的像素值
lines = cv.HoughLinesP(edges, rho=1, theta=np.pi / 180, threshold=100,
minLineLength=30,maxLineGap=10) # 返回线段,不是直线
for line in lines:
x1,y1,x2,y2=line[0]
cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow('line_detection', image)
src=cv.imread('linedetect.jpg')
line_possible_detection(src)
cv.waitKey(0)
cv.destroyAllWindows()
结果: