OpenCV学习笔记基础篇(十六):寻找凸包、使用多边形将轮廓包围

前言:

笔者目前在校本科大二,有志于进行计算机视觉、计算机图形学方向的研究,准备系统性地、扎实的学习一遍OpenCV的内容,故记录学习笔记,同时,由于笔者同时学习数据结构、机器学习等知识,会尽量根据自己的理解,指出OpenCV的应用,并在加上自己理解的前提下进行叙述。
若有不当之处,希望各位批评、指正。


本篇学习内容:

1.寻找凸包
2.使用多边形将轮廓包围


1.寻找凸包

1.1 凸包

摘自博客:https://www.cnblogs.com/aiguona/p/7232243.html
凸包(Convex Hull)是一个计算几何(图形学)中的概念。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。
X的凸包可以用X内所有点(X1,…Xn)的线性组合来构造.

很多数学学科中都会提到类似的概念,例如“凸区域”、“闭包”等。在仅考虑二维点集的情况下,这个概念可以很直观:给一个点集S,尽可能少地在S中选k个点,将这k个点连起来,所围成的多边形为一凸多边形,并且所有S中的点都在这个凸多边形内。
我这样叙述的定义可能不太严谨,各位也可以想象点集S为二维平面上的一个个钉子,凸包就是将一个橡皮筋撑开,然后放手,橡皮筋围成的凸多边形的顶点集合就是凸包了。

另外,博客https://www.cnblogs.com/aiguona/p/7232243.html中还介绍了一种计算凸包的方法,Graham扫描法,非常清晰易懂。
在这里插入图片描述

1.2 寻找凸包

用convexHull()寻找凸包

OpenCV使用Sklansky算法来寻找凸包。

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

对参数进行介绍:
points: 输入的点集,可以是Mat形式,也可以是Point型的vector
hull: 输出的凸包,可以输出int型的vector,或者Point型的vector。对于第一种情况,由于凸包是输入集的子集,所以输出的vector保存的是输入集的下标指引;对于第二种情况,输出的是凸包的所有点。
clockwise: 如果为true,输出的凸包是顺时针的,如果是false,输出的凸包是逆时针的。前提是假定坐标轴的X轴指向右,Y轴指向上。
returnPoints: 如果hull那边输出到一个int型的vector或者Point型的vector,那不用管这个参数。如果hull那边输出到一个Mat,则这个参数设为true时,返回的Mat储存的是凸包的点;设为false时,返回的是输入集的下标指引。

下面给出一个例子,这个例子的写法借鉴于OpenCV的一个例子:https://docs.opencv.org/4.x/d7/d1d/tutorial_hull.html

Mat src = imread("E:/program/image/k.jpg");
Mat src_gray;
cvtColor(src, src_gray, COLOR_BGR2GRAY);
blur(src_gray, src_gray, Size(5, 5));
Mat img;
Canny(src_gray, img, 30, 60);
vector<vector<Point>> contours;
findContours(img, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
vector<vector<Point>> hull(contours.size());
for (int i = 0; i < contours.size(); i++) {
	convexHull(contours[i], hull[i]);
}
Mat dst = Mat::zeros(img.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++) {
	drawContours(dst, contours, i, Scalar(255, 255, 0));
	drawContours(dst, hull, i, Scalar(255, 255, 0));
}
imshow("dst", dst);
waitKey();

在这里插入图片描述

2.使用多边形将轮廓包围

OpenCV提供了多种类似的函数,如:
boundingRect()计算灰度图像的点集或非零像素的上-右(up-right)边界矩形
minAreaRect()返回可旋转的最小面积的包围矩形
minEnclosingCircle()返回面积最小的包围圆形
approxPolyDP()返回以指定的精度近似多边形曲线(用另一个顶点较少的曲线/多边形逼近一条曲线或一个多边形,使它们之间的距离小于或等于指定的精度)

由于参数较少,我把函数声明一起放在下面:

Rect cv::boundingRect	(InputArray array)	

RotatedRect cv::minAreaRect	(InputArray points)	

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

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

然后给出一个例子,来介绍怎么使用这些函数,以及怎么利用这些函数的返回值在输入图像上进行绘画。另外,approxPolyDP可以检测一个图像中存在的一些多边形轮廓,这里没有举相关的例子,读者可以参考《OpenCV3编程入门》。

RNG& rng = theRNG();
vector<Point> points;
Mat img = Mat::zeros(Size(600, 600), CV_8UC3);
for (int i = 0; i < 20; i++) {
	Point p;
	p.x = rng.uniform(img.cols / 4, img.cols * 3 / 4);
	p.y = rng.uniform(img.rows / 4, img.rows * 3 / 4);
	circle(img, p, 2, Scalar(255, 255, 255), 1, LINE_8, 0);
	points.push_back(p);
}
RotatedRect box = minAreaRect(points);
Rect r = boundingRect(points);

Point2f center;
float radius;
minEnclosingCircle(points, center, radius);

Point2f v[4];
box.points(v);
for (int i = 0; i < 4; i++) {
	line(img, v[i], v[(i + 1) % 4], Scalar(0, 0, 255), 1, LINE_AA);
}

rectangle(img, r, Scalar(0, 255, 0), 1, LINE_8, 0);

circle(img, center, radius, Scalar(255, 255, 0), 1, LINE_8, 0);

vector<Point> poly;
approxPolyDP(points, poly, 3, true);
Rect r1 = boundingRect(poly);
rectangle(img, r1, Scalar(0, 255, 255), 1, LINE_8, 0);

imshow("img", img);
waitKey();

注意,在寻找好多边形轮廓后,我又用boundingRect()来寻找轮廓的上-右边界矩形,这样画出来的矩形正好会和直接对点集进行boundingRect()后绘制的矩形重合。
在这里插入图片描述

参考文献:

  1. OpenCV官方文档:https://docs.opencv.org/4.x/
  2. 《OpenCV3编程入门》毛星云、冷雪飞等编著
  3. https://www.cnblogs.com/aiguona/p/7232243.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值