OpenCV笔记(十四)——使用Sobel算子对图像进行微分运算

1. 什么是Sobel算子

2. 为什么要对图像做微分

3. 如何对图像做微分

 

当我们要对图像进行边缘检测的时候,我们注意到,在边缘处像素的强度的变化率是很大的。而微分恰好是表示这种变化率的很好的方式。

在对图像做微分的时候,我们也是对像素做卷积运算,而这里我们使用的kernel,就是Sobel算子。

在使用Sobel算子做微分的时候,我们计算行方向上的变化

G_{x} = \begin{bmatrix}
-1 & 0 & +1  \\
-2 & 0 & +2  \\
-1 & 0 & +1
\end{bmatrix} * I

和列方向上的变化,

G_{y} = \begin{bmatrix}
-1 & -2 & -1  \\
0 & 0 & 0  \\
+1 & +2 & +1
\end{bmatrix} * I

再对这两个变化求平方和的开方,

G = \sqrt{ G_{x}^{2} + G_{y}^{2} }

或者绝对值相加

G = |G_{x}| + |G_{y}|

就得到一幅表现图像边缘的图像。当然这样的计算是结果是近似的而非精确的。

OpenCV提供了一种更快速而且更近似于精确值的方法,就是使用Scharr算子来代替Sobel算子,

G_{x} = \begin{bmatrix}
-3 & 0 & +3  \\
-10 & 0 & +10  \\
-3 & 0 & +3
\end{bmatrix}

G_{y} = \begin{bmatrix}
-3 & -10 & -3  \\
0 & 0 & 0  \\
+3 & +10 & +3
\end{bmatrix}

另外,Sobel算子一般与高斯平滑结合起来使用,所以我们得到的结果也稍微有些抵抗噪声的效果。

 

OpenCV给出的实例代码如下:

 1 int main( int, char** argv )
 2 {
 3 
 4   Mat src, src_gray;
 5   Mat grad;
 6   const char* window_name = "Sobel Demo - Simple Edge Detector";
 7   int scale = 1;
 8   int delta = 0;
 9   int ddepth = CV_16S;
10 
11   /// Load an image
12   src = imread( argv[1] );
13 
14   if( !src.data )
15     { return -1; }
16 
17   GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
18 
19   /// Convert it to gray
20   cvtColor( src, src_gray, COLOR_RGB2GRAY );
21 
22   /// Create window
23   namedWindow( window_name, WINDOW_AUTOSIZE );
24 
25   /// Generate grad_x and grad_y
26   Mat grad_x, grad_y;
27   Mat abs_grad_x, abs_grad_y;
28 
29   /// Gradient X
30   //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
31   Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
32   convertScaleAbs( grad_x, abs_grad_x );
33 
34   /// Gradient Y
35   //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
36   Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
37   convertScaleAbs( grad_y, abs_grad_y );
38 
39   /// Total Gradient (approximate)
40   addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
41 
42   imshow( window_name, grad );
43 
44   waitKey(0);
45 
46   return 0;
47 }

在第9行我们令ddepth为CV_16S,是因为我们在计算微分的过程中,值会超过0~255这一范围。

在第32行和第37行,我们就把16位的mat转换为8位的了。注意,这种转换不是简单地截取低8位的地址,而是取所能表示的最大值。如1024,则被转换为255,如-128,则被转换为0。而0到255之间的不变。

 

posted @ 2014-12-03 21:51  nipan  阅读(3845)  评论(0编辑  收藏  举报