首先我们要明确几点基本概念:
1、检测边缘实际上就是求梯度
2、求梯度会放大我们的噪声
3、梯度的方向与我们的边缘的方向垂直
如果对1不清楚的话建议看看这篇博客:
如果对2不清楚的话建议看看我的这篇博客来看看仿真结果:
如果对3不清楚的话可以理解下下面这张图:
左下为全黑像素,右上为全白像素,那么对角线就是边界!我们的梯度方向显然是从黑到白即45°即▽F方向,而我们的边缘方向为-45°阶梯方向,显然垂直!
浅析Sobel算子:
首先这是一个检测垂直边缘的Sobel算子(如果你不清楚的话我们会在后面进行代码验证):
这个矩阵可以看成这两个矩阵相乘:
而前面一个向量是垂直方向的一阶导(所以是检测垂直边缘), 后面一个向量则是进行平滑(为了平滑求导放大的噪声)!
下面我们看一下代码实验与结果:
首先我们有一张拥有明显边缘的图片doorbell.jpg:
- clc;clear;
- im = imread('doorbell1.jpg');
- im = rgb2gray(im);
-
- hx = [-1 -2 -1; 0 0 0;1 2 1]; %检测水平边缘的sobel滤波器
- hy = hx'; %检测垂直边缘的sobel滤波器
-
- gx = filter2(hx, im);
- gy = filter2(hy, im);
- figure(1);
- imshow(abs(gy),[]);
- colormap jet;
- colorbar;
-
让我们看看结果:
红色的值比较高,代表很强的垂直边缘,蓝色的值比较低代表很弱的垂直边缘。
从图中放大可以看到,水平部分比较红,垂直部分比较蓝:
这验证了我们的理论是正确的!
让我们将imshow(abs(gx),[])中的gx改为gy查看结果:
放大可以看到水平部分值比较高!
再次验证了我们的理论!
上面看的都是我们的水平梯度和垂直梯度,下面让我们看下梯度的赋值和角度!
在上述代码后面加上
- magnitude = sqrt(gx.^2 + gy.^2);
- angel = atan(gy./gx)*180/pi; %转换到角度
- figure(2);
- imshow(magnitude,[]);
- colormap jet;
- colorbar;
- figure(3);
- imshow(angel,[]);
- colormap jet;
- colorbar;
幅值上不难理解,基本上有边缘的地方值会比较大!
角度上也很直观,右边的的颜色柱标明了角度!
有了幅值和角度,我们就可以自己筛选任意自己想要的边缘!
比如我们想把所有的这些40°左右的边缘检测出来:
因为通过观察上面的幅度图和角度图可以发现这些部分的幅度大约在100,角度大约为40°,所以我们在上面代码的基础上添加下面这一段代码,就可以实现!
- e = ((abs(magnitude - 200) < 30) & (abs(angel - 40)<10));
- figure(4);
- imshow(e,[]);
- colormap jet;
- colorbar;
结果:
可以发现检测效果还是不错的!