OpenCV学习笔记(十七):查找并绘制轮廓:findContours()

1、findContours() 函数

该函数使用Suzuki85算法从二值图像中检索轮廓。轮廓线是一种用于形状分析、目标检测和识别的有效工具。
opencv轮廓检测之FindContours函数算法解释
该函数从二值图像中检索轮廓,并返回检索到的轮廓数。函数将填充指针first_contour。它将包含指向第一个最外层轮廓的指针,如果没有检测到轮廓,则为空(如果图像是完全黑色的)。可以使用h_next和v_next链接从first_contour到达其他轮廓。绘制等值线讨论中的示例说明了如何使用等值线进行连接组件检测。轮廓还可以用于形状分析和对象识别-请参阅OpenCV示例目录中的squares.c。

void findContours(
InputOutputArray image,         // 要绘制轮廓的图像,8位单通道的图像(256级灰度图)
OutputArrayOfArrays contours,   // 所有输入的轮廓,找到的轮廓,其中每个轮廓会被存储为vector<Point>,// 所以contours的类型就是vector<vector<Point>>。
OutputArray hierarchy,          // 层次结构,可选的输出向量,包含关于图像的拓扑结构信息。// 其具有跟轮廓数相同的元素个数,类型为vector<Vec4i>// 后一个轮廓、前一个轮廓、第一个子轮廓、父轮廓的索引编号,如果没有对应项,该值设置为-1
int mode,                       // 检索轮廓的模式分别表示
int method,                     // 为轮廓的近似办法
Point offset=Point()           // 代表轮廓点的偏移量,可以设置为任意值。
);
==mode==
RETR_EXTERNAL 表示只检测外轮廓    
RETR_LIST 检测的轮廓不建立等级关系    
RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边 界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。    
RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demo
==method==
CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法

2、drawContours()函数

该函数使用算法从二值图像中检索轮廓。绘制轮廓线或填充轮廓线。如果厚度≥0,该函数在图像中绘制轮廓轮廓;如果厚度<0,则填充轮廓边界区域。
参考博客:
轮廓的层级关系详解
OpenCV中findcontours函数hierarchy轮廓层级详解

void drawContours(
InputOutputArray image,         // 要绘制轮廓的图像
InputArrayOfArrays contours,    // 所有输入的轮廓,每个轮廓被保存成一个point向量(vector<vector<Point>>)
int contourIdx,                 // 指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
const Scalar& color,            // 绘制轮廓所用的颜色
int thickness=1,               // 绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充(CV_FILLED 填充内部)
int lineType=8,                // 绘制轮廓的线的连通性(LINE_AA 抗锯齿线形)
InputArray hierarchy=noArray(),    // 关于层级的可选参数,只有绘制部分轮廓时才会用到(hierarchy=hierarchy,绘制所有轮廓)
int maxLevel=INT_MAX,          // 绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
Point offset=Point()           // 代表轮廓点的偏移量,可以设置为任意值
)
==maxLevel==
maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点

3、approxPolyDP()函数

以指定的精度近似生成多边形曲线。
函数逼近一条曲线或另一条曲线/顶点较少的多边形,使它们之间的距离小于或等于指定的精度。它使用Douglas-Peucker算法

void approxPolyDP(
InputArray curve,           // 输入的点集(存储在std::vector或Mat中的二维点的输入向量)
OutputArray approxCurve,    // 输出的点集,当前点集是能最小包容指定点集的。draw出来即是一个多边形;
double epsilon,             // 指定的精度,也即是原始曲线与近似曲线之间的最大距离。
bool closed                 // 若为true,则说明近似曲线是闭合的,它的首位都是相连,反之,若为false,则断开。
);

4、示例一:

#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int main()
{QCoreApplication a(argc, argv);// 1、加载源图像//Mat srcImage = imread( "F:/C++/2. OPENCV 3.1.0/TEST/a.jpg", 1 );Mat srcImage = imread( "F:/C++/2. OPENCV 3.1.0/TEST/test3.png", 1 );if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }cvtColor(srcImage,srcImage,CV_BGR2GRAY);// 2、初始化结果图Mat dstImage = Mat::zeros(srcImage.size(), CV_8UC3);Mat dstImage1 =dstImage.clone();// 3、srcImage取大于阈值119的那部分//threshold(srcImage,srcImage,119,255,THRESH_BINARY);srcImage = srcImage > 119; // 取 阈值大于119imshow( "取阈值后的原始图", srcImage );// 4、定义轮廓和层次结构// 储存所有轮廓(每个轮廓为一个点向量集)// 储存每个轮廓的层次元素,hierarchy[i][0]~hierarchy[i][1] 分别表示后一个、前一个、父轮廓、内嵌轮廓的索引编号vector<vector<Point> > contours;vector<Vec4i> hierarchy;// 5、查找轮廓// RETR_EXTERNAL 表示只检测外轮廓// RETR_LIST 检测的轮廓不建立等级关系// RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边 界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。// RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demo// CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1// CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息// CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法findContours( srcImage, contours, hierarchy,CV_RETR_CCOMP ,CV_CHAIN_APPROX_SIMPLE );cout<<"轮廓数量:"<<contours.size()<<endl;// 6、遍历所有顶层的轮廓, 以随机颜色绘制出每个连接组件颜色// hierarchy[index][0] 表示该某轮廓点集的后一个轮廓点集 索引号// hierarchy[index][1] 表示该某轮廓点集的前一个轮廓点集 索引号// hierarchy[index][2] 表示该某轮廓点集的第一个子轮廓(内嵌)点集 索引号// hierarchy[index][3] 表示该某轮廓点集的父轮廓点集 索引号for( int index = 0; index >= 0; index = hierarchy[index][0] ) // 绘制轮廓//for( int index = 0; index < contours.size(); index ++ ) // 绘制所有内外轮廓{Scalar color( rand()&255, rand()&255, rand()&255 );drawContours( dstImage, contours, index, color, 1, 8,hierarchy ); //CV_FILLED}//    // 7、检测到的所有轮廓分别用不同颜色画出来
//    for(size_t j=0; j<contours.size(); j++)
//    {//        Scalar color( rand()&255, rand()&255, rand()&255 ); // 为每个轮廓新建随机颜色
//        for (size_t i = 0; i < contours[j].size(); i++)
//            {//                // 绘制边缘
//                line(dstImage1, contours[j][i], contours[j][(i + 1) % contours[j].size()], color, 1, 8);
//            }
//    }// 8、显示最后的轮廓图imshow( "轮廓图", dstImage );//imshow( "边缘图", dstImage1 );waitKey(0);return 0;
}

结果:


分析:
1)“0轮廓"的下一条轮廓是"1轮廓”;“1轮廓"的下一条"3轮廓”;
2)"2轮廓"和"3轮廓"的下一条没有;
3)"0轮廓"和"2轮廓"上一条是-1表示没有;
4)“1轮廓"的子轮廓是"2轮廓”,其他的轮廓没有子轮廓。(1轮廓包含2轮廓)
5)"0、1、3轮廓"没有父轮廓,说明其为最外层轮廓(且为同级轮廓)
PS:轮廓层次关系 看hierarchy[index][2]和hierarchy[index][3]的值

5、示例二:

#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;int levels = 3; // 精度等级
Mat img;
RNG g_rng(12345);
vector<vector<Point> > contours,contours0;
vector<Vec4i> hierarchy;int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 1、 读取图片img = imread( "F:/C++/2. OPENCV 3.1.0/TEST/face.png", 0 );if(!img.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }imshow( "image", img );//       // 绘制6个人脸用于测试
//       // 创建 500*500 单通道矩阵 灰度图
//       Mat img = Mat::zeros(w, w, CV_8UC1);
//       for( int i = 0; i < 6; i++ )
//       {//           int dx = (i%2)*250 - 30;
//           int dy = (i/2)*150;
//           const Scalar white = Scalar(255);
//           const Scalar black = Scalar(0);
//           if( i == 0 )
//           {//               for( int j = 0; j <= 10; j++ )
//               {//                   double angle = (j+5)*CV_PI/21;
//                   line(img, Point(cvRound(dx+100+j*10-80*cos(angle)),
//                       cvRound(dy+100-90*sin(angle))),
//                       Point(cvRound(dx+100+j*10-30*cos(angle)),
//                       cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
//               }
//           }
//           ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 );
//           ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
//           ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
//           ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 );
//           ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
//           ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
//       }// 显示
//       namedWindow( "image", 1 );
//       imshow( "image", img );// 2、提取轮廓findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);// 3、对轮廓进行逼近 获得多边形曲线轮廓向量点集contours.resize(contours0.size());for( size_t k = 0; k < contours0.size(); k++ )approxPolyDP(Mat(contours0[k]), contours[k], 0.1, true);// 4、滑动条调用// 设置轨迹条,控制从-3到3的轮廓等级namedWindow( "contours", 1 );createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );on_trackbar(0,0);// 初次调用waitKey(0);return a.exec();
}

轮廓控制回调函数

static void on_trackbar(int, void*)
{// 1、创建目标图像Mat cnt_img = Mat::zeros(img.rows, img.cols, CV_8UC3);// 2、处理参数int _levels = levels - 3;// 3、绘制轮廓// _levels <= 0 ? 3 : -1Scalar color = Scalar( g_rng.uniform(0, 255), g_rng.uniform(0,255), g_rng.uniform(0,255) );//任意值drawContours( cnt_img, contours,_levels <= 0 ? 3 : -1, Scalar(128,255,255),1, LINE_AA, hierarchy, std::abs(_levels) ); imshow("contours", cnt_img);
}

结果:


OpenCV学习笔记(十七):查找并绘制轮廓:findContours(),drawContours(),approxPolyDP()相关推荐

  1. OpenCV学习笔记(十七)——K均值聚类

    当我们要预测的是一个离散值时,做的工作就是"分类".机器学习模型还可以将训练集中的数据划分为若干个组,每个组被称为一个"簇(cluster)".它的重要特点是在 ...

  2. OpenCV学习笔记(5)_ ellipse绘制函数浅析

    OpenCV学习笔记(5)_ ellipse绘制函数浅析 文章目录 OpenCV学习笔记(5)_ ellipse绘制函数浅析 1. ellipse第一种重载--绘制椭圆弧 1.1 函数原型 1.2 参 ...

  3. OpenCV学习笔记(六)之轮廓提取与角度测量

      查找图像的轮廓在图像处理及应用中扮演着重要的角色.openCV 中的轮廓指的是由一系列点组成的点的集合,不同的轮廓可以有不同的点集.openCV中,轮廓是由STL风格的vector<> ...

  4. OpenCV学习笔记(Python)———— 画轮廓

    画轮廓 : 1)运用cv2.findContours()函数 import cv2# 读取数据 mupian = cv2.imread(r"图片地址,例:E:\dataset\train\i ...

  5. OpenCV学习笔记(Python)———— 主动轮廓模型

    本文包含主动轮廓模型代码以及实例分割代码 原图: 效果图: 主动轮廓模型: morphsnakes.py # -*- coding: utf-8 -*- """ ==== ...

  6. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(一)查找并绘制轮廓

    第8章 图像轮廓与图像分割修复 8.1 查找并绘制轮廓 8.1.1 寻找轮廓:findContours()函数 1.作用:在二值图像中寻找轮廓 2.函数原型: void findcontours(In ...

  7. opencv学习笔记20:图像轮廓

    图像轮廓 Contours:轮廓 轮廓是将没有连着一起的边缘连着一起. 边缘检测检测出边缘,边缘有些未连接在一起. 注意问题 1.对象为二值图像,首先进行阈值分割或者边缘检测. 2.查找轮廓需要更改原 ...

  8. OpenCV学习笔记(九)——图像轮廓(下)

    <OpenCV轻松入门:面向Python>学习笔记(九) 1-3 查找并绘制轮廓.矩特性及Hu矩 4-5 轮廓拟合及凸包 6. 利用形状场景算法比较轮廓 6.1 计算形状场景距离 6.2 ...

  9. OpenCV学习笔记(五十六)——InputArray和OutputArray的那些事core OpenCV学习笔记(五十七)——在同一窗口显示多幅图片 OpenCV学习笔记(五十八)——读《Mast

    OpenCV学习笔记(五十六)--InputArray和OutputArray的那些事core 看过OpenCV源代码的朋友,肯定都知道很多函数的接口都是InputArray或者OutputArray ...

最新文章

  1. Redis 笔记(01)— 安装、启动配置、开启远程连接、设置密码、远程连接
  2. 简单介绍C语言使用四种方法初始化结构体
  3. 解决No module named 'sklearn.cross_validation'
  4. springBoot 全局异常方式处理自定义异常 @RestControllerAdvice + @ExceptionHandler
  5. 如何选择漏电保护器规格型号_施工现场三级电箱如何配置?图示详解,清晰明了!...
  6. queue源码java_看看AbstractQueue源码Java9
  7. [转]nginx反向代理网站(网易、百度之类的)
  8. delphi7存取配置文件与sqlserver数据库连接_SQL Server基础知识概念要点详细讲解
  9. 小数分数转换c语言,这是把小数转换成分数的程序,可是输入0.6666无限循环
  10. oracle用户密码复杂度查询,11gR2 Database用户密码复杂度验证
  11. 干货| LeNet-5模型详解(附Python详细代码及注释)
  12. CF1166E The LCMs Must be Large
  13. 六大方法来对付网站出现的负面信息
  14. 萤火虫小程序_漫展情报蛋趣携福利来萤火虫IDO漫展咯
  15. 如何理解 图像傅里叶变换的频谱图
  16. 第五届A/B组 地宫取宝 JAVA
  17. 慧荣SM2262EN跑RDT教程
  18. TensorFlow学习笔记(4)——TensorFlow实现GloVe
  19. Laravel数据快速填充
  20. 生信漫谈如何做出美美的多序列比对图

热门文章

  1. 图灵奖大佬+谷歌团队,为通用人工智能背书!CV 任务也能用 LM 建模!
  2. 一位老师,一位领导,一个让全体学生考上目标学校的故事
  3. 2018 支付宝Java开发四面:Ngnix+MQ队列+集群+并发抢购
  4. 论文浅尝 | 基于知识库的类型实体和关系的联合抽取
  5. 国科大高级人工智能9-模糊数学和遗传算法
  6. java对List的优雅排序
  7. 非 GUI 模式运行 JMeter 压力测试
  8. [PA 2014]Kuglarz
  9. Reporting Services 的伸缩性和性能表现规划(转载)
  10. 为什么0.1无法被二进制小数精确表示?