B12 - 图像处理 - 霍夫变换

将二维座标 x, y 转换到霍夫空间上,然后通过投票区分出图像的直线或圆

什么是霍夫变换?

  • 霍夫变换是一种特征提取,用来识别找出目标中的特征,例如线条、圆、椭圆
  • 人类很容易将图片的线、圆、椭圆区分出来,对于计算机来说却不是如此简单,计算机发挥自己最擅长的功能,直接计算由边缘点 + 参数组成的所有线条、圆、椭圆,然后在霍夫空间通过投票决定哪些参数

霍夫变换原理?

  • 经过直线公式转换后,在原始图片是一个直线的点,会映射到霍夫空间的一个点上,通过统计霍夫空间中映射点的次数,可以确定原始图片的点。如上图检测到 10 个边缘点,依次取两个点去求解公式的两个参数 θ\thetaρ\rho,然后将这两个参数绘制到霍夫空间(横轴 θ\theta、纵轴 ρ\rho),对于那些在一条直线上的点,其 θ\theta,ρ\rho 值相等,所以绘制到霍夫空间的同一点

x1cosθ+y1cosθ=ρx_{1} \cos \theta+y_{1} \cos \theta=\rho

  • 变换不同的公式,可以通过霍夫空间检测直线、圆、椭圆

霍夫直线变换?

  • 直接检测直线: 使用公式 a×x1+b=y1a \times x_{1} + b =y_1 将原始坐标转到由参数 a、b 组成的霍夫空间,但是如果原图存在垂直 x 轴的直线,参数 a 将无穷大,也就意味无法遍历霍夫空间,因此不采用这个方法
  • 法线检测直线: 使用公式 x1cosθ+y1cosθ=ρx_{1} \cos \theta+y_{1} \cos \theta=\rho 将原始坐标转到由参数 θ\theta,ρ\rho 组成的霍夫空间,其中 ρ\rho 表示表示原点到直线的距离、θ\theta 表示原点与直线的垂直线与 x 轴的夹角,一般范围在 [0,180]
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    img = cv2.imread('../data/shapes.jpg')
    drawing = np.zeros(img.shape[:], dtype=np.uint8) # 创建画板
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150)
    # 霍夫直线变换
    lines = cv2.HoughLines(edges, 0.8, np.pi / 180, 90)
    # 将检测的线画出来(极坐标)
    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))
    cv2.imshow('hough lines', np.hstack((img, drawing)))

概率霍夫直线变换?

  • 标准霍夫变换需要遍历每个点,无论是耗时还是计算量都相对较大,而且它得到的是整一条线(r 和 θ),并不知道原图中直线的端点。提出了统计概率霍夫直线变换,是一种改进的霍夫变换,它采取一种概率挑选机制,不是所有的点都进行计算,而是随机的选取一些点来进行计算
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    img = cv2.imread('../data/shapes.jpg')
    drawing = np.zeros(img.shape[:], dtype=np.uint8) # 创建画板
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150)
    ## 霍夫直线变换
    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)

什么是霍夫圆变换?

  • 类似于霍夫直线变换,直线是用 (r,θ) 二元座标表示,圆是用 (x_center, y_center, r) 三元来表示,首先在原图的点上用固定半径 r 去绘制圆,多个圆的交点就是圆心。从二维变成了三维,数据量变大了很多,所以一般使用霍夫梯度法减少计算量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    img = cv2.imread('ball.jpg')
    image = img.copy()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 600, param1=100, param2=30, minRadius=60, maxRadius=90)
    circles = np.int0(np.around(circles))
    for i in circles[0,:]:
    cv2.circle(img,(i[0],i[1]),i[2],(0,0,255),3)
    cv2.circle(img,(i[0],i[1]),2,(255,0,255),10)
    cv2.rectangle(img,(i[0]-i[2],i[1]+i[2]),(i[0]+i[2],i[1]-i[2]),(0,255,0),3)

参考:

  1. https://zhuanlan.zhihu.com/p/203292567