OpenCV-Python:霍夫变换

2019/04/19 17:47
阅读数 147

霍夫变换常用来在图像中提取直线和圆等几何形状。如下图:

我们下面来看看如何使用霍夫变换来检测直线。一条直线可以用数学表达式 y = mx + 或者 ρ = xcosθ + y sinθ表示(极坐标)

简单说明一下:

ρ 是从原点到直线的垂直距离,θ是直线的垂线与横轴顺时针方向的夹角,如下图所示:

首先创建一个2D数组(累加器),初始化累加器,所有的值都为0。行表示 ρ,列表示 θ。这个数组的大小决定了最后结果的准确性。如果你希望角度精确到1°,你就需要180列。对于 ρ,最大值为图片对角线距离。

想象一下我们有一个大小为100x100的直线位于图像中央。取直线上的第一个点,我们知道此处的(x,y)值,把x和y带入公式:ρ = xcosθ + y sinθ,然后遍历 θ 的取值0, 1, 2, 3,...,180.分别求出对应的 ρ 值,这样我们就得到了一系列(ρ,θ)的数值对,如果这个数值对在累加器中也存在相应的位置,就在这个位置上加 1。由于同一条直线上的点必然会有同样的(ρ,θ)。所以现在累加器中的(50,90)=1。现在取直线上的第二个点。重复上边的过程。更新累加器中的值。现在累加器中(50,90)的值为 2。你每次做的就是更新累加器中的值。对直线上的每个点都执行上边的操作,每次操作完成之后,累加器中的值就加 1,但其他地方有时会加 1, 有时不会。按照这种方式下去,到最后累加器中(50,90)的值肯定是最大的。如果你搜索累加器中的最大值,并找到其位置(50,90),这就说明图像中有一条直线,这条直线到原点的距离为 50,它的垂线与横轴的夹角为 90 度。

OpenCV中首先计算(r,θ) 累加数,累加数超过一定值后就认为在同一直线上(有一个阈值)。

霍夫直线变换

import cv2
import numpy as np
# 1.加载图片,转为二值图
img = cv2.imread('shapes.jpg')
drawing = np.zeros(img.shape[:], dtype=np.uint8)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
# 2.霍夫直线变换
lines = cv2.HoughLines(edges, 0.8, np.pi / 180, 90)

函数中:

  • 参数1:要检测的二值图(一般是阈值分割或边缘检测后的图)
  • 参数2:距离 ρ 的精度,值越大,考虑越多的线
  • 参数3:角度 θ 的精度,值越小,考虑越多的线
  • 参数4:累加数阈值,值越小,考虑越多的线
# 3.将检测的线画出来(注意是极坐标噢)
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 * (-b))
    y2 = int(y0 - 1000 * (a))
    cv2.line(drawing, (x1, y1), (x2, y2), (0, 0, 255))

统计概率霍夫直线变换

前面的方法又称为标准霍夫变换,它会计算图像中的每一个点,计算量比较大,另外它得到的是整一条线,并不知道原图中直线的端点。所以提出了概率霍夫直线变换(Probabilistic Hough Transform),是一种改进的霍夫变换:

drawing = np.zeros(img.shape[:], dtype=np.uint8)
# 统计概率霍夫线变换
lines = cv2.HoughLinesP(edges, 0.8, np.pi / 180, 90, minLineLength=50, maxLineGap=10)

# 将检测的线画出来
for line in lines: x1, y1, x2, y2 = line[0] cv2.line(drawing, (x1, y1), (x2, y2), (0, 255, 0), 1, lineType = cv2.LINE_AA) cv2.imwrite('houghlines4.jpg', drawing)

参数:

  • minLineLength:最短长度阈值,比这个长度短的线会被排除
  • maxLineGap:同一直线两点之间的最大距离

cv2.LINE_AA:抗锯齿线型

霍夫圆变换

霍夫圆变换跟直线变换类似,只不过线是用(r,θ),圆是用(x_center,y_center,r)来表示,从二维变成了三维,数据量变大了很多;所以一般使用霍夫梯度法去减少计算量

drawing = np.zeros(img.shape[:], dtype=np.uint8)
# 霍夫圆变换
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 20, param2=30)
circles = np.int0(np.around(circles))

# 将检测的圆画出来
for i in circles[0, :]:
    cv2.circle(drawing, (i[0], i[1]), i[2], (0, 255, 0), 2)  # 画出外圆
    cv2.circle(drawing, (i[0], i[1]), 2, (0, 0, 255), 3)  # 画出圆心
cv2.imwrite('HoughCircles.jpg', drawing)
  • 参数2:变换方法,一般使用霍夫梯度法,详情:HoughModes
  • 参数3 dp=1:表示霍夫梯度法中累加器图像的分辨率与原图一致
  • 参数4:两个不同圆圆心的最短距离
  • 参数5:param2跟霍夫直线变换中的累加数阈值一样

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部