五、边缘检测
边缘检测的一般步骤:滤波--->增强--->检测。
边缘检测的主要算子和滤波器有:Canny算子、Soble算子、Laplace算子和Scharr滤波器。
滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但是导数对噪声很敏感,所以需要采用滤波来改善和噪声有关的边缘检测器的性能。主要的滤波为高斯滤波。
增强:增强边缘的基础是确定图像各点领域强度的变化值。增强算法可以将图像领域强度值有明显变化的点突显出来。其可以通过计算梯度幅度值来确定。
检测:经过增强的图像,领域很多点的梯度值会比较大,而这些点又不是想要的点。常用的检测方法是用阈值来排除这些点。
1.Canny算子
Canny的目标是找到一个最优的边缘检测算法。其评价标准为:
1.低错误率:标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
2.高定位性:标识出的边缘要与图像中实际边缘尽可能的接近。
3.最小响应:图像边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
Canny边缘检测的步骤为:
1.消除噪声,主要是使用高斯滤波。下图为一个5*5的高斯核的示例。
2.计算梯度幅值和方向。
(1)先用一对卷积分别作用于x和y方向。
(2)然后计算梯度幅值和方向(近似到几个可能的角度,如0,45,90,135)。
3.非极大值抑制。排除非边缘像素,仅仅保留一些细线条。
4.滞后阈值。滞后阈值需要一个高阈值和低阈值,若高于高阈值,则保留为边缘像素;若低于低阈值,则排除;若在高低之间,则仅仅保留连接到高于高阈值像素的像素。一般高低阈值比在2:1到3:1之间。
void Canny(InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize=3,
bool L2gradient=false )
参数1:输入图像。
参数2:输出图像。
参数3:第一个滞后阈值。
参数4:第二个滞后阈值。
参数5:sobel算子的孔径大小,默认值为3.
参数6:计算图像梯度幅值的标识,默认值false。
//载入原始图
Mat src = imread("1.jpg");
Canny(src, src, 3, 9,3 );
imshow("【效果图】Canny边缘检测", src);
2.Sobel算子
Sobel算子是一个主要用作边缘检测的离散微分算子。结合了高斯平滑和微分求导,用来计算图像灰度的近似梯度。其计算过程类似Canny算子,只是没有求方向。
void Sobel (
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
int ksize=3,
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT );
参数1:输入图像。
参数2:输出图像。
参数3:输出图像深度,支持如下组合:
- 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
- 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
参数4:x方向上的差分阶数。
参数5:y方向上的差分阶数。
参数6:核大小,必须取1,3,5,7。
参数7:计算导数时可选的缩放因子,默认值为1。
参数8:表示存入目标图之前可选的值,默认为0。
参数9:边界类型。
当内核大小为3时,Sobel内核很可能产生比较明显的误差,这时需要使用Scharr函数,只能作用大小为3的内核,其内核如下:
int main( )
{
//【0】创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y,dst;
//【1】载入原始图
Mat src = imread("1.jpg");
//【2】显示原始图
imshow("【原始图】sobel边缘检测", src);
//【3】求 X方向梯度
Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
imshow("【效果图】 X方向Sobel", abs_grad_x);
//【4】求Y方向梯度
Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
imshow("【效果图】Y方向Sobel", abs_grad_y);
//【5】合并梯度(近似)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
imshow("【效果图】整体方向Sobel", dst);
waitKey(0);
return 0;
}
3.Laplace算子
Laplace算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度(grad)的散度(div)。由于使用了图像梯度,所以内部的代码调用了Sobel。若让原图减去Laplace可以增强对比度,其定义为:
void Laplacian(InputArray src,
OutputArray dst,
int ddepth,
int ksize=1,
double scale=1,
double delta=0,
intborderType=BORDER_DEFAULT );
参数1:输入图像。
参数2:输出图像。
参数3:图像深度。
参数4:核大小,大小为正奇数,默认值为1。
参数5:计算可选缩放因子,默认值为1。
参数6:delta,默认为0。
参数7:边界类型。
int main( )
{
//【0】变量的定义
Mat src,src_gray,dst, abs_dst;
//【1】载入原始图
src = imread("1.jpg");
//【2】显示原始图
imshow("【原始图】图像Laplace变换", src);
//【3】使用高斯滤波消除噪声
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
//【4】转换为灰度图
cvtColor( src, src_gray, CV_RGB2GRAY );
//【5】使用Laplace函数
Laplacian( src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT );
//【6】计算绝对值,并将结果转换成8位
convertScaleAbs( dst, abs_dst );
//【7】显示效果图
imshow( "【效果图】图像Laplace变换", abs_dst );
waitKey(0);
return 0;
}
4.Scharr滤波器
void Scharr(
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
double scale=1,
double delta=0,
intborderType=BORDER_DEFAULT )
参数1:输入图像。
参数2:输出图像。
参数3:图像深度。
参数4:x方向的差分阶数。
参数5:y方向的差分阶数。
参数6:缩放因子。
参数7:delta。
参数8:边界类型。
int main( )
{
//【0】创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y,dst;
//【1】载入原始图
Mat src = imread("1.jpg");
//【2】显示原始图
imshow("【原始图】Scharr滤波器", src);
//【3】求 X方向梯度
Scharr( src, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
imshow("【效果图】 X方向Scharr", abs_grad_x);
//【4】求Y方向梯度
Scharr( src, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
imshow("【效果图】Y方向Scharr", abs_grad_y);
//【5】合并梯度(近似)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
//【6】显示效果图
imshow("【效果图】合并梯度后Scharr", dst);
waitKey(0);
return 0;
}