Hough transform for image processing to detect straight lines

I. Introduction

The Hough transform is a feature extraction that is widely used in image analysis, computer vision, and digital image processing. Invented by Richard Duda and Peter Hart in 1972 AD, and called the generalized Hough transform (generalized Hough transform), the generalized Hough transform is related to the patent of Paul Hough in 1962 earlier. The classic Hough transform is to detect straight lines in the picture. After that, the Hough transform can not only recognize straight lines, but also recognize any shape, such as circles and ellipses. In 1981, because of a journal paper "Generalizing the Hough transform to detect arbitrary shapes" by DanaH.Ballard, the Hough transform became popular in the computer vision community. The Hough transform is used to identify features in objects, such as lines. His algorithm flow is roughly as follows. Given an object and the type of shape to be identified, the algorithm will perform voting in the parameter space to determine the shape of the object, and this is determined by the partial in the accumulator space. The maximum (local maximum) to determine.

Two, Hough transform

A straight line can be composed of two points A = ( x 1 , y 1 ) A=(x_1,y_1)A=(x1,y1) sumB = ( x 2 , y 2 ) B=(x_2,y_2)B=(x2,y2) determined (Cartesian coordinates)

On the other hand, y = kx + by=kx+by=kx+b can also be written about( k , q ) (k,q)k,q ) function expression (Hough space):
{ q = − kx 1 + y 1 q = − kx 2 + y 2 \left\{ \begin{aligned} q=-kx_1+y_1 \\ q=-kx_2 +y_2 \\ \end{aligned} \right.{ q=kx1+y1q=kx2+y2
The space transformation process is as shown in the figure below:
the transformed space becomes the Hough space. That is: a straight line in the Cartesian coordinate system corresponds to a point in the Hough space.
insert image description here
The reverse is also true (a straight line in the Hough space corresponds to a point in the Cartesian coordinate system):
insert image description here
two points in the Cartesian coordinate system correspond to two lines in the Hough space:
insert image description here
if the three points in the Cartesian coordinate system are collinear, the corresponding The three lines of the Hough space intersect at one point.
insert image description here
The basic method of post-processing of the Hough transform: select the point where as many straight lines as possible converge.
However, according to the Cartesian coordinate system, the following situation will appear: when the line of the points in the image space is perpendicular to the x-axis, the slope is infinite, and the intersection point cannot be found in the Hough space. Thus, people eventually introduced the notation of polar coordinates.
insert image description here
The principle of Hough line detection in polar coordinates is exactly the same as that in rectangular coordinates. The only thing that needs to be re-derived is the polar coordinate parameter function of Hough space: y = ( − cos θ sin θ ) x +
rsin θ y=\left (-\frac{cos\theta}{sin\theta}\right)x+\frac{r}{sin\theta}y=(sinθcosθ)x+sinθr
Definition:
r = xcos θ + ysin θ r=xcos\theta+ysin\thetar=xcosθ+ys in θ
if for a given point( x 0 , y 0 ) (x_0,y_0)(x0,y0) , meaning that each pair of( r , θ ) (r,\theta)(r,θ ) Represents a path through the point( x θ , y θ ) (x_\theta,y_\theta)(xi,yi) straight line. We draw all the straight lines passing through it on the plane of polar coordinates to polar radius and polar angle, and we will get a sinusoidal curve. For example, for a given point ( x 0 = 8 and y 0 = 6 )(x_0= 8 and y_0= 6)(x0=8 sum y0=6 ) We can draw the following figure (on the plane):
insert image description here
the conversion formula between polar coordinates and Cartesian coordinates, from polar coordinates( r , θ ) (r, θ)(r,θ ) in the Cartesian coordinate system( x , y ) (x,y)(x,y ) :
{ x = rcos θ y = rsin θ \left\{ \begin{aligned} x=rcos\theta \\ y=rsin\theta \\ end{aligned} \right.{ x=rcosθy=rsinθ
Convert ( x , y ) from Cartesian coordinates (x,y)(x,y ) to polar coordinates( r , θ ) (r,θ)(r,θ ) :
r = x 2 + y 2 θ = arctan ( y / x ) r=\sqrt{x^2+y^2} \\ \theta=arctan(y/x)r=x2+y2 i=a rc t an ( y / x )
in the polar coordinate system is actually the same: a point in polar coordinates → a straight line in Hough space, but the Hough space is no longer[ k , q ] [k,q][k,q ] , but( r , θ ) (r,\theta)(r,i ) .

3. Line detection

From the above introduction, we can see that drawing x − y xyxPoints in the y coordinate space correspond to curves in the parameter space, and then calculate the intersection point of the curves in the parameter space to obtain the parameters to be sought. But there is still a problem. When there are multiple intersections of the curves in the parameter space, how to choose the most likely solution?
In the specific calculation, the parameter space can be divided into the so-called accumulation unitA ( θ , ρ ) A(\theta,\rho)A ( i ,ρ ) . As shown in the figure below, forx − y xyxEach non-background point of the y plane ( xk , yk ) (x_k,y_k)(xk,yk) ,θ \thetaθ is equal to each possible subdivision value, according toθ = − xk θ + yk \theta=-x_k\theta+y_ki=xki+ykCalculate the corresponding ρ \rhoρ value, each calculation of a group ofA ( θ , ρ ) A(\theta,\rho)A ( i ,ρ ) ,forA ( θ , ρ ) = A ( θ , ρ ) + 1 A(\theta,\rho)=A(\theta,\rho)+1A ( i ,r )=A ( i ,r )+1 . After computing all results, findA ( θ , ρ ) A(\theta,\rho)A ( i ,ρ ) corresponding to the peak value ofθ \thetaθ andρ \rhoρ , the straight line can be detected. ( θ min , θ max ) (\theta_{min},\theta_{max})( imin,imax) ( ρ m i n , ρ m a x ) (\rho_{min},\rho_{max}) ( rmin,rmax) is the desired parameter range:0 ο ≤ θ ≤ 18 0 ο 0^\omicron \leq\theta \leq180^\omicron0Thei180ο − D ≤ θ ≤ D -D \leq\theta \leq D DiD, D D D is the length of the diagonal of the image. θ \thetaθ andρ \rhoThe number of subdivisions of ρ determines the accuracy of the detection results.
insert image description here

During the voting process, you can watch the GIF below.
insert image description here
The cyan dots on the left represent the pixels on the image, and the yellow dots represent the search for each point from different angles. The right half is the voting plate, the lighter the color, the more votes.

4. Code implementation

1. hough detection

def lines_detector_hough(img,ThetaDim=None, DistStep=None, threshold=None, halfThetaWindowSize=2,
                         halfDistWindowSize=None):
    '''

    :param img: 经过边缘检测得到的二值图
    :param ThetaDim: hough空间中theta轴的刻度数量(将[0,pi)均分为多少份),反应theta轴的粒度,越大粒度越细
    :param DistStep: hough空间中dist轴的划分粒度,即dist轴的最小单位长度
    :param threshold: 投票表决认定存在直线的起始阈值
    :return: 返回检测出的所有直线的参数(theta,dist)和对应的索引值,
    '''
    row,col= edge.shape
    if ThetaDim == None:
        ThetaDim = 90
    if DistStep == None:
        DistStep = 1
    # 计算距离分段数量
    MaxDist = np.sqrt(row ** 2 + col ** 2)
    DistDim = int(np.ceil(MaxDist / DistStep))

    if halfDistWindowSize == None:
        halfDistWindowSize = int(DistDim /50)

    # 建立投票
    accumulator = np.zeros((ThetaDim, DistDim))  # theta的范围是[0,pi). 在这里将[0,pi)进行了线性映射.类似的,也对Dist轴进行了线性映射
    #
    sinTheta = [np.sin(t * np.pi / ThetaDim) for t in range(ThetaDim)]
    cosTheta = [np.cos(t * np.pi / ThetaDim) for t in range(ThetaDim)]
    #计算距离(rho)
    for i in range(row):
        for j in range(col):
            if not edge[i, j] == 0:
                for k in range(ThetaDim):
                    accumulator[k][int(round((i * cosTheta[k] + j * sinTheta[k]) * DistDim / MaxDist))] += 1
    M = accumulator.max()
#---------------------------------------
    #非极大抑制
    if threshold == None:
        threshold = int(M * 1.369/ 10)
    result = np.array(np.where(accumulator > threshold))  # 阈值化
    #获得对应的索引值
    temp = [[], []]
    for i in range(result.shape[1]):
        eight_neiborhood = accumulator[
                           max(0, result[0, i] - halfThetaWindowSize + 1):min(result[0, i] + halfThetaWindowSize,
                                                                              accumulator.shape[0]),
                           max(0, result[1, i] - halfDistWindowSize + 1):min(result[1, i] + halfDistWindowSize,
                                                                             accumulator.shape[1])]
        if (accumulator[result[0, i], result[1, i]] >= eight_neiborhood).all():
            temp[0].append(result[0, i])
            temp[1].append(result[1, i])
    #记录原图所检测的坐标点(x,y)
    result_temp= np.array(temp)
#-------------------------------------------------------------
    result = result_temp.astype(np.float64)
    result[0] = result[0] * np.pi / ThetaDim
    result[1] = result[1] * MaxDist / DistDim
    return result,result_temp

2. Draw a straight line code

def drawLines(lines, edge, color=(255, 0, 0), err=3):

    '''
    :param lines: 检测后的直线参数
    :param edge: 原图
    :param color: 直线的颜色
    :param err:检测的可接受的误差值
    :return: 无
    '''

    if len(edge.shape) == 2:
        result = np.dstack((edge, edge, edge))
    else:
        result = edge
    Cos = np.cos(lines[0])
    Sin = np.sin(lines[0])

    for i in range(edge.shape[0]):
        for j in range(edge.shape[1]):
            e = np.abs(lines[1] - i * Cos - j * Sin)
            if (e < err).any():
                result[i, j] = color
    plt.imshow(result, cmap='gray')
    plt.axis('off')
    plt.show()

3. Draw hough space code

def data_img(data):

    '''
    :param data: 直线上含有的点(x,y)
    :return: 输出hough空间图像
    '''

    fig = plt.figure()  # 新建画布
    ax = axisartist.Subplot(fig, 111)  # 使用axisartist.Subplot方法创建一个绘图区对象ax
    fig.add_axes(ax)
    ax.axis[:].set_visible(False)  # 隐藏原来的实线矩形
    ax.axis["x"] = ax.new_floating_axis(0, 0, axis_direction="bottom")  # 添加x轴
    ax.axis["y"] = ax.new_floating_axis(1, 0, axis_direction="bottom")  # 添加y轴
    ax.axis["x"].set_axisline_style("->", size=1.0)  # 给x坐标轴加箭头
    ax.axis["y"].set_axisline_style("->", size=1.0)  # 给y坐标轴加箭头
    t = np.arange(-np.pi / 2, np.pi / 2, 0.1)
    ax.annotate(text='x', xy=(2 * math.pi, 0), xytext=(2 * math.pi, 0.1))  # 标注x轴
    ax.annotate(text='y', xy=(0, 1.0), xytext=(-0.5, 1.0))  # 标注y轴
    for i in range(data.shape[1]):
        rho = data[0][i] * np.cos(t) + data[1][i] * np.sin(t)
        plt.plot(t, rho)
    plt.show()

4. Test results

insert image description here
Click here to download the detection picture.
All codes can be found in my GitHub warehouse . If the code is useful, please click star
hough. Before detection, the canny operator needs to detect the basic edge. Click here to view the relevant content of the canny algorithm

If this article is helpful to you, follow and like it! ! ! ! !

Guess you like

Origin blog.csdn.net/weixin_42491648/article/details/131848334