最小包围凸包

最近看了最小凸包绘制,官方代码没看,常见的有Graham's scan算法

其实原理比较简单,先将点集排序,之后根据左转进行栈扫描。

1.点集排序:一般取x坐标最小的,并且尽量y大的,靠左下角的点作为起始点。然后连接起始点P0与其他点,计算连线与竖直向下方向的夹角,按照大小顺序排列。

2.然后根据排序之后的点,依次判定该点是否左转,若是左转,入栈,否则,取下一点。判断左转可以根据矢量面积法:

S(P1,P2,P3)=|y1 y2 y3|= (x1-x3)*(y2-y3)-(y1-y3)*(x2-x3)  第一点为P1,向量方向为P1P2,判断P3点,若S为正,P3在向量P1P2的左侧。

1.点集排序:

void sort(vector<Point>& pss)
{
    //pss是装着原本的点
    //bound是排好序的点
    //首先排序

    int count = pss.size();
    int first_Point = 0;
    float* Aangle = new float[count]; //动态数组存放角度
    int Min_x = pss[0].x ;
    int Max_y = pss[0].y ;
    for(int i=0;i<pss.size();i++)  //取横坐标最小的点 y最大
    {

        if ( (pss[i].x < Min_x)||((pss[i].x == Min_x)&&(pss[i].y>Max_y)))
        {
            Min_x = pss[i].x;
            Max_y = pss[i].y;
            first_Point = i;  
        } 
    }
    Point temp = pss[0];       //将起始点放到首位置
    pss[0] = pss[first_Point];
    pss[first_Point]= temp;

//计算连线与竖直方向夹角

    for(int i=0;i<pss.size();i++)
    {
        float angle = -1.0* (float)(pss[i].y -pss[0].y )/(float)(pss[i].x -pss[0].x );//图像坐标系有点区别
        Aangle[i] = angle;    
    }

//根据夹角排序 

    for(int i = 1;i<count-1;i++)
    {
        for(int j = i+1;j<count;j++)
        {
            if(Aangle[j]<Aangle[i]) 
            {
                float temp = Aangle[i];
                Aangle[i] = Aangle[j];
                Aangle[j] = temp;

                Point temp1  =pss[i];
                pss[i] = pss[j];
                pss[j] = temp1;
            }
        }
    }
    delete []Aangle;
}

2.栈扫描

void scan(vector<Point>& pss,vector<Point>& bound)
{
    int i = 1;//记录拐点索引
    int pop_count= 0;//记录弹出次数
    
    bound.push_back(pss[0]);
    bound.push_back(pss[1]);
    bound.push_back(pss[2]);
    //根据已排好序列的点连接线段
    do 
    {
        //计算夹角
        float angle = ((bound[i].y - bound[i+1].y)*(bound[i-1].x - bound[i+1].x) - (bound[i-1].y - bound[i+1].y)*(bound[i].x - bound[i+1].x));
        if(angle<=0.0)//左转
        {
            bound.push_back(pss[(i+2+pop_count)%pss.size()]);// 压栈
            i++;//索引加1
        }
        else//右转
        {
            bound.erase(bound.end()-2);//弹出倒数第二个点
            pop_count++;//弹出次数加1 
            i--;.//索引减1
        }

    } while (pss[0] != *bound.rbegin() );//回到起始点
}

测试:

void test(vector<Point> point)

{

       vector<Point> bound;

      sort(point);

      scan(point,bound);

      for (int i =0;i<bound.size()-1;i++)
      {
         line(img,bound[i],bound[i+1],Scalar(0,0,255),2,8,0);
      }
      line(img,bound[0],bound[bound.size()-1],Scalar(0,0,255),2,8,0);
      imshow("dst",img);

}

结果:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值