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

OPENCV——查找图形轮廓

图像形状查找在OPENCV里面是非常常见的功能,它常用于视觉任务、目标检测、图像分割等等。在OPENCV中通常使用Canny函数、findContours函数、drawContours函数结合在一起去做轮廓的形检测。

一、重要函数讲解

1.1findContours函数的简介以及定义

在OPENCV中通常使用findContours函数去寻找图片的轮廓,也是OPENCV中处理轮廓最重要的函数之一,它常用于找到二值图像中所有物体的轮廓。它的实现原理是通过扫描一张二值图像,然后找到所有的轮廓,并把所有的数据存储在向量里面。下面我们来看看findContours的函数定义

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

第一个参数:image输入的二值图像,这个图像通常是用在边缘检测、阈值处理等等

第二个参数:contours输出的轮廓集合,每一个轮廓都是由点组成,通常用vector<vector<Point>>来表示

第三个参数:hierarchy输出的轮廓层次结构,这通常表示轮廓之间的父子关系,这个是可选参数,通常用vector<Vec4i> hierarchy来表示。比方说,第i个轮廓,hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]、hierarchy[i][3], 依次为第i个轮廓[Next、Pervious、First_Child,Parent], 这表示的是相同等级下种下一轮廓、前一轮廓,第一个子轮廓和父轮廓的索引号。若轮廓i没有下一个,前一个或者父级轮廓,则层次相应的元素是负数。如下图:

Next表示同一级别的下一个轮廓索引,若我们图片中取出轮廓0,同一水平的下一个是轮廓1。所以说当轮廓 == 0的时候,NEXT就是轮廓1。

Previous表示同一级别的上一个轮廓索引,如轮廓1的同一级别的上一个是轮廓0。以此类推,轮廓2的上一个轮廓是轮廓1。

First_Child表示的是当前轮廓的第一个子轮廓的索引。比方说,对于轮廓2,子轮廓是2a,所以轮廓2的First_Child是轮廓2a相对应的索引值。而对于3a来说,它有两个轮廓分别是6,7, 但这里只能取第一个轮廓,所以这里是6。

Parent表示的是当前轮廓的父轮廓索引,比方说对于轮廓6和轮廓7来说,它们的父轮廓都是3a。

第四个参数:mode轮廓检索模式,通常有以下选项

枚举值作用适用场景
RETR_EXTERNAL只检测最外层轮廓,忽略所有内部孔洞轮廓最常用,嵌入式优先推荐;只需要物体外轮廓、不需要内部细节时使用,计算量最小
RETR_LIST检测所有轮廓,但不建立层级关系,所有轮廓同级需要全部轮廓但不需要父子关系时使用
RETR_CCOMP检测所有轮廓,建立两层层级(外层 + 内层孔洞)有孔洞的物体(比如圆环、带孔零件)检测
RETR_TREE检测所有轮廓,建立完整的树形层级关系需要完整轮廓嵌套结构的复杂场景

第五个参数:method轮廓近似方法,通常有以下的几种方法

枚举值作用适用场景
CHAIN_APPROX_SIMPLE压缩水平、垂直、对角线方向的冗余点,只保留线段端点;比如矩形轮廓只存 4 个角点强烈推荐,大幅减少轮廓点数量,节省内存和计算量,嵌入式必用
CHAIN_APPROX_NONE保存轮廓上所有像素点,点数量极多需要精确轮廓轨迹的高精度场景,一般不用

第六个参数:offset轮廓点偏移量,默认(0,0)

输出数据结构说明

  • contours是二维向量:contours[0]是第一条轮廓,contours[0][0]是第一条轮廓的第一个点坐标;
  • 每条轮廓都是一组连续的Point(x,y)坐标,围成闭合的边缘。

1.2 ​​​​​​​drawContours函数的简介以及定义

在OPENCV中drawContours常用于绘制图像的轮廓,如上图,我们来看看这个函数的API定义:

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轮廓的集合,它是由一系列的点组成

第三个参数:contourIdx、轮廓索引数组,指定要绘制哪些轮廓

第四个参数:contourColor轮廓颜色,使用Scalar类型表示

第五个参数:thickness轮廓线宽,默认1

第六个参数:lineType轮廓线类型,默认为LINE_8

第七个参数:hierarchy轮廓层次结构,用于绘制轮廓的父子关系。默认为noArray()

第八个参数:maxLevel表示绘制轮廓的最大层级数量若maxLevel 为0,则只绘制指定的轮廓;若maxLevel 为1,则绘制轮廓极其所有嵌套轮廓;若maxLevel 为2,则绘制轮廓、所有嵌套轮廓、所有嵌套到嵌套的轮廓。

第九个参数:轮廓点的偏移量,默认为(0,0)

1.3 ​​​​​​​Canny函数的简介以及定义

Canny函数主要用在OPENCV的边缘检测计算,边缘检测是OPENCV图像中非常重要的功能,它的功能如(上图一)。它能够高效地提取图像中的边缘信息,而Canny边缘检测是OPENCV里面最优秀和最精准的边缘检测方法。Canny的工作原理可以分为以下比较重要的步骤进行处理,分别是高斯滤波(将图像转换为灰度图像,高斯滤波作用是平滑图像,让Canny检测的时候准确率更高)、梯度强度和方向的计算(计算图像中每个像素的强度和方向、强度表示像素点的边缘强度、梯度表示的是边缘方向这里的梯度需要用到sobel因子)、非极大抑制(经过NMS操作后,会除去一些不是边缘的像素点)、双阈值处理(给出一个阈值,若超过这个阈值的边缘则会被保留)、边缘链接(经过双阈值处理过后,强边缘则会留下来,弱边缘则会被抑制,并会把所有的强边缘全部连接起来),步骤如下图2

下面是双阈值的处理的图解:当梯度值大于maxVal则认为是强边界;当minVal < 梯度值 < maxVal跟边界有连接的部分则保留,否则废弃;梯度值< minVal则废弃。另外需要注意的是高阈值与低阈值的比例最好是2:13:1之间。

下面我们来看看Canny的函数定义:

void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false );

第一个参数:image输入的图像,这个图像一定要单通道灰度图

第二个参数:edges输出的边缘图像,这个图像也必须是单通道黑白图

第三个参数:threshold1第一个滞后性阈值,低阈值,小于低阈值则认为是弱边缘,就是需要抛弃的边缘。

第四个参数:threshold2第二个滞后性阈值,高阈值,大于高阈值被认为强边缘,需要保留的边缘

第五个参数:apertureSize指的是Sobel算子大小,这个值默认为3,代表的是3*3的矩阵大小。

第六个参数:L2gradient是计算图像梯度幅度值的情况,这个值默认为False;若选择True,则使用更精确的L2范数进行计算

二、查找图形轮廓并画框

经过上一章节的讲解,我们对整个OPENCV提取轮廓的API有了一个大致的了解。本章节主要是讲解如何通过代码来实现OPENCV的轮廓检测提取然后进行画框。具体的如下图:

完成轮廓检测,需要做以上步骤。分别是imread读取图片(这个图片默认是3通道)、利用cvtColor把8VU3的三通道图片转换成灰度图(8VU1)、调用Canny对灰度图像进行边缘检测、调用findContours去查找轮廓、循环轮廓数量然后调用drawContours进行画框操作。

代码

#include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/imgproc.hpp> #include <iostream> using namespace cv; using namespace std; int main() { Mat img = imread("shape.png"); Mat imgGray; cvtColor(img, imgGray, COLOR_RGB2GRAY); Mat imgCanny; Canny(imgGray, imgCanny, 25, 75); vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); Mat drawing = Mat::zeros(imgCanny.size(), CV_8UC3); for(int i = 0; i < contours.size(); i++) { Scalar color = Scalar(255, 255, 0); drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point()); } imwrite("contour,jpg",drawing); return 0; }

2.1. 读取原图

Mat img = imread("shape.png");
  • 读取当前目录下的shape.png,默认以BGR 三通道彩色格式加载。

2.2. 灰度化转换

Mat imgGray; cvtColor(img, imgGray, COLOR_RGB2GRAY);
  • 功能:将彩色图转为单通道灰度图,Canny 只支持单通道输入。

2.3. Canny 边缘检测

Mat imgCanny; Canny(imgGray, imgCanny, 25, 75);
  • 功能:对灰度图做边缘检测,输出单通道二值边缘图(白色为边缘,黑色为背景)。
  • 参数说明:低阈值 25,高阈值 75,高低阈值比例为 3:1,大于75表示的是强边缘需要保存下来的,小于25是弱边缘需要忽略的,25-75之间的边缘则会用算法进行候选;默认使用 3×3 Sobel 算子。

2.4. 轮廓检测

vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

处理完边缘后,就findContours可以通过findContours去查找所有图像的轮廓了,由于我们检测的图像没有内嵌的形状,所以我们选择RETR_EXTERNAL的模式只检测外轮廓,Method的方法则选择CHAIN_APPROX_SIMPLE存储所有的轮廓点。注意:contours一般是用vector<vector<point>>表示,hierarchy通常用vector<Vec4i>表示.

2.5. 轮廓绘制与保存

1. 创建绘制画布

Mat drawing = Mat::zeros(imgCanny.size(), CV_8UC3);
  • 创建一张和原图尺寸一致的纯黑三通道图像,作为绘制轮廓的背景板,方便突出显示轮廓。

2. 循环绘制所有轮廓

for(int i = 0; i < contours.size(); i++) { Scalar color = Scalar(255, 255, 0); drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point()); }
  • 遍历每一条检测到的轮廓,逐个绘制到黑色画布上。
  • 参数说明:
    • 颜色Scalar(255,255,0):BGR 格式下为青色(蓝 + 绿满值,红为 0)。
    • 线宽2:轮廓线条粗细为 2 像素。
    • 线型8:8 连通线型,线条更平滑。
    • maxLevel=0:只绘制当前层级轮廓,配合RETR_EXTERNAL使用时无额外效果。

3. 保存结果图

imwrite("contour,jpg",drawing);
http://www.gsyq.cn/news/1596423.html

相关文章:

  • 设计 Token 多主题管理与跨端同步:从单一变量到系统化主题引擎
  • 8个实用技巧:如何让qBittorrent搜索功能变得像谷歌一样强大
  • 光伏并网逆变器设计与优化:全国大学生电子设计竞赛实战
  • 如何快速提升中文文献管理效率:Zotero茉莉花插件的终极解决方案
  • 3个核心场景深度解析:WELearn网课助手如何重塑你的学习体验
  • 三步解锁PotPlayer智能字幕翻译:免费实现多语言视频无障碍观看
  • 微信群消息自动转发终极指南:如何告别手动复制粘贴
  • 猫抓浏览器扩展:三步解决在线视频下载难题的终极指南
  • 3步搞定窗口遮挡难题:AlwaysOnTop让你告别Alt+Tab的终极方案
  • AI证书含金量怎么样判断?别只看宣传词
  • UI自动化测试实战:从元素定位到框架搭建的完整指南
  • 65.野生作家诞生记
  • Nginx安全升级实战指南:从漏洞修复到持续运维
  • 飞书文档批量导出工具:3步实现企业知识库自动化迁移的终极方案
  • 质量管理-IPQC是指什么?
  • K老答——其实一直都在
  • qBittorrent搜索插件终极指南:一键解锁20+种子搜索引擎
  • K老答——所见皆漏
  • WordPress站长必读:钓鱼邮件攻击链深度解析与防御指南
  • 金相显微镜在PCB切片分析中的深度应用
  • 广义模型论:稳定性理论与Borel复杂性分析的交叉研究
  • 上位机YOLO推理优化实录:我是怎么把CPU推理速度提上去的
  • 实测 Paperxie 科研绘图模块:先看样例再出图,全学科论文配图不用再啃 Origin
  • 记录AI学习之路Day12:AIGC
  • 抖音卡黑屏技术原理与防御指南:从网络攻击到平台风控
  • CloakBrowser实战:Python浏览器指纹伪装与反检测自动化指南
  • Zenodo数据获取终极指南:zenodo_get工具深度解析与实战应用
  • REFramework终极指南:如何快速解决RE引擎游戏启动崩溃问题
  • 2026手机拍摄制作工作证照片保姆级详细教程,尺寸规范+实操步骤一次讲清
  • 【2026】Mastercam2026 R2安装教程 保姆级图文步骤详解(附安装包)手把手教你如何进行Mastercam的下载和安装