理论
- 最重要的卷积之一是计算图像中的导数(或它们的近似值)。
- 我们想要检测图像中存在的边缘。 例如:
- 为了更加图形化,让我们假设我们有一维图像。 边缘由下图中的“跳跃”强度显示:
- 如果我们采用一阶导数(实际上,这里显示为最大值),则可以更容易地看到边缘“跳跃”.
- 因此,根据上面的解释,我们可以推断出可以通过定位梯度高于其邻居的像素位置(或概括,高于阈值)来执行检测图像中的边缘的方法。
Sobel运算符
- Sobel算子是一个离散微分算子。 它计算图像强度函数的梯度的近似值。
- Sobel算子结合了高斯平滑和差分。
公式
- 我们计算两个导数:
- 水平变化:这是通过将I与奇数大小的内核Gx进行卷积来计算的。 例如,对于内核大小为3,Gx将计算为:
- 垂直变化:这是通过将I与具有奇数大小的内核Gy进行卷积来计算的。 例如,对于内核大小为3,Gy将被计算为:
- 在图像的每个点,我们通过组合上面的两个结果来计算该点的梯度的近似值:
- 虽然有时会使用以下更简单的公式:
- 当内核的大小为3时,上面显示的Sobel内核可能会产生明显的不准确性(毕竟,Sobel只是导数的近似值)。 OpenCV通过使用cv :: Scharr函数解决了大小为3的内核的这种不准确性。 这比标准的Sobel功能更快但更准确。 它实现了以下内核:
代码
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
int main( int, char** argv )
{
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( argv[1] );
if( src.empty() )
{ return -1; }
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
cvtColor( src, src_gray, COLOR_RGB2GRAY );
namedWindow( window_name, WINDOW_AUTOSIZE );
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
//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 );
//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 );
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
imshow( window_name, grad );
waitKey(0);
return 0;
}
解释
该函数采用以下参数:
(1)src_gray:输入图像。 这是CV_8U
(2)grad_x / grad_y :输出图像。
(3)ddepth:输出图像的深度。 我们将其设置为CV_16S以避免溢出。
(4) x_order:x方向导数的阶数。
(5) y_order:y方向导数的阶数。
(6) scale,delta和BORDER_DEFAULT:我们使用默认值。
请注意,要计算x方向的渐变,我们使用:xorder = 1和yorder = 0。 我们类似地为y方向做。
- 我们声明我们将要使用的变量:
- 我们加载源图像src:
- 我们将cv :: GaussianBlur应用于我们的图像以减少噪音(内核大小= 3)
- 现在我们将滤波后的图像转换为灰度图像:
- 我们计算x和y方向的导数。 为此,我们使用函数cv :: Sobel,如下所示:
- 我们将部分结果转换回CV_8U:
- 最后,我们尝试通过添加两个方向渐变来近似渐变
- 最后,我们展示了我们的结果: