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

news2024/11/15 8:45:04

文章目录

    • 基本原理
      • 关键函数和参数
      • 注意事项
    • 示例代码
      • 示例效果
      • 代码详解
      • 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函数适用于简单的轮廓检测任务,尤其是当不需要关心轮廓之间的层次关系时。例如,在一些形状分析或对象检测的应用中,只需要获取所有的轮廓而不关心它们的嵌套关系,此时可以使用这个简化的函数原型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1966580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科研经历——8/1——第一次审稿教程

文章目录 引言正文审稿通知审核论文界面Add reviewRequest view 评审结果 总结 引言 头一次被ICONIP选做审稿人&#xff0c;我还是听懵的&#xff0c;毕竟我也是主要的提交者之一&#xff0c;居然让我审人家的的文章&#xff0c;还是挺懵的&#xff01;不过&#xff0c;还是很…

【吊打面试官系列-Dubbo面试题】Dubbo 和 Dubbox 之间的区别?

大家好&#xff0c;我是锋哥。今天分享关于 【Dubbo 和 Dubbox 之间的区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Dubbo 和 Dubbox 之间的区别&#xff1f; Dubbo 和 Dubbox 是两个在 Java 开发领域中使用的 RPC 框架。它们之间的关系比较复杂&#x…

HT for Web 轻松构建组态拓扑结构

在现代的数据可视化和网络管理中&#xff0c;拓扑图是一种非常重要的工具。它可以直观地展示节点(Node)和节点之间的关系(Edge)。无论是在 2D 还是 3D 环境中&#xff0c;拓扑图都可以帮助我们更好地理解和管理复杂的系统。 然而&#xff0c;由于这些拓扑图通常极为复杂&#…

JAVA(多线程)day 7.31

ok了家人们今天学习多线程&#xff0c; 一.多线程相关概念 1.1 并行与并发 并行&#xff1a;在同一时刻&#xff0c;有多个任务在多个 CPU 上同时执行。 并发&#xff1a;在同一时刻&#xff0c;有多个任务在单个 CPU 上交替执行。 1.2 多线程 cpu同时执行多个程序。 好处…

Mix、Lab是一种生活状态#Mixlab夏日T恤

shadow&#xff1a;这款怎么样&#xff1f; 混合实验家 千*然 15:53&#xff1a; " 夸夸&#xff0c;帅爆 logo很有细胞活力啊&#xff01; 再开个白款&#xff0c;夏天凉快&#xff01;大T&#xff0c;冲&#xff01; ” shadow: 往常都是孵化社区成员的项目&#xff0c…

非线性面板数据实证模型及 Stata 具体操作步骤

目录 一、引言 二、文献综述 三、理论原理 四、实证模型 五、稳健性检验 六、程序代码及解释 一、引言 在当今的经济和社会研究中&#xff0c;非线性面板数据模型的应用日益广泛。这类模型能够更好地捕捉数据中的复杂关系&#xff0c;为研究者提供更深入和准确的分析结果。…

json-server(快速搭建本地 RESTful API 的工具)

json-server 是什么? Json-server 是一个零代码快速搭建本地 RESTful API 的工具。它使用 JSON 文件作为数据源,并提供了一组简单的路由和端点,可以模拟后端服务器的行为。 github地址:GitHub - typicode/json-server: Get a full fake REST API with zero coding in less…

VS code-解决云服务器重装镜像后vs code无法连接的问题

问题描述&#xff1a;从centos换到ubantu后&#xff0c;xshell能直接连接上&#xff08;没有更改ssh配置信息&#xff09;&#xff0c;但是vscode连不上&#xff08;配置文件因为端口号和ip是一样的&#xff0c;也没法改&#xff09; 解决办法&#xff1a; 找到vs code config…

从DevOps到DevSecOps是怎样之中转变?

DevSecOps是DevOps实践的自然演进&#xff0c;其重点是将安全集成到软件开发和部署流程中。在DevOps和DevSecOps发展之前&#xff0c;企业通常在在软件部署前进行集中的安全测试&#xff0c;导致安全介入严重滞后&#xff0c;漏洞分风险无法及时修复&#xff0c;影响上线交付。…

MGTR-250M 以电折水设备-助力取水计量监测体系建设

一体式以电折水智能终端通过高度集成化设计&#xff0c;巧妙融合了空气开关、开关电源、隔离变压器、接触器、智能电表、RTU、4G通信模块、定位模块等八大核心功能&#xff0c;不仅展现了经济高效和智能运维的双重优势&#xff0c;更以其超强的安全防护能力确保了使用的高度安全…

CV相关知识

在计算机科学和人工智能领域&#xff0c;"CV" 通常指的是 "Computer Vision"&#xff08;计算机视觉&#xff09;。计算机视觉是研究如何使计算机能够从图像或视频中获取、处理和理解视觉信息的科学和技术。计算机视觉的目标是使计算机能够自动执行人类视觉…

干净清爽的网页给用户浏览体验的重要性!此刻便展现了出来

干净清爽的网页给用户浏览体验的重要性 艾斯视觉作为ui设计和前端开发从业者&#xff0c;其观点始终认为&#xff1a;网页已成为信息传播和交流的重要平台。一个干净清爽的网页设计不仅能够吸引用户的眼球&#xff0c;更能提升用户的浏览体验&#xff0c;从而在激烈的网络竞争…

完整模型训练套路 试写 以CIFAR10分类数据集为例

思路步骤&#xff1a; 第一步准备数据集&#xff1a;&#xff08;训练集&#xff0c;测试集&#xff09; import torchvisionTrain_dataset torchvision.datasets.CIFAR10("./data",True,transformtorchvision.transforms.ToTensor()) Test_Dataset torchvision…

小程序记账系统2024

小程序记账系统2024,编号weixin002 下载在最后 技术栈: 前台:js 后台:java 展示: 下载地址: CSDN现在上传有问题,有兴趣的朋友先收藏.正常了贴上下载地址 备注:

十二星座男、被戴绿帽后有啥反应 。

白羊座——火爆型 金牛座——沉默型 双子座——智慧型 巨蟹座——自毁型 狮子座——暴力型 处女座——喊包型 天秤座——潇洒型 天蝎座——有仇必报型 射手座——气过就算型 摩羯座——冷傲型 双鱼座——大方伟大型 水瓶座——飘忽型

Shell脚本的进程管理

进程管理是系统管理的重要方面&#xff0c;通过对进程的监控、启动、停止和重启&#xff0c;可以保证系统的稳定运行。Shell脚本是一种强大的工具&#xff0c;可以对进程进行自动化管理&#xff0c;提高效率和准确性。 参考&#xff1a;shell脚本进程管理 - CSDN文库 shell脚本…

JavaScript(四)——JavaScript 语法

目录 JavaScript 语法 JavaScript 字面量 JavaScript 变量 JavaScript 操作符 JavaScript 语句 JavaScript 关键字 JavaScript 注释 JavaScript 数据类型 JavaScript 函数 JavaScript 字母大小写 JavaScript 字符集 驼峰命名法 小驼峰命名法 大驼峰命名法&#xf…

DrissionPage,一个超实用的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超实用的 Python 库 - DrissionPage。 Github地址&#xff1a;https://github.com/g1879/DrissionPage 在网页数据抓取和自动化测试中&#xff0c;Selenium 和 Requests 是…

LeetCode-35 - 在排序数组中查找元素的第一个和最后一个位置

力扣35题 题目描述&#xff1a;在排序数组中查找元素的第一个和最后一个位置 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必…

N-way K-shot Few shot learning

首先需要明确的是少样本领域的数据划分和大规模监督学习方法的数据划分不一样。在大规模监督学习方法中&#xff0c;训练集和测试集是混合后按比例随机切分&#xff0c;训练集和测试集的数据分布一致。以分类问题为例&#xff0c;切分后训练集中的类别和测试集中的类别相同&…