目录
3.3 Andrew 的单调链(Andrew's monotone chain)
1 概念讲解及用处
凸包检测是指找到一个凸多边形,该多边形的顶点包围了给定点集中的所有点。凸包检测在计算机视觉和图像处理中有广泛的应用,例如:
轮廓分析:用于提取和分析图像中的对象轮廓。
目标识别:用于识别并定位目标的外形。
地物提取:用于从卫星图像或遥感图像中提取地理要素的轮廓。
凸包是一个凸多边形,它是由给定点集中的顶点所构成的最小凸多边形。凸包检测的目标是找到这个凸多边形,该多边形能够将给定点集中的所有点都包围在内。
以下是凸包的一些基本原理:
凸性:一个图形或点集在任意两点之间的连线上的所有点都位于这条连线的同一侧,则称该图形或点集是凸的。也就是说,对于任意两个点A和B,点集中的所有其他点都位于线段AB的同一侧或线段AB上。
凸壳:给定一个点集,凸壳是包含这个点集中所有点的最小凸多边形。凸壳的边界由点集中的某些点组成,这些点位于凸多边形的周长上。凸壳可以是一个简单的多边形(不自交)或一个复杂的多边形(自交)。
凸包算法:常用的凸包算法有 Graham Scan 算法、Jarvis March 算法和 QuickHull 算法等。这些算法的核心思想是通过遍历点集,找到构成凸包的边界点。
点集的排序:为了实现凸包算法,通常需要对点集进行排序。一种常见的方法是按照点的极角对点集进行排序,以便在构建凸包时能够按照顺序连接边界点。
凸包的性质:凸包具有以下性质:
凸包是一个凸多边形,它的内部角度都小于180度。
凸包的边界由凸多边形的顶点组成,这些顶点是给定点集中的极值点(极大或极小)。
凸包的边界上任意两个相邻顶点之间的连线与凸包内部所有点的连线都不会相交。
凸包检测在计算机视觉和图像处理中有广泛的应用,如轮廓分析、目标识别和地物提取等。通过找到凸包,我们可以得到点集的凸性特征,并从中提取有用的信息。
2 函数详解
OpenCV提供了几个函数来进行凸包检测,包括convexHull()、convexityDefects()和isContourConvex()等。
2.1 convexHull() 函数
convexHull() 函数用于计算给定轮廓的凸包。凸包是一个多边形,完全包围了原始轮廓,并且没有凹陷部分。它是由轮廓上最外层点组成的。该函数返回一个新的轮廓,表示凸包。
void convexHull(
InputArray points,
OutputArray hull,
bool clockwise = false,
bool returnPoints = true
);
points:输入参数,表示要计算凸包的点集。
hull:输出参数,表示计算得到的凸包。
clockwise:可选参数,指定是否按顺时针方向输出凸包,默认为 false。
returnPoints:可选参数,指定是否返回点坐标(true)或点在输入点集中的索引(false),默认为 true。
2.2 convexityDefects() 函数
convexityDefects() 函数用于计算给定凸包和轮廓之间的凸缺陷。凸缺陷是指凸包与轮廓之间的空洞或凹陷区域。该函数返回一个向量,其中每个元素包含三个值:起始点、结束点和最远点,用于表示凸缺陷。
void convexityDefects(
InputArray contour,
InputArray convexHull,
OutputArray convexityDefects
);
contour:输入参数,表示原始轮廓。
convexHull:输入参数,表示凸包。
convexityDefects:输出参数,表示计算得到的凸缺陷。
2.3 isContourConvex() 函数
isContourConvex() 函数用于判断给定轮廓是否是凸形。如果轮廓是凸形,则返回 true;否则返回 false。
bool isContourConvex(
InputArray contour
);
contour:输入参数,表示要判断的轮廓。bool isContourConvex(
InputArray contour
);
contour:输入参数,表示要判断的轮廓。
综上所述,
convexHull() 用于计算轮廓的凸包。
convexityDefects() 用于计算凸包和轮廓之间的凸缺陷。
isContourConvex() 用于判断轮廓是否是凸形。
3 凸包算法简单介绍
计算凸包的常见算法有 Graham 扫描法(Graham's scan)、Jarvis 愚蠢算法(Jarvis march)、Andrew 的单调链(Andrew's monotone chain)和 QuickHull 算法等。以下是对这些算法的简要介绍:
3.1 Graham 扫描法(Graham's scan)
该算法首先找到一个最低的点作为起始点,
然后将其他点按照相对于起始点的极角进行排序。
排序后,依次检查每个点是否构成了逆时针方向的转动。如果不是,则移除该点,直到所有点都被处理完毕。
最后,根据剩余的点构建凸包。
Graham 扫描法的时间复杂度为 O(nlogn),其中 n 是点的数量。
3.2 Jarvis 愚蠢算法(Jarvis march)
该算法从点集中找到最左边的点作为起始点,并初始化当前点为起始点。
然后,以逆时针方向搜索下一个点,确保所选的点总是能够使得其他未访问的点在其右侧。
重复上述步骤,直到回到起始点形成闭合的凸包。
Jarvis 愚蠢算法的时间复杂度为 O(nh),其中 h 是凸包的边数,n 是点的数量。
3.3 Andrew 的单调链(Andrew's monotone chain)
该算法首先将点按照 x 坐标进行排序。
然后,分别构建上凸壳和下凸壳。对于每个凸壳来说,它们都是从左到右的连续点集。
最后,将两个凸壳合并得到完整的凸包。
Andrew 的单调链算法的时间复杂度为 O(nlogn),其中 n 是点的数量。
3.4 QuickHull 算法
该算法通过递归地划分点集来构建凸包。
首先,找到最左边和最右边的点,将它们添加到凸包中。
然后,根据这条直线将点集分成两部分,并对每一部分递归地执行相同的操作。
最后,将所有的凸包边界点连接起来形成完整的凸包。
QuickHull 算法的时间复杂度通常为 O(nlogn),但在最坏情况下可能达到 O(n^2)。
4 用C++编写代码进行实现
以下是一个使用OpenCV进行凸包检测的示例代码:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
// 读取图像并转为灰度图像
Mat image = imread("contour.jpg");
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
// 二值化图像
Mat binary;
threshold(gray, binary, 127, 255, THRESH_BINARY);
// 查找轮廓
std::vector<std::vector<Point>> contours;
findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 计算凸包
std::vector<std::vector<Point>> hull(contours.size());
for (int i = 0; i < contours.size(); i++) {
convexHull(contours[i], hull[i]);
}
// 绘制凸包
Mat result(image.size(), CV_8UC3);
drawContours(result, hull, -1, Scalar(0, 255, 0), 2);
// 显示结果图像
imshow("Convex Hull", result);
waitKey(0);
return 0;
}