cvHoughCircle 解析

在网上看了一篇关于cvHoughCircle函数的解析,转载过来看看。学习Opencv,不少人只能调用函数而不能作出适配或者修改,这是一种遗憾,貌似很少有这方面的博客,不知道有人推荐一些关于OpenCV源代码解析的资料没有?

一、基本原理

opencv中圆识别的基本原理如下:

1、canny算子求图像的单像素二值化边缘

2、假设我们需要找半径为R的所有圆,则对于边缘图中的每一个边缘点,该边缘点的切线的法线方向上(正负两个方向),寻找到该边缘点距离为R的点,将该点的计数加1(初始化所有点的计数都是0)

3、找到计数值大于门限值的点,即圆心所在的点

 二、代码分析

代码在/modules\imgproc\src\hough.cpp文件icvHoughCirclesGradient函数中

 

[cpp]  view plain copy
  1. static void  
  2. icvHoughCirclesGradient( CvMat* img, float dp, float min_dist,  
  3.                          int min_radius, int max_radius,  
  4.                          int canny_threshold, int acc_threshold,  
  5.                          CvSeq* circles, int circles_max )  
  6. {  
  7. //参数:  
  8. //img: 输入图像  
  9. //dp: 识别精度,1.0表示按照原图精度  
  10. //min_dist: 圆心点位置识别精度  
  11. //min_radius: 所需要找的圆的最小半径  
  12. //max_radius:所需要找的圆的最大半径  
  13. //canny_threshold:canny算子的高阀值  
  14. //acc_threshold:累加器阀值,计数大于改阀值的点即被认为是可能的圆心  
  15. //circles: 保存找到的符合条件的所有圆  
  16. //circles_max: 最多需要的找到的圆的个数  
  17.   
  18.     const int SHIFT = 10, ONE = 1 << SHIFT, R_THRESH = 30;  
  19.     cv::Ptr<CvMat> dx, dy;  
  20.     cv::Ptr<CvMat> edges, accum, dist_buf;  
  21.     std::vector<int> sort_buf;  
  22.     cv::Ptr<CvMemStorage> storage;  
  23.       
  24.     int x, y, i, j, k, center_count, nz_count;  
  25.     float min_radius2 = (float)min_radius*min_radius;  
  26.     float max_radius2 = (float)max_radius*max_radius;  
  27.     int rows, cols, arows, acols;  
  28.     int astep, *adata;  
  29.     float* ddata;  
  30.     CvSeq *nz, *centers;  
  31.     float idp, dr;  
  32.     CvSeqReader reader;  
  33.       
  34.     //canny算子求单像素二值化边缘,保存在edges变量中  
  35.     edges = cvCreateMat( img->rows, img->cols, CV_8UC1 );  
  36.     cvCanny( img, edges, MAX(canny_threshold/2,1), canny_threshold, 3 );  
  37.       
  38.     //sobel算子求水平和垂直方向的边缘,用于计算边缘点的法线方向  
  39.     dx = cvCreateMat( img->rows, img->cols, CV_16SC1 );  
  40.     dy = cvCreateMat( img->rows, img->cols, CV_16SC1 );  
  41.     cvSobel( img, dx, 1, 0, 3 );  
  42.     cvSobel( img, dy, 0, 1, 3 );  
  43.       
  44.     //dp表示识别精度  
  45.     if( dp < 1.f )  
  46.         dp = 1.f;  
  47.     idp = 1.f/dp;  
  48.       
  49.     //accum用作累加器,包含图像中每一个点的计数。图像中每一个点都有一个计数,点的计数表示每一个canny边缘点法线方向上,  
  50.     //到该点距离为R的边缘点的个数,初始化为0  
  51.     accum = cvCreateMat( cvCeil(img->rows*idp)+2, cvCeil(img->cols*idp)+2, CV_32SC1 );  
  52.     cvZero(accum);  
  53.       
  54.     storage = cvCreateMemStorage();  
  55.     nz = cvCreateSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage );  
  56.       
  57.     //centers用于保存可能的圆心点  
  58.     centers = cvCreateSeq( CV_32SC1, sizeof(CvSeq), sizeof(int), storage );  
  59.       
  60.     rows = img->rows;  
  61.     cols = img->cols;  
  62.     arows = accum->rows - 2;  
  63.     acols = accum->cols - 2;  
  64.     adata = accum->data.i;  
  65.     astep = accum->step/sizeof(adata[0]);  
  66.       
  67.     //以下这个循环用于获取所有可能的圆边缘点,存储在nz中,同时设置  
  68.     //累加器中的值  
  69.     for( y = 0; y < rows; y++ )  
  70.     {  
  71.         const uchar* edges_row = edges->data.ptr + y*edges->step;  
  72.         const short* dx_row = (const short*)(dx->data.ptr + y*dx->step);  
  73.         const short* dy_row = (const short*)(dy->data.ptr + y*dy->step);  
  74.           
  75.         for( x = 0; x < cols; x++ )  
  76.         {  
  77.             float vx, vy;  
  78.             int sx, sy, x0, y0, x1, y1, r, k;  
  79.             CvPoint pt;  
  80.       
  81.             vx = dx_row[x];  
  82.             vy = dy_row[x];  
  83.       
  84.             if( !edges_row[x] || (vx == 0 && vy == 0) )  
  85.                 continue;  
  86.       
  87.             float mag = sqrt(vx*vx+vy*vy);  
  88.             assert( mag >= 1 );  
  89.               
  90.             //sx表示cos, sy表示sin  
  91.             sx = cvRound((vx*idp)*ONE/mag);  
  92.             sy = cvRound((vy*idp)*ONE/mag);  
  93.               
  94.             x0 = cvRound((x*idp)*ONE);  
  95.             y0 = cvRound((y*idp)*ONE);  
  96.               
  97.             //循环两次表示需要计算两个方向,法线方向和法线的反方向  
  98.             for( k = 0; k < 2; k++ )  
  99.             {  
  100.                 //半径方向的水平增量和垂直增量  
  101.                 x1 = x0 + min_radius * sx;  
  102.                 y1 = y0 + min_radius * sy;  
  103.                   
  104.                 //在法线方向和反方向上,距离边缘点的距离为输入的最大半径和最小半径范围内找点  
  105.                 //每找到一个点,该点的累加器计数就加1  
  106.                 for( r = min_radius; r <= max_radius; x1 += sx, y1 += sy, r++ )  
  107.                 {  
  108.                     int x2 = x1 >> SHIFT, y2 = y1 >> SHIFT;  
  109.                     if( (unsigned)x2 >= (unsigned)acols || (unsigned)y2 >= (unsigned)arows )  
  110.                         break;  
  111.                     adata[y2*astep + x2]++;  
  112.                 }  
  113.                 //反方向  
  114.                 sx = -sx; sy = -sy;  
  115.             }  
  116.               
  117.             //保存可能的圆边缘点  
  118.             pt.x = x; pt.y = y;  
  119.             cvSeqPush( nz, &pt );  
  120.         }  
  121.     }  
  122.   
  123.     nz_count = nz->total;  
  124.     if( !nz_count )  
  125.         return;  
  126.       
  127.     //累加器中,计数大于阀值的点,被认为可能的圆心点。因为计算各点计数过程中,距离有误差,所以  
  128.     //在与阀值比较时,该点计数先要与4邻域内的各个点的计数比较,最大者才能和阀值比较。可能的圆心  
  129.     //点保存在centers中  
  130.     for( y = 1; y < arows - 1; y++ )  
  131.     {  
  132.         for( x = 1; x < acols - 1; x++ )  
  133.         {  
  134.             int base = y*(acols+2) + x;  
  135.             if( adata[base] > acc_threshold &&  
  136.                 adata[base] > adata[base-1] && adata[base] > adata[base+1] &&  
  137.                 adata[base] > adata[base-acols-2] && adata[base] > adata[base+acols+2] )  
  138.             cvSeqPush(centers, &base);  
  139.         }  
  140.     }  
  141.   
  142.     center_count = centers->total;  
  143.     if( !center_count )  
  144.         return;  
  145.   
  146.     sort_buf.resize( MAX(center_count,nz_count) );  
  147.       
  148.     //链表结构的certers转化成连续存储结构sort_buf  
  149.     cvCvtSeqToArray( centers, &sort_buf[0] );  
  150.       
  151.     //经过icvHoughSortDescent32s函数后,以sort_buf中元素作为adata数组下标,   
  152.     //adata中的元素降序排列, 即adata[sort_buf[0]]是adata所有元素中最大的,   
  153.     //adata[sort_buf[center_count-1]]是所有元素中最小的  
  154.     icvHoughSortDescent32s( &sort_buf[0], center_count, adata );  
  155.       
  156.     cvClearSeq( centers );  
  157.       
  158.     //经过排序后的元素,重新以链表形式存储到centers中  
  159.     cvSeqPushMulti( centers, &sort_buf[0], center_count );  
  160.       
  161.     dist_buf = cvCreateMat( 1, nz_count, CV_32FC1 );  
  162.     ddata = dist_buf->data.fl;  
  163.       
  164.     dr = dp;  
  165.     min_dist = MAX( min_dist, dp );  
  166.     min_dist *= min_dist;  
  167.       
  168.     //对于每一个可能的圆心点,计算所有边缘点到该圆心点的距离。由于centers中的  
  169.     //元素已经经过前面排序,所以累加器计数最大的可能圆心点最先进行下面的操作  
  170.     for( i = 0; i < centers->total; i++ )  
  171.     {  
  172.         int ofs = *(int*)cvGetSeqElem( centers, i );  
  173.         y = ofs/(acols+2) - 1;  
  174.         x = ofs - (y+1)*(acols+2) - 1;  
  175.         float cx = (float)(x*dp), cy = (float)(y*dp);  
  176.         float start_dist, dist_sum;  
  177.         float r_best = 0, c[3];  
  178.         int max_count = R_THRESH;  
  179.           
  180.         //如果该可能的圆心点和已经确认的圆心点的距离小于阀值,则表示  
  181.         //这个圆心点和已经确认的圆心点是同一个点  
  182.         for( j = 0; j < circles->total; j++ )  
  183.         {  
  184.             float* c = (float*)cvGetSeqElem( circles, j );  
  185.             if( (c[0] - cx)*(c[0] - cx) + (c[1] - cy)*(c[1] - cy) < min_dist )  
  186.                 break;  
  187.         }  
  188.   
  189.         if( j < circles->total )  
  190.             continue;  
  191.           
  192.         cvStartReadSeq( nz, &reader );  
  193.           
  194.         //求所有边缘点到当前圆心点的距离,符合条件的距离值保存在ddata中  
  195.         for( j = k = 0; j < nz_count; j++ )  
  196.         {  
  197.             CvPoint pt;  
  198.             float _dx, _dy, _r2;  
  199.             CV_READ_SEQ_ELEM( pt, reader );  
  200.             _dx = cx - pt.x; _dy = cy - pt.y;  
  201.             _r2 = _dx*_dx + _dy*_dy;  
  202.             if(min_radius2 <= _r2 && _r2 <= max_radius2 )  
  203.             {  
  204.                 ddata[k] = _r2;  
  205.                 sort_buf[k] = k;  
  206.                 k++;  
  207.             }  
  208.         }  
  209.   
  210.         int nz_count1 = k, start_idx = nz_count1 - 1;  
  211.         if( nz_count1 == 0 )  
  212.             continue;  
  213.         dist_buf->cols = nz_count1;  
  214.         cvPow( dist_buf, dist_buf, 0.5 );  
  215.           
  216.         //经过如下处理后,以sort_buf中元素作为ddata数组下标,ddata中的元素降序排列,   
  217.         //即ddata[sort_buf[0]]是ddata所有元素中最大的, ddata[sort_buf[nz_count1-1]]  
  218.         //是所有元素中最小的  
  219.         icvHoughSortDescent32s( &sort_buf[0], nz_count1, (int*)ddata );  
  220.           
  221.         //对所有的距离值做处理,求出最可能圆半径值,max_count为到圆心的距离为最可能半径值的点的个数  
  222.         dist_sum = start_dist = ddata[sort_buf[nz_count1-1]];  
  223.         for( j = nz_count1 - 2; j >= 0; j-- )  
  224.         {  
  225.             float d = ddata[sort_buf[j]];         
  226.             if( d > max_radius )  
  227.                 break;  
  228.               
  229.             if( d - start_dist > dr )  
  230.             {  
  231.                 float r_cur = ddata[sort_buf[(j + start_idx)/2]];  
  232.                 if( (start_idx - j)*r_best >= max_count*r_cur ||  
  233.                     (r_best < FLT_EPSILON && start_idx - j >= max_count) )   
  234.                 {  
  235.                     r_best = r_cur;  
  236.                     max_count = start_idx - j;  
  237.                 }  
  238.                 start_dist = d;  
  239.                 start_idx = j;  
  240.                 dist_sum = 0;  
  241.             }  
  242.             dist_sum += d;  
  243.         }  
  244.         //max_count大于阀值,表示这几个边缘点构成一个圆  
  245.         if( max_count > R_THRESH )  
  246.         {  
  247.             c[0] = cx;  
  248.             c[1] = cy;  
  249.             c[2] = (float)r_best;  
  250.             cvSeqPush( circles, c );  
  251.             if( circles->total > circles_max )  
  252.             return;  
  253.         }  
  254.     }  
  255. }  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值