图像处理-霍夫直线检测(2):python实现

在图像中要解决的霍夫直线检测是针对二值图的, 验证哪些前景或者边缘像素点是共线的。 如图9-11所示是一个宽度为10、 高度为10的二值图, 在这里前景像素点是用白色(灰度值是255) 标注的, 目的是验证哪些白色像素点是共线的。


 首先要根据每一个白色像素点的坐标, 对应“画”出霍夫空间中的曲线, 但是真正在程序实现中因为自变量0≤θ<180°有无数个点, 所以需要描出无数个点才能“画”出对应的曲线, 因此程序中我们只能离散化处理,可以每间隔△θ计算一个对应的ρ, △θ通常取1°, 即计算0°、 1°、 2°、 …、 179°对应的ρ值。 当然, 为了描出更多的曲线上的点, 可以令△θ取更小的值, 但是一般取1°就足够了, 所以根据每一个白色像素点的坐标就需要计算180个坐标点, 如表9-3所示。

那么如何利用计算出的θoρ空间中的这些点去验证哪些像素点是共线的呢? 在9.2.1节中, 是用曲线相交方式来验证的, 但是这里只是从曲线上取了一些离散的点, 所以需要引入一个工具, 称为“计数器”, 或者“投票器”, 或者二维直方图。 如图9-12所示, 假设有10个坐标, 向一个计数器(投票器) 投票。

投票的汇总结果如图9-13所示, 可以从投票器中很容易得出坐标(3, 1) 出现了3次, 坐标(0, -1) 出现了2次等。
 

如何构造霍夫空间中的计数器? 假设在xoy平面内有任意一点(x1, y1) , 过该点有无数条直线, 但是原点到这些直线的距离不会超过 ^{\sqrt{}^{ {x_{1}}^{2} + {y_{1}}^{2}}}。 图像矩阵宽度为W、 高度为H, 那么可以构造以下计数器, 用L代表整数round(^{\sqrt{W^{^{2}} + H^{^{2}}}})+1, 如图9-14所示:

其中mΔθ<180, -L+nΔρ≤L, 一般就取Δθ=1°, Δρ=1。 以图9-11所示的二值图为例, 这幅图像宽度为10、 高度为10, 所以令

 四个前景像素点的坐标分别为(6, 4) 、 (5, 5) 、 (3, 7) 、 (2, 8) , 按照表9-3求出的所有霍夫空间中的点坐标一共有4×180个, 比如(45°, round(6 cos 45°+4 sin 45°) ) =(45°,7) , (45°, round(5 cos 45°+5 sin 45°) ) =(45°, 7) , (45°, round(3 cos 45°+7 sin45°) ) =(45°, 7) , (45°, round(2 cos 45°+8 sin 45°) ) =(45°, 7) 等, 投票到如图9-14所示的计数器中, 在计数器(45°, 7) 这个位置的计数是4, 这里的(45°, 7) 对应到图9-9中相交的点 \left ( \pi /4, 10/\sqrt{2}\right ), 表明有四个像素点是共线的。 通过以上过程就可以实现标准的霍夫直线检测了。 通过定义函数HTLine来实现该功能, 其中输入参数image是一张二值图, 返回值是计数器及对应的哪些点是共线的。

coda:

# -*- coding: utf-8 -*-
import sys
import numpy as np
import cv2
import math
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#霍夫极坐标变换:直线检测
def HTLine (image,stepTheta=1,stepRho=1):
    #宽、高
    rows,cols = image.shape
    #图像中可能出现的最大垂线的长度
    L =  round(math.sqrt(pow(rows-1,2.0)+pow(cols-1,2.0)))+1
    #初始化投票器
    numtheta = int(180.0/stepTheta)
    numRho = int(2*L/stepRho + 1)
    accumulator = np.zeros((numRho,numtheta),np.int32)
    #建立字典
    accuDict={}
    for k1 in range(numRho):
        for k2 in range(numtheta):
            accuDict[(k1,k2)]=[]
    #投票计数
    for y in range(rows):
        for x  in range(cols):
            if(image[y][x] == 255):#只对边缘点做霍夫变换
                for m in range(numtheta):
                    #对每一个角度,计算对应的 rho 值
                    rho = x*math.cos(stepTheta*m/180.0*math.pi)+y*math.sin(stepTheta*m/180.0*math.pi)
                    #计算投票哪一个区域
                    n = int(round(rho+L)/stepRho)
                    #投票加 1
                    accumulator[n,m] += 1
                    #记录该点
                    accuDict[(n,m)].append((x,y))
    return accumulator,accuDict
#主函数
if __name__ == "__main__":
    if len(sys.argv)>1:
        #输入图像
        I = cv2.imread(sys.argv[1],cv2.IMREAD_GRAYSCALE)
    else:
        print ("Usage: python HTPLine.py image")
    #canny 边缘检测
    edge = cv2.Canny(I,50,200)
    #显示二值化边缘
    cv2.imshow("edge",edge)
    #霍夫直线检测
    accumulator,accuDict = HTLine(edge,1,1)
    #计数器的二维直方图方式显示
    rows,cols = accumulator.shape
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    X,Y = np.mgrid[0:rows:1, 0:cols:1]
    surf = ax.plot_wireframe(X,Y,accumulator,cstride=1, rstride=1,color='gray')
    ax.set_xlabel(u"$\\rho$")
    ax.set_ylabel(u"$\\theta$")
    ax.set_zlabel("accumulator")
    ax.set_zlim3d(0,np.max(accumulator))
    #计数器的灰度级显示
    grayAccu = accumulator/float(np.max(accumulator))
    grayAccu = 255*grayAccu
    grayAccu = grayAccu.astype(np.uint8)
    #只画出投票数大于 60 直线
    voteThresh = 60
    for r in range(rows):
        for c in range(cols):
            if accumulator[r][c] > voteThresh:
                points = accuDict[(r,c)]
                cv2.line(I,points[0],points[len(points)-1],(255),2)
    cv2.imshow('accumulator',grayAccu)
    
    #显示原图
    cv2.imshow("I",I)
    plt.show()
    cv2.imwrite('accumulator.jpg',grayAccu)
    cv2.imwrite('I.jpg',I)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

结果:

    

                           (a)计数器的三维展示图              (b)计数器的灰度级显示             (c)检测到的直线

猜你喜欢

转载自blog.csdn.net/zfjBIT/article/details/87287382