本文是对OpenCV2.4.13文档的部分翻译,作个人学习之用,并不完整。
前两个中我们看过卷积的示例,最重要的卷积就是图像的导数积分。
比如我们想获得图像中的边缘,像素强度会出现突然的显著变化,我们就可以使用导数来表示变化。梯度的高度变化表示在图像中有很大的变化。
在一维图中,边缘就可以表示为灰度的突然变化:
边缘的“跳跃”可以用求第一导数的方式更好地看到。
所以我们可推断在图像中检测边缘可以通过定位梯度远高于邻近点的像素的位置。
Sobel算子:
一种非连续的微分算子,计算了一幅图像中灰度函数的梯度的近似值,它包含了高斯平滑和高斯微分。
公式:
1.计算两个导数:
水平变化:用奇数大小的核卷积I,如一个大小为3的核:
垂直变化:用奇数大小的核卷积I,如一个大小为3的核:
2.图像上的每一点我们都可以用上述两个结果计算该点导数的积分:
可以简化为
注意:当核的大小为3时,Sobel核可能导致明显的错误(毕竟Sobel只是导数的积分)。OpenCV用函数Scharr为大小为3的核处理了这种不准确,这与Sobel函数一样快但更精确,它实现了下面的核:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/**
* @function main
*/
int main()
{
Mat src, src_gray;
Mat grad;
const char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
/// 加载图像
src = imread("lena.jpg");// argv[1]
if( !src.data )
{ return -1; }
// 先用大小为3的核进行高斯模糊消噪
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
/// 转换成灰度图
cvtColor( src, src_gray, COLOR_RGB2GRAY );
/// 创建窗口
namedWindow( window_name, WINDOW_AUTOSIZE );
/// 生成核grad_x和grad_y
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
// 计算x和y方向上的导数,Sobel(原灰度图CV_8U,输出图像,输入图像的深度(用CV_16S防止溢出),x方向上的导数阶数,y方向上的导数阶数,scale delta BORDER_DEFAULT使用默认值)
// x方向时设x方向上为1,y方向上为0
/// 卷积X
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );//将部分结果转换为CV_8U
/// 卷积Y
// y方向时设x方向上为0,y方向上为1
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );//将部分结果转换为CV_8U
/// 总卷积(通过将两个方向的导数加在一起来求积分)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
imshow( window_name, grad );
waitKey(0);
return 0;
}
结果: