凸包(Convex Hull)构造算法——Graham扫描法

凸包(Convex Hull)

在图形学中,凸包是一个非常重要的概念。简明的说,在平面中给出N个点,找出一个由其中某些点作为顶点组成的凸多边形,恰好能围住所有的N个点。

这十分像是在一块木板上钉了N个钉子,然后用一根绷紧的橡皮筋它们都圈起来,这根橡皮筋的形状就是所谓的凸包。

计算凸包的一个著名算法是Graham Scan法,它的时间复杂度与所采用的排序算法时间复杂度相同,通常采用线性对数算法,因此为\( O\left(N\mathrm{log}\left(N\right)\right) \)。

1. 找到所有点\( P_{0,1,...,N-1} \)中最下方的点,记为\( P_{L} \);

2. 计算所有其他的点\( P_{i}\left(i\neq L\right) \) 与 \( P_{L} \)构成的向量\( \overrightarrow{P_{L}P_{i}} \)相对于水平轴的夹角。因为所有的点都在该\( P_{L} \)上方,因此向量的取值范围为\( \left(0, 180\right) \) ,所以可以用余切值代替角度值;

3. 对所有其他的点按照第2步算出的角度进行排序,且\( P_{L} \)为排序后的数组的第0位;

4. 从点\( P_{L} \)开始,依此连接每一个点(已经排序过),每连接一个点检测连线的走向是否是逆时针的,如果是则留下该点的前一个点,反之去除前一个点,使之与前面第二个点直接连接,继续这一检测,直到是逆时针或者所有点都被检测过为止。

判断三个点依此连成两条线段走向是否为逆时针,用这两条线段向量的叉积判断:叉积>0,逆时针;反之顺时针或者共线。

这里采用Qt 5.7实现了一个算法的演示程序,其中算法的部分如下(由于在Qt的坐标系中,y向下增长,因此在计算纵坐标差值时需要取相反数)。

void DisplayWidget::calConvexHull()
{
    int size = m_points.size();
    if (size < 3)
    {
        return;
    }

    // First: find the lowest point
    int maxY = 0;
    int indexOfLowest = -1;
    for (int i = 0; i < size; i++)
    {
        if (m_points.at(i).y() > maxY)
        {
            maxY = m_points.at(i).y();
            indexOfLowest = i;
        }
    }

    std::swap(*m_points.begin(), *(m_points.begin() + indexOfLowest));
    QPoint &lowestPoint = *(m_points.begin());

    // Second: calculate ctan(angles)
    double *ctanAngles = new double[size];
    for (int i = 1; i < size; i++)
    {
        double deltaY = lowestPoint.y() - m_points.at(i).y() + DBL_EPSILON;
        double deltaX = m_points.at(i).x() - lowestPoint.x();
        ctanAngles[i] = deltaX / deltaY;
    }

    // Third: Sort subscript
    int *subscript = new int[size];
    for (int i = 1; i < size; i++)
    {
        subscript[i] = i;
    }
    std::sort(subscript + 1, subscript + size, [ctanAngles](int a1, int a2) { return ctanAngles[a2] < ctanAngles[a1]; });

    // Fourth: Calculate convex hull
    std::vector<QPoint> convexHullPoints;
    convexHullPoints.push_back(*m_points.begin());
    convexHullPoints.push_back(m_points.at(subscript[1]));

    for (int i = 2; i < size; i++)
    {
        convexHullPoints.push_back(m_points.at(subscript[i]));
        while (convexHullPoints.size() > 3 &&
               !isAnticlockwise(*(convexHullPoints.end() - 3), *(convexHullPoints.end() - 2), *(convexHullPoints.end() - 1)))
        {
            *(convexHullPoints.end() - 2) = *(convexHullPoints.end() - 1);
            convexHullPoints.pop_back();
        }
    }

    m_convexHullPoints = std::move(convexHullPoints);

    delete[] ctanAngles;
    delete[] subscript;
}

效果如下:

 

程序源码:http://files.cnblogs.com/files/HolyChen/ConvexHull.rar

时间: 2024-12-24 08:59:41

凸包(Convex Hull)构造算法——Graham扫描法的相关文章

opencv笔记(二十四)——得到轮廓之后找到凸包convex hull

当我们得到一张轮廓之后,我们可以对其运用convexHull方法,寻找该轮廓的凸包. 一个轮廓可以有无数个包围它的外壳,而其中表面积最小的一个外壳,就是凸包. void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true ) points是一个contour. vector<Point>类型或者Mat类型 hull是输出,也是一个点集vector<Poin

[POJ1113&amp;POJ1696]凸包卷包裹算法和Graham扫描法应用各一例

凸包的算法比较形象好理解 代码写起来也比较短 所以考前看一遍应该就没什么问题了..>_< POJ1113 刚开始并没有理解为什么要用凸包,心想如果贴着城堡走不是更好吗? 突然发现题目中有要求在满足把所有点包括在内的情况下周长最短...这不就是凸包的性质吗? 而且显然如果城堡是凹的话,往里面绕一圈肯定会使周长增加... 然后可以从简单的三角形四边形推广出去,发现每个拐角-左右各90度之后所有的加和为180度 也就是在城堡周长的基础上再加一个半径为L的圆周长即是所求答案. 上次的模板写错了...应

计算几何 : 凸包学习笔记 --- Graham 扫描法

凸包 (只针对二维平面内的凸包) 一.定义 简单的说,在一个二维平面内有n个点的集合S,现在要你选择一个点集C,C中的点构成一个凸多边形G,使得S集合的所有点要么在G内,要么在G上,并且保证这个凸多边形的面积最小,我们要求的就是这个C集合. 二.算法 求凸包的算法很多,常用的有两种: 1.  Graham扫描法,运行时间为O(nlgn). 2.  Jarvis步进法,运行时间为O(nh),h为凸包中的顶点数. 这里主要讨论第一种算法:Graham扫描法 Graham扫描法: 基本思想:使用一个栈

OpenCV入门之寻找图像的凸包(convex hull)

介绍   凸包(Convex Hull)是一个计算几何(图形学)中的概念,它的严格的数学定义为:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包.  在图像处理过程中,我们常常需要寻找图像中包围某个物体的凸包.凸包跟多边形逼近很像,只不过它是包围物体最外层的一个凸集,这个凸集是所有能包围这个物体的凸集的交集.如下图所示: 在上图中,绿色线条所包围的凸集即为白色图形的凸包.  在opencv中,通过函数convexHulll能很容易的得到一系列点的凸包,比如由点组成的轮廓

[算法课][分治]寻找凸包 (Convex Hull)

凸包问题是算法中经典的题目了,最近算法课讲分治问题时提到了Convex Hull,算法导论的书上也花了篇幅讨论了Convex Hull的求解,主要是Graham方法. 为了能更好地理解分治和Graham这两种解法,我决定自己动手把代码写一遍. 然而,在写之前,我发现我大一学的用行列式求解由三个点围城的三角形面积已经忘得差不多了,现在补充一下: 利用这个计算结果来判断点p3在p1p2直线的左侧还是右侧 下面是分治算法求解: #include <iostream> #include <alg

凸包-Graham扫描法

RT,Graham扫描法求凸包分为3步: 1.找到y最小的点 2.以y最小的点O为原点,求其余所有点相对O的极角并按极角从小到大排序 3.对于排序后的点集,配合栈,完成Graham扫描. ConvexHull.py #coding=utf-8 import math import numpy import pylab as pl #画原始图 def drawGraph(x,y): pl.title("The Convex Hull") pl.xlabel("x axis&qu

Computational Geometry PA1 Convex Hull (凸包)

题目链接:http://dsa.cs.tsinghua.edu.cn/oj/problem.shtml?id=710 CG2015 PA1-1 Convex Hull (凸包) Description (描述) After learning Chapter 1, you must have mastered the convex hull very well. Yes, convex hull is at the kernel of computational geometry and serv

Convex hull凸包

把一个平面上给出的点都包含进去的最小凸多边形.逆时针输出凸包的各个顶点. 1.Graham扫描法 (O(n*logn))-------旋转扫除的技术: 2.Jarvis march步进法(O(n*h))h为凸包的顶点数--------打包的技术 应用:求二维平面最远点对. uva,109 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 5 using namespace std; 6

Monotone Chain Convex Hull(单调链凸包)

1 Monotone Chain Convex Hull(单调链凸包)算法伪代码: 2 //输入:一个在平面上的点集P 3 //点集 P 按 先x后y 的递增排序 4 //m 表示共a[i=0...m]个点,ans为要求的点; 5 struct P 6 { 7 int x,y; 8 friend int operator < (P a, P b) 9 { 10 if((a.x<b.x) || (a.x==b.x && a.y<b.y)) 11 return 1; 12 r