经典霍夫变换(Hough Transform)

  • 引言
  • 历史和简介
    • 历史
    • 简介
  • 原理
    • 例子1
    • 例子2
  • 霍夫变换提取直线如何实现?实现机理是为何?
  • 实现使用的例子说明描述
    • 简单闭合矩形
    • 城市场景
    • 遥感应用
  • 实现算法描述
  • 代码实现
    • matlab版本
    • python实现
    • Opencv实现
  • 浅提霍夫变换提取圆
    • 步骤
    • 霍夫变换提取圆的一些实现链接
  • 浅提广义霍夫变换
  • 基本霍夫变换的限制
  • 结语
  • 引言

      ​ 本文讲述霍夫变换的一些内容,并加入一些理解性东西,参考了部分博客等相关性内容。希望能对霍夫变换有所了解,也希望看到的人如果发现错误及时帮忙纠正。博主水平有限,还望赐教。

    历史和简介

    历史

      ​霍夫变换(Hough Transform)是在1959年由气泡室(Bubble Chamber)照片的机器分析而发明,发明者Paul Hough在1962年获得美国专利,被命名为Method and Means for Recognizing Complex Patterns(用于识别复杂图案的方法和手段)。该专利对直线采用斜截距参数化,但由于斜率可能变成无穷大,这有可能导致无限变换空间(unbounded transform space)。

      ​现在使用的霍夫变换是1972年由Richard Duda和Peter Hart所发明,称为“广义霍夫变换[GHT]”(Use of the Hough Transformation to Detect Lines and Curves in Pictures,1972)。

      ​然后1981年在Dana H. Ballard的计算机视觉社区中出现一篇文章名为 Generalizing the Hough transform to detect arbitrary shapes,从而推广开来。

      ​该文描述了使用模板匹配原理对霍夫变换进行修改。要知道霍夫变换最初是为了分析定义的形状(如线、圆、椭圆等)而开发。通过了解其形状并旨在其找出图像中的位置和方向,这种改变使得霍夫变换能够检测用其模型描述的任意对象。这将图像中查找对象(用模型描述)的问题通过查找模型在图像中的位置来解决。利用广义霍夫变换(GHT),找到模型位置的问题转换为寻找将模型映射到图像中的变换参数的问题。给定变换参数的值,就可以确定模型在图像中的位置。

      ​后来产生了更多霍夫变换的变体和扩展,比如KHT,3DKHT,这里不细致说明。

    简介

      ​霍夫变换是一个特征提取技术。其可用于隔离图像中特定形状的特征的技术,应用在图像分析、计算机视觉和数字图像处理领域。目的是通过投票程序在特定类型的形状内找到对象的不完美实例。这个投票程序是在一个参数空间中进行的,在这个参数空间中,候选对象被当作所谓的累加器空间中的局部最大值来获得,所述累加器空间由用于计算霍夫变换的算法明确地构建。最基本的霍夫变换是从黑白图像中检测直线(线段)。Hough变换主要优点是能容忍特征边界描述中的间隙,并且相对不受图像噪声的影响。

    原理

      ​霍夫变换最简单的是检测直线。我们知道,直线的方程表示可以由斜率和截距表示(这种表示方法,称为斜截式),如下所示:

    y=mx+b

      如果用参数空间表示则为(b,m),即用斜率和截距就能表示一条直线。

      但是这样会参数问题,垂直线的斜率不存在(或无限大),这使得斜率参数m的值接近于无限。为此,为了更好的计算,Richard O. Duda和Peter E. Hart在1971年4月,提出了Hesse normal form(Hesse法线式)

    r = x*cos(θ) + y*sin(θ)

      其中r是原点到直线上最近点的距离(其他人可能把这记录为p,下面也可以把r看成参数p),θ是x轴与连接原点和最近点直线之间的夹角。如图1所示。

    图1
    图 1

      因此,可以将图像的每一条直线与一对参数(r,θ)相关联。这个参数(r,θ)平面有时被称为霍夫空间,用于二维直线的集合。

    在概念上,霍夫变换很接近Radon变换有人将之看成同一变换的不同形式

      经过Hough变换,将图像空间中的一个点映射到Hough空间,如图2所示。

    图2
    图 2

    图2:固定一个点(3,4),在角度θ取[0,2π]时,r的取值范围情况。θ取[0,2π]时,r的取值范围情况。

    该图是用matlab绘制,代码如下

    % 一个点的坐标为(3,4)
    x=3;
    y=4;
    %将给定的一个定点映射到霍夫变换空间
    theta=0:pi/200:2*pi;% 角度
    r=x*cos(theta)+y*sin(theta);
    plot(theta,r);%绘图
    set(gca,'XTick',[0:pi/10:2*pi]);   % 修改x轴坐标间隔
     xlabel('变量\theta')
     ylabel('变量r')
     
     
         
         
         
         

      运行结果

    图27
    图27

      只是个示范使用,参数可自调。

    python实现

      由于本博主电脑是python2,python3共存,所以需要加入一定的注释表明是用哪个版本的python。要运行下面代码需要安装几个东西,在cmd命令行进行运行。代码本人是用python2编辑。(如果只装一个版本,下面的安装代码请自行调整),我没有将其逆霍夫变换回原图上了,大家可以自行绘制。

    py -2 -m pip install numpy
    py -2 -m pip install matplotlib
    py -2 -m pip install scipy
    py -2 -m pip install scikit-image
     
     
         
         
         
         

      源码

    #! python2
    # coding: utf-8
    
    import numpy as np
    import matplotlib.pyplot as plt
    
    from skimage.transform import hough_line
    from skimage.draw import line
    
    img = np.zeros((100, 150), dtype=bool)
    img[30, :] = 1
    img[:, 65] = 1
    img[35:45, 35:50] = 1
    rr, cc = line(60, 130, 80, 10)
    img[rr, cc] = 1
    img += np.random.random(img.shape) > 0.95
    
    out, angles, d = hough_line(img)
    
    fix, axes = plt.subplots(1, 2, figsize=(7, 4))
    
    axes[0].imshow(img, cmap=plt.cm.gray)
    axes[0].set_title('Input image')
    
    axes[1].imshow(
        out, cmap=plt.cm.bone,
        extent=(np.rad2deg(angles[-1]), np.rad2deg(angles[0]), d[-1], d[0]))
    axes[1].set_title('Hough transform')
    axes[1].set_xlabel('Angle (degree)')
    axes[1].set_ylabel('Distance (pixel)')
    
    plt.tight_layout()
    plt.show()
    
    

      运行结果

    图28
    图28

    Opencv实现

      opencv的关于霍夫变换提取的函数可以在Opencv的该文档见到
    Opencv关于houghlines函数
      博主电脑安装的是opencv2.4.13版本,代码是来自于浅墨大神(毛星云)的代码实现。

      实验的原图

    图29
    图29
    //-----------------------------------【头文件包含部分】---------------------------------------  
    //      描述:包含程序所依赖的头文件  
    //----------------------------------------------------------------------------------------------   
    #include   
    #include   
    
    //-----------------------------------【命名空间声明部分】---------------------------------------  
    //      描述:包含程序所使用的命名空间  
    //-----------------------------------------------------------------------------------------------   
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------  
    //      描述:控制台应用程序的入口函数,我们的程序从这里开始  
    //-----------------------------------------------------------------------------------------------  
    int main()
    {
        //【1】载入原始图和Mat变量定义     
        Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
        Mat midImage, dstImage;//临时变量和目标图的定义  
    
        //【2】进行边缘检测和转化为灰度图  
        Canny(srcImage, midImage, 50, 200, 3);//进行一次canny边缘检测  
        cvtColor(midImage, dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图  
    
        //【3】进行霍夫线变换  
        vector lines;//定义一个矢量结构lines用于存放得到的线段矢量集合  
        HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);
    
        //【4】依次在图中绘制出每条线段  
        for (size_t i = 0; i < lines.size(); i++)
        {
            float rho = lines[i][0], theta = lines[i][1];
            Point pt1, pt2;
            double a = cos(theta), b = sin(theta);
            double x0 = a*rho, y0 = b*rho;
            pt1.x = cvRound(x0 + 1000 * (-b));
            pt1.y = cvRound(y0 + 1000 * (a));
            pt2.x = cvRound(x0 - 1000 * (-b));
            pt2.y = cvRound(y0 - 1000 * (a));
            line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA);
        }
    
        //【5】显示原始图    
        imshow("【原始图】", srcImage);
    
        //【6】边缘检测后的图   
        imshow("【边缘检测后的图】", midImage);
    
        //【7】显示效果图    
        imshow("【效果图】", dstImage);
    
        waitKey(0);
    
        return 0;
    }
      
      
          
          
          
          

      运行结果

    图30
    图30

    浅提霍夫变换提取圆

      我们可以使用这个相同的程序来检测具有分析描述的其他特征。例如,在圆圈的情况下,参数方程为

      其中a 和b是圆心的坐标,r是半径。在这种情况下,算法的计算复杂度开始增加,因为我们现在在参数空间和三维累加器中有三个坐标。(通常,累加器阵列的计算和大小随着参数数量的增加而多项式增加,因此,基本霍夫技术仅适用于简单曲线。)

    步骤

      它的算法步骤如下:

    1. 首先创建累加器空间,由每个像素单元格构成。最初每个单元格都设置为0。
    2. 然后对于每个图像中的边缘点(i,j),按照圆方程(i−a)^2+(j−b)^2=r^2,将那些可能是一个圆中心的单元格值进行累加。这些单元格在等式中由字母a表示。
    3. 然后在前面的步骤中由每个可能找到的值a,找到满足等式的所有可能值b。
    4. 搜索累加器空间中的局部最大值。这些单元格表示算法检测到的圆圈。

       如果我们不知道事先定位的圆的半径,可以使用三维累加器空间来搜索具有任意半径的圆。当然,这在计算上更加昂贵。

       该方法还可以检测部分位于累加器空间外部的圆,只要该圆的区域内仍有足够的圆。

    霍夫变换提取圆的一些实现链接

       matlab关于霍夫变换提取圆
    Matlab圆提取

       关于opencv的提取圆(给出了C++,C和Python)
    Opencv官方文档关于圆提取

       python实现提取圆
    Python圆提取

    浅提广义霍夫变换

       当我们希望隔离的特征的形状不具有描述其边界的简单解析方程时,使用广义Hough变换。在这种情况下,我们不使用曲线的参数方程,而是使用查找表来定义边界位置和方向与霍夫参数之间的关系。(必须使用原型形状在初步阶段计算查找表值。)

      例如,假设我们知道所需特征的形状和方向。(见下图)我们可以我们可以(xref,yref)在特征中指定一个任意参考点,其中定义了特征的形状(即r从边界到这个参考点的法线的距离和角度ω。我们的查找表(即 R表)将由这些距离和方向对组成,由边界的方向ω索引。

    图31
    图31

    图31:R表组件的描述。

      霍夫变换空间现在是根据图像中形状的可能位置来定义的,即可能的范围 (xref,yref)。换句话说,转换定义为:

    经典霍夫变换(Hough Transform)_第1张图片

    (对于特定的已知方向r ,β值和来自于表ω值)。如果所需特征的方向未知,则该过程变得复杂,因为我们必须通过引入额外参数来扩展累加器,以考虑方向。

    基本霍夫变换的限制

      霍夫变换只有在大量投票落入正确的分箱时才有效,因此可以在背景噪音中轻松检测分箱。这意味着垃圾箱不能太小,否则有些选票会落入邻近垃圾箱,从而降低主垃圾箱的可见度。

      另外,当参数数量很大时(也就是说,当我们使用通常超过三个参数的霍夫变换时),单个分箱中投的平均投票数非常低,而这些分箱对应的实际数字在图像中的投票数量并不一定比其邻居多得多。复杂性以一定的速率增加,O(A^(m-2))其中每个附加参数A是图像空间的大小和m是参数的数量。因此,必须非常小心地使用Hough变换来检测线条或圆圈以外的任何其他内容。

      最后,霍夫变换的大部分效率取决于输入数据的质量:为了使霍夫变换高效,必须检测边缘。在噪声图像上使用Hough变换是一个非常棘手的问题,一般而言,之前必须使用降噪阶段。在图像被斑点破坏的情况下(如雷达图像中的情况),Radon变换有时更适合检测线,因为它通过求和来衰减噪声。

    结语

      本博文主要描述基本经典的霍夫变换,描述了霍夫变换如何提取直线及其原理,展示了部分例子和代码实现,也扩展了一部分霍夫变换的简单描述,希望能对看者有所借鉴。

你可能感兴趣的:(计算机视觉,图像处理)