3.8 Sobel导数- Sobel Derivatives
对图像中的像素值随着位置的变化求导,导数大(梯度值变化大)的地方可能是边缘线。因此求图像中边缘线的方法之一是可以定位梯度值大于邻域(或者大于某一阈值)的像素。
Sobel算子:Sobel算子是一个离散微分算子(discrete differentiation operator)。用于计算图像中描述灰度变化的函数的近似梯度。Sobel算子结合了高斯平滑和微分求导。
利用Sobel算子计算梯度变化。设原图像为 I 。
-1 0 +1-1 -2 -1
举例:检测水平变化: Gx = [-2 0 +2 ] * I检测垂直变化:Gy =
[ 0 0 0
] * I
-1 0 +1+1 +2 +1
然后求出近似梯度 G = sqrt(Gx * Gx + Gy * Gy),或者 G = |Gx| + |Gy| 。
代码:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/** @function main */
int main( int argc, char** argv )
{
Mat src, src_gray;
Mat grad;
char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
int c;
/// 读入图像
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// 先对图像进行高斯平滑降噪,核大小为3
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
/// 转换为灰度图
cvtColor( src, src_gray, CV_RGB2GRAY );
/// 创建显示窗口
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// 创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
/// 求 X方向梯度
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT ); // 注1
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );// 尺度化,求绝对值,图像矩阵数据转换成 CV_8U 数据类型
/// 求Y方向梯度
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT ); // 注2
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );// 尺度化,求绝对值,图像矩阵数据转换成 CV_8U 数据类型
/// 合并梯度(近似) 2.4节 图像混合
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
imshow( window_name, grad );
waitKey(0);
return 0;
}
Sobel函数中,grad_x,grad_y都是输出图像,ddepth是输出图像的深度,设定为CV_16S防止外溢。
x_order,y_order分别是x,y方向上求导的阶数,在代码中对应1,0(X方向求导)和0,1(Y方向求导)。
ksize是索伯核的大小,必须是1,3,5,7中的一个,代码中为3。
scale,delta,BORDER_DEFAULT都使用默认值。
注1和注2: 当核的大小为3时,Sobel算子可能产生比较明显的误差。OpenCV提供了Scharr函数,该函数仅作用于大小为3的核,运算速度同 Sobel函数,结果更加精确。
3.9 Laplace 算子 - Laplace Operator
2阶导数可以用来检测边缘点(边缘点二阶导数为0),Laplacian 函数实现Laplacian算子,由于使用了图像梯度,内部调用了Sobel。
边缘检测的过程同上一节,加载图像 -> 高斯平滑降噪 -> 转换为灰度图 -> 对灰度图使用Laplacian算子 -> 将输出图像的深度转换为 CV_8U -> 显示结果。
其中 Laplacian 函数的用法:
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
src_gray:输入图像。
dst :输出图像。
ddepth:输出图像的深度。
kernel_size:内部调用的Sobel算子的核大小。
scale,delta,BORDER_DEFAULT:使用默认值。
3.10 Canny 边缘检测 - Canny Edge Detector
最优边缘检测的三个主要评价标准:
1.
低错误率:标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
2.
高定位性:标识出的边缘要与图像中的实际边缘尽可能接近。
3.
最小响应:图像中的边缘只能标识一次。
Canny算子被很多人认为是边缘检测的 最优算子。
步骤:
1. 消除噪声:高斯平滑。
2. 计算梯度幅度和方向:类似Sobel算子。
3. 非极大值抑制:保留候选边缘,排除非边缘像素。
4. 滞后阈值(Hysteresis):Canny算子使用两个阈值(低阈值和高阈值)。
如果某一像素梯度大于高阈值,该像素被保留为边缘像素。
如果某一像素梯度小于低阈值,该像素被排除。
如果某一像素梯度在两个阈值之间,只有它和大于高阈值的边缘像素相连时才被保留为边缘像素。
Canny算子推荐 高阈值:低阈值 的比例为 2:1和3:1之间。
Canny( image, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
image:输入图像。
detected_edges:输出边缘图像。
lowThreshold:低阈值。
lowThreshold*ratio:高阈值。
kernel_size:核大小。