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

在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);





   

你可能感兴趣的:(算法,image,matlab,float,border)