1、图像轮廓

1.1图像轮廓与API函数

轮廓是一系列相连的点组成的曲线,代表了物体的基本外形,相对于边缘,轮廓是连续的,边缘并不全部连续。一般地,获取图像轮廓要经过下面几个步骤:

1)     读取图片。

2)     将彩色图像转换成灰度图像。

3)     将灰度图像转换成二值图形并查找其二值图像边缘即可(如canny边缘检测)。

4)     显示轮廓边缘。

findContours寻找轮廓函数,原型为

CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
/** @overload */
CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset = Point());

1)image:图像,单通道灰度图。

2)contours:检测到的轮廓,包含对象边界点(x,y)的坐标,每个轮廓存储为一个点向量可用point类型的vector存储。

3)hierarchy:轮廓的拓扑信息,每个轮廓contours[i]包含4个hierarchy[i]元素,hierarchy[i][0]-- hierarchy[i][3],分别代表后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的编号。

4)mode:轮廓检索模式。

RETR_EXTERNAL :只检索最外面的轮廓;

RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;

RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,次层是空洞的内层边界;

RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;

返回值的含义

5)method:轮廓逼近方法。

CHAIN_APPROX_NONE:输出轮廓的每个像素。

CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,函数只保留他们的终点坐标。

drawContours绘制轮廓函数,原型为:

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

image – 输入图像,单通道灰度图。

contours - 所有的输入轮廓,每个轮廓为点矢量(a point vector)/点向量形式,与findcontours中的contours 形式一致。

contourIdx - 指定轮廓列表的索引 ID(将被绘制),若为负数,则所有的轮廓将会被绘制。

color - 绘制轮廓的颜色。

thickness - 绘制轮廓线条的宽度,若为负值或CV.FILLED则将填充轮廓内部区域。

lineType – 线条的类型,8连通型或4连通型。

hierarchy - 层次结构信息,与函数findcontours()的hierarchy有关

maxLevel - 绘制轮廓的最高级别。若为0,则绘制指定轮廓;若为1,则绘制该轮廓和所有嵌套轮廓(nested contours);若为2,则绘制该轮廓、嵌套轮廓(nested contours)/子轮廓和嵌套-嵌套轮廓(all the nested-to-nested contours)/孙轮廓,等等。该参数只有在层级结构时才用到。

offset - 按照偏移量移动所有的轮廓(点坐标)。

1.2图像轮廓实例

测试代码如下。

int main() {
RNG rng();
Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp");
imshow("原图", src);
/// 转成灰度并模糊化降噪
cvtColor(src, src_gray, CV_BGR2GRAY);
blur(src_gray, src_gray, Size(, )); int thresh = ;
int max_thresh = ;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// 用Canny算子检测边缘
Canny(src_gray, canny_output, thresh, thresh * , );
/// 寻找轮廓
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(, )); /// 绘出轮廓
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
for (int i = ; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(, ), rng.uniform(, ), rng.uniform(, ));
drawContours(drawing, contours, i, color, , , hierarchy, , Point());
} /// 在窗体中显示结果
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing); waitKey();
}

输出结果为:

测试2,结果如下图。

2、凸包

2.1凸包与API函数

在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,…Xn)的凸组合来构造。简单来讲,对于一个二维空间的点集,这个点集当中的一些点总可以形成一个凸多边形,而这个凸多边形之内恰好可以包括除了组成凸包这个凸多边以外的所有点,而这个凸多边形就是凸包。凸包可以想象成一条刚好包住所有点的橡皮圈,对于给定二维平面上的点集,凸包常常就是将最外层的点连接起来构成的凸多边形,它能包含点击中所有的点。

物体的凸包检测常应用在物体识别、手势识别及边界检测等领域。理解物体形状或轮廓的一种方法是计算物体的凸包,然后计算其凸缺陷。下图人手图像画图了凸包线轮廓,然后标出了凸缺陷A—H。黑色的轮廓线为convexity hull, 而convexity hull与手掌之间的部分为convexity defects. 每个convexity defect区域有四个特征量:起始点(startPoint),结束点(endPoint),距离convexity hull最远点(farPoint),最远点到convexity hull的距离(depth)。

OpenCV使用convexHull函数做物体轮廓凸包检测:

CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
bool clockwise = false, bool returnPoints = true );

point:输入的二维点集,可储存在向量或矩阵Mat中,代表轮廓点

hull:输出凸包,这是一个整数索引的载体或点的矢量,可以是vector<vector<Point>>和vector<vector<int>>

clockwise:方向标志位,true为顺时针,false为逆时针方向。

return Point :操作标准位,默认true,表示返回凸包的各点,否则返回凸包各点的指数。

OpenCV使用convexityDefects函数做轮廓凸包缺陷检测:

CV_EXPORTS_W void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );

coutour: 输入参数,检测到的轮廓,可以调用findContours函数得到;

convexhull: 输入参数,检测到的凸包,可以调用convexHull函数得到。注意,convexHull函数可以得到vector<vector<Point>>和vector<vector<int>>两种类型结果,这里的convexhull应该为vector<vector<int>>类型,否则通不过ASSERT检查;

convexityDefects:输出参数,检测到的最终结果,应为vector<vector<Vec4i>>类型,Vec4i存储了起始点(startPoint),结束点(endPoint),距离convexity hull最远点(farPoint)以及最远点到convexity hull的距离(depth)。

2.2图像凸包检测实例

图像轮廓与凸包实例测试代码如下。

int main() {
RNG rng();
Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp");
imshow("原图", src);
/// 转成灰度并模糊化降噪
cvtColor(src, src_gray, CV_BGR2GRAY);
//blur(src_gray, src_gray, Size(5, 5));
imshow("src_gray", src_gray); int thresh = ;
int max_thresh = ;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// 用Canny算子检测边缘
Canny(src_gray, canny_output, thresh, thresh * , );
imshow("canny_output", canny_output);
/// 寻找轮廓
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(, ));
//通过发现轮廓得到的候选点来绘制凸包
vector<vector<Point>> convexs(contours.size());
for (size_t i = ; i < contours.size(); i++) {
convexHull(contours[i], convexs[i], false, true);
} /// 绘出轮廓
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
Mat drawing_convex = Mat::zeros(canny_output.size(), CV_8UC3);
for (int i = ; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(, ), rng.uniform(, ), rng.uniform(, ));
drawContours(drawing, contours, i, color, , , hierarchy, , Point());
//白色线画出凸包
drawContours(drawing_convex, convexs, i, Scalar(, , ), , LINE_8, noArray(), , Point());
} imshow("Contours", drawing);
imshow("drawing_convex", drawing_convex);
waitKey();
}

输出结果为:

测试2,输出结果为:

3、使用多边形将轮廓包围

3.1多边形包围轮廓和API

当我们得到对象轮廓后,可用boundingRect()得到包覆此轮廓的最小正矩形,minAreaRect()得到包覆轮廓的最小斜矩形,minEnclosingCircle()得到包覆此轮廓的最小圆形。

CV_EXPORTS_W void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
CV_EXPORTS_W Rect boundingRect( InputArray points );
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
CV_OUT Point2f& center, CV_OUT float& radius );

3.2多边形包围轮廓测试实例

测试代码如下:

int main() {
RNG rng();
Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\img3.bmp");
imshow("原图", src);
/// 转成灰度并模糊化降噪
cvtColor(src, src_gray, CV_BGR2GRAY);
//blur(src_gray, src_gray, Size(5, 5));
imshow("src_gray", src_gray); int thresh = ;
int max_thresh = ;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// 用Canny算子检测边缘
Canny(src_gray, canny_output, thresh, thresh * , );
imshow("canny_output", canny_output);
/// 寻找轮廓
findContours(canny_output, contours, hierarchy, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(, )); vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point2f>center(contours.size());
vector<float>radius(contours.size());
for (int i = ; i < contours.size(); i++)
{
approxPolyDP(Mat(contours[i]), contours_poly[i], , true);
boundRect[i] = boundingRect(Mat(contours_poly[i]));
minEnclosingCircle(contours_poly[i], center[i], radius[i]);
} /// 绘出轮廓
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
for (int i = ; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(, ), rng.uniform(, ), rng.uniform(, ));
drawContours(drawing, contours, i, color, , , hierarchy, , Point());
/// 画多边形轮廓 + 包围的矩形框 + 圆形框
rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, , , );
circle(drawing, center[i], (int)radius[i], color, , , );
} imshow("Contours", drawing);
waitKey();
}

输出结果如下:

如果需要填充轮廓空心空白,其中drawContours的参数thicknes代表绘制轮廓线条的宽度,若为负值或CV.FILLED,表示填充轮廓内部区域。所以将此参数置-1就出现下面结果。

4、参考文献

1、《OpenCV3 编程入门》,电子工业出版社,毛星雨著

2、《学习OpenCV》,清华大学出版社,Gary Bradski, Adrian kaehler著

3、用opencv检测convexity defects

https://www.it610.com/article/4474290.htm

4、计算物体的凸包

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/shapedescriptors/hull/hull.html

5、提取轮廓的原理和代码实例

https://blog.csdn.net/qq_29796317/article/details/78297920

6、在图像中寻找轮廓

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html

7、【计算几何/凸包】安德鲁算法(Andrew's Algorithm)详解

https://blog.csdn.net/peng0614/article/details/81193484

技术博客,转载请注明。

https://www.cnblogs.com/pingwen/p/12374877.html

OpenCV3入门(十)图像轮廓的更多相关文章

  1. OpenCV计算机视觉学习(8)——图像轮廓处理(轮廓绘制,轮廓检索,轮廓填充,轮廓近似)

    如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 1, ...

  2. Android入门(十二)SQLite事务、升级数据库

    原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...

  3. C#基础入门 十

    C#基础入门 十 Windows应用程序的界面设计 Form.cs:窗体文件,一般用于存放程序员为窗体编写的代码: Form.Designer.cs:窗体设计文件,其中的代码是由VS自动生成的,一般不 ...

  4. Spring入门(十四):Spring MVC控制器的2种测试方法

    作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环 ...

  5. opencv 6 图像轮廓与图像分割修复 3 图像的矩,分水岭,图像修补

    图像的矩 矩的计算:moments()函数 计算轮廓面积:contourArea()函数 #include "opencv2/highgui/highgui.hpp" #inclu ...

  6. 网络编程懒人入门(十):一泡尿的时间,快速读懂QUIC协议

    1.TCP协议到底怎么了? 现时的互联网应用中,Web平台(准确地说是基于HTTP及其延伸协议的客户端/服务器应用)的数据传输都基于 TCP 协议. 但TCP 协议在创建连接之前需要进行三次握手(如下 ...

  7. matlab 提取图像轮廓(图像边缘提取)

    利用edge()函数提取图像轮廓,绘制出对象的边界和提取边界坐标信息,matlab实现代码如下: close all;clear all;clc; % 提取图像轮廓,提取图像边缘 I = imread ...

  8. OpenCV笔记(3)(Canny边缘检测、高斯金字塔、拉普拉斯金字塔、图像轮廓、模板匹配)

    一.Canny边缘检测 Canny边缘检测是一系列方法综合的结果.其中主要包含以下步骤: 1.使用高斯滤波器,平滑图像,滤除噪声. 2.计算图像中每个像素点的梯度强度和方向. 3.应用非极大值抑制(N ...

  9. OpenCV3入门(十四)图像特效—挤压、哈哈镜、扭曲

    一.图像挤压特效 1.原理 图像压效果本质的图像坐标的非线性变换,将图像向内挤压,挤压的过程产生压缩变形,从而形成的效果. 挤压效果的实现是通过极坐标的形式,设图像中心为O(x,y),某点距离中心O的 ...

随机推荐

  1. Linux.cp命令总提示是否覆盖

    执行cp命令,其实是默认执行了cp -i命令的别名,因此总提示是否覆盖. 修改~/.bashrc,注释“alias cp='cp -i'”即可. [root@xxxx test]# vi ~/.bas ...

  2. 5.JavaSE之数据类型详解

    数据类型: 强类型语言: 要求变量的使用严格要求符合规定,写错了就不行,所有变量都必须先定义后才能使用,否则是不能使用的. 比如Java.C++都是强类型语言,也就是说,一旦定义了一个变量,只定义了某 ...

  3. Nginx配置不同端口号映射二级域名

    upstream xx{ #ip_hash; server 127.0.0.1:1008; } server { listen 80; server_name xx.xxx.com; location ...

  4. mongo 查询 距离 某个点 多少 米距离 感谢 提供的数据。 感谢 mvc的 demo 。反正 就是各种感谢 文档之类的。

    昨天 去面试来着, 问了一下mong . 我记得mong支持 地理位置索引的,说了一下. 然后 面试官说 查询某个点 的 多少米范围, 这个该怎么实现? 我懵逼了.... 回去 查询了一下. 发现有 ...

  5. Ceph 之RGW Cache

    Overview 缓存是为达到系统快速响应的一项关键技术,Ceph 作为一个复杂的分布式存储系统,有多种.多级缓存存在.缓存按照位置分为: 客户端缓存 服务端缓存 网络中缓存 按照部署方式分为: 单体 ...

  6. [SDOI2011]染色(树链剖分)

    [SDOI2011]染色(luogu) Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段 ...

  7. Educational Codeforces Round 80 (Rated for Div. 2)部分题解

    A. Deadline 题目链接 题目大意 给你\(n,d\)两个数,问是否存在\(x\)使得\(x+\frac{d}{x+1}\leq n\),其中\(\frac{d}{x+1}\)向上取整. 解题 ...

  8. Java8新特性一点通 | 回顾功能接口Functional Interface

    Functional Interface Functional Interface是什么? 功能接口是java 8中的新增功能,它们只允许一个抽象方法.这些接口也称为单抽象方法接口(SAM接口).这些 ...

  9. Linux 误删catlina.out导致磁盘空间爆满,无法查询到大文件解决办法

    大概是前俩天吧,发现公司的网站不定时的出现接口调不通的情况,便让手下小弟去服务器上查看一下,小弟告我磁盘空间满了,于是我让他处理一下.结果没想到他直接把 catlina.out 给干掉了.后果可想而知 ...

  10. 使用nginx构建一个具备缓存功能的反向代理服务器

    上游服务一般不提供公网访问. upstream模块,名字叫local 这个时候访问,都是由反向代理服务处理返回的. 有了反向代理服务后,拿变量和值会出错,tcp是有对端地址的,反向代理与客户端是一个t ...