我是靠谱客的博主 老实月饼,最近开发中收集的这篇文章主要介绍轮廓(查找和绘制轮廓、轮廓的表达与组织、轮廓的特性),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

    • 1、轮廓的定义
    • 2、如何在图像中找到轮廓
      • opencv自带的查找轮廓函数:findContours()
    • 3、轮廓的表达方式
      • 1.顶点的序列
      • 2.Freeman链码
    • 4、轮廓之间的组织方式
    • 5、轮廓的特点(这部分可以展开来详细探讨,这里不做过多解释)
    • 6、轮廓的匹配
    • 7、绘制轮廓drawContours()
    • 8、例子
      • 动态检测图形的轮廓
        • 步骤
        • 理解
        • 演示效果

1、轮廓的定义

轮廓是构成任何一个形状的边界或外形线。

2、如何在图像中找到轮廓

利用OpenCV提供的方法cvFindContours()可以很方便的查找轮廓。cvFindContours()方法从二值图像中寻找轮廓。
因此此方法处理的图像可以是从cvCanny()函数得到的有边缘像素的图像,或者从cvThreshold()及cvAdaptiveThreshold()得到的图像,这时的边缘是正和负区域之间的边界。

opencv自带的查找轮廓函数:findContours()

参数讲解:
参数
参数
参数:
参数

1】检测轮廓
//定义轮廓和层次结构
vector<vector<Point>> contours;			//每个轮廓存储为一个点向量
vector<Vec4i>hierarchy;			//	Vec4i
findContours(srcImage,
			contours,		//轮廓数组
			hierarchy,
			RETR_EXTERNAL,		//获取外轮廓
			CHAIN_APPROX_NONE			//获取每个轮廓中的每个像素
			);

3、轮廓的表达方式

1.顶点的序列

序列是内存存储器中可以存储的一种对象。序列是某种结构的链表。序列在内存中被实现为一个双端队列,因此序列可以实习快速的随机访问,以及快速删除顶端的元素,但是从中间删除元素则稍慢些。
用多个顶点(或各点间的线段)来表达轮廓。假设要表达一个从(0,0)到(2,2)的矩形,
(1)如果用点来表示,那么依次存储的可能是:(0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1);
(2)如果用点间的线段来表达轮廓,那么依次存储的可能是:(0,0),(2,0),(2,2),(0,2)。

2.Freeman链码

Freeman链码需要一个起点,以及从起点出发的一系列位移。每个位移有8个方向,从0~7分别指向从正北开始的8个方向。假设要用Freeman链码表达从(0,0)到(2,2)的矩形,可能的表示方法是:起点(0,0),方向链2,2,4,4,6,6,0,0。

4、轮廓之间的组织方式

在查找到轮廓之后,不同轮廓是怎么组织的呢?根据不同的选择,它们可能是:(1)列表;(2)双层结构;(3)树型结构。
从纵向上来看,列表只有一层,双层结构有一或者两层,树型结构可能有一层或者多层。
如果要遍历所有的轮廓,可以使用递归的方式。

5、轮廓的特点(这部分可以展开来详细探讨,这里不做过多解释)

1.轮廓的多边形逼近
轮廓的多边形逼近指的是:使用多边形来近似表示一个轮廓。
多边形逼近的目的是为了减少轮廓的顶点数目。
多边形逼近的结果依然是一个轮廓,只是这个轮廓相对要粗旷一些。 可以使用方法cvApproxPoly()

2.轮廓的关键点
轮廓的关键点是:轮廓上包含曲线信息比较多的点。关键点是轮廓顶点的子集。
可以使用cvFindDominantPoints函数来获取轮廓上的关键点,该函数返回的结果一个包含 关键点在轮廓顶点中索引 的序列。再次强调:是索引,不是具体的点。如果要得到关键点的具体坐标,可以用索引到轮廓上去找。
3.轮廓的周长和面积
轮廓的周长可以用cvContourPerimeter或者cvArcLength函数来获取。
轮廓的面积可以用cvContourArea函数来获取。

4.轮廓的边界框
有三种常见的边界框:矩形、圆形、椭圆。
(1)矩形:在图像处理系统中提供了一种叫Rectangle的矩形,不过它只能表达边垂直或水平的特例;OpenCv中还有一种叫Box的矩形,它跟数学上的矩形一致,只要4个角是直角即可。
如果要获取轮廓的Rectangle,可以使用cvBoundingRect函数。
如果要获取轮廓的Box,可以使用cvMinAreaRect2函数。
(2)圆形
如果要获取轮廓的圆形边界框,可以使用cvMinEnclosingCircle函数。
(3)椭圆
如果要获取轮廓的椭圆边界框,可以使用cvFitEllipse2函数。
5.轮廓的矩
矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个粗略特征。
1空间矩
空间矩的实质为面积或者质量。可以通过一阶矩计算质心/重心。
公式
重心:重心
2中心矩
中心矩体现的是图像强度的最大和最小方向(中心矩可以构建图像的协方差矩阵),其只具有平移不变性,所以用中心矩做匹配效果不会很好。
中心
3归一化的中心矩
归一化后具有尺度不变性。
距
4Hu矩
12
6.轮廓的轮廓树
轮廓树用来描述某个特定轮廓的内部特征。注意:轮廓树跟轮廓是一一对应的关系;轮廓树不用于描述多个轮廓之间的层次关系。
轮廓树的创建过程:
从一个轮廓创建一个轮廓树是从底端(叶子节点)到顶端(根节点)的。首先搜索三角形突出或者凹陷的形状的周边(轮廓上的每一个点都不是完全和它的相邻点共线的)每个这样的三角形被一条线段代替,这条线段通过连接非相邻点的两点得到;因此实际上三角形或者被削平或者被填满。每个这样的替换都把轮廓的顶点减少,并且给轮廓树创建一个新节点。如果这样的一个三角形的两侧有原始边,那么她就是得到的轮廓树的叶子;如果一侧已是一个三角形,那么它就是那个三角形的父节点。这个过程的迭代最终把物体的外形简称一个四边形,这个四边形也被剖开;得到的两个三角形是根节点的两个子节点。
结果的二分树最终将原始轮廓的形状性比编码。每个节点被它所对应的三角形的信息所注释。
这样建立的轮廓树并不太鲁棒,因为轮廓上小的改变也可能会彻底改变结果的树,同时最初的三角形是任意选取的。为了得到较好的描述需要首先使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么收到旋转影响的状态。
可以用函数cvCreateContourTree来构造轮廓树。
7.轮廓的凸包和凸缺陷
这块内容会在后续详细探讨。
8.轮廓的成对几何直方图
成对几何直方图(pairwise geometrical histogram PGH)是链码编码直方图(chain code histogram CCH)的一个扩展或者延伸。CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字。这种直方图的一个优良性质为当物体旋转45度,那么新直方图是老直方图的循环平移。这样就可以不受旋转影响。
(1)轮廓保存的是一系列的顶点,轮廓是由一系列线段组成的多边形。对于看起来光滑的轮廓(例如圆),只是线段条数比较多,线段长度比较短而已。实际上,电脑中显示的任何曲线都由线段组成。
(2)每两条线段之间都有一定的关系,包括它们(或者它们的延长线)之间的夹角,两条线段的夹角范围是:(0,180)。
(3)每两条线段上的点之间还有距离关系,包括最短(小)距离、最远(大)距离,以及平均距离。最大距离我用了一个偷懒的计算方法,我把轮廓外界矩形的对角线长度看作了最大距离。
(4)成对几何直方图所用的统计数据包括了夹角和距离。

6、轮廓的匹配

1.Hu矩匹配
轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。
2.轮廓树匹配
用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。
3.成对几何直方图匹配
在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹配。

7、绘制轮廓drawContours()

轮廓的绘制比较简单,用上面提到的方法取得轮廓的所有点,然后把这些点连接成一个多边形即可。
使用函数drawContours(),更加方便。
1
2
使用例子:

//【2】绘制轮廓
//遍历所有顶层的轮廓,用不同的颜色绘制出来
int index = 0;
for (; index >= 0; index = hierarchy[index][0])
{
	Scalar color(rand() & 255,rand() & 255,rand() & 255);		//随机数
	drawContours(dstImage,		//outputImage
				contours, 		//轮廓信息
				index, 			//当前轮廓的索引值
				color, 			//轮廓颜色
				FILLED, 		//绘制在轮廓内部
				8,				//8连通线型
				hierarchy);		//轮廓间的层次信息
}

8、例子

动态检测图形的轮廓

步骤

【1】读取原图,转为灰度图并高斯模糊
【2】canny检测图像边缘(滑动条控制阈值)
【3】对canny算子扫描后的图像进行查找轮廓
【4】绘制轮廓

理解

Canny之类的边缘检测算法可以根据像素间的差异检测出轮廓边界的像素,但是它并没有将轮廓作为一个整体。
对canny扫描后的图像查找轮廓是一种较好的选择。

#include <opencv2/opencv.hpp>
#include <iostream>
#include "windows.h"
#include <stdio.h>
#define WINDOW_NAME "【程序窗口】"			//为窗口标题定义的宏

using namespace cv;
using namespace std;
//===========================动态检测图形的轮廓====================

//=================全局变量声明=================
Mat g_srcImage;
Mat g_grayImage;
int g_nThresh = 80;
int g_nThresh_max = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;

//=============全局函数声明===============
void on_ThreshChange(int,void*);
int main()
{
	// Read image 读取图像
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);		//字体为绿色
	//载入原图
	g_srcImage = imread("D:\opencv_picture_test\lena.jpg",1);
	//Mat srcImage = imread("D:\opencv_picture_test\形态学操作\孔洞.png", 0);	//读取灰度图
	//转换成灰度并且模糊化降噪
	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
	blur(g_grayImage, g_grayImage, Size(3, 3));
	//创建窗口
	namedWindow("原始图窗口", WINDOW_AUTOSIZE);
	imshow("原始图窗口", g_srcImage);
	//创建滑动条并初始化
	createTrackbar("canny 阈值", "原始图窗口", &g_nThresh,
		g_nThresh_max, on_ThreshChange);
	on_ThreshChange(0,0);
	waitKey(0);
	return 0;
}
void on_ThreshChange(int, void*)
{
	//用Canny算子检测边缘
	Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);
	//寻找轮廓.
	findContours(g_cannyMat_output, g_vContours, g_vHierarchy,
		RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//绘出轮廓
	Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
	for (int i = 0; i < g_vContours.size(); i++)
	{
		Scalar color = Scalar(g_rng.uniform(0, 255),
			g_rng.uniform(0, 255), g_rng.uniform(0, 255));//任意值
		drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy,
			0, Point());
	}
	//显示效果图
	imshow("轮廓图",drawing);
}

演示效果

演示结果

参考链接:
主讲轮廓的特性;
轮廓的矩;
轮廓的矩;
以及《《OpenCV3编程入门》毛星云编著_电子工业出版社》。

最后

以上就是老实月饼为你收集整理的轮廓(查找和绘制轮廓、轮廓的表达与组织、轮廓的特性)的全部内容,希望文章能够帮你解决轮廓(查找和绘制轮廓、轮廓的表达与组织、轮廓的特性)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部