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

来源:互联网 发布:一楼土木人淘宝店 编辑:程序博客网 时间:2024/05/09 22:07

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

// 仿照matlab,自适应求高低两个门限CV_IMPL void AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high){    CvSize size;    IplImage *imge=0;    int i,j;    CvHistogram *hist;    int hist_size = 255;    float range_0[]={0,256};    float* ranges[] = { range_0 };    double  PercentOfPixelsNotEdges = 0.7;    size = cvGetSize(dx);    imge = cvCreateImage(size, IPL_DEPTH_32F, 1);    // 计算边缘的强度, 并存于图像中    float maxv = 0;    for(i = 0; i < size.height; i++ )    {        const short* _dx = (short*)(dx->data.ptr + dx->step*i);        const short* _dy = (short*)(dy->data.ptr + dy->step*i);        float* _image = (float *)(imge->imageData + imge->widthStep*i);        for(j = 0; j < size.width; j++)        {            _image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));            maxv = maxv < _image[j] ? _image[j]: maxv;        }    }        // 计算直方图    range_0[1] = maxv;    hist_size = (int)(hist_size > maxv ? maxv:hist_size);    hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);    cvCalcHist( &imge, hist, 0, NULL );    int total = (int)(size.height * size.width * PercentOfPixelsNotEdges);    float sum=0;    int icount = hist->mat.dim[0].size;        float *h = (float*)cvPtr1D( hist->bins, 0 );    for(i = 0; i < icount; i++)    {        sum += h[i];        if( sum > total )            break;     }    // 计算高低门限    *high = (i+1) * maxv / hist_size ;    *low = *high * 0.4;    cvReleaseImage( &imge );    cvReleaseHist(&hist);}

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


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


void AdaptiveFindThreshold(const CvArr* image, double *low, double *high, int aperture_size=3){                                                                              cv::Mat src = cv::cvarrToMat(image);                                   const int cn = src.channels();                                         cv::Mat dx(src.rows, src.cols, CV_16SC(cn));                           cv::Mat dy(src.rows, src.cols, CV_16SC(cn));                                                                                                          cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLIC);cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLIC);                                                                               CvMat _dx = dx, _dy = dy;                                              _AdaptiveFindThreshold(&_dx, &_dy, low, high);                                                                                                        }                                                                                                                                                             // 仿照matlab,自适应求高低两个门限                                            void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high)   {                                                                              CvSize size;                                                           IplImage *imge=0;                                                      int i,j;                                                               CvHistogram *hist;                                                     int hist_size = 255;                                                   float range_0[]={0,256};                                               float* ranges[] = { range_0 };                                         double PercentOfPixelsNotEdges = 0.7;                                  size = cvGetSize(dx);                                                  imge = cvCreateImage(size, IPL_DEPTH_32F, 1);                          // 计算边缘的强度, 并存于图像中                                        float maxv = 0;                                                        for(i = 0; i < size.height; i++ )                                      {                                                                      const short* _dx = (short*)(dx->data.ptr + dx->step*i);        const short* _dy = (short*)(dy->data.ptr + dy->step*i);        float* _image = (float *)(imge->imageData + imge->widthStep*i);for(j = 0; j < size.width; j++)                                {                                                              _image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));        maxv = maxv < _image[j] ? _image[j]: maxv;                                                                                    }                                                              }                                                                      if(maxv == 0){                                                         *high = 0;                                                     *low = 0;                                                      cvReleaseImage( &imge );                                       return;                                                        }                                                                                                                                                     // 计算直方图                                                          range_0[1] = maxv;                                                     hist_size = (int)(hist_size > maxv ? maxv:hist_size);                  hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);          cvCalcHist( &imge, hist, 0, NULL );                                    int total = (int)(size.height * size.width * PercentOfPixelsNotEdges); float sum=0;                                                           int icount = hist->mat.dim[0].size;                                                                                                                   float *h = (float*)cvPtr1D( hist->bins, 0 );                           for(i = 0; i < icount; i++)                                            {                                                                      sum += h[i];                                                   if( sum > total )                                              break;                                                 }                                                                      // 计算高低门限                                                        *high = (i+1) * maxv / hist_size ;                                     *low = *high * 0.4;                                                    cvReleaseImage( &imge );                                               cvReleaseHist(&hist);                                                  }                                                                              

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

IplImage *out = cvCloneImage(src);double low = 0.0, high = 0.0;AdaptiveFindThreshold(src, &low, &high);cvCanny(src, out, low, high);