havendblog

【opencv入门之十一】霍夫变换:霍夫线变换,霍夫圆变换

参考网站:

http://blog.csdn.net/poem_qianmo/article/details/26977557

 

1、霍夫变换概述:

    霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。

 

2、霍夫线变换:

  在使用霍夫线变换之前,首先要对图像进行边缘检测的处理,(霍夫线变换的直接输入只能是边缘二值图像)。

  opencv支持三种不同的霍夫线变换:

    标准霍夫变换(Standard Hough Transform,SHT),由HoughLines函数调用

    多尺度霍夫变换(Multi-Scale Hough Transform,MSHT),由HoughLines函数调用

    累计概率霍夫变换(Progressive Probabilistic Hough Transform, PPHT),由HoughLinesP函数调用

  因为PPHT执行效率很高,所以相比于HoughLines函数,我们更倾向于HoughLinesP函数。

 

2_1、HoughLines函数

void HoughLines(InputArray image,
           OutputArray lines,  //存储了霍夫线变换检测到线条的输出矢量,每条线是具有两个元素的矢量
           double rho,      //以像素为单位的距离精度
           double theta,      //以弧度为单位的角度精度
           int threshold,     //累加平面的阈值参数,大于阈值的线段才可以被检测通过并返回到结果中
           double srn=0,      //
           double stn=0 )     //
 1 //【1】载入原始图和Mat变量定义     
 2     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 3     Mat midImage,dstImage;//临时变量和目标图的定义  
 4   
 5     //【2】进行边缘检测和转化为灰度图  
 6     Canny(srcImage, midImage, 50, 200, 3);//进行一此canny边缘检测  
 7     cvtColor(midImage,dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图  
 8   
 9     //【3】进行霍夫线变换  
10     vector<Vec2f> lines;//定义一个矢量结构lines用于存放得到的线段矢量集合  
11     HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0 );  
12   
13     //【4】依次在图中绘制出每条线段  
14     for( size_t i = 0; i < lines.size(); i++ )  
15     {  
16         float rho = lines[i][0], theta = lines[i][1];  
17         Point pt1, pt2;  
18         double a = cos(theta), b = sin(theta);  
19         double x0 = a*rho, y0 = b*rho;  
20         pt1.x = cvRound(x0 + 1000*(-b));  
21         pt1.y = cvRound(y0 + 1000*(a));  
22         pt2.x = cvRound(x0 - 1000*(-b));  
23         pt2.y = cvRound(y0 - 1000*(a));  
24         line( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);  
25     }  

 

2_2、HoughLinesP函数

void HoughLinesP(InputArray image,
          OutputArray lines,    //存储了检测到的线条的输出矢量,每一条线是具有四个元素的矢量
          double rho,        //以像素为单位的距离精度
          double theta,       //以弧度为单位的角度精度
           int threshold,      //累加平面的阈值参数,即识别某部分为图中的一条直线是它在累加平面中必须达到的值
           double minLineLength=0,  //表示最低线段的长度
           double maxLineGap=0 )   //允许将同一行点与点之间连接起来的最大的距离
 1 //【1】载入原始图和Mat变量定义     
 2     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 3     Mat midImage,dstImage;//临时变量和目标图的定义  
 4   
 5     //【2】进行边缘检测和转化为灰度图  
 6     Canny(srcImage, midImage, 50, 200, 3);//进行一此canny边缘检测  
 7     cvtColor(midImage,dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图  
 8   
 9     //【3】进行霍夫线变换  
10     vector<Vec4i> lines;//定义一个矢量结构lines用于存放得到的线段矢量集合  
11     HoughLinesP(midImage, lines, 1, CV_PI/180, 80, 50, 10 );  
12   
13     //【4】依次在图中绘制出每条线段  
14     for( size_t i = 0; i < lines.size(); i++ )  
15     {  
16         Vec4i l = lines[i];  
17         line( dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186,88,255), 1, CV_AA);  
18     }  

 

 

3、霍夫圆变换

  在opencv中,我们通过  “霍夫梯度法”  的方法来解决圆变换的问题。

  原理:

    【1】对图像应用边缘检测

    【2】对边缘图像中的每一个非零点,考虑其局部梯度,(用Sobel()函数计算 x 和 y 方向的 Sobel 一阶导数得到梯度 )。

    【3】利用得到的梯度,由斜率指定的直线上的每一个点都在累加器中被累加,这里的斜率是从一个指定的最小值到指定的最大值的距离。(这里不懂)

    【4】标记边缘图像中每一个非0像素的位置。

    【5】然后从二维累加器中这些点中选择候选的中心,这些中心都大于给定阈值并且大于其所有近邻。这些候选的中心按照累加值降序排列,以便于最支持像素的中心首先出现。(不懂)

    【6】对每一个中心,考虑所有的非0像素

    【7】这些像素按照其与中心的距离排序。从到最大半径的最小距离算起,选择非0像素最支持的一条半径。

    【8】如果一个中心收到边缘图像非0像素最充分的支持,并且到前期被选择的中心有足够的距离,那么它就会被保留下来。

   缺点:

    【1】在霍夫梯度法中,我们使用Sobel导数来计算局部梯度,那么随之而来的假设是,其可以视作等同于一条局部切线,并这个不是一个数值稳定的做法。在大多数情况下,这样做会得到正确的结果,但或许会在输出中产生一些噪声。

    【2】在边缘图像中的整个非0像素集被看做每个中心的候选部分。因此,如果把累加器的阈值设置偏低,算法将要消耗比较长的时间。第三,因为每一个中心只选择一个圆,如果有同心圆,就只能选择其中的一个。

    【3】因为中心是按照其关联的累加器值的升序排列的,并且如果新的中心过于接近之前已经接受的中心的话,就不会被保留下来。且当有许多同心圆或者是近似的同心圆时,霍夫梯度法的倾向是保留最大的一个圆。可以说这是一种比较极端的做法,因为在这里默认Sobel导数会产生噪声,若是对于无穷分辨率的平滑图像而言的话,这才是必须的。

 

3_1、HoughCircles()函数

  

void HoughCircles(InputArray image,
          OutputArray circles,    //存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x,y,radius)表示
           int method,         //使用的检测方法,目前就只有霍夫梯度法一种,CV_HOUGH_GRADIENT
           double dp,          //用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。
                            上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。
                            如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
           double minDist,      //为霍夫变换检测到的圆的圆心之间的最小距离
           double param1=100,    //它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
           double param2=100,    //它越大的话,能通过检测的圆就更加接近完美的圆形了
           int minRadius=0,      //表示圆半径的最小值
           int maxRadius=0 )     //表示圆半径的最大值

  注意:

    使用此函数可以很容易地检测出圆的圆心,但是它可能找不到合适的圆半径。我们可以通过第八个参数minRadius和第九个参数maxRadius指定最小和最大的圆半径,来辅助圆检测的效果。或者,我们可以直接忽略返回半径,因为它们都有着默认值0,单单用HoughCircles函数检测出来的圆心,然后用额外的一些步骤来进一步确定半径。

 1 //【1】载入原始图和Mat变量定义     
 2     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 3     Mat midImage,dstImage;//临时变量和目标图的定义  
 4   
 5     //【2】显示原始图  
 6     imshow("【原始图】", srcImage);    
 7   
 8     //【3】转为灰度图,进行图像平滑  
 9     cvtColor(srcImage,midImage, CV_BGR2GRAY);//转化边缘检测后的图为灰度图  
10     GaussianBlur( midImage, midImage, Size(9, 9), 2, 2 );  
11   
12     //【4】进行霍夫圆变换  
13     vector<Vec3f> circles;  
14     HoughCircles( midImage, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 );  
15   
16     //【5】依次在图中绘制出圆  
17     for( size_t i = 0; i < circles.size(); i++ )  
18     {  
19         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));  
20         int radius = cvRound(circles[i][2]);  
21         //绘制圆心  
22         circle( srcImage, center, 3, Scalar(0,255,0), -1, 8, 0 );  
23         //绘制圆轮廓  
24         circle( srcImage, center, radius, Scalar(155,50,255), 3, 8, 0 );  
25     }  
26   

 

 

4、综合实践

  想实现检测圆的,不知道是不是因为算法太慢,还是参数调的不好。

  1 //******************************【程序说明】*****************************
  2 //    程序名称:opencv霍夫变换:霍夫线变换、霍夫圆变换
  3 //    opencv版本:2.4.13
  4 //    日期:2017/9/25
  5 //**********************************************************************
  6 
  7 
  8 //******************************【头文件包含部分】*****************************
  9 //    描述:包含程序所依赖的头文件
 10 //*****************************************************************************
 11 //#include <opencv2/opencv.hpp>
 12 #include <opencv2/highgui/highgui.hpp>
 13 #include <opencv2/imgproc/imgproc.hpp>
 14 //#include <iostream>
 15 
 16 
 17 //******************************【命名空间声明部分】*****************************
 18 //    描述:包含程序所使用的命名空间
 19 //*****************************************************************************
 20 using namespace cv;
 21 using namespace std;
 22 
 23 
 24 //******************************【全局变量声明部分】*****************************
 25 //    描述:全局变量声明
 26 //*****************************************************************************
 27 Mat g_srcImage, g_midImage, g_dstImage;    //原始图、 中间图、 效果图
 28 Mat g_srcImage_circle,g_midImage_circle,g_dstImage_circle;//
 29 //vector<Vec4i> g_lines;    //定义一个矢量结构g_lines用于存放得到的线段矢量集合
 30 //变量接收的TrackBar位置参数
 31 int g_nthreshold_1 = 100;    //【累计概率霍夫变换】的累加平面的阈值参数
 32 int g_nthreshold_2 = 100;    //【标准霍夫变换】的累加平面的阈值参数
 33 int g_nthreshold_3 = 100;
 34 
 35 //******************************【全局函数声明部分】*****************************
 36 //    描述:全局函数声明
 37 //*****************************************************************************
 38 static void ShowHelpText();                //帮助文字显示
 39 static void on_HoughLinesP( int, void* );    //回调函数
 40 static void on_HoughLines( int, void* );    //回调函数
 41 static void on_HoughCircles( int, void* );    //回调函数
 42 
 43 //******************************【main()部分】*****************************
 44 //    描述:控制台应用程序的入口函数,我们的程序从这里开始
 45 //*****************************************************************************
 46 int main()
 47 {
 48     //初始化
 49     system("color 5F");
 50     ShowHelpText();
 51 
 52     //读取原图
 53    // g_srcImage = imread( "1.jpg", 1 );    //这张照片为检测直线的
 54    // if(!g_srcImage.data) { printf("Oh,no,读取srcImage错误!!!!\n"); return false; }
 55    // imshow("【原图1窗口】", g_srcImage );
 56 
 57     g_srcImage_circle = imread( "5.jpg", 1 );        //这照片为检测圆形的
 58      if(!g_srcImage_circle.data) { printf("Oh,no,读取g_srcImage_circle错误!!!!\n"); return false; }
 59     imshow("【原图2窗口】", g_srcImage_circle );
 60 
 61     //进行边缘检测和转化为灰度图
 62     //Canny( g_srcImage, g_midImage, 50, 200, 3 );    //进行一次canny边缘检测
 63     //cvtColor( g_midImage, g_dstImage, CV_GRAY2BGR );    //转化边缘检测后的图为灰度图
 64     //imshow( "【图1,canny边缘检测效果图】", g_dstImage );
 65 
 66     Canny( g_srcImage_circle, g_midImage_circle, 50, 200, 3 );    //进行一次canny边缘检测
 67     cvtColor( g_midImage_circle, g_dstImage_circle, CV_GRAY2BGR );    //转化边缘检测后的图为灰度图
 68     imshow( "【图2,canny边缘检测效果图】", g_dstImage_circle );
 69 
 70     //累计概率霍夫变换的实现
 71     //namedWindow("【累计概率霍夫变换效果图】", 1);
 72     //createTrackbar( "值:", "【累计概率霍夫变换效果图】", &g_nthreshold_1, 200, on_HoughLinesP );
 73     //on_HoughLinesP( g_nthreshold_1, 0 );
 74 
 75     //标准霍夫变换的实现
 76     //namedWindow("【标准霍夫变换效果图】", 1);
 77     //createTrackbar("值:", "【标准霍夫变换效果图】", &g_nthreshold_2, 200, on_HoughLines );
 78     //on_HoughLines( g_nthreshold_2, 0 );
 79 
 80     //霍夫圆变换的实现
 81     namedWindow("【霍夫圆变换效果图】", 1);
 82     //createTrackbar("值:", "【霍夫圆变换效果图】", &g_nthreshold_3, 200, on_HoughCircles );
 83     on_HoughCircles( g_nthreshold_3, 0 );
 84 
 85 
 86     int key = 0;
 87 
 88     //轮询获取按键信息
 89     while(1)
 90     {
 91         key = waitKey(9);//读取键值到key变量中
 92 
 93         //根据key变量的值,进行不同的操作
 94         switch(key)
 95         {
 96             //*********************【程序退出】************************************
 97         case 27://按键ESC
 98             return 0;
 99             break;
100         case \'q\'://按键Q
101             return 0;
102             break;
103         case \'a\'://按键A按下,....
104             break;
105         case \'w\':
106             break;
107             //*********************【】***********************
108         case \'d\':
109             break;
110         case \'s\':
111             break;
112         }
113     }
114 
115     return 0;
116 }
117 
118 
119 
120 //******************************【ShowHelpText()部分】*****************************
121 //    描述:输出一些帮助信息
122 //*****************************************************************************
123 static void ShowHelpText()
124 {
125     //输出一些帮助信息  
126     printf("\n\n\n\t请调整滚动条观察图像效果~\n\n");  
127 }
128 
129 
130 //******************************【on_HoughLines()部分】*****************************
131 //    描述:【累计概率霍夫变换PPHT】
132 //*****************************************************************************
133 static void on_HoughLinesP( int, void* )
134 {
135     //定义局部变量存储全局变量
136     Mat dstImage = g_dstImage.clone();
137     Mat midImage = g_midImage.clone();
138 
139     //调用HoughLinesP函数
140     vector<Vec4i> mylines;
141     HoughLinesP( midImage, mylines, 1, CV_PI/180, g_nthreshold_1+1, 50, 10 );
142 
143     //循环遍历绘制每一条线段
144     for( size_t i=0; i<mylines.size(); i++ )
145     {
146         Vec4i l = mylines[i];
147         line( dstImage, Point(l[0],l[1]), Point(l[2],l[3]), Scalar(23, 180, 55), 1, CV_AA );
148     }
149     //显示图像
150     imshow("【累计概率霍夫变换效果图】", dstImage);
151 }
152 
153 
154 
155 
156 //******************************【on_HoughLines()部分】*****************************
157 //    描述:【标准霍夫变换SHT】
158 //*****************************************************************************
159 static void on_HoughLines( int, void* )
160 {
161     //定义局部变量存储全局变量
162     Mat dstImage = g_dstImage.clone();
163     Mat midImage = g_midImage.clone();
164 
165     //调用HoughLines函数
166     vector<Vec2f> lines;    //定义一个矢量结构lines用于存放得到的线段矢量集合
167     HoughLines( midImage, lines, 1, CV_PI/180, g_nthreshold_2+1, 0, 0 );        //转化边缘检测后的图为灰度图
168 
169     //依次子啊图中绘制出每条线条
170     for( size_t i=0; i<lines.size(); i++ )
171     {
172         float rho=lines[i][0], theta=lines[i][1];
173         Point pt1, pt2;
174         double a = cos(theta), b = sin(theta);
175         double x0 = a*rho, y0 = b*rho;
176         pt1.x = cvRound( x0 + 1000*(-b) );
177         pt1.y = cvRound( y0 + 1000*(a) );
178         pt2.x = cvRound( x0 - 1000*(-b) );
179         pt2.y = cvRound( y0 - 1000*(a) );
180         line( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA );
181     }
182     //显示图像
183     imshow("【标准霍夫变换效果图】", dstImage);
184 }
185 
186 
187 
188 //******************************【on_HoughCircles()部分】*****************************
189 //    描述:【霍夫圆变换】
190 //*****************************************************************************
191 static void on_HoughCircles( int, void* )
192 {
193     //定义局部变量存储全局变量
194     Mat dstImage = g_dstImage_circle.clone();
195     Mat midImage = g_midImage_circle.clone();
196 
197     //调用霍夫圆函数
198     vector<Vec3f> circles;
199     HoughCircles( midImage, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 100, 0,0 );
200 
201     //依次在图中绘制出图
202     for( size_t i = 0; i < circles.size(); i++ )
203     {
204         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
205         int radius = cvRound(circles[i][2]);
206         circle( dstImage, center, 3, Scalar(0,255,0), -1, 8, 0 );
207         circle( dstImage, center, radius, Scalar(155,50,255), 3, 8, 0 );
208     }
209     //显示图像
210     imshow("【霍夫圆变换效果图】", dstImage );
211 }

 

分类:

技术点:

相关文章: