Opencv 笔记9 轮廓

一、轮廓

一个轮廓代表 一系列的点(像素),这一系列的点构成一个有序的点集,所以可以把一个轮廓理解为 一个有序的点集。

二、程序显示

在使用这些算子的时候一般都要进行图像预处理,其目的是为了查找更加清晰的轮廓

一般的预处理的主要包括:灰度值化、图像去噪(高斯滤波、均值滤波等)、二值化、形态学处理:morphologyEx() 等

findContours

findContours(
InputOutputArray  binImg, 		//输入8bit图像(二值图像)
 utputArrayOfArrays  contours,	//返回的轮廓点集合,一个list,每一个元素是一个轮廓,轮廓是一个N*1*2的ndarray
OutputArray,  hierachy			// 图像的拓扑结构(轮廓之间的层次关系):是一个N*4的array,hierarchy[i][0]~hierarchy[i][3]分别表示第i个轮廓的后一个轮廓,
int mode, 						//轮廓返回的模式 轮廓检索模式
                                              RETR_EXTERNAL: 只检索最外层的轮廓 (返回值会设置所有hierarchy[i][2]=hierarchy[i][3]=-1)
                                              RETR_LIST: 检索所有的轮廓,但不建立轮廓间的层次关系(hierarchy relationship)
                                              RETR_CCOMP:  检测所有的轮廓,但只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层,只有内围轮廓不再包含子轮廓时,其为内层。
                                              RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

int method,						//发现方法:
                                   CHAIN_APPROX_NONE: 保存物体边界上所有连续的轮廓点到contours中,即点(x1,y1)和点(x2,y2),满足max(abs(x1-x2),abs(y2-y1))==1,则认为其是连续的轮廓点
                                   CHAIN_APPROX_SIMPLE: 仅保存轮廓的拐点信息到contours,拐点与拐点之间直线段上的信息点不予保留
                                   CHAIN_APPROX_TC89_L1: 采用Teh-Chin chain近似算法 
                                   CHAIN_APPROX_TC89_KCOS:采用Teh-Chin chain近似算法
Point offset=Point()			//轮廓像素的位移(默认没有位移(0, 0))所有的轮廓信息相对于原始图像的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量(在图片裁剪时比较有用)
)

drawContours


drawContours(
InputOutputArray  binImg, 		// 绘制的轮廓的图像矩阵
OutputArrayOfArrays  contours,	//找到的全部轮廓对象
Int contourIdx,					//轮廓索引号 表示指定一个轮廓进行绘制;若为负数,表示绘制所有轮廓
const Scalar & color,			//绘制颜色
int  thickness,					//绘制线宽
int  lineType,					//线的类型(默认8)
InputArray hierarchy,			//拓扑结构图
int maxlevel,					//最大层数(0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓)
Point offset=Point(),			//轮廓位移
)

 

 代码显示(代码是随便写得,很多细节不考虑):

const  char* INPUT_TITLE = "INPUT-IMAGE1";
const  char* OUT_TITLE = "OUT_WIN";
Mat  base, src, src2, map_x, map_y, dst, src_gary, temp;
int  max_Threshod = 255;
int Threshod_value = 100;
void Callback_Canny(int, void*);
void Callback_Canny(int, void*)
{
	Mat  cannyoutput;

	vector<vector<Point>> contours;

	vector<Vec4i> hierachy;//  拓补结构层次

	// 2 、使用canny 算子来提取边缘  得到一个2值化的图像
	Canny(src_gary, cannyoutput, Threshod_value, Threshod_value * 2, 3, false);
	imshow("Canny", cannyoutput);

	
	Mat element = getStructuringElement(MORPH_CROSS, Size(10, 1), Point(-1, -1));
	dilate(cannyoutput, cannyoutput, element, Point(-1, -1), 2);
	imshow("dilate", cannyoutput);
	threshold(cannyoutput, cannyoutput,0,255, THRESH_OTSU + THRESH_BINARY);
	imshow("threshold", cannyoutput);
	// 3、  找到轮廓
	findContours(cannyoutput, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	
	//  4   画出轮廓
	dst = Mat::zeros(src_gary.size(), CV_8SC3);
	base = src.clone();
	RNG r(12345);
	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar sc = Scalar(r.uniform(0, 255), r.uniform(0, 255), r.uniform(0, 255));
		drawContours(dst, contours, i, sc, 2, 8, hierachy, 0, Point(0, 0));
		drawContours(base, contours, i, sc, 2, 8, hierachy, 0, Point(0, 0));
	}
	imshow(OUT_TITLE, dst);
	imshow(OUT_TITLE, base);

}
int main(int args, char* arg)
{
	//目标图像
	src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\501.png");
	if (!src.data)
	{
		printf("could not  load  image....\n");
	}
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	namedWindow(OUT_TITLE, CV_WINDOW_AUTOSIZE);
	// 1,将这个图像转换成灰度图像
	cvtColor(src, src_gary, CV_BGR2GRAY);
	GaussianBlur(src_gary, src_gary,Size(3,3),1);
	createTrackbar(" Threshod:", OUT_TITLE, &Threshod_value, max_Threshod, Callback_Canny);
	Callback_Canny(0, 0);
	waitKey(0);
	return 0;
}

三、轮廓的周长与面积

上面我们已经找到了轮廓,是一些点的集合,那么我们要计算面积和周长等如何来计算了,OpenCV 给出了下面这这些函数:

contourArea

contourArea  函数调用形式  主要用于计算图像轮廓的面积
double contourArea(
InputArray contour,://输入的点,一般是图像的轮廓点
bool oriented=false ) //表示某一个方向上轮廓的的面积值,顺时针或者逆时针,一般选择默认false

arcLength

arcLength 主要是计算图像轮廓的周长、
double arcLength(InputArray curve)
InputArray curve:表示图像的轮廓
bool closed:表示轮廓是否封闭的
)

示例如下:

 

 

 

 

 代码显示: 

//目标图像
	src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\503.jpg");
	if (!src.data)
	{
		printf("could not  load  image....\n");
	}
	imshow("原图", src);
	cvtColor(src, src_gary, CV_BGR2GRAY);
	imshow("cvtColor", src_gary);


	threshold(src_gary, src_gary, 177, 255, THRESH_BINARY);
	Mat element = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
	dilate(src_gary, src_gary, element, Point(-1, -1), 1);
	imshow("threshold", src_gary);
	vector<vector<Point>> contours_p;
	//vector<Vec4i> hierachy;//  拓补结构层次
	findContours(src_gary, contours_p, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	int xcon = boundingRect(contours_p[0]).x;
	int ycon = boundingRect(contours_p[0]).y;
	int width = boundingRect(contours_p[0]).width;
	int hight = boundingRect(contours_p[0]).height;
	Rect  rect = boundingRect(contours_p[0]);
	RotatedRect rect2 = minAreaRect(contours_p[0]);
	// ==============最小外接圆==========================================
	Point2f center;
	float radius;
	minEnclosingCircle(contours_p[0], center, radius);
	src_copy_circle = src.clone();
	circle(src_copy_circle, center, radius, Scalar(0, 0, 255), 1, 8);
	imshow("circle", src_copy_circle);
	//==============最优拟合椭圆===============

	RotatedRect box = fitEllipse(contours_p[0]);
	src_copy_fitEllipse = src.clone();
	ellipse(src_copy_fitEllipse, box, Scalar(0, 0, 255), 1, CV_AA);
	imshow("ellipse", src_copy_fitEllipse);


	//============外接矩形=================
	src_copy = src.clone();
	src_copy_sm = src.clone();
	rectangle(src_copy, rect, Scalar(0, 0, 255), 1, 8);
	imshow("rectangle", src_copy);
	//==============旋转矩形===============
	Point2f vertices[4];
	rect2.points(vertices);	
	for (int i = 0; i < 4; i++)
	{
		line(src_copy_sm, vertices[i], vertices[(i + 1) % 4], Scalar::all(255), 1, 8);
	}
	imshow("RotatedRect", src_copy_sm);
	//========================外界三角形======================

    //CV_EXPORTS_W double minEnclosingTriangle(InputArray points, CV_OUT OutputArray triangle);
	vector<Point2f>triangle;
	minEnclosingTriangle(contours_p[0], triangle);
	src_triangle = src.clone();
	for (int i = 0; i < 3; i++)
	{
		line(src_triangle, triangle[i], triangle[(i + 1) % 3], Scalar(255, 255, 0), 1, 8);
	}
	imshow("triangle", src_triangle);
	==============拟合多边形===============
	//approxPolyDP(contours_p[i], contours_poly[i], 15, true);
	//  InputArray curve:输入曲线,数据类型可以为vector<Point>
	//	OutputArray approxCurve:输出折线,数据类型可以为vector<Point>。
	//	double epsilon:判断点到相对应的line segment 的距离的阈值。(距离大于此阈值则舍弃,小于此阈值则保留,epsilon越小,折线的形状越“接近”曲线。)
	//	bool closed:曲线是否闭合的标志位。
	src_p = src.clone();
	 vector<vector<Point>> contours_poly(contours_p.size());//用于存放折线点集
	for (int i = 0; i < contours_p.size(); i++)
	{
		approxPolyDP(contours_p[i], contours_poly[i], 10, true);

		drawContours(src_p, contours_poly, i, Scalar(0, 255, 0), 2, 8);  //绘制
	}
	imshow("approx", src_p);


	waitKey(0);
	return 0;

 拟合直线:

void cv::fitLine(
InputArray points, // 二维点的数组或vector,可以是二维点的cv::Mat数组,也可以是二维点的STL vector。
OutputArray line, // 输出直线,Vec4f (2d) 或   Vec6f (3d) 输出参数的前半部分给出的是直线的方向,而后半部分给出的是直线上的一点
int distType, // 距离类型,拟合直线时,要使输入点到拟合直线的距离和最小化(即下面公式中的cost最小化),可供选的距离类型如下表所示,ri表示的是输入的点到直线的距离
double param, // 距离参数,与所选的距离类型有关。当此参数被设置为0 时,该函数会自动选择最优值
double reps, // 径向的精度参数,拟合直线所需要的径向精度,通常该值被设定为0.01
double aeps // 角度精度参数,拟合直线所需要的角度精度,通常该值被设定为0.01
);

 最小外包三角形

点和轮廓的位置关系:

么空间中任意一点和 这个轮廓无非有三种关系:点在轮廓外、点在轮廓上、点在轮廓内。OpenCV提供的函数:

 参数measureDist是bool类 型,当其值为false时,函数pointPolygonTest的返回值有三种,即+1、0、-1,+1代表pt在 点集围成的轮廓内,0代表pt在点集围成的轮廓上,-1代表pt在点集围成的轮廓外;当其 值为true时,则返回值为pt到轮廓的实际距离。

int main()
{
	vector<Point> contours;
	contours.push_back(Point2f(0, 0));
	contours.push_back(Point2f(50, 30));
	contours.push_back(Point2f(100, 0));
	contours.push_back(Point2f(100, 100));
	Mat  img2, img3;
	Mat  img = Mat::zeros(Size(230,230),CV_8UC1); 
	int  num = contours.size(); //点的数量
	for (int i = 0; i < num; i++)
	{
		line(img, contours[i], contours[(i + 1)%4],Scalar(255),1);
	}
	// 首尾相连
	// 标注点的位置
	   img2 = img.clone();
	   img3 = img.clone();

	circle(img, Point(80, 40), 3, Scalar(255), CV_FILLED);
	circle(img2, Point(50, 0), 3, Scalar(255), CV_FILLED);
	circle(img3, Point(100, 100), 3, Scalar(255), CV_FILLED);
	// 点在轮廓内
	double  dist1 = pointPolygonTest(contours, Point2f(80, 40),true);
	cout << "dist1   " << dist1 << endl;
	double  dist2 = pointPolygonTest(contours, Point2f(50, 0), true);
	cout << "dist2   " << dist2 << endl;
	double  dist3 = pointPolygonTest(contours, Point2f(100, 100), true);
	cout << "dist3   " << dist3 << endl;
	imshow("轮廓", img);
	imshow("轮廓2", img2);
	imshow("轮廓3", img3);
	waitKey(0);
	return 0;
}

轮廓的凸包缺陷

通过函数convexHull可以得到点集的最小凸包,OpenCV还提 供了一个函数:

void convexityDefects(
InputArray contour,//检测到的轮廓
InputArray convexhull, //检测到的凸包 vector<vector<Point>>和vector<vector<int>>两种类型结果
 OutputArrayconvexityDefects)//输出参数,检测到的最终结果 vector<vector<Vec4i>>类型,Vec4i存储了起始点(startPoint),结束点(endPoint),距离convexity hull最远点(farPoint)以及最远点到convexity hull的距离(depth)

    vector<Point> contours;
	contours.push_back(Point2f(20, 20));
	contours.push_back(Point2f(50, 70));
	contours.push_back(Point2f(20, 120));
	contours.push_back(Point2f(120, 120));
	contours.push_back(Point2f(100, 70));
	contours.push_back(Point2f(120, 20));
	vector<int> hull;
	convexHull(contours, hull,false,false);
	//计算凸包
	vector<Vec4i> defects;
	convexityDefects(contours,hull, defects);
	//
	for (int i = 0; i < defects.size(); i++)
	{
		cout << defects[i] << endl;
	}
	waitKey(0);
	return 0;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值