数学原理:霍夫变换
1.直线检测
数学原理(个人观点):简单来说,就是把直角坐标与极坐标之间做变换,通过点映射直线,通过遍历所有像素点,极坐标曲线交点便是检测到的可能直线。
api:
1.标准霍夫变换,输入二值图像
cv.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]]) → lines
几个常用参数解释:
image:8位,单通道二进制源图像。
lines:输出直线向量,两个元素的向量(ρ,θ)代表一条直线,ρ是从原点(图像的左上角)的距离,θ是直线的角度(单位是弧度),0表示垂直线,π/2表示水平线
rho:距离步长
theta:角度步长
threshold:阈值,只有大于该值的点才有可能被当作极大值,即至少有多少条正弦曲线交于一点才被认为是直线
简单调参:
rho一般取1,theta取pi/180,阈值可调,其它默认即可
注:函数返回lines为三维数组,为每个直线的极坐标交点的极径,极角,可通过公式解算出直线端点进行绘制
完整源码:
def line_detection():#直线检测(霍夫变换),噪声敏感
img = cv.imread('C:\\Users\\Lin Xi\\Desktop\\OpenCV\\road.jpg')
img2 = np.copy(img)
#dst = cv.GaussianBlur(img,(5,5),0)#优先选择ksize,ksize为0,通过sigmaX解算ksize,sigma一般取5~25
cv.imshow('road',img)
dst = cv.Canny(img,150,300,apertureSize = 3)
cv.imshow('edges',dst)
lines = cv.HoughLines(dst,1,np.pi/180,230,None,0,0)#标准霍夫变换,输入二值图像
if lines is not None:
for i in range(len(lines)):
rho = lines[i][0][0]
theta = lines[i][0][1]
a = math.cos(theta)
b = math.sin(theta)
x0 = a * rho
y0 = b * rho
pt1 = (int (x0 + 1000 * (-b)),int(y0 + 1000 *a))
pt2 = (int (x0 - 1000 * (-b)),int(y0 - 1000 *a))
cv.line(img,pt1,pt2,(0,0,255),2,8,0)
cv.imshow('hough',img)
效果图:
2.累计概率霍夫变换,输入二值图像
cv.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) → lines
常用参数解释:
跟上个api参数基本类似
minLineLength:最低线长度。较短的线段被拒绝。
maxLineGap:同一直线上各点之间的最大允许间隔,以连接它们。
用min和max作门限,钳住检测到的直线,参数可根据实际调整
注:函数返回三维数组,最后一维依次为每条直线的端点坐标值
完整源码:
def line_detection():#直线检测不能是灰度只能是二值,虽然官方文档显示输入8bit单通道图像
img = cv.imread('C:\\Users\\Lin Xi\\Desktop\\OpenCV\\road.jpg')
img2 = np.copy(img)
#dst = cv.GaussianBlur(img,(5,5),0)#优先选择ksize,ksize为0,通过sigmaX解算ksize,sigma一般取5~25
cv.imshow('road',img)
dst = cv.Canny(img,150,300,apertureSize = 3)
cv.imshow('edges',dst)
linesp = cv.HoughLinesP(dst,1,np.pi/180,100,None,50,10)#累计概率霍夫变换,输入二值图像
if linesp is not None:
for i in range(len(linesp)):
l = linesp[i][0]
cv.line(img2,(l[0],l[1]),(l[2],l[3]),(0,0,255),2,8,0)
cv.imshow('hough p',img2)
效果图:
最后强调,霍夫检测api均对噪声敏感,噪点会被直接参与检测算法,因此要事先做好滤波
2.圆检测
数学原理:跟直线检测原理类似,但圆检测为三维坐标(x0,y0,r),不能像二维霍夫直线检测那样对于多个边缘点越多这些点对应的三维空间曲线交于一点那么他们经过的共同圆上的点就越多,算法复杂度过高,这里采用霍夫梯度法
api:
cv.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) → circles
常用参数解释:
image:单通道灰度图(直线检测必须是二值图)
method:定义检测图像中圆的方法。设为cv.HOUGH_GRADIENT即可
minRadius:半径的最小大小(以像素为单位)
maxRadius:半径的最大大小(以像素为单位)
min和max作用类似于上述直线检测,两个param均为经验值
完整源码:
def circle_detection():#霍夫圆检测,输入灰度非二值图像
img = cv.imread('C:\\Users\\Lin Xi\\Desktop\\OpenCV\\circle.jpg')
cv.imshow('input',img)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
rows,cols = img.shape[:2]
circles = cv.HoughCircles(gray,cv.HOUGH_GRADIENT,1,rows/4,None,param1 = 100,param2 = 30,minRadius = 20,maxRadius = 150)
if circles is not None:
for i in circles[0,:]:
center = (i[0],i[1])
radius = i[2]
cv.circle(img,center,2,(0,255,0),-1,8,0)
cv.circle(img,center,radius,(0,255,255),2,8,0)
cv.imshow('circle',img)
效果图: