【CImg】霍夫变换——直线检测

霍夫变换——直线检测

    此处膜拜大神(学到很多):http://blog.csdn.net/jia20003/article/details/7724530

    这个博客更了很多图像处理算法的底层实现解析,都很详细易懂,先mark

========================我是分割线=============================

霍夫变换:CV中常用的识别几何图形的方法,其中最简单的应用就是直线检测

     主要原理是对于边缘的每一个像素点(x0,y0),把可能经过它的所有直线y=kx+b,映射到k-b空间(即hough space),然后投票

     但是,对于与x轴垂直的直线,斜率不存在,无法表示,所以用参数方程表示,r = x * cos(theta) + y * sin (theta), 其中(x,y)表示某一个边缘的像素点,r表示经过该点直线到原点的距离,theta表示r与x正轴的夹角。

     原理分析如下图:(画得..还挺丑哈哈哈...手残)

      

      

     所以最终的霍夫空间可以用r-theta表示。

     对于每个边缘点映射之后,在霍夫空间进行投票,每次有直线方程满足(r, theta)点,此处的像素值+1: 

        

     最后可以得到一张这样的hough-space图像:

     

     某一个点越白(像素值越大)表示,越多的点经过这条直线,这就有可能是一条边界直线

     过滤,求出局部极大值,可以得到几条直线方程(四条单像素宽直线),然后就可以根据直线方向在原图标定角点

       

以下为具体步骤以及实现:

     1. 彩色图像RBG->灰度图Gray

        (opencv上需要注意颜色空间是RGB还是BGR,CImg中RGB分别对应0,1,2通道)

      2.       去噪(高斯核)

      3.       边缘提取(梯度算子、拉普拉斯算子、canny; 此处实现用sobel)

      4.       二值化(判断此处是否为边缘点,就看灰度值==255)

         5.       映射到霍夫空间(此处准备两个容器,一个CImg用来展示hough-space概况,一个数组hough-space用来储存voting的值,因为投票过程往往有某个极大值超过255,多达几千,不能直接用灰度图来记录投票信息)

      6.        取局部极大值,设定阈值,过滤干扰直线

      7.        绘制直线、标定角点

实现

    1. 转灰度

      可以用自带API,或者自己写

    2. 高斯去噪(采用了一个标准差为1的高斯核)

      

    3. sobel算子提取边界

     sobel时梯度算子的一种

     

    4. 二值化(应该设置一个阈值,对不同的图,不同的阈值,以便完整显示边界)

     在高斯去噪和边界提取之后都需要二值化

     以下时同一张图片的二值化(阈值分别为60、80、100、127),可见,保持较好的边缘信息需要合适的阈值

   

    5. 映射到霍夫空间

     先在原图构造一个x-y平面,一一对应各点的直线方程计算O(0,0)为事实上的原点,O‘(width/2,height/2)为构造平面的原点

     然后构造一个hough-space,其中纵轴表示theta的刻度,theta取值0~PI,分成500个刻度,r的最大值为max_length=sqrt((width/2)^2 + (height/2)^2),又r存在正负值,故而hough-space的横轴需要2*max_length

       

      

    //霍夫空间,图像初始化
    CImg<unsigned char> output(2 * max_length, hough_space, 1, 1);
    int** hough = new int*[500];
    for (int k = 0; k < hough_space; k ++)
        hough[k] = new int[2*max_length] ();
    output.fill(0);

    //检测每一个点的所有可能直线方程,并记录投票,以及最大值
    int max_hough = 0;
    for (int x = 0; x < width; x ++) {
        for (int y = 0; y < height; y ++) {
            int temp = (int)inputImage.atXYZC(x, y, 1, 0);
            if (temp == 0)continue;
            else {
                for (int degree = 0; degree < hough_space; degree ++) {
                    double r = (x - centerX) * cos(degree * hough_intervals) + (y - centerY) * sin(degree * hough_intervals);
                    r += max_length;
                    if (r < 0 || (r >= 2 * max_length))continue;
                    unsigned char temp = output.atXYZC((unsigned int)r, degree, 1, 0) + 1;
                    output.atXYZC((unsigned int)r, degree, 1, 0) = temp;
                    hough[degree][(int)r] ++;
                    if (max_hough < hough[degree][(int)r])max_hough = hough[degree][(int)r];
                }
            }
        }
    }
    cout << "max_hough = " << max_hough << endl;    

     6. 取局部极大值,设定阈值,过滤干扰直线(直线方程存储在lines中)

//输出直线轨迹
    CImg<unsigned char> output1(width, height, 1, 1);
    output1.fill(0);

    //设置阈值
    int threshold = int(max_hough * value);
    cout << "threshold = " << threshold << endl;
    int count = 0;
    vector<pair<int, int> > lines;
    //遍历hough空间,找到所有比阈值大的点
    for (int row = 0; row < hough_space; row ++) {
        for (int col = 0; col < 2 * max_length; col ++) {
            bool newLines = true;
            int temp = hough[row][col];
            if (hough[row][col] > threshold) {
                for (int k = 0; k < lines.size(); k ++) {
                    //判断极值
                    if ((abs(lines[k].first - row) < 15 || abs((500 - lines[k].first) + row) < 5) && abs(lines[k].second - col) < 300) {
                        if (hough[row][col] > hough[lines[k].first][lines[k].second]) {
                            lines[k].first = row;
                            lines[k].second = col;
                        }
                        newLines = false;
                    }
                }
                if (newLines) {
                    lines.push_back(make_pair(row, col));
                    //cout << "push " << row << " "<< col << endl;
                }
            }
        }
    }

     7. 绘制直线、标定角点(角点信息存储在node中)

      因为有的直线斜率K可能不存在,所以我判断两条直线相较的条件是在draw lines的时候,看一下某像素点是不是已经被标记直线,若是,则说明有直线与当前直线相交,记录交点(但是这种方法不是很好,最后讨论优缺点)

 1 //角点
 2     vector<pair<int, int> > node;
 3
 4     //draw lines
 5     for (int k = 0; k < lines.size(); k ++) {
 6         int row = lines[k].first;
 7         int col = lines[k].second;
 8         //cout << "line " << k << " = " << row << " " << col << endl;
 9         double dy = sin(row * hough_intervals);
10         double dx = cos(row * hough_intervals);
11         if ((row <= hough_space / 4 ) || (row >= 3 * hough_space / 4)) {
12             for (int sRow = 0; sRow < height; ++sRow) {
13                 int sCol;
14                 if (row == 0 || row == 500)sCol =  (int)(col - max_length) + centerX;
15                 sCol = (int)((col - max_length - ((sRow - centerY) * dy)) / dx) + centerX;
16                 if (sCol < width && sCol >= 0) {
17                     if((int)output1.atXYZC(sCol, sRow, 1, 0) == 255)node.push_back(make_pair(sCol, sRow));
18                     else output1.atXYZC(sCol, sRow, 1, 0) = (unsigned char)255;
19                 }
20             }
21         }
22         else {
23             for (int sCol= 0; sCol < width; ++sCol) {
24                 int sRow;
25                 if(row == 250)sRow = (int)(col - max_length) + centerY;
26                 sRow = (int)((col - max_length - ((sCol - centerX) * dx)) / dy) + centerY;
27                 if (sRow < height && sRow >= 0) {
28                     if((int)output1.atXYZC(sCol, sRow, 1, 0) == 255)node.push_back(make_pair(sCol, sRow));
29                     else output1.atXYZC(sCol, sRow, 1, 0) = (unsigned char)255;
30                 }
31             }
32         }
33     }
34
35     //在原图上标记
36     CImg<unsigned char> output2(scrImage);
37
38     //标记
39     for (int k = 0; k < lines.size(); k ++) {
40         unsigned int w = output2.width();
41         unsigned int h = output2.height();
42
43         int range = 50;
44
45         cout << "node x = " << node[k].first << " " << "   y = " << node[k].second << endl;
46
47         for (int c = -range; c < range; c ++) {
48             for (int r = -range; r < range; r ++) {
49                 int distance = (int)sqrt(c * c + r * r + 0.0);
50                 if (node[k].first>= range && node[k].first < width - range && node[k].second >= range && node[k].second < height - range) {
51                     if (distance <= 50 && node[k].first + c >= 0 && node[k].first + c < width && node[k].second + r >= 0 && node[k].second + r < height) {
52                         output2.atXYZC(node[k].first + c, node[k].second + r, 1, 0) = (unsigned char)(255);
53                         output2.atXYZC(node[k].first + c, node[k].second + r, 1, 1) = (unsigned char)(0);
54                         output2.atXYZC(node[k].first + c, node[k].second + r, 1, 2) = (unsigned char)(255);
55                     }
56                 }
57             }
58         }
59     }

分析

   几幅图像的实验结果如下:

   去噪、提取边缘、二值化之后(图1\2\3\4)

   

   依次为图1\2\3\4的霍夫空间表示

     

   

   

     

   分别为图1\2\3\4的边界直线绘制,可知四张图的边界都可以检测到

   

   在原图上标定交点

   

   可以发现,四张图中,只有图2的角点没有标好,其余三张图的边界直线都有斜率K不存在的情况,所以,我的标定方法适用,当直线的斜率存在时,就很可能出现一下情况:(红蓝分别表示两条直线的像素点,可以看到虽然它们相交,但是在像素表示上并无交点,这时候需要多加一个判断,是否需要用直线方程y=kx+b来直接求出交点)

   

时间: 2024-11-10 00:13:21

【CImg】霍夫变换——直线检测的相关文章

opencv学习笔记霍夫变换——直线检测

参考大佬博文:blog.csdn.net/jia20003/article/details/7724530 lps-683.iteye.com/blog/2254368 openCV里有两个函数(比较常用)处理霍夫变换直线检测,有什么区别呢. CvHoughLine:是用于标准的霍夫变换方法 CvHoughLine2:可以使用三种霍夫变换的方法,分别是标准霍夫变换(SHT).多尺度标准霍夫变换(MSHT).累计概率霍夫变换(PPHT). 函数原型: CvSeq* cvHoughLines2( C

Hough变换直线检测

Hough变换直线检测 [email protected] http://blog.csdn.net/kezunhai 霍夫变换是图像变换中的经典算法之一,主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等).霍夫变换寻找直线与圆的方法相比与其它方法可以更好的减少噪声干扰.Hough变换的基本原理在于利用点与线的对偶性,将原始图像空间的曲线通过转换到参数空间的一个点. 从图中可以看到,x-y坐标和K-b坐标有点--线的对偶性.x-y坐标中的P1.P2对应于k-b坐标中的L1.L2:

边缘提取与直线检测

计算机中的边缘算法主要是依靠梯度差来计算,常见的有sobel算子,lapacian算子等,在实现方法上都大同小异,OpenCV中对这类函数都有封装,使用起来很方便: 1.Sobel算子的边缘检测 我们先找一张灰度图像,这里用一张照片,取在HSV色域的V通道: sobel算子有两个方向: -1 -2 -1 0 0 0 1 2 1 -1 0 1 -2 0 2 -1 0 1 分别用来检测水平方向与竖直方向上的边缘, cv::Sobel(image, sobelX, CV_16S, 1, 0);//1,

直线检测透视变换

透视变换首先需要给定四个角点.本文谈下通过直线检测找到角点进行透视变换的思路. 首先检测图片中的直线 如霍夫变换,LSD等. 连接直线 找到图像前景的四条直线 得到四个角点 透视变换完结果

Python+OpenCV图像处理(十四)—— 直线检测

简介: 1.霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法.主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等).最基本的霍夫变换是从黑白图像中检测直线(线段). 2.Hough变换的原理是将特定图形上的点变换到一组参数空间上,根据参数空间点的累计结果找到一个极大值对应的解,那么这个解就对应着要寻找的几何形状的参数(比如说直线,那么就会得到直线的斜率k与常熟b,圆就会得到圆心与半径等等) 3.霍夫线变

Python+OpenCV图像处理之直线检测

霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法.主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等). python实现 import cv2 import numpy as np # 使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成 __author__ = "boboa" # 标准霍夫线变换 def line_detection_demo(image): gray = cv2.cvtColor(image, cv2.COLO

霍夫直线检测进行复杂环境的树干提取

霍夫直线检测进行复杂环境的树干提取 霍夫直线检测是常用的直线检测算法,原理比较简单. 我叙述一下我对霍夫直线检测算法的理解:将像素点在图像中的二维坐标,通过坐标变换转化为极坐标,然后通过比较每个点在极坐标下的角度值,如果角度值相同,则判定为同直线. 该算法有几个可调参数: C++: void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLin

霍夫直线检测算法进行树干提取

霍夫直线检测算法进行树干提取 霍夫直线检测是常用的直线检测算法,原理比较简单. 我叙述一下我对霍夫直线检测算法的理解:将像素点在图像中的二维坐标,通过坐标变换转化为极坐标,然后通过比较每个点在极坐标下的角度值,如果角度值相同,则判定为同直线. 该算法有几个可调参数: C++: void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLe

OpenCV2马拉松第22圈——Hough变换直线检测原理与实现

计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27220445 收入囊中 Hough变换 概率Hough变换 自己实现Hough变换直线检测 葵花宝典 先看一下我实现的效果图 下面,我们进入Hough变换的原理讲解. 看上图,我们知道,经过一点(x0,y0)的直线可以表示成y0 = mox + b0 反过来看方程,b = –x0m + y0 ,于是我们从原来的坐标系转移到了Hough空间,m是横