最近在用vs和opencv库在做图像处理的项目,关于轮廓识别部分,我查阅了一些资料, 现结合自己的理解整理出来,希望能对你有用。

1.contours概述
在利用openCV对图像进行处理时,我们可能会需要提取出图片上物体的轮廓,进而判断其形状、获取轮廓上的点等等。为了便捷地获得图像轮廓,我们可以使用opencv库中的Contours相关API接口。
Contours可以简单地被理解为一条由其上全部连续点组成的曲线,这些点通常颜色相同、像素值接近。一张图片上可能有多条轮廓,而每条轮廓由多个连续的像素点组成。Contours主要用于形状分析、物体检测识别和轮廓周长面积计算等场合[1]。

2.常用函数
识别轮廓时,opencv中最常用的两个API就是 findContours() 和 drawContours() ,下面对这两者进行介绍。

findContours()
该函数用于提取图像中的轮廓,并将轮廓信息存储起来。

findContours( InputOutputArray image,//输入图像,一般为二值图OutputArrayOfArrays contours,//用于保存轮廓信息的数组OutputArray hierarchy,//轮廓层级关系int mode,//轮廓的检索模式int method,//定义轮廓的近似方法Point offset=Point());//偏移量

以上六个参数的详细解释如下。
(1)为了提高轮廓识别准确度,输入的image一般为二值图像,即经过灰度化,滤波,canny、sobel或laplace边缘检测后的图像。
(2)我们需要定义一个容量可变的容器,来保存图像的轮廓信息。那么,第二个参数contours便来源于此。contours可以用 std::vector<std::vector<Point>> contours;语句进行定义。在此语句中,std::vectorC++中的一个类,表示容量可变的数组容器。该数组由多个contours组成,数组中的每一个元素即为一个contours,每一个contours由多个Point组成。那么,为什么要求数组的大小可以改变呢?这是因为在检测轮廓之前,计算机并不知道图像上的独立轮廓到底有多少个,如果提前设定数组大小,可能会出现数据溢出的情况。为了避免数据丢失,用std::vector定义容器,随着更多的轮廓被检测出,数组容量也不断被扩大。这里可能不太容易理解,但下面的代码一看你就懂了。
(3)hierarchy表示轮廓的层级关系。用语句vector<Vec4i> hierarchy;来定义,一个hierarchy包含4个整型数据,分别表示:后一个轮廓的序号、前一个轮廓的序号、子轮廓的序号、父轮廓的序号,用于体现图像不同轮廓之间的层级关系[2]。hierarchy参数项与下面的mode参数相关联,选择不同的检索模式,hierarchy的值不同。
(4)mode为轮廓的检索模式,有4个选项,不同选项最终生成出来的轮廓样式有所不同[3]。你可以根据自己的需求进行选择,如下表所示。

模式 特点
CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,轮廓之间不建立层级关系
CV_RETR_CCOMP 检测所有轮廓,轮廓之间只建立两个层级关系,不存在父轮廓或子轮廓关系
CV_RETR_TREE 检测所有轮廓,轮廓之间建立完整的层级关系,全部四个层级关系
CV_RETR_EXTERNAL 只检测最外围轮廓 ,忽略内围轮廓,轮廓之间不存在层级关系

(5)method,用来定义轮廓近似方法。根据前面的内容,我们知道轮廓是由物体外形的边界点组成的曲线,包含着边界上的点。但是,contours中是否一定存储着所有边界点呢?答案是不一定,这由轮廓近似方法method决定。
如果设置选项为CV_CHAIN_APPROX_NONE,所有边界点都被保存。然而,它们并不一定都能派上用场。比如说,识别到一条直线轮廓,为了显示它,我们只须存储它的两个端点就够了,可以舍弃掉其余的点。要做到这点,将选项改为CV_CHAIN_APPROX_SIMPLE即可,舍弃冗余的点,从而节省存储空间。结合下面的图片进行理解,左侧矩形轮廓通过CV_CHAIN_APPROX_NONE得到,共734个点;右侧矩形轮廓通过CV_CHAIN_APPROX_SIMPLE得到,只有4个点。可见,后者节省了很多空间。

不同的模式所提取出来的轮廓点有所不同,如下表所示。

模式 特性
CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点
CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,拐点与拐点之间直线段上的信息点不予保留
CV_CHAIN_APPROX_TC89_L1 或 CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似法中的一个

(6)Point(a,b)可以用作图像中轮廓位置的修改。例如Point(20,30)表示每个轮廓点向右偏移20个像素点、向下偏移30个像素点。(默认右为正、下为正)

drawContours()
该函数将提取出来的轮廓信息绘制出来,显示在图片上。

void drawContours(InputOutputArray image, //输出的目标图像
InputArrayOfArrays contours, //输入前面findContours提取的轮廓数组
int contourIdx, //数组contours中的元素序号,0、1、2...
const Scalar& color, //轮廓的颜色
int thickness=1, //轮廓线宽度
int lineType=8, //轮廓线型
InputArray hierarchy=noArray(), //轮廓层级关系
int maxLevel=INT_MAX, //轮廓绘制最高层次
Point offset=Point() );//偏移量

以上为drawContours函数及其参数的解释,其中大部分很容易理解或者在上文中已解释过。这里详细介绍第8个参数int maxLevel=INT_MAX,此参数表示绘制轮廓线的最大级别。如果为0,则只绘制指定的轮廓。如果值为1,函数将绘制该轮廓线和所有内嵌轮廓线。如果值为2,则该函数绘制轮廓线、所有内嵌轮廓线,和比内嵌轮廓线再低一级的内嵌轮廓线,以此类推。仅当存在可用的层次结构时才考虑此参数,就是说hierarchy有效时这个参数才有用。

3.代码实现
下面结合实例对图像轮廓识别地过程进行解释。

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;Mat src, src_gray, src_blur, src_canny, src_cont;int main(int argc, char** argv){src = imread("C:\\Users\\ASUS\\Pictures\\beauties\\lindaiyu.jpg");if(!src.data){printf("can not open the mark_image!!\n");system("pause");return -1;}namedWindow("tupian", CV_WINDOW_AUTOSIZE);imshow("tupian", src);cvtColor(src, src_gray, CV_BGR2GRAY);  //将图像灰度化blur(src_gray, src_blur, Size(3, 3));  //对图像进行均值滤波Canny(src_blur, src_canny, 40, 120);src_cont = Mat::zeros(src.size(), CV_8UC3);  //创建一个与src尺寸相同的纯色图片vector<vector<Point>> contours;  //定义一个存储轮廓数组的容器vector<Vec4i> hierarchy;  // 每一个hierarchy由4个整形变量组成findContours(src_canny, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));   //寻找轮廓RNG rng(0);  //定义随机数,初始值为0for(int i = 0; i < contours.size(); i++){Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255));  //定义一个随机颜色drawContours(src_cont, contours, i, color, 1, 8, hierarchy, 0, Point(0,0));  //绘制第i个轮廓}namedWindow("contours", CV_WINDOW_AUTOSIZE);imshow("contours", src_cont);waitKey(0);return 0;
}

首先,对图像进行灰度化处理,再调用blur()函数去除噪声,然后使用canny算子获得图像边缘,之后找到图像的每一个轮廓,最后依次绘制出来。至此,轮廓识别的整个过程便搞定了。原图与轮廓图的对比如下所示。

4.查找指定轮廓上的点
识别出轮廓并在图像中显示出来之后,我们或许还有进一步的需求,例如判断轮廓形状、获得每条轮廓上像素点的信息、计算轮廓周长面积等,下面我们介绍查找轮廓点的方法。
在上面的程序中加入以下代码即可。

for(int i = 0; i < contours.size(); i++){for(int j = 0; j< contours[i].size(); j++){cout<<"第 "<<i<<" 个轮廓上第 "<<j<<" 个点的坐标为:";cout<<contours[i][j].x<<", "<<contours[i][j].y<<endl;}
}

contours.size()返回整张图片上轮廓的总数目;contours[i].size()返回第 i 个轮廓上像素点的总数;contours[i][j]表示第 i 个轮廓上的第 j 个像素点,contours[i][j].xcontours[i][j].y分别为该点的x和y坐标值。
输出结果如下图所示。

至此,本文对Contours概念及相关函数做了简要的介绍,并读取出轮廓上所有的像素点,大家如有疑问或其它见解欢迎在评论区讨论。

参考引用
[1].opencv官网上关于contours的介绍(https://docs.opencv.org/3.3.1/d4/d73/tutorial_py_contours_begin.html)
[2].轮廓层级的进一步解释(https://blog.csdn.net/qq_33810188/article/details/81285867)
[3].findContours函数参数详解(https://blog.csdn.net/laobai1015/article/details/76400725)

opencv中关于轮廓检测识别Contours及相关函数的介绍相关推荐

  1. OpenCV中图像轮廓检测

    OpenCV中图像轮廓检测 通过之前的Canny方法可以得到图像的边界,但是我们无法得到边界的数学信息.所以就有了今天的图像轮廓检测. 在OpenCV中图像轮廓检测的API: findContours ...

  2. Opencv中的轮廓检测及应用

    在openCV中,我们可以对图片的轮廓进行检测,虽然轮廓的检测看起来很简单,只是对一个图形的边框进行描绘,但是,它在很多领域上都应用到了,例如:人脸识别.车辆检测.视频采集等,下面我会通过人脸识别和车 ...

  3. OpenCV-Python 中文教程15——OpenCV 中的轮廓

    OpenCV-Python 中文教程15--OpenCV 中的轮廓 一.初识轮廓 目标 • 理解什么是轮廓 • 学习找轮廓,绘制轮廓等 • 函数: cv2.findContours(), cv2.dr ...

  4. OpenCV中的轮廓及性质

    转:https://www.kancloud.cn/aollo/aolloopencv/272892 OpenCV中的轮廓 1.1什么是轮廓 轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有 ...

  5. OpenCV 中的轮廓-轮廓的层次结构

    轮廓的层次结构 目标 现在我们要学习轮廓的层次结构了,比如轮廓之间的父子关系. 原理 在前面的内容中我们使用函数 cv2.findContours 来查找轮廓,我们需要传入一个参数:轮廓提取模式(Co ...

  6. opencv中的侧脸检测

    opencv中的侧脸检测 opencv中的haarcascade_profileface.xml检测时只能检测右侧脸 Opencv 2.4.3以后添加了侧脸检测,要想检测侧脸只需将加载的文件改为haa ...

  7. OpenCV学习记录之视频中的火焰检测识别

    主要完成两个视频中火焰的检测,主要结合RGB判据和HIS判据,设定合适的阈值条件,检测出火焰对应像素的区域,将原图二值化,经过中值滤波以及数学形态学的膨胀运算等图像处理,消除一些噪声及离散点,连通一些 ...

  8. OpenCV学习笔记(12)——OpenCV中的轮廓

    什么是轮廓 找轮廓.绘制轮廓等 1.什么是轮廓 轮廓可看做将连续的点(连着边界)连在一起的曲线,具有相同的颜色和灰度.轮廓在形态分析和物体的检测和识别中很有用. 为了更加准确,要使用二值化图像.在寻找 ...

  9. opencv:图像轮廓检测-细胞轮廓

    对于下面的图像,进行细胞(有彩色的都是细胞)的轮廓识别,要求分割尽可能准确,轮廓是封闭的曲线. 个人的思路: 第一:通过opencv读取图片: 第二:对原图进行灰度化处理,简化矩阵,提高处理速度: 第 ...

最新文章

  1. 微信小程序获取text的值与获取input的输入的值
  2. Django(part46)--form表单验证
  3. socketserver 源码浅读
  4. python iterator iterable_Python中Iterator和Iterable的区别
  5. MyBatis 实现增删CRUD
  6. aes 结尾 特殊字符_乔迁庆典主持词开头及结尾
  7. HAXM 6.0.5显示不兼容Windows
  8. 使用jdk进行数据迁移(sqlite迁移mysql)
  9. 资源收集:2020年11月行政区划代码
  10. 最新MT2503_GPS调试工具资料下载
  11. 记:STM32F205双USB开发做device
  12. html怎么让视频自动循环播放,【前端】div 加载视频并自动循环播放
  13. cv2高动态范围成像(HDRI、HDR)
  14. guid主分区表损坏如何处理_GUID分区表简介
  15. 【松鼠科学会】头脑练功房:冥想真的有效吗?
  16. 利用zabbix监控网宿cdn的流量
  17. 华为隐藏鸿蒙,鸿蒙系统有隐私空间吗_华为鸿蒙系统有隐私空间吗
  18. NekoHTML 和 XPath
  19. DSP芯片CSL的使用
  20. [one-hot]one-hot实现的几种方式

热门文章

  1. 社区传媒突破场景限制,“新潮传媒”们如何逐鹿市场?
  2. DCDC电路中的前馈电容
  3. C++实现LRU算法
  4. 基于大恒相机SDK,二次开发
  5. yum安装killall
  6. 关于客户端信息流思考
  7. oracle 无效列索引
  8. 手机厂商玩人群细分究竟有没有意义
  9. 编写一个编程,提示用户输入年龄,然后显示改年龄对应的秒数(C)
  10. 抽象类和抽象方法的使用