当前位置: 首页 > news >正文

霍夫变换直线检测

霍夫变换直线检测houghlines及opencv的实现分析
导读:
1. houghlines的算法思想
2. houghlines实现需要考虑的要素
3. houghlines的opencv实现,代码分析
4. houghlines的效率分析,改进

1. houghlines的算法思想
检测直线,houghlines标准算法,不考虑线段,不检测线段端点。

在直角坐标系和极坐标系的对应关系,点、直线在两个坐标系中是对偶关系。
即直角坐标系中的点是极坐标系中的线,直角坐标系中的线是极坐标系中的点。
反过来,也成立。

图像可知看做直角坐标系,检测图像中的直线,可以转化为统计检测极坐标系中的点(r,theta)。


2. houghlines实现需要考虑的因素
hough空间(离散极坐标)的表示
原因:
图像中直线的表示,由斜率和截距表示,而极坐标中用(r, theta)表示.
r = cos(theta)*x + sin(theta)*y

对于点(x0, y0) , 在极坐标中就是一条直线(很多对(r,theta)点):
r = cos(theta)*x0 + sin(theta)*y0

r,theta就是一对hough空间的变量表示。
旋转的theta不容易表示,若将r,theta看成直角坐标空间。
一个点(x0, y0), 就是一个正弦曲线。
r = cos(theta)*x0 + sin(theta)*y0

角坐标系中的一点

 

对应于r-theta空间的一条正弦曲线

多个点在(r,theta)平面上就是多条正弦曲线,而多条正弦曲线会相交,交点就是直角坐标系中的直线

 

直角坐标系中的一条直线上的三个点

对应于r-theta空间中三条曲线,并交于一点


接下来,就是要考虑 将r,theta离散化,形成离散化的hough空间,类似于一个mat矩阵/图像,用于统计交点的个数。
opencv取rtho,theta参数作为离散度量,空间分辨率。
则hough空间的大小为:
    numangle = cvRound(CV_PI / theta);
    numrho = cvRound(((width + height) * 2 + 1) / rho); // 有冗余,在r上留有一定的空白

 

 

    
3. 代码分析

rho: 霍夫空间的r粒度大小
theta: 旋转角度的粒度
threshold:直线上有多少个点的阈值
lines:输出lines结果
linesMax:lines的最大个数

static void
icvHoughLinesStandard( const CvMat* img, float rho, float theta,
                       int threshold, CvSeq *lines, int linesMax )
{
    cv::AutoBuffer<int> _accum, _sort_buf;
    cv::AutoBuffer<float> _tabSin, _tabCos;

    const uchar* image;
    int step, width, height;
    int numangle, numrho;
    int total = 0;
    float ang;
    int r, n;
    int i, j;
    float irho = 1 / rho;
    double scale;

    CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );

    image = img->data.ptr;
    step = img->step;
    width = img->cols;
    height = img->rows;

    numangle = cvRound(CV_PI / theta);    // 霍夫空间,角度方向的大小
    numrho = cvRound(((width + height) * 2 + 1) / rho);  // r的空间范围

    _accum.allocate((numangle+2) * (numrho+2));
    _sort_buf.allocate(numangle * numrho);
    _tabSin.allocate(numangle);
    _tabCos.allocate(numangle);
    int *accum = _accum, *sort_buf = _sort_buf;
    float *tabSin = _tabSin, *tabCos = _tabCos;
    
    memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );

    for( ang = 0, n = 0; n < numangle; ang += theta, n++ ) // 计算正弦曲线的准备工作,查表
    {
        tabSin[n] = (float)(sin(ang) * irho);
        tabCos[n] = (float)(cos(ang) * irho);
    }

    // stage 1. fill accumulator
    for( i = 0; i < height; i++ )
        for( j = 0; j < width; j++ )
        {
            if( image[i * step + j] != 0 )      // 将每个非零点,转换为霍夫空间的离散正弦曲线,并统计。
                for( n = 0; n < numangle; n++ )
                {
                    r = cvRound( j * tabCos[n] + i * tabSin[n] );
                    r += (numrho - 1) / 2;
                    accum[(n+1) * (numrho+2) + r+1]++;
                }
        }

    // stage 2. find local maximums  // 霍夫空间,局部最大点,采用四邻域判断,比较。(也可以使8邻域或者更大的方式), 如果不判断局部最大值,同时选用次大值与最大值,就可能会是两个相邻的直线,但实际上是一条直线。选用最大值,也是去除离散的近似计算带来的误差,或合并近似曲线。
    for( r = 0; r < numrho; r++ )   
        for( n = 0; n < numangle; n++ )
        {
            int base = (n+1) * (numrho+2) + r+1;
            if( accum[base] > threshold &&
                accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
                accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
                sort_buf[total++] = base;
        }

    // stage 3. sort the detected lines by accumulator value    // 由点的个数排序,依次找出哪些最有可能是直线
    icvHoughSortDescent32s( sort_buf, total, accum );

    // stage 4. store the first min(total,linesMax) lines to the output buffer
    linesMax = MIN(linesMax, total);
    scale = 1./(numrho+2);
    for( i = 0; i < linesMax; i++ )  // 依据霍夫空间分辨率,计算直线的实际r,theta参数
    {
        CvLinePolar line;
        int idx = sort_buf[i];
        int n = cvFloor(idx*scale) - 1;
        int r = idx - (n+1)*(numrho+2) - 1;
        line.rho = (r - (numrho - 1)*0.5f) * rho;
        line.angle = n * theta;
        cvSeqPush( lines, &line );
    }
}



4. 效率分析
    houghlines的计算效率比较低O(n*n*m),耗时较长,而且没有检测直线的端点。
    改进
统计概论霍夫直线检测houghlinesP是一个改进,不仅执行效率较高,而且能检测直线的两个端点。
思想:
先随机检测出一部分直线,然后将直线上点的排查掉,再进行其他直线检测

1. 首先仅统计图像中非零点的个数,对于已经确认是某条直线上的点就不再变换了。
2. 对所以有非零点逐个变换到霍夫空间
    a. 并累加到霍夫统计表(图像)中,并统计最大值
    b. 最大值与阈值比较,小于阈值,则继续下一个点的变换
    c. 若大于阈值,则有一个新的直线段要产生了
    d. 计算直线上线段的端点、长度,如果符合条件,则保存此线段,并mark这个线段上的点不参与其他线段检测变换

相关文章:

  • Netflix 混沌工程手册 Part 3:实践方法
  • 又一款博客园Android客户端低调推出
  • 基于虹软 人脸识别的闸机开发经验及源码分享
  • python 安装第三方模块
  • ajax与json
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • RPC
  • UI2Code智能生成Flutter代码——版面分析篇
  • ios设备唯一标识获取策略
  • Windows下使用资源管理器管理FTP指南
  • 激活函数汇总
  • java压缩 GZIP进行简单压缩,ZIP进行多文件保存
  • mongoDB 文档操作_增
  • 我的开源项目:FLV封装格式分析器
  • 【Leetcode】104. 二叉树的最大深度
  • Google 是如何开发 Web 框架的
  • 《Java编程思想》读书笔记-对象导论
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • JAVA_NIO系列——Channel和Buffer详解
  • JS 面试题总结
  • node入门
  • spring-boot List转Page
  • 解析 Webpack中import、require、按需加载的执行过程
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 使用权重正则化较少模型过拟合
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 一道闭包题引发的思考
  • 一天一个设计模式之JS实现——适配器模式
  • 用Visual Studio开发以太坊智能合约
  • 运行时添加log4j2的appender
  • FaaS 的简单实践
  • postgresql行列转换函数
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • #DBA杂记1
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #QT项目实战(天气预报)
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (1)SpringCloud 整合Python
  • (9)STL算法之逆转旋转
  • (9)目标检测_SSD的原理
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (强烈推荐)移动端音视频从零到上手(下)
  • (转)Mysql的优化设置
  • ./configure,make,make install的作用(转)
  • .net Application的目录
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET Core 项目指定SDK版本
  • .net访问oracle数据库性能问题
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • .net专家(高海东的专栏)