21.OpenCV获取图像轮廓信息

news2025/4/17 13:00:44

OpenCV获取图像轮廓信息

在计算机视觉领域,识别和分析图像中的对象形状是一项基本任务。OpenCV 库提供了一个强大的工具——轮廓检测(Contour Detection),它能够帮助我们精确地定位对象的边界。这篇博文将带你入门 OpenCV 的轮廓检测,理解其原理,并学会如何在 C++ 中使用它。

1.什么是轮廓?

简单来说,轮廓可以看作是一条连接所有具有相同颜色或灰度值的连续点(沿着边界)的曲线。

更准确地说,轮廓是图像中强度或颜色发生显著变化的区域的边界。在 OpenCV 中,轮廓检测通常作用于二值图像(只有黑色和白色像素的图像)。算法会寻找白色(或非零)区域的边界,并将这些边界表示为一系列点的坐标。

通过检测轮廓,我们可以实现目标分割、形状分析、物体计数、特征提取及对象检测和识别等任务。

轮廓介绍

如上图1,2,3 轮廓还有层次,有最外的轮廓1依次往里包括2,3轮廓。

2. OpenCV 轮廓检测函数

2.1 findContours 函数

findContours 是 OpenCV 中最常用的轮廓检测函数,其函数原型如下:

void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy,
                  int mode, int method, Point offset = Point());
  • image:输入的二值图像(非零值表示前景)。
  • contours:输出轮廓,存储为 std::vector<std::vector<Point>>
  • hierarchy:输出轮廓的层级信息,描述轮廓之间的嵌套关系。
  • mode:轮廓检索模式,如 RETR_EXTERNAL(只检测最外层轮廓)、RETR_LIST(检测所有轮廓但不建立层级关系)、RETR_TREE(检测所有轮廓并重构完整的层级结构)。
  • method:轮廓近似方法,如 CHAIN_APPROX_SIMPLE(压缩水平、垂直和对角冗余点)和 CHAIN_APPROX_NONE(存储所有点)。
  • offset:可选参数,用于给输出轮廓的所有点加上偏移值。

2.3 drawContours 函数

drawContours 函数用于在图像上绘制检测到的轮廓。它可以帮助直观展示轮廓检测和形状分析的结果。函数原型如下:

void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx,
                  const Scalar& color, int thickness = 1, int lineType = LINE_8,
                  InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point());

  • image:输入输出图像,即在该图像上绘制轮廓。
  • contours:轮廓集合,通常由 findContours 得到,类型为 std::vector<std::vector<Point>>
  • contourIdx:指定绘制哪一个轮廓;若设为 -1,则绘制所有轮廓。
  • color:绘制轮廓的颜色,如 Scalar(0,255,0) 表示绿色。
  • thickness:轮廓线的粗细;若为负值,则填充轮廓内部。
  • lineType:线型,默认 LINE_8
  • hierarchy:轮廓的层级信息,可选。
  • maxLevel:绘制轮廓的最大层级,默认 INT_MAX
  • offset:绘制时添加的偏移量。

drawContours 常用于可视化轮廓检测结果,结合 findContours 使用可以将检测到的目标区域在图像上直观标记出来。

2.3 轮廓检测的基本步骤

使用 OpenCV C++ 进行轮廓检测通常遵循以下步骤:

  1. 读取图像 (Read Image): 使用 cv::imread 加载你想要处理的源图像到 cv::Mat 对象。
  2. 转换为灰度图 (Convert to Grayscale): 使用 cv::cvtColor 将彩色图像转换为灰度图,以简化图像信息。
  3. 二值化 (Thresholding): 使用 cv::threshold 将灰度图像转换为二值图像。这是轮廓检测的关键步骤,因为 findContours 函数通常需要二值图像作为输入。
  4. 查找轮廓 (Find Contours): 使用 cv::findContours() 函数来检测图像中的所有轮廓。
  5. 绘制轮廓 (Draw Contours): (可选)使用 cv::drawContours() 函数将检测到的轮廓绘制在原始图像或新的画布上,以便可视化。

2.4 参考代码

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main() {
    // 读取灰度图像
    Mat src = imread("E:/image/test.png");
    if (src.empty()) {
        cerr << "图像加载失败!" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    GaussianBlur(gray, gray, Size(5, 5), 0);

    Mat binary;
    double otsu_thresh_val = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
    cout << "Otsu 自动选择的阈值为:" << otsu_thresh_val << endl;

    imshow("原始灰度图像", gray);
    imshow("otsu 二值化", binary);
	//查找轮廓
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    cout << hierarchy.size();

	//绘制所有轮廓
    Mat contourImage = src.clone();
    drawContours(contourImage, contours, -1, Scalar(0, 0, 255), 1);
  
    imshow("contour", contourImage);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

轮廓检测结果

如果只想要最外面的轮廓只需要把参数RETR_TREE改为**RETR_EXTERNAL**

3. 计算并绘制轮廓几何特征

在检测到轮廓后,我们可以计算并绘制轮廓的几何特征,例如:

  • 面积 (contourArea)
  • 周长 (arcLength)
  • 重心
  • 最小外接矩形 (boundingRect)
  • 最小外接圆 (minEnclosingCircle)

3.1 参考代码

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取图像并转换为灰度图
    Mat src = imread("E:/image/test1.png");
    if (src.empty()) {
        cerr << "图像加载失败!" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);

    // 应用阈值处理得到二值图像
    Mat binary;
    threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

    // 检测轮廓
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    // 创建用于显示轮廓的图像
    Mat contourImage = src.clone();

    for (size_t i = 0; i < contours.size(); i++) {
        // 绘制轮廓
        drawContours(contourImage, contours, static_cast<int>(i), Scalar(0, 255, 0), 2);

        // 计算轮廓面积和周长
        double area = contourArea(contours[i]);
        double perimeter = arcLength(contours[i], true);
        if (area < 500) continue;

        // 计算重心
        Moments M = moments(contours[i]);
        int cx = static_cast<int>(M.m10 / M.m00);
        int cy = static_cast<int>(M.m01 / M.m00);
        circle(contourImage, Point(cx, cy), 5, Scalar(255, 0, 0), -1);

        // 计算外接矩形
        Rect boundingBox = boundingRect(contours[i]);
        rectangle(contourImage, boundingBox, Scalar(0, 0, 255), 2);
		//最小外接矩形
        RotatedRect box2 = minAreaRect(contours[i]);
        Point2f vertices[4];
        box2.points(vertices);
        for (int j = 0; j < 4; j++) {
            line(contourImage, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 255), 1);
        }

        // 计算最小外接圆
        Point2f center;
        float radius;
        minEnclosingCircle(contours[i], center, radius);
        circle(contourImage, center, static_cast<int>(radius), Scalar(255, 255, 0), 2);

        // 在图像上标注几何特征
        putText(contourImage, "Area: " + to_string(static_cast<int>(area)),
            Point(boundingBox.x, boundingBox.y - 10), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 1);
        putText(contourImage, "Perimeter: " + to_string(static_cast<int>(perimeter)),
            Point(boundingBox.x, boundingBox.y + boundingBox.height + 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 1);
    }

    // 显示结果
    imshow("轮廓检测与几何特征", contourImage);
    waitKey(0);
    return 0;
}

代码简介

  1. 轮廓检测:使用 findContours 提取二值图像中的所有轮廓。
  2. 绘制轮廓:使用 drawContours 绘制检测到的轮廓。
  3. 计算几何特征
    • 面积和周长:使用 contourAreaarcLength 计算。
    • 重心:通过 moments 计算轮廓的中心点,并用 circle 绘制。
    • 最小外接矩形:使用 boundingRect 计算,并用 rectangle 绘制。
    • 最小外接圆:使用 minEnclosingCircle 计算,并用 circle 绘制。
  4. 标注特征信息:使用 putText 在图像上显示面积和周长。
  5. 面积筛选:代码中过滤不显示面积小于500的轮廓
    获取轮廓信息

4. 应用场景

轮廓检测技术在多个领域都有广泛应用,包括但不限于:

  • 目标分割和计数

    利用轮廓检测可以分割图像中的独立目标,并统计数量,如细胞计数、车牌识别等。

  • 形状分析

    分析轮廓的几何特征(如周长、面积、凸包、旋转角度等),用于目标识别和匹配。

  • 运动跟踪

    在视频处理中,通过检测运动目标的轮廓,进一步实现对象跟踪和行为分析。

  • OCR 前处理

    对文档图像进行轮廓检测,提取文字区域,为光学字符识别(OCR)提供准备工作。

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

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

相关文章

医学图像分割效率大幅提升!U-Net架构升级,助力精度提升5%!

在医学图像分割领域&#xff0c;U-Net模型及其变体的创新应用正在带来显著的性能提升和效率优化。最新研究显示&#xff0c;通过引入结构化状态空间模型&#xff08;SSM&#xff09;和轻量级LSTM&#xff08;xLSTM&#xff09;等技术&#xff0c;VMAXL-UNet模型在多个医学图像数…

智能设备运行监控系统

在工业 4.0 与智能制造浪潮下&#xff0c;设备运行效率与稳定性成为企业竞争力的核心要素。然而&#xff0c;传统设备管理模式面临数据采集分散、状态分析滞后、维护成本高昂等痛点。为破解这些难题&#xff0c;设备运行监控系统应运而生&#xff0c;通过融合智能传感、5G 通信…

服务器报错:xxx/libc.so.6: version `GLIBC_2.32‘ not found

/lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.32 not found (required by ./aima-sim-app-main) 解决思路 根据错误信息&#xff0c;您的应用程序 aima-sim-app-main 和 libmujoco.so.3.1.6 库依赖于较新的 GNU C Library (glibc) 版本&#xff08;如 GLIBC_2.32, GLIBC…

Flutter之页面布局一

目录&#xff1a; 1、页面布局一2、无状态组件StatelessWidget和有状态组件StatefulWidget2.1、无状态组件示例2.2、有状态组件示例2.3、在 widget 之间共享状态1、使用 widget 构造函数2、使用 InheritedWidget3、使用回调 3、布局小组件3.1、布置单个 Widget3.2、容器3.3、垂…

架构思维: 数据一致性的两种场景深度解读

文章目录 Pre案例数据一致性问题的两种场景第一种场景&#xff1a;实时数据不一致不要紧&#xff0c;保证数据最终一致性就行第二种场景&#xff1a;必须保证实时一致性 最终一致性方案实时一致性方案TCC 模式Seata 中 AT 模式的自动回滚一阶段二阶段-回滚二阶段-提交 Pre 架构…

大数据knox网关API

我们过去访问大数据组件&#xff0c;如sparkui&#xff0c;hdfs的页面&#xff0c;以及yarn上面看信息是很麻烦的一件事。要记每个端口号&#xff0c;比如50070&#xff0c;8090&#xff0c;8088&#xff0c;4007&#xff0c;如果换到另一个集群&#xff0c;不同版本&#xff0…

【Tauri2】015——前端的事件、方法和invoke函数

目录 前言 正文 准备 关键url 获取所有命令 切换主题set_theme 设置大小 获得版本version 名字name 监听窗口移动 前言 【Tauri2】005——tauri::command属性与invoke函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146581991?spm1001.2014.3001.…

密码学基础——分组密码的运行模式

前面的文章中文我们已经知道了分组密码是一种对称密钥密码体制&#xff0c;其工作原理可以概括为将明文消息分割成固定长度的分组&#xff0c;然后对每个分组分别进行加密处理。 下面介绍分组密码的运行模式 1.电码本模式&#xff08;ECB&#xff09; 2.密码分组链接模式&…

Python----计算机视觉处理(Opencv:道路检测完整版:透视变换,提取车道线,车道线拟合,车道线显示,)

Python----计算机视觉处理&#xff08;Opencv:道路检测之道路透视变换) Python----计算机视觉处理&#xff08;Opencv:道路检测之提取车道线&#xff09; Python----计算机视觉处理&#xff08;Opencv:道路检测之车道线拟合&#xff09; Python----计算机视觉处理&#xff0…

基于飞桨框架3.0本地DeepSeek-R1蒸馏版部署实战

深度学习框架与大模型技术的融合正推动人工智能应用的新一轮变革。百度飞桨&#xff08;PaddlePaddle&#xff09;作为国内首个自主研发、开源开放的深度学习平台&#xff0c;近期推出的3.0版本针对大模型时代的开发痛点进行了系统性革新。其核心创新包括“动静统一自动并行”&…

docker初始环境搭建(docker、Docker Compose、portainer)

docker、Docker Compose和portainer的安装部署、使用 docker、Docker Compose和portainer的安装部署、使用一.安装docker1.失败的做法2.首先卸载旧版本&#xff08;没安装则下一步&#xff09;3.配置下载的yum来源&#xff0c;不然yum search搜不到4.安装启动docker5.替换国内源…

开源RuoYi AI助手平台的未来趋势

近年来&#xff0c;人工智能技术的迅猛发展已经深刻地改变了我们的生活和工作方式。 无论是海外的GPT、Claude等国际知名AI助手&#xff0c;还是国内的DeepSeek、Kimi、Qwen等本土化解决方案&#xff0c;都为用户提供了前所未有的便利。然而&#xff0c;对于那些希望构建属于自…

element-ui自制树形穿梭框

1、需求 由于业务特殊需求&#xff0c;想要element穿梭框功能&#xff0c;数据是二级树形结构&#xff0c;选中左边数据穿梭到右边后&#xff0c;左边数据不变。多次选中左边相同数据进行穿梭操作&#xff0c;右边数据会多次增加相同的数据。右边数据穿梭回左边时&#xff0c;…

Linux系统学习Day04 阻塞特性,文件状态及文件夹查询

知识点4【文件的阻塞特性】 文件描述符 默认为 阻塞 的 比如&#xff1a;我们读取文件数据的时候&#xff0c;如果文件缓冲区没有数据&#xff0c;就需要等待数据的到来&#xff0c;这就是阻塞 当然写入的时候&#xff0c;如果发现缓冲区是满的&#xff0c;也需要等待刷新缓…

Python基础——Pandas库

对象的创建 导入 Pandas 时&#xff0c;通常给其一个别名“pd”&#xff0c;即 import pandas as pd。作为标签库&#xff0c;Pandas 对象在 NumPy 数组基础上给予其行列标签。可以说&#xff0c;列表之于字典&#xff0c;就如 NumPy 之于 Pandas。Pandas 中&#xff0c;所有数…

C++: 类型转换

C: 类型转换 &#xff08;一&#xff09;C语言中的类型转换volatile关键字 修饰const变量 &#xff08;二&#xff09;C四种强制类型转换1. static_cast2. reinterpret_cast3. const_cast4. dynamic_cast总结 (三)RTTI &#xff08;一&#xff09;C语言中的类型转换 在C语言中…

STM32——DAC转换

DAC简介 DAC&#xff0c;全称&#xff1a;Digital-to-Analog Converter&#xff0c;扑指数字/模拟转换器 ADC和DAC是模拟电路与数字电路之间的桥梁 DAC的特性参数 1.分辨率&#xff1a; 表示模拟电压的最小增量&#xff0c;常用二进制位数表示&#xff0c;比如&#xff1a…

Kafka的索引设计有什么亮点

想获取更多高质量的Java技术文章&#xff1f;欢迎访问Java技术小馆官网&#xff0c;持续更新优质内容&#xff0c;助力技术成长 Java技术小馆官网https://www.yuque.com/jtostring Kafka的索引设计有什么亮点&#xff1f; Kafka 之所以能在海量数据的传输和处理过程中保持高…

在深度学习中,如何统计模型的 ​​FLOPs(浮点运算次数)​​ 和 ​​参数量(Params)

在深度学习中&#xff0c;统计模型的FLOPs&#xff08;浮点运算次数&#xff09;和参数量&#xff08;Params&#xff09;是评估模型复杂度和计算资源需求的重要步骤。 一、参数量&#xff08;Params&#xff09;计算 参数量指模型中所有可训练参数的总和&#xff0c;其计算与…

Linux之Shell脚本--命令提示的写法

原文网址&#xff1a;Linux之Shell脚本--命令提示的写法-CSDN博客 简介 本文介绍Linux的Shell脚本命令提示的写法。 场景描述 在写脚本时经常会忘记怎么使用&#xff0c;需要进行命令提示。比如&#xff1a;输入-h参数&#xff0c;能打印用法。 实例 新建文件&#xff1a…