OpenCV2学习笔记(七):使用Canny算子检测轮廓

在:http://blog.csdn.net/liyuefeilong/article/details/43927909 中,主要讨论了使用sobel算子和拉普拉斯变换进行边缘检测。其中主要使用了了对梯度大小进行阈值化以得到二值的边缘图像的方法。在一幅图像中,边缘往往包含着重要的视觉信息,因为它们描绘出图像元素的轮廓。然而,仅仅使用简单的二值边缘图像有两大缺陷:

  1. 使用这种方法检测得到的边缘过粗,这意味着难以实现物体的精确定位。
  2. 难以找到这样的阀值,既能足够低检测到所有重要的边缘,同时也不至于包含过多次要的边缘。

这两个问题正是此节所使用的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 算法包含许多可以调整的参数,它们将影响到算法的计算的时间与实效。

  1. 高斯滤波器的大小:第一步所用的平滑滤波器将会直接影响 Canny 算法的结果。较小的滤波器产生的模糊效果也较少,这样就可以检测较小、变化明显的细线。较大的滤波器产生的模糊效果也较多,将较大的一块图像区域涂成一个特定点的颜色值。这样带来的结果就是对于检测较大、平滑的边缘更加有用,例如彩虹的边缘。
  2. 阈值:使用两个阈值比使用一个阈值更加灵活,但是它还是有阈值存在的共性问题。设置的阈值过高,可能会漏掉重要信息;阈值过低,将会把枝节信息看得很重要。很难给出一个适用于所有图像的通用阈值。目前还没有一个经过验证的实现方法。
时间: 2024-11-13 11:07:11

OpenCV2学习笔记(七):使用Canny算子检测轮廓的相关文章

OpenCV2学习笔记(六):检测图像颜色小程序

设计一个界面,用来检测一幅图像的颜色分布,开发平台为Qt5.3.2+OpenCV2.4.9. 该程序的主要步骤如下: 1. 载入图像,选定一种颜色: 2. 设定阈值,在该值范围内判定像素属于预设的颜色: 3. 在界面的Label中输出结果. 首先,新建一个Qt Widgets Application,其中基类选择为QWidget,在创建完项目后,添加一个检测图像颜色的类ColorDetector.并在在Qt项目的.pro文件中添加: INCLUDEPATH+=C:\OpenCV\install\

OpenCV2学习笔记(九):视频流读取与处理

由于项目需要,计划实现九路视频拼接,因此必须熟悉OpenCV对视频序列的处理.视频信号处理是图像处理的一个延伸,所谓的视频序列是由按一定顺序进行排放的图像组成,即帧(Frame).在这里,主要记录下如何使用Qt+OpenCV读取视频中的每一帧,之后,在这基础上将一些图像处理的算法运用到每一帧上(如使用Canny算子检测视频中的边缘). 一. 读取视频序列 OpenCV提供了一个简便易用的框架以提取视频文件和USB摄像头中的图像帧,如果只是单单想读取某个视频,你只需要创建一个cv::VideoCa

第十七篇:博采众长--初探WDDM驱动学习笔记(七)

基于WDDM驱动的DirectX视频加速重定向框架设计与实现 现在的研究生的论文, 真正质量高的, 少之又少, 开题开得特别大, 动不动就要搞个大课题, 从绪论开始到真正自己所做的内容之间, 是东拼西凑地抄概念, 抄公式, 达到字数篇幅的要求, 而自己正真做了什么, 有哪些实际感受, 做出的内容, 相比前面的东拼西凑就几点内容, 之后就草草结束, 步入感谢的段落. 原因不光只有学生自己, 所谓的读研, 如果没有一个环境, 学生有再大的愿望, 再强的毅力, 到头来也只是空无奈. 有些导师要写书,

马哥学习笔记七——LAMP编译安装之MYSQL

1.准备数据存放的文件系统 新建一个逻辑卷,并将其挂载至特定目录即可.这里不再给出过程. 这里假设其逻辑卷的挂载目录为/mydata,而后需要创建/mydata/data目录做为mysql数据的存放目录. 2.新建用户以安全方式运行进程: # groupadd -r mysql # useradd -g mysql -r -s /sbin/nologin -M -d /mydata/data mysql # chown -R mysql:mysql /mydata/data 3.安装并初始化my

Lua学习笔记(七):迭代器与泛型for

1.迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量.每次闭包的成功调用后这些外部局部变量都保存他们的值(状态).当然如果要创建一个闭包必须要创建其外部局部变量.所以一个典型的闭包的结构包含

python学习笔记七:条件&循环语句

1.print/import更多信息 print打印多个表达式,使用逗号隔开 >>> print 'Age:',42 Age: 42   #注意个结果之间有一个空格符 import:从模块导入函数 import 模块 from 模块 import 函数 from 模块 import * 如果两个模块都有open函数的时候, 1)使用下面方法使用: module1.open()... module2.open()... 2)语句末尾增加as子句 >>> import ma

swift学习笔记(七)自动引用计数

与Object-c一样,swift使用自动引用计数来跟踪并管理应用使用的内存.当实例不再被使用时,及retainCount=0时,会自动释放是理所占用的内存空间. 注:引用计数仅适用于类的实例,因为struct和enumeration属于值类型,也就不牵涉引用,所以其存储和管理方式并不是引用计数. 当一个实例被初始化时,系统会自动分配一定的内存空间,用于管理属性和方法.当实例对象不再被使用时,其内存空间被收回. swift中的引用类型分为三种,即Strong强引用,weak弱引用和无主引用unw

Swift学习笔记七:闭包

闭包可以 捕获 和存储其所在上下文中任意常量和变量的引用. Swift 会为您管理在 捕获 过程中涉及到的内存操作. 在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: 1. 全局函数是一个有名字但不会捕获任何值的闭包 2. 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 3. 闭包表达式是一个可以捕获其上下文中变量或常量值的没有名字的闭包 一.闭包表达式 闭包函数类似于Objective-C中的block.下面我们用事实说话: let counts =

Linux System Programming 学习笔记(七) 线程

1. Threading is the creation and management of multiple units of execution within a single process 二进制文件是驻留在存储介质上,已被编译成操作系统可以使用,准备执行但没有正运行的休眠程序 进程是操作系统对 正在执行中的二进制文件的抽象:已加载的二进制.虚拟内存.内核资源 线程是进程内的执行单元 processes are running binaries, threads are the smal