当前位置: 首页 > news >正文

OpenCV 图像处理 轮廓检测基本原理

文章目录

    • 基本原理
      • 关键函数和参数
      • 注意事项
    • 示例代码
      • 示例效果
      • 代码详解
      • findContours 函数原型
      • findContours函数变体

基本原理

轮廓发现是图像处理中的一个重要步骤,用于检测物体的边界和形状。

  1. 图像预处理
    轮廓发现通常在灰度图像上进行。因此,首先将图像转换为灰度图像。接下来,应用滤波器来减少噪声。常用的滤波器有高斯模糊(Gaussian Blur),它有助于平滑图像并减少噪声。

  2. 边缘检测
    在预处理后的图像上应用边缘检测算法。常用的边缘检测算法是Canny边缘检测器,它能有效地检测出图像中的边缘。Canny边缘检测器使用梯度的方向和幅度来找到图像中的边缘。

  3. 轮廓提取
    一旦得到二值化的边缘图像,就可以使用OpenCV的findContours函数来提取轮廓。findContours函数将图像中的每一个边缘视为一个轮廓,并返回一个轮廓列表。每个轮廓都由一系列点组成,这些点定义了轮廓的形状。

  4. 轮廓的层次结构
    findContours函数不仅可以返回轮廓,还可以返回轮廓的层次结构。这对于包含内嵌轮廓(如嵌套在其他轮廓中的孔洞)的图像非常有用。层次结构信息存储了每个轮廓的父子关系。

关键函数和参数

  • cv2.findContours(image, mode, method)

    • image: 输入的二值图像(通常是边缘检测的结果)。
    • mode: 轮廓检索模式,如cv2.RETR_EXTERNAL(只检测外轮廓)、cv2.RETR_TREE(检测所有轮廓并构建层次结构)。
    • method: 轮廓逼近方法,如cv2.CHAIN_APPROX_SIMPLE(只保存轮廓的必要点)、cv2.CHAIN_APPROX_NONE(保存所有轮廓点)。
  • cv2.drawContours(image, contours, contourIdx, color, thickness)
    用于在图像上绘制轮廓。

注意事项

  1. 图像的预处理
    轮廓发现对输入图像的质量非常敏感。良好的预处理(如去噪、对比度增强等)可以显著提高轮廓检测的效果。

  2. 边缘检测器的选择
    边缘检测器的参数(如Canny边缘检测器的阈值)需要根据图像的特征进行调整。

  3. 轮廓的近似和表示
    对于复杂的形状,可以使用多边形逼近(如Douglas-Peucker算法)来简化轮廓。

轮廓发现技术广泛应用于对象检测、形状分析、图像分割等领域。在这些应用中,轮廓的精确提取和表示对于后续处理和分析至关重要。

示例代码

在OpenCV中,使用C++进行轮廓发现通常包括以下主要步骤:读取图像、灰度化、边缘检测、轮廓发现和绘制轮廓。以下是一个基本的C++代码示例,展示如何使用OpenCV进行这些操作:

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {// 读取图像Mat src = imread("image.jpg");if (src.empty()) {cout << "无法加载图像!" << endl;return -1;}// 转换为灰度图像Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);// 应用高斯模糊以去除噪声Mat blurred;GaussianBlur(gray, blurred, Size(5, 5), 1.5);// 进行Canny边缘检测Mat edges;Canny(blurred, edges, 100, 200);// 发现轮廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);// 在原图上绘制轮廓Mat drawing = Mat::zeros(edges.size(), CV_8UC3);for (size_t i = 0; i < contours.size(); i++) {Scalar color = Scalar(255, 0, 0); // 轮廓的颜色drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0);}// 显示结果imshow("轮廓", drawing);waitKey(0);return 0;
}

示例效果

在这里插入图片描述

代码详解

  1. 读取图像

    Mat src = imread("image.jpg");
    

    使用imread函数加载图像。

  2. 灰度化

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    

    使用cvtColor函数将彩色图像转换为灰度图像。

  3. 高斯模糊

    Mat blurred;
    GaussianBlur(gray, blurred, Size(5, 5), 1.5);
    

    使用GaussianBlur函数对灰度图像进行平滑处理,以减少噪声。

  4. Canny边缘检测

    Mat edges;
    Canny(blurred, edges, 100, 200);
    

    使用Canny函数进行边缘检测。这里100200是低和高阈值,用于控制边缘的检测。

  5. 发现轮廓

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    

    使用findContours函数提取图像中的轮廓。RETR_TREE参数用于获取轮廓的层次结构,CHAIN_APPROX_SIMPLE用于压缩水平、垂直和对角直线段,只保留它们的终点。

  6. 绘制轮廓

    Mat drawing = Mat::zeros(edges.size(), CV_8UC3);
    for (size_t i = 0; i < contours.size(); i++) {Scalar color = Scalar(255, 0, 0); // 轮廓的颜色drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0);
    }
    

    使用drawContours函数在图像上绘制检测到的轮廓。

  7. 显示结果

    imshow("轮廓", drawing);
    waitKey(0);
    

    使用imshow函数显示绘制好的图像,并使用waitKey等待用户按键。

在实际应用中,可以根据具体需求调整模糊参数、Canny边缘检测的阈值,以及findContours的模式和方法参数。

findContours 函数原型

OpenCV 中的 findContours 函数用于检测图像中的轮廓。其函数原型如下:

void findContours(InputOutputArray image,OutputArrayOfArrays contours,OutputArray hierarchy,int mode,int method,Point offset = Point()
);

参数详解

  1. image: InputOutputArray

    • 输入图像,通常为二值化图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
    • 类型通常为 CV_8UC1,即单通道8位无符号整数。
  2. contours: OutputArrayOfArrays

    • 检测到的轮廓列表,每个轮廓是一个点的向量(即 std::vector<Point>)。每个点表示轮廓的一部分。
    • 具体类型为 std::vector<std::vector<Point>>
  3. hierarchy: OutputArray

    • 可选的层次结构输出向量。对于每个轮廓,hierarchy[i][0] 表示下一个轮廓的索引,hierarchy[i][1] 表示前一个轮廓的索引,hierarchy[i][2] 表示第一个子轮廓的索引,hierarchy[i][3] 表示父轮廓的索引。
    • 如果不需要层次结构,可以传递 noArray() 或一个空的 Mat
  4. mode: int

    • 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值有:
      • RETR_EXTERNAL: 只检索最外层的轮廓。
      • RETR_LIST: 检索所有轮廓,不建立层次关系。
      • RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。
      • RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
  5. method: int

    • 轮廓逼近方法,指定如何对轮廓点进行存储。可选值有:
      • CHAIN_APPROX_NONE: 存储所有的轮廓点。
      • CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。
      • CHAIN_APPROX_TC89_L1CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
  6. offset: Point (默认值为 Point())

    • 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI区域进行轮廓检测时尤其有用。

findContours函数变体

它不要求输出层次结构的层次信息。这种简化版的函数原型对于只关心检测到的轮廓而不需要它们之间的层次结构关系的情况是有用的。其具体定义如下:

CV_EXPORTS void findContours(InputArray image,OutputArrayOfArrays contours,int mode,int method,Point offset = Point()
);

参数详解

  1. image: InputArray
    • 输入图像,通常是一个二值图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
    • 类型通常为 CV_8UC1,即单通道8位无符号整数。
  2. contours: OutputArrayOfArrays
    • 检测到的轮廓列表,每个轮廓是一个点的向量(即 std::vector<Point>)。每个点表示轮廓的一部分。
    • 具体类型为 std::vector<std::vector<Point>>
  3. mode: int
    • 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值包括:
      • RETR_EXTERNAL: 只检索最外层的轮廓。
      • RETR_LIST: 检索所有轮廓,不建立层次关系。
      • RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。
      • RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
  4. method: int
    • 轮廓逼近方法,指定如何对轮廓点进行存储。可选值包括:
      • CHAIN_APPROX_NONE: 存储所有的轮廓点。
      • CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。
      • CHAIN_APPROX_TC89_L1CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
  5. offset: Point (默认值为 Point())
    • 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI(感兴趣区域)进行轮廓检测时尤其有用。

使用场景

这个版本的findContours函数适用于简单的轮廓检测任务,尤其是当不需要关心轮廓之间的层次关系时。例如,在一些形状分析或对象检测的应用中,只需要获取所有的轮廓而不关心它们的嵌套关系,此时可以使用这个简化的函数原型。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JDK 8 升级 17 及 springboot 2.x 升级 3.x 指南
  • C语言 柔性数组 详解
  • 锅总详解开源组织之ASF
  • Fiddler学习笔记
  • 基于java的人居环境整治管理系统(源码+lw+部署文档+讲解等)
  • 公布一批神马爬虫IP地址,真实采集数据
  • 防止xss(跨站脚本攻击)
  • golang中struct的tag -简记
  • 枚举知识点(完结)
  • 从零开始学习机器学习,掌握AI未来的关键!
  • 嵌入式人工智能(40-基于树莓派4B的水滴传感器和火焰传感器)
  • c语言(8.1)
  • 二进制搭建 Kubernetes v1.20(中)
  • 【优秀python案例】基于Python的豆瓣电影TOP250爬虫与可视化设计与实现
  • Flutter 初识:数据表格和卡片
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • Angular Elements 及其运作原理
  • CAP理论的例子讲解
  • Centos6.8 使用rpm安装mysql5.7
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • ES6核心特性
  • Invalidate和postInvalidate的区别
  • JAVA SE 6 GC调优笔记
  • java 多线程基础, 我觉得还是有必要看看的
  • js递归,无限分级树形折叠菜单
  • Linux Process Manage
  • Magento 1.x 中文订单打印乱码
  • python 学习笔记 - Queue Pipes,进程间通讯
  • SpriteKit 技巧之添加背景图片
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 技术胖1-4季视频复习— (看视频笔记)
  • 使用 @font-face
  • 回归生活:清理微信公众号
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​io --- 处理流的核心工具​
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • #数据结构 笔记三
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (1)虚拟机的安装与使用,linux系统安装
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (LeetCode 49)Anagrams
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (八)Spring源码解析:Spring MVC
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (七)Activiti-modeler中文支持
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (原)本想说脏话,奈何已放下
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)winform之ListView
  • (转)一些感悟
  • .net 8 发布了,试下微软最近强推的MAUI