OpenCV学习之路(十八) 图像的轮廓

 

目录

查找并绘制轮廓:

查找并绘制物体的凸包

使用多边形将轮廓包围

图像的矩


 

查找并绘制轮廓

查找并绘制凸包

轮廓外接矩形和最小外接圆

轮廓最小外接矩形和外接椭圆

图像的矩

 

查找并绘制轮廓:

1. findContours() 函数。查找轮廓。函数原型如下:

void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());

(1)第一个参数,InputArray 类型的 image,源图像。8位单通道。图像的非零元素被视为 1,0 像素值保留为 0,所以图像为二进制。也可以使用 compare()、inrange()、threshold()、adaptivethreshold()、canny() 等函数由灰度图或彩色图创建二值图。此函数将会在提取图像轮廓的同时修改图像的内容。如果第四个参数为cv::RETR_CCOMP或cv::RETR_FLOODFILL,输入图像可以是32-bit整型图像(CV_32SC1) 。

(2)第二个参数,OutputArrayOfArrays 类型的 contours、检测到的轮廓、函数调用后的运算结果存在这里。每个轮廓存储为一个点向量,即用 Point 类型的 vector 表示。

(3)第三个参数,OutputArray 类型的 hierarchy,可选的输出向量,包含图像的拓扑信息。其作为轮廓数量的表示,包含了很多元素。每个轮廓 contours[ i ] 对应 4 个 hierarchy 元素 hierarchy[ i ][ 0 ] ~ hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果没有对应项,对应的 hierarchy[ i ] 值设置为负数。

(4)第四个参数,int 类型的 mode,轮廓检索模式,如下所示:

      RETR_EXTERNAL,表示只检索最外层轮廓。对所有轮廓,设置 hierarchy[i][2] = hierarchy[i][3] = -1。

      RETR_LIST,提取所有的轮廓,并且放置在 list 中。检测的轮廓不建立等级关系。

      RETR_CCOMP,提取所有轮廓,并且将其组织为双层结构(顶层为连通域的外围边界,次层为孔的内层边界)。

      RETR_TREE,提取所有轮廓,并重新建立网状的轮廓结构。

(5)第五个参数,int 类型的 method,为轮廓的近似方法。如下所示:

     CHAIN_APPROX_NONE,获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1。

     CHAIN_APPROX_SIMPLE,压缩水平、垂直、对角线方向的元素,只保留该方向的终点坐标。如矩形只留四个点。

     CHAIN_APPROX_TC89_L1 和 CHAIN_APPROX_TC89_KCOS, 使用 Teh-Chinl 链逼近算法中的一个。

 

2. drawContours() 函数,绘制轮廓。函数原型如下:

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

(1)第一个参数,InputArray 类型的 image,输入输出图像。

(2)第二个参数,InputArrayOfArrays 类型的 contours,所有的输入轮廓,findContours() 查找到的。

(3)第三个参数,int 类型的 contourIdx,轮廓绘制的指示变量。若为负值,则绘制全部轮廓。

(4)第四个参数,const Scalar& 类型的 color,轮廓的颜色。

(5)第五个参数,int 类型的 thickness,轮廓线条的粗细度,默认值为 1。若为负值,便会绘制在轮廓的内部。可选为FILLED 宏变量。

(6)第六个参数,int 类型的 lineType,线条的类型,默认值为 8。有如下类型:

        8, 8 联通线型;

        4,4 联通线型;

        LINE_AA, 抗锯齿线型。

(7)第七个参数,InputArray 类型的 hierarchy,可选的层次结构信息,默认值为 noArray()。

(8)第八个参数,int 类型的 maxLevel,表示用于绘制轮廓的最大等级,默认值为 INT_MAX。

(9)第九个参数,Point 类型的 offset,可选的轮廓偏移参数,用指定的偏移量 offset = (dx, dy)偏移需要绘制的轮廓。默认值为 Point()。

 

简单示例代码如下:

#include
#include

using namespace cv;
using namespace std;

void on_ContourThresh(int, void*);

int g_contourThresh = 100;
int g_maxContourThresh = 255;

const char* contourThreshStr = "阈值";

Mat srcContourImg, grayContourImg;
RNG rng(12345);

int main()
{
	srcContourImg = imread("HappyFish.jpg");
	imshow("原图", srcContourImg);

	cvtColor(srcContourImg, grayContourImg, COLOR_BGR2GRAY);
	blur(grayContourImg, grayContourImg, Size(3, 3));

	namedWindow("效果图", WINDOW_AUTOSIZE);

	createTrackbar(contourThreshStr, "效果图", &g_contourThresh, g_maxContourThresh, on_ContourThresh);
	
	on_ContourThresh(0, 0);

	waitKey(0);
	return 0;
}

void on_ContourThresh(int, void*)
{
	Mat cannyImg;
	vector> contours; //存储轮廓矢量
	vector hierarchy; //存储轮廓拓扑结构

	Canny(grayContourImg, cannyImg, g_contourThresh, 2 * g_contourThresh);

	//查找轮廓
	findContours(cannyImg, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//输出 RGB 图像
	Mat dstImg = Mat::zeros(cannyImg.size(), CV_8UC3);

	for (int i = 0; i < contours.size(); i++)
	{
		//随机给定颜色
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		//绘制轮廓
		drawContours(dstImg, contours, (int)i, color, 2, 8, hierarchy, 0, Point(0, 0));
	}

	imshow("效果图", dstImg);
}

代码运行如下所示:

OpenCV学习之路(十八) 图像的轮廓_第1张图片

 

查找并绘制物体的凸包

凸包就是将物体最外层的点连接起来构成的凸多边形,因此能够包含物体所有的点。

理解物体形状或轮廓的一种比较有用的方式就是计算该物体的凸包,然后计算其凸缺陷。

1. convexHull() 函数。寻找凸包。函数原型如下:

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

(1)第一个参数,InputArray 类型的 points,输入的二维点集,可以点 Mat 或 vector。

(2)第二个参数,OutputArray 类型的 hull,输出参数,调用函数后找到的凸包。

(3)第三个参数,bool 类型的 clockwise,操作方向标识符,默认值为false。为 true 时,输出的凸包为顺时针方向;为 false 时,输出的凸包为逆时针方向。并且假定坐标系的 x 轴指向右,y 轴指向上方。

(4)第四个参数,bool 类型的 returnPoints ,操作标识符,默认值为 true。为 true 时,函数返回凸包的各个点;为 false 时,返回凸包各点的指数。当输出数组是 std::vector 时,此标志被忽略。

 

简单示例代码如下:

#include
#include

using namespace cv;
using namespace std;

void on_ContourThresh(int, void*);

int g_contourThresh = 100;
int g_maxContourThresh = 255;

const char* contourThreshStr = "阈值";

Mat srcContourImg, grayContourImg;
RNG rng(12345);

int main()
{
	srcContourImg = imread("HappyFish.jpg");
	imshow("原图", srcContourImg);

	cvtColor(srcContourImg, grayContourImg, COLOR_BGR2GRAY);
	blur(grayContourImg, grayContourImg, Size(3, 3));

	namedWindow("效果图", WINDOW_AUTOSIZE);

	createTrackbar(contourThreshStr, "效果图", &g_contourThresh, g_maxContourThresh, on_ContourThresh);
	
	on_ContourThresh(0, 0);

	waitKey(0);
	return 0;
}

void on_ContourThresh(int, void*)
{
	Mat cannyImg;
	vector> contours; //存储轮廓矢量
	vector hierarchy; //存储轮廓拓扑结构

	Canny(grayContourImg, cannyImg, g_contourThresh, 2 * g_contourThresh);
	//threshold(grayContourImg, cannyImg, g_contourThresh, 2 * g_contourThresh,THRESH_BINARY);

	//查找轮廓
	findContours(cannyImg, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//查找凸包
	vector> hull(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
		convexHull(contours[i], hull[i],false);
	}

	//输出 RGB 图像
	Mat dstImg = Mat::zeros(cannyImg.size(), CV_8UC3);

	for (size_t i = 0; i < contours.size(); i++)
	{
		//随机给定颜色
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		//绘制轮廓
		//drawContours(dstImg, contours, (int)i, color, 1, 8, hierarchy, 0, Point(0, 0));
		//绘制凸包
		drawContours(dstImg, hull, (int)i, color, 3, 8, vector(), 0, Point(0, 0));
	}

	imshow("效果图", dstImg);
}

运行结果如下:

OpenCV学习之路(十八) 图像的轮廓_第2张图片

 

使用多边形将轮廓包围

1. boundingRect() 函数,返回外部矩形边界。函数原型如下:

Rect boundingRect(InputArray poins);

唯一的参数,输入的二维点集。可以为 Mat 类型或 std::vector。

2. minAreaRect() 函数,寻找最小包围矩形。函数原型如下:

RotatedRect minAreaRect(InputArray points);

唯一的参数,输入的二维点集。可以为 Mat 类型或 std::vector。

3. minEnclosingCircle() 函数,寻找最小包围圆形。函数原型如下:

void minEnclosingCircle(InputArray points, Point2f& center, float& radius);

(1)第一个参数,输入的二维点集。可以为 Mat 类型或 std::vector。

(2)第二个参数,Point2f& 类型的 center,圆的输出圆心。

(3)第三个参数,float& 类型的 radius,圆的输出半径。

4. fitEllipse() 函数,用椭圆拟合二维点集。函数原型如下:

RotateRect fitEllipse(InputArray points);

唯一的参数,输入的二维点集。可以为 Mat 类型或 std::vector。

5. approxPolyDP() 函数,逼近多边形曲线。函数原型如下:

void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

(1)第一个参数,输入的二维点集。可以为 Mat 类型或 std::vector。

(2)第二个参数,OutputArray 类型的 approxCurve,多边形逼近的结果,其类型应和输入的二维点集的类型一致。

(3)第三个参数,double 类型的 epsilon,逼近的精度,为原始曲线和即近曲线间的最大值。

(4)第四个参数,bool 类型的 closed,为 true 时,近似的曲线为封闭曲线(第一个顶点和最后一个顶点相连);为 false 时,近似的曲线不封闭。

 

简单示例代码如下:

#include
#include

using namespace cv;
using namespace std;

void on_ContourThresh(int, void*);

int g_contourThresh = 100;
int g_maxContourThresh = 255;

const char* contourThreshStr = "阈值";

Mat srcContourImg, grayContourImg;
RNG rng(12345);

int main()
{
	srcContourImg = imread("HappyFish.jpg");
	imshow("原图", srcContourImg);

	cvtColor(srcContourImg, grayContourImg, COLOR_BGR2GRAY);
	blur(grayContourImg, grayContourImg, Size(3, 3));

	namedWindow("效果图", WINDOW_AUTOSIZE);

	createTrackbar(contourThreshStr, "效果图", &g_contourThresh, g_maxContourThresh, on_ContourThresh);
	
	on_ContourThresh(0, 0);

	waitKey(0);
	return 0;
}

void on_ContourThresh(int, void*)
{
	Mat cannyImg;
	vector> contours; //存储轮廓矢量
	vector hierarchy; //存储轮廓拓扑结构

	Canny(grayContourImg, cannyImg, g_contourThresh, 2 * g_contourThresh);
	//threshold(grayContourImg, cannyImg, g_contourThresh, 2 * g_contourThresh,THRESH_BINARY);

	//查找轮廓
	findContours(cannyImg, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//查找凸包
	vector> hull(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
		convexHull(contours[i], hull[i],false);
	}

	//多边形逼近轮廓、外接矩形、最小外接圆形 及 最小外接矩形
	vector> polyContours(contours.size());
	vector rectContours(contours.size());
	vector center(contours.size());
	vector radius(contours.size());
	vector minRectContours(contours.size());

	//输出 RGB 图像
	Mat dstImg = Mat::zeros(cannyImg.size(), CV_8UC3);

	for (size_t i = 0; i < contours.size(); i++)
	{
		//随机给定颜色
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

		//绘制轮廓
		drawContours(dstImg, contours, (int)i, color, 1, 8, hierarchy, 0, Point(0, 0));

		//绘制凸包
		drawContours(dstImg, hull, (int)i, color, 3, 8, vector(), 0, Point(0, 0));

		//绘制多边形逼近轮廓
		approxPolyDP(contours[i], polyContours[i], 3,true);
		drawContours(dstImg, polyContours, (int)i, color, 3, 8, vector(), 0, Point(0, 0));

		//绘制外接矩形
		rectContours[i] = boundingRect(contours[i]);
		rectangle(dstImg, rectContours[i], color);

		//绘制最小外界圆形
		minEnclosingCircle(contours[i], center[i], radius[i]);
		circle(dstImg, center[i], radius[i], color);

		//绘制最小外接矩形
		minRectContours[i] = minAreaRect(contours[i]);
		Point2f minRect[4];
		minRectContours[i].points(minRect);
		for (int j = 0; j < 4; j++)
		{
			line(dstImg, minRect[j], minRect[(j + 1) % 4], color);
		}

	}

	imshow("效果图", dstImg);
}

运行效果如下所示:

OpenCV学习之路(十八) 图像的轮廓_第3张图片

 

图像的矩

图像的矩讲解

矩函数在图像分析中有着广泛的应用,如模式识别、目标分类、目标识别与方位估计、图像编码与重构等。从一幅数字图像中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量的关于该图像不同类型的几何特性信息,比如大小、位置、方向及形状等。图像矩的这种特性描述能力被广泛地引用在各种图像处理、计算机视觉和机器人技术领域的目标识别与方位估计中。一阶矩与形状有关,二阶矩显示曲线围绕直线平均值的扩展程度,三阶矩则是关于平均值的对称性的测量。由二阶矩和三阶矩可以导出一组共 7 个不变矩。而不变矩是图像的统计特性,满足平移、伸缩、旋转均不变的不变性,在图像识别领域得到了广泛的应用。

OpenCV中计算一个图像的矩一般由如下三个函数 配合求取。

1. moments() 函数,用来计算多边形和光栅形状的最高达三阶的所有矩。矩用来计算形状的重心、面积、主轴和其他形状特征,      如 7Hu 不变量等。函数原型如下:

Moments moments(InputArray array, bool binaryImage = false);

(1)第一个参数,InputArray 类型的 array,输入参数,可以是光栅图像(单通道,8 位或浮点的二维数组)或二维数组(1N 或 N1)。

(2)第二个参数,bool 类型的 binaryImage,默认值为 false。若此参数为 true,则所有非零像素为 1。此参数仅对于图像使用。

2. contourArea() 函数,用于计算整个轮廓或部分轮廓的面积。函数原型如下:

double contourArea(InputArray contour, bool oriented = false);

(1)第一个参数,InputArray 类型的 contour,输入的向量,二维点(轮廓顶点),可以为 std::vector 或 Mat 类型。

(2)第二个参数,bool 类型的 oriented,面向区域标识符,默认值为 false。为 true 时,该函数返回一个带符号的面积值,其正负取决于轮廓的方向(顺时针还是逆时针)。根据这个特性我们可以根据面积的符号来确定轮廓的位置。为 false 时,表示以绝对值返回,不带符号。

3. arcLength() 函数,用于计算封闭轮廓的周长或曲线的长度。函数原型如下:

double arcLength(InputArray curve, bool closed);

(1)第一个参数,InputArray 类型的 curve,输入的二维点集,可以为 std::vector 或 Mat 类型。

(2)第二个参数,bool 类型的 closed,用于指示曲线是否封闭的标识符。

 

简单示例代码如下:

#include
#include

using namespace std;
using namespace cv;

void on_Moments(int, void*);

int g_momentThresh = 100;
int g_maxMomentThresh = 255;

const char* momentThreshStr = "阈值";

Mat momentSrcImage,momentGrayImage;

int main()
{
	momentSrcImage = imread("smarties.png");
	imshow("原图", momentSrcImage);

	blur(momentSrcImage, momentSrcImage, Size(3, 3));
	cvtColor(momentSrcImage, momentGrayImage, COLOR_BGR2GRAY);

	namedWindow("效果图", WINDOW_AUTOSIZE);
	createTrackbar(momentThreshStr, "效果图", &g_momentThresh, g_maxMomentThresh, on_Moments);

	on_Moments(0, 0);

	waitKey(0);
	return 0;
}

void on_Moments(int, void*)
{
	RNG rng;
	Mat cannyImg, dstImg;

	vector> contours;
	vector hierarchy;

	//边缘检测
	Canny(momentGrayImage, cannyImg, g_momentThresh, 2 * g_momentThresh);

	//查找轮廓
	findContours(cannyImg, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//计算矩
	vector mu(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mu[i] = moments(contours[i], false);
	}

	//计算 图像灰度的“质心”
	vector mc(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mc[i] = Point2f(static_cast(mu[i].m01 / mu[i].m00), static_cast(mu[i].m10 / mu[i].m00));
	}

	//画出轮廓和“质心”
	Mat tempImage = Mat::zeros(cannyImg.size(), CV_8UC3);
	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(tempImage, contours, (int)i, color, 4);
		circle(tempImage, mc[i], 3, color, -1);
	}

	/*
	输出信息:
		1.通过矩 m00表示的轮廓的面积;
		2.通过 contourArea() 函数计算出来的面积;
		3.通过 arcLength() 函数计算出来的轮廓的周长
		
	*/
	for (size_t i = 0; i < contours.size(); i++)
	{
		printf(">通过矩 m00 计算出轮廓[%d]的面积:(M_00) = %.2f \n OpenCV 函数计算出的面积=%.2f , 周长=%.2f \n\n", (int)i, mu[i].m00, contourArea(contours[i]), arcLength(contours[i], true));

	}

	imshow("效果图", tempImage);

}

代码运行结果如下:

OpenCV学习之路(十八) 图像的轮廓_第4张图片

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(OpenCV,学习)