边缘检测

计算机视觉中的边缘检测

2015-04-14  本文已影响857人  FindWG

边缘检测是计算机视觉中最重要的概念之一。这是一个很直观的概念,在一个图像上运行图像检测应该只输出边缘,与素描比较相似。我的目
标不仅是清晰地解释边缘检测是怎样工作的,同时也提供一个新而又容易的方法只需要最小工作来明显地提高边缘检测。

通过获得这些边缘,许多计算机算法才得以有可能实现,因为在一个场景中边缘包含着绝大部分(至少很多)的信息。

举个例子,我们都记得 Windows XP 的那个绿色小山坡和蓝色天空的背景。



当我们的大脑试图去理解这个场景时,我们知道这是草地,看起来很统一。然后,我们看到了飘着些许白云的天空。这些对象的每一个都是分离的,而且它们之间有一个边缘。这就是场景中为什么绝大部分的信息存在与边缘中。

这也是为什么边缘检测是计算机视觉中的重要概念。通过把图像减少到只剩下边缘,使得对于许多算法更容易识别、学习、或者处理场景。

边缘检测:滤波

边缘检测的大多数是基于滤波的。通常来说,滤波是一个消除的动作。比如:过滤水,是消除寄生虫。相似地,当我们尝试找到图像边缘时,我是在尝试消除掉除图像边缘之外的东西。

消除那些不是有用的边缘的图像部分,而留下合适的边缘也是困难所在。我怎么知道这是不是有用的边缘,比如,我对 Windows XP 背景运行 Canny edge dectector 程序,效果如下。

你能看到那些小草的细小刀刃似的边缘,这很让人讨厌,而且没有真正提供有用的信息。甚至那些云朵也不是非常清晰。现在,你能在大多数的 Canny edge dectector 上设置一些边界,这些边界设置了阈值(或者 非极大值抑制),所有的边缘必须满足这个阈值才能划分为“重要的”边缘。与其在 Canny edge 的阈值上区分,不如我们更广泛地来谈谈,并且搭建几个滤波器。

边缘检测:高斯滤波器

高斯滤波器是边缘检测最基础的滤波器之一,虽然还有其他的,但是高斯滤波器会贯穿这篇文章。高斯滤波器,正如其名,是一个基于高斯分布的滤波器。

它看起来像一个抛物线(除了在二维的情况)。通过矩阵乘法,高斯滤波器能被应用到每一个像素上。它实现的是混合效果,让最中心的像素尽量小基于它相邻的像素。举个例子,如对我的猫的图像运行一个均匀分布的高斯滤波器,我能够得到下面的图像:

5.jpg

你可以看到图像变得模糊了,高斯滤波器就是获取所有像素,让像素值的成分是与相邻像素有关。

为了让高斯滤波器在边缘检测中有效,我们可以使用从 x 和 y 方面求导出来的高斯滤波器。这也许听起来有点反直觉,或者不知所谓,但是如果我们看到这种导数高斯滤波器的图像,这种想法就很清晰了。

当你把一个高斯的 x 和 y 分量求导后,一个大的波峰 和波谷就出现了。如果你明白导数,思考下这种情况,你应该能很快想到。由于高斯函数峰上数值上的巨大变化,从而导致了高斯导数波峰和波谷的出现。

如果我们把上面的写成代码,那相当直接(至少对于 Matlab 和 Python 来说):

% Takes the derivative of a 5x5 gaussian, with a sigma    
[hx, hy] = gradient(fspecial('gaussian',[5 5],sigma));

这就是了,一行代码就能得到我们所想要的高斯,然后对x 和 y 分量求导。

边缘检测:应用滤波器

我们已经有了两个高斯滤波器,我们把他们应用到图像中。我们同样使用非极大值限制,即如果不是极大值的话,就把像素值设置为0。换种说法就是消除噪声。

应用滤波器的代码如下:

% Convert an image to double for increased precision
img = double(img);
% Find two derived gaussians with respect to x and y
[hx, hy] = gradient(fspecial('gaussian',[5 5],sigma));
% Run the filters over the image, generating a filtered image
% Leaves x edges
gx = double(imfilter(img,hx,'replicate', 'conv'));
% Leaves y edges
gy = double(imfilter(img,hy,'replicate', 'conv'));
% Take the absolute value, and combine the x and y edges
mag = sqrt((gx .* gx) + (gy .* gy));
% Use non-maxima suppression 
[mag, ] = max(mag, [], 3);

如果我们将它应用我的猫的一张图片,我我们就得到一下的图片:

有趣地是,我们同样也能这种方法应用到 RGB 图像上去,同样也会得到彩色的边缘。

普通的边缘滤波器应用到猫的RGB图像

这两张图片应该都能代表像素和它相邻之间像素的颜色的差异,只不过彩色图像有3层,而黑白图像只有1层。(层,翻译不是很好)

边缘检测:方向滤波器

为什么要限制我们自己仅仅使用绝对的 x 和 y 方向的滤波器呢?让我们也来构建一些方向滤波器。这个方法(或多或少)来源于 Freeman 和 Adelson 的《The design and use of steerable filters》论文,这个想法使我们能够把我们的高斯滤波器放置在不同的方向。

根本上,我们把我们的高斯滤波器放置到不同的方向上去,以基于与高斯相关的边缘的角度来创建不同数值。比如,如果我们把高斯函数放置到45度,应用到45度角的图像上,应该可以比在0度的高斯函数得到更高的数量级。

在这种情况下,我生成了几种不同的方向滤波器:

上行x分量,下行y分量

各种各样的高斯函数产生了相对于x 和 y 分量上的90、45、-45 和 22.5 度的滤波器。这也就产生了不同的边缘大小,尽管这些滤波器应该检测出相近的边缘。

我使用的代码与单个滤波器使用的代码几乎相同,但是我不同以往地把他们混合在一起。它看起来有点杂乱,但是我通过让我运行每个滤波器表达更明白来尽可能使它看起来更清晰。

% Create four filters
[hx, hy] = gradient(fspecial('gaussian',[5 5],sigma));
[hx1, hy1] = altOrientFilter1(hx, hy);
[hx2, hy2] = altOrientFilter2(hx, hy);
[hx3, hy3] = altOrientFilter3(hx, hy);

% Run first gaussian filter on image
gx = double(imfilter(img,hx,'replicate', 'conv'));
gy = double(imfilter(img,hy,'replicate', 'conv'));
% Run second gaussian filter on image
gx1 = double(imfilter(img,hx1,'replicate', 'conv'));
gy1 = double(imfilter(img,hy1,'replicate', 'conv'));
% Run third gaussian filter on image
gx2 = double(imfilter(img,hx2,'replicate', 'conv'));
gy2 = double(imfilter(img,hy2,'replicate', 'conv'));
% Run fourth gaussian filter on image
gx3 = double(imfilter(img,hx3,'replicate', 'conv'));
gy3 = double(imfilter(img,hy3,'replicate', 'conv'));

% Merge all filters
squareGD = (gx .* gx) + (gy .* gy);
squareGD = squareGD + (gx1 .* gx1) + (gy1 .* gy1);
squareGD = squareGD + (gx2 .* gx2) + (gy2 .* gy2);
squareGD = squareGD + (gx3 .* gx3) + (gy3 .* gy3);

% Run non-maxima supression
[mag, ] = max(sqrt(squareGD), [], 3);

如果你靠近点看的话,你能看到数量大小的不同,特别是那些皱纹。如果我们混合了所有图像,我们就能得到一张轻微较好的边缘检测。

方向滤波器和非方向滤波器之间并没有很大的不同,但是我们也应该看到多种方向的结果有些许的提高。

边缘检测:提高彩色域

过去两年,我在不同的彩色域上做了大量的测试和实验。特别地是,Lab 彩色域是另一种描述图像的方式。比如,我们知道的RGB 和灰度图像,或者你甚至可能知道YUV空间。 Lab 彩色域与之非常相似。

我对Lab 彩色感兴趣的原因是它对产生场景的边缘有着优异的能力。

Lab 彩色空间的每个字母表示:

事实上,这些颜色通道非常适合发现颜色变化梯度,正自然而然地黄色很少出现另一个黄色周围,红色和绿色也是如此。(尽管我已经彻底证明了)。Lab 彩色空间与我们人类怎样察觉颜色中的亮度有很强的相关性。与RGB相反的是,在Lab 彩色空间中亮度有它自己的分离通道,这使得它能更好地处理颜色的差异,这些差异也是亮度与颜色相关之处。

为了最小化额外的代码,我们所要做的就是把输入的图像转化为Lab彩色空间的图像。你可以做一些最优化,不过你仅仅做了这额外的一步也能明显提高适宜的边缘检测。

% Convert an image to the Lab color space
colorTransform = makecform('srgb2lab');
img = applycform(rgbImg, colorTransform);

% Make it double to improve representation
img = double(img);

% Find x and y derivative of a 9x9 gaussian
[hx, hy] = gradient(fspecial('gaussian',[9 9],sigma));

% Apply filters    
gx = double(imfilter(img,hx,'replicate'));
gy = double(imfilter(img,hy,'replicate'));

% Find absolute value
gSquared = sqrt(gx .* gx) + (gy .* gy);

% Apply non-maxima suppression (find best points for edges) 
[mag, ] = max(gSquared, [], 3);

如果我们将 Windows XP 小山坡的背景图像转换为Lab,我们将得到下面的图像:

Windows XP 背景的Lab空间图像

然后如果我们施加滤波器(非极大值抑制)将得到下面的图像,清晰的包含着草地、云、草地和天空的分界线的图象。

在Lab 空间上边缘检测

最终,如果我们运行非极大值抑制,那么我们将得到比本文开头提到的 Canny边缘检测器要好得多的边缘效果。

Lab 彩色空间边缘检测

平均来说,这种方法会比普通方法提高 10% 左右的边缘检测精度。这是对 The Berkeley Segmentation Dataset and Benchmark 运行 F-measure 测试得到的结果。

边缘检测:结语

有无数的方法去做边缘检测,这里讲述的方法绝不是最好、最容易实现、最容易解释的。

我使用这些方法解释它是因为我对它们很有兴趣。。。加上这是 UIUC的CS543课程《计算机视觉》布置的作业,所以你也在上这门课,请不要抄袭我的代码!

我已经把我们所有的实现都放到了 github 上了。包括用 C++ 的 OpenCV 实现。

然而,如果你想跟我的猫照相,这没什么问题。

建议阅读

参考文献

[1] Canny, John. “A computational approach to edge detection.” Pattern Analysis and Machine Intelligence, IEEE Transactions on 6 (1986): 679-698.

[2] Freeman, William T., and Edward H. Adelson. “The design and use of steerable filters.” IEEE Transactions on Pattern analysis and machine intelligence 13.9 (1991): 891-906.


这是本人课余时间的翻译,如有错误,还请耐心指出,谢谢!
原文链接请点击原文链接

上一篇下一篇

猜你喜欢

热点阅读