在:http://blog.csdn.net/liyuefeilong/article/details/43927909 中,主要讨论了使用sobel算子和拉普拉斯变换进行边缘检测。其中主要使用了了对梯度大小进行阈值化以得到二值的边缘图像的方法。在一幅图像中,边缘往往包含着重要的视觉信息,因为它们描绘出图像元素的轮廓。然而,仅仅使用简单的二值边缘图像有两大缺陷:
- 使用这种方法检测得到的边缘过粗,这意味着难以实现物体的精确定位。
- 难以找到这样的阀值,既能足够低检测到所有重要的边缘,同时也不至于包含过多次要的边缘。
这两个问题正是此节所使用的Canny算法所要尝试解决的。
Canny算子通常基于sobel算子(尽管也可以使用其他的梯度算子),其核心思想是使用两个不同的阈值以确定哪些点属于轮廓,这样根据两个阈值分别进行划分,得到两幅边缘图。之后,Canny算法组合两幅边缘图以生成一副“最优”的轮廓图。如果存在连续的边缘点,则将低阀值图像中的边缘点与高阀值图像中的边缘相连接,那么就保留低阀值图像中的边缘点。这种使用双阀值以得到二值图像的策略被称为磁滞阀值化。
Canny算法对于两个阈值的选择有一定的要求。对于较低那个阈值,应该包括所有被认为是属于明显图像轮廓的边缘像素。而较高的阈值的角色应该是定义属于所有重要轮廓的边缘,它应该排除所有异常值。
在OpenCV中,实现Canny算法的函数是cv::Canny,该函数的调用方法如下:
cv::contours;
cv::Canny(image, // 输入的灰度图像
contours, // 输出轮廓
125, // 低阈值
200); // 高阈值
主要代码如下,直接在main函数中添加:
#include <QCoreApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cv::Mat image = cv::imread("c:/peng.jpg", 0);
if(!image.data)
{
qDebug() << "No input image";
}
cv::Mat result;
cv::Canny(image, result, 150, 220);
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
cv::namedWindow("Canny Result");
cv::imshow("Canny Result", result);
return a.exec();
}
效果:
以下是Sobel算子的输出效果:
对比Sobel算子,Canny算子能得到较薄的边缘,这是因为Canny算法采用了额外的策略来提升图像的质量。在使用磁滞阈值化之前,所有在梯度大小并非最大值的边缘点都被移除。这样一来梯度的朝向总是与边缘垂直,因此该方向的局部梯度最大值对应的是轮廓强度最大的点。Canny 算法适用于不同的场合。它的参数允许根据不同实现的特定要求进行调整以识别不同的边缘特性。
此外,Canny 算法包含许多可以调整的参数,它们将影响到算法的计算的时间与实效。
- 高斯滤波器的大小:第一步所用的平滑滤波器将会直接影响 Canny 算法的结果。较小的滤波器产生的模糊效果也较少,这样就可以检测较小、变化明显的细线。较大的滤波器产生的模糊效果也较多,将较大的一块图像区域涂成一个特定点的颜色值。这样带来的结果就是对于检测较大、平滑的边缘更加有用,例如彩虹的边缘。
- 阈值:使用两个阈值比使用一个阈值更加灵活,但是它还是有阈值存在的共性问题。设置的阈值过高,可能会漏掉重要信息;阈值过低,将会把枝节信息看得很重要。很难给出一个适用于所有图像的通用阈值。目前还没有一个经过验证的实现方法。