霍夫变换检测直线

霍夫变换检测直线

  • 前言
    • 霍夫变换检测直线,原理+代码(Python)
  • 一、霍夫变换
    • Hough Transform
    • Demo
    • Pseudocode
  • 二、代码实现
    • 方法1.使用opencv库函数
    • 方法2.MyHough


前言

霍夫变换检测直线,原理+代码(Python)

一、霍夫变换

Hough Transform

(Reference:link.)

1.笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
霍夫空间中的 k k k q q q 就相当于笛卡尔坐标系中的 x x x y y y

霍夫变换检测直线_第1张图片

2.霍夫空间的一条直线,对应笛卡尔坐标系的一个点

霍夫变换检测直线_第2张图片

3 .两个点对应霍夫空间的情形

霍夫变换检测直线_第3张图片

4.三点共线对应霍夫空间的情形

霍夫变换检测直线_第4张图片

  • 可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点

5.多个点,可构成不止一条直线的情况

霍夫变换检测直线_第5张图片

  • 霍夫空间中存在多个交点(也即笛卡尔坐标系中存在多条可能构成的直线)该怎么处理?
    在Hough Space中选择由尽可能多直线汇成的交点作为(笛卡尔坐标系中)的直线
    霍夫变换检测直线_第6张图片

但是如果像下图这种情况呢?也即直线斜率为无穷大的情况
k = ∞ k=∞ k=是不方便表示的,而且 q q q 也不便于表示,Question:如何解决?

霍夫变换检测直线_第7张图片

Answer:笛卡尔坐标系中,借用极坐标进行表示

霍夫变换检测直线_第8张图片

在极坐标系下,极坐标的点→霍夫空间的直线
只不过霍夫空间不再是 [ k , q ] [k,q] [k,q]的参数,而是 [ ρ , θ ] [\rho,\theta] [ρ,θ]的参数,
给出对比图:
霍夫变换检测直线_第9张图片

Demo

Reference: link.

  • 如上图,假定在一个 8 ∗ 8 8*8 88的平面像素中有一条直线
  • 从左上角像素点 ( 1 , 8 ) (1,8) (1,8)开始,分别计算此点 θ θ θ分别为0°、45°、90°、135°、180°时的 ρ \rho ρ,图中可以看出 ρ \rho ρ分别为 1 、 ( 9 √ 2 ) / 2 、 8 、 ( 7 √ 2 ) / 2 、 − 1 1、(9√2)/2、8、(7√2)/2、-1 1(92)/28(72)/21,并给这5个值分别记一票
  • 同理计算像素点 ( 3 , 6 ) (3,6) (3,6),计算此点 θ \theta θ 为0°、45°、90°、135°、180°时的 ρ \rho ρ,再给计算出来的5个 ρ \rho ρ值分别记一票,此时就会发现 ρ = ( 9 √ 2 ) / 2 \rho = (9√2)/2 ρ=(92)/2的这个值已经记了两票
  • 以此类推,遍历完整个 8 ∗ 8 8*8 88 的像素空间的时候 ρ = ( 9 √ 2 ) / 2 \rho = (9√2)/2 ρ=(92)/2就记了5票, 别的 ρ \rho ρ值的票数均小于5票
  • 所以得到该直线在这个 8 ∗ 8 8*8 88的像素坐标中的极坐标方程为 (9√2)/2=xCos45°+ySin45°,到此该直线方程就求出来了。(PS:但实际中θ的取值不会跨度这么大,一般是1度)

Pseudocode

霍夫变换检测直线_第10张图片
上述的 θ \theta θ = − 90 -90 90~ 180 180 180 应改为 [ − 90 , 90 ] [-90,90] [90,90]或者 [ 0 , 180 ] [0,180] [0,180]

二、代码实现

方法1.使用opencv库函数

import cv2
import numpy as np
 
img = cv2.imread('book.jpg')
img1 = img.copy()
img2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.GaussianBlur(gray, (3, 3), 0)
edges = cv2.Canny(gray, 50, 150) #first,use canny to detect edges 

#======================method one : Hough transform==================
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)
#the last param means :Only lines that get >=x votes will be returned 
for line in lines:
    rho = line[0][0]
    theta = line[0][1]
    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(img1, (x1, y1), (x2, y2), (0 ,206 ,209), 2)

#========method two : the probabilistic Hough transform============
lines = cv2.HoughLinesP(edges, 1, np.pi/180,threshold=50, minLineLength=53,maxLineGap=10)
for line in lines:
    x1 = line[0][0] #end-point one
    y1 = line[0][1]
    x2 = line[0][2] #end-point two
    y2 = line[0][3]
    cv2.line(img2, (x1, y1), (x2, y2), (132, 112 ,255), 2)
 
cv2.imshow('Hough transfrom', img1)
cv2.imshow('the probabilistic Hough transform', img2)
cv2.waitKey(0)

方法2.MyHough

代码如下(示例):

import numpy as np
import cv2 
def MyFastHoughLine(edges,rho_step=1, angle_step=1, threshold=5,linesMax=20):
    """Hough transform for lines
    
    utilize numpy's matrix multiply to make computation more efficient in both momory and time
    when in stage1 filling accumulator

    input:
    : edges - 2D binary image with nonzeros representing edges
    : angle_step -  Default step is 1.
    : lines_are_white - boolean indicating whether lines to be detected are white
    : threshold - one line must have more than this value in accumulator vote

    return:
    : accumulator - 2D array of the hough transform accumulator
    : resultLines - lists composed of many  [line_i's rho , line_i'theta]
    """
    width, height = edges.shape
    # Rho and Theta ranges
    thetas = np.deg2rad(np.arange(-90.0, 90.0, angle_step)) #theta sequence
    numtheta = len(thetas)
    maxdist = int(np.ceil(np.sqrt(width*width + height*height))) 
    rhos = np.arange(-maxdist, maxdist, rho_step)         #rho sequence
    numrho = len(rhos)

    # Cache some reusable values
    cos_theta = np.cos(thetas)
    sin_theta = np.sin(thetas)

    #===============stage 1. fill accumulator================================
    accumulator = np.zeros((numrho+2, numtheta+2)) 
    #why+2? To simplify the "margin case" process in stage2's NMS
    y_idxs, x_idxs = np.nonzero(edges)  #(row, col) indexes to edges
    # z.reshape(-1,1): reshape z into an array with only 1 coloumn
    # matrix(aXb) = matrix0(aX1) X matrix1(1Xb)
    xcosthetas = np.dot(x_idxs.reshape((-1,1)), cos_theta.reshape((1,-1))) 
    ysinthetas = np.dot(y_idxs.reshape((-1,1)), sin_theta.reshape((1,-1)))
    rhosmat = np.round(xcosthetas + ysinthetas) + maxdist # ρ= x*cosθ +y*sinθ 
    rhosmat = rhosmat.astype(np.int16) #rhosmat's shape = num_edgeDots * numtheta
    for i in range(numtheta):          #accumulator's shape =  (2+numrho) * (2+numtheta)  
        rhosValue,counts = np.unique(rhosmat[:,i], return_counts=True)
        for j in range(len(counts)):
            rhoidx = rhosValue[j]+1
            accumulator[rhoidx,i+1] = counts[j]
        # accumulator[rhos+1 ,i+1] = counts
    
    #===============stage 2. NMS-find local maximums==============================
    lines = [] # a list composed of many [[rho_idx,theta_idx],count_i]
    for i in range(0,numrho):
        for j in range(0,numtheta):
            count = accumulator[i+1,j+1]
            if(  count >  threshold 
                and count >  accumulator[i  ,j+1] # without  =?
                and count >= accumulator[i+2,j+1] # but with =?
                and count >  accumulator[i+1,j  ] 
                and count >= accumulator[i+1,j+2]):
                theta = thetas[j]
                rho = rhos[i]
                lines.append([[rho,theta],count])

    #===============stage 3. sort detected lines by vote value=====================
    lines.sort(key = lambda x:x[1],reverse=True)
 
    #===============stage 4. store the first min(total,linesMax) lines=============
    linesMax = min(linesMax, len(lines))
    resultLines = []
    for i in range(linesMax):
        tmp = lines[i][0]
        resultLines.append(tmp)

    return accumulator, resultLines
img = cv2.imread('book.jpg')
img1 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3, 3), 0)
edges = cv2.Canny(blur, 50, 150) 
edges = cv2.dilate(edges,(3,3))  
edges = cv2.erode(edges,(3,3)) #to get more fluent lines

#===================HoughLines=======================
accumulator, lines = MyFastHoughLine(edges)
for line in lines:
    rho = line[0]
    theta = line[1]
    # print(rho,theta)
    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(img1, (x1, y1), (x2, y2), (0 ,206 ,209), 2)
cv2.imwrite("MyHoughLines.png",img1)
cv2.imshow('Hough transfrom', img1)
cv2.waitKey(0)
  • 结果
    霍夫变换检测直线_第11张图片

你可能感兴趣的:(计算机视觉,算法)