OPENCV——查找图形轮廓

发布时间:2026/6/26 23:44:59
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输出的轮廓集合每一个轮廓都是由点组成通常用vectorvectorPoint来表示第三个参数hierarchy输出的轮廓层次结构这通常表示轮廓之间的父子关系这个是可选参数通常用vectorVec4i hierarchy来表示。比方说第i个轮廓hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]、hierarchy[i][3], 依次为第i个轮廓[Next、Pervious、First_ChildParent], 这表示的是相同等级下种下一轮廓、前一轮廓第一个子轮廓和父轮廓的索引号。若轮廓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:1到3: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); vectorvectorPoint contours; vectorVec4i 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. 轮廓检测vectorvectorPoint contours; vectorVec4i hierarchy; findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);处理完边缘后就findContours可以通过findContours去查找所有图像的轮廓了由于我们检测的图像没有内嵌的形状所以我们选择RETR_EXTERNAL的模式只检测外轮廓Method的方法则选择CHAIN_APPROX_SIMPLE存储所有的轮廓点。注意contours一般是用vectorvectorpoint表示hierarchy通常用vectorVec4i表示.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 像素。线型88 连通线型线条更平滑。maxLevel0只绘制当前层级轮廓配合RETR_EXTERNAL使用时无额外效果。3. 保存结果图imwrite(contour,jpg,drawing);