分享

在OpenCV中自适应确定canny算法的分割门限

 oskycar 2016-04-21

在OpenCV中用canny算子进行边缘检测速度很快,不过有点不爽的就是高低阈值需要输入。在OpenCV中自适应确定canny算法的分割门限 一文仿照matlab中的做法,对canny函数进行了修改,以便当用户没有指定高低阈值时,由函数自适应确定阈值。代码如下:

  1. // 仿照matlab,自适应求高低两个门限  
  2. CV_IMPL void AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high)  
  3. {  
  4.     CvSize size;  
  5.     IplImage *imge=0;  
  6.     int i,j;  
  7.     CvHistogram *hist;  
  8.     int hist_size = 255;  
  9.     float range_0[]={0,256};  
  10.     float* ranges[] = { range_0 };  
  11.     double  PercentOfPixelsNotEdges = 0.7;  
  12.     size = cvGetSize(dx);  
  13.     imge = cvCreateImage(size, IPL_DEPTH_32F, 1);  
  14.     // 计算边缘的强度, 并存于图像中  
  15.     float maxv = 0;  
  16.     for(i = 0; i < size.height; i++ )  
  17.     {  
  18.         const short* _dx = (short*)(dx->data.ptr + dx->step*i);  
  19.         const short* _dy = (short*)(dy->data.ptr + dy->step*i);  
  20.         float* _image = (float *)(imge->imageData + imge->widthStep*i);  
  21.         for(j = 0; j < size.width; j++)  
  22.         {  
  23.             _image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));  
  24.             maxv = maxv < _image[j] ? _image[j]: maxv;  
  25.         }  
  26.     }  
  27.       
  28.     // 计算直方图  
  29.     range_0[1] = maxv;  
  30.     hist_size = (int)(hist_size > maxv ? maxv:hist_size);  
  31.     hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);  
  32.     cvCalcHist( &imge, hist, 0, NULL );  
  33.     int total = (int)(size.height * size.width * PercentOfPixelsNotEdges);  
  34.     float sum=0;  
  35.     int icount = hist->mat.dim[0].size;  
  36.       
  37.     float *h = (float*)cvPtr1D( hist->bins, 0 );  
  38.     for(i = 0; i < icount; i++)  
  39.     {  
  40.         sum += h[i];  
  41.         if( sum > total )  
  42.             break;   
  43.     }  
  44.     // 计算高低门限  
  45.     *high = (i+1) * maxv / hist_size ;  
  46.     *low = *high * 0.4;  
  47.     cvReleaseImage( &imge );  
  48.     cvReleaseHist(&hist);  
  49. }  

然后对cvCanny函数进行以下修改。
在函数体中,当程序用两个sobel算子计算完水平和垂直两个方向的梯度强度过后加入以下代码
  1. // 自适应确定阈值  
  2. if(low_thresh == -1 && high_thresh == -1)  
  3. {  
  4.     AdaptiveFindThreshold(dx, dy, &low_thresh, &high_thresh);  
  5. }  
 
这样,在调用cvCanny函数时,指定高低门限为-1,则cvCanny函数就自适应确定门限。
 
最后,重新编译cv库,对lib和dll库进行更新。


但是上述代码存在一个问题,当图片是全黑的图时,maxv计算的结果为0,在调用cvCanny检测时,会造成段错误。另外,为了免去修改源码以及重新编译cv库,修改代码如下:


  1. void AdaptiveFindThreshold(const CvArr* image, double *low, double *high, int aperture_size=3)  
  2. {                                                                                
  3.     cv::Mat src = cv::cvarrToMat(image);                                     
  4.     const int cn = src.channels();                                           
  5.     cv::Mat dx(src.rows, src.cols, CV_16SC(cn));                             
  6.     cv::Mat dy(src.rows, src.cols, CV_16SC(cn));                             
  7.                                                                                  
  8.     cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLIC);  
  9.     cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLIC);  
  10.                                                                                  
  11.     CvMat _dx = dx, _dy = dy;                                                
  12.     _AdaptiveFindThreshold(&_dx, &_dy, low, high);                           
  13.                                                                                  
  14. }                                                                                
  15.                                                                                  
  16. // 仿照matlab,自适应求高低两个门限                                              
  17. void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high)     
  18. {                                                                                
  19.     CvSize size;                                                             
  20.     IplImage *imge=0;                                                        
  21.     int i,j;                                                                 
  22.     CvHistogram *hist;                                                       
  23.     int hist_size = 255;                                                     
  24.     float range_0[]={0,256};                                                 
  25.     float* ranges[] = { range_0 };                                           
  26.     double PercentOfPixelsNotEdges = 0.7;                                    
  27.     size = cvGetSize(dx);                                                    
  28.     imge = cvCreateImage(size, IPL_DEPTH_32F, 1);                            
  29.     // 计算边缘的强度, 并存于图像中                                          
  30.     float maxv = 0;                                                          
  31.     for(i = 0; i < size.height; i++ )                                        
  32.     {                                                                        
  33.         const short* _dx = (short*)(dx->data.ptr + dx->step*i);          
  34.         const short* _dy = (short*)(dy->data.ptr + dy->step*i);          
  35.         float* _image = (float *)(imge->imageData + imge->widthStep*i);  
  36.         for(j = 0; j < size.width; j++)                                  
  37.         {                                                                
  38.             _image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));          
  39.             maxv = maxv < _image[j] ? _image[j]: maxv;               
  40.                                                                              
  41.         }                                                                
  42.     }                                                                        
  43.     if(maxv == 0){                                                           
  44.         *high = 0;                                                       
  45.         *low = 0;                                                        
  46.         cvReleaseImage( &imge );                                         
  47.         return;                                                          
  48.     }                                                                        
  49.                                                                                  
  50.     // 计算直方图                                                            
  51.     range_0[1] = maxv;                                                       
  52.     hist_size = (int)(hist_size > maxv ? maxv:hist_size);                    
  53.     hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);            
  54.     cvCalcHist( &imge, hist, 0, NULL );                                      
  55.     int total = (int)(size.height * size.width * PercentOfPixelsNotEdges);   
  56.     float sum=0;                                                             
  57.     int icount = hist->mat.dim[0].size;                                      
  58.                                                                                  
  59.     float *h = (float*)cvPtr1D( hist->bins, 0 );                             
  60.     for(i = 0; i < icount; i++)                                              
  61.     {                                                                        
  62.         sum += h[i];                                                     
  63.         if( sum > total )                                                
  64.             break;                                                   
  65.     }                                                                        
  66.     // 计算高低门限                                                          
  67.     *high = (i+1) * maxv / hist_size ;                                       
  68.     *low = *high * 0.4;                                                      
  69.     cvReleaseImage( &imge );                                                 
  70.     cvReleaseHist(&hist);                                                    
  71. }                                                                                

这样在使用canny算子检测边缘时,需要两步调用:

  1. IplImage *out = cvCloneImage(src);  
  2. double low = 0.0, high = 0.0;  
  3. AdaptiveFindThreshold(src, &low, &high);  
  4. cvCanny(src, out, low, high);  
  1. <pre code_snippet_id="348460" snippet_file_name="blog_20140516_5_5088750"></pre>  
  2. <pre></pre>  
  3. <pre></pre>  
  4. <pre></pre>  
  5. <pre></pre>  
  6.      

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多