寻找凸包 convex hull(二)——Graham_Scan

来源:互联网 发布:语音软件 编辑:程序博客网 时间:2024/05/21 07:47

这篇继续凸包得的寻找,上篇文章中使用了暴力的方法,在网上又看了一些资料下面会介绍常用的算法 Graham_Scan

首先介绍一下有向面积的概念:



算法的大致步骤:

1)对于给定的点集 data 寻找其中y坐标最小的点作为起始点p

2)  把坐标原点移动到p处,然后计算每个点与x轴正方向的夹角

3)根据夹角的大小排序,得到排序后的点集data

4)设点集data中有N个数据,数组CHS用来保存满足条件的边界点

      把data[N-1]和data[0]放入CHS中,然后放入data[1],如果CHS[0](data[N-1]),CHS[1](data[0]),CHS[2](data[1]),

三点构成的三角形有向面积为正(注意CHS[0]是起点,CHS[2]是终点),则认为data[1]满足边界条件继续把data[2]放入CHS中;

放入data[2]后如果发现CHS[1](data[0]),CHS[2](data[1]),CHS[3](data[2]),构成的有向面积为负,那么判断data[1]是不满足条件的,

需要删除;接着按照上面的规律放入剩余的全部数据;示意图如下



上图中序号越大,夹角越大。先把6号和1号放入数组CHS中,扫描2号发现612满足逆时针条件;当按照夹角由小到大扫描时会先扫描到3号,而123组合满足逆时针条件;接着扫描到4号,234组合不满足逆时针条件,所以就要删除3号直接连接24号,此时124组合满足逆时针条件;接着就是扫描5号,观察245是否满足逆时针条件

下面贴上代码

#include "stdafx.h"#include "convexhull.h"#include <cmath>#include <cstdlib>#include <opencv2/opencv.hpp>#include <iostream>using namespace std;using namespace cv;#define Width 640#define Height 480#define PointNum 18struct _Point{Point pt;float angle;void operator=(const _Point &pp){this->pt = pp.pt;this->angle = pp.angle;}bool operator >(const _Point &pp){return(this->angle > pp.angle);}};static vector<_Point> data(PointNum);//原始数据static vector<_Point> CHS(PointNum);//保存最后结果static int sp = 0;//用来指示数组CHS的下标//计算有向面积//如果 返回值大于零则ABC是按照逆时针分布,=0 ABC在一条直线上,<0 ABC顺时针分布int cross(Point A, Point B, Point C){return(A.x*B.y + B.x*C.y + C.x*A.y - A.y*B.x - B.y*C.x - C.y*A.x);}//void Swap(_Point &a, _Point &b){_Point temp = a;a = b;b = temp;}//随机产生点void generatePoint(vector<_Point> & input){RNG rng(1234);for (int i=0; i<PointNum; i++){input[i].pt.x = rng.uniform(50, 540);input[i].pt.y = rng.uniform(50, 400);}}//简单排序template<class T>void bubbleSort(T & a, int n){//for (int i=0; i<n-1 ; i++)//{//for (int j=i+1; j<n; j++)//{//if(a[i]>a[j])//{//Swap(a[i], a[j]);//}//}//}bool changed = true;for (int i=0; i<n && changed; i++){for (int j=0; j<n-i-1; j++){changed = false;if(a[j] > a[j+1]){Swap(a[j], a[j+1]);changed = true;}}}}//Graham Scan 算法的实现void GrahamScan(){for(int i=0; i<data.size(); i++)//寻找y值最小的点{if(data[0].pt.y > data[i].pt.y)Swap(data[0], data[i]);}//移动坐标系原点到data[0]处for(int i=0; i<data.size(); i++){data[i].pt.x -= data[0].pt.x;data[i].pt.y -= data[0].pt.y;data[i].angle = atan2((float)data[i].pt.y, (float)data[i].pt.x);}//按与x轴夹角由小到大排序bubbleSort(data, data.size());for(int i=0; i<data.size(); i++)cout<<data[i].angle<<endl;CHS[0] = data[PointNum-1];CHS[1] = data[0];int k = 1;sp = 2;while(k < data.size()){CHS[sp] = data[k];if(cross(CHS[sp-2].pt, CHS[sp-1].pt, CHS[sp].pt) > 0)//如果连续的三点为逆时针则选中当前点{k++;sp++;}else//如果为顺时针则要剔除上一个点{sp--;}}}void GradamScan_test(){Mat img;img.create(Size(Width, Height), CV_8UC3);generatePoint(data);GrahamScan();for (int i=0; i<data.size(); i++){circle(img, data[i].pt, 10, Scalar(0,0,255));}for (int i=0; i<sp-1; i++){line(img, CHS[i].pt, CHS[i+1].pt, Scalar(0,0,255), 2, 8);imshow("ss", img);waitKey(1000);}}


运行函数GradamScan_test,结果如下图


0 0