170316.道格拉斯-普克算法

道格拉斯-普克算法

道格拉斯-普克算法 (Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法、迭代适应点算法、分裂与合并算法)是乌尔斯·拉默(Urs Ramer)于1972年以及大卫·道格拉斯(David Douglas)和托马斯·普克(Thomas Peucker)于1973年提出的一种简化线的一种经典算法。

它是通过减少曲线中点的数量,得出一条尽可能完整的表达原有曲线的特征的曲线。

一般来讲,道格拉斯-普克算法需要一个极差D,极差D的值越大,证明曲线简化的越多,反之极差越小,表示简化的越小

通俗的理解,道格拉斯-普克算法是通过分治策略处理一组曲线,而极差D则可以理解为一个比较值,或者可以说是一个临界点,通过对垂距大于此临界点的点进行保留,对小于此临界点的点进行删除,达到简化曲线的效果

算法的基本思路是:

对曲线的首末点虚连一条直线,求此曲线其余所有点到直线的距离(垂距),从中找出最大距离值dmax ,用dmax与限差D相比:若dmax <D,这条曲线上的中间点全部舍去;若dmax ≥D,保留dmax 对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法,当曲线无法分为两部分时结束。

具体步骤如下:

(1) 在曲线首尾两点间虚连一条直线,求出其余各点到该直线的距离。

(2)选其最大者与极差D相比较,若大于极差D,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去。

(3)以保留的点为中心,将已知曲线分成两部分处理,重复执行第1、2步操作,迭代操作,即仍选该直线距离最大者与极差D比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差为D的曲线点坐标

具体的实现过程如下图(此图来自于网络)

在计算最大距离的时候(垂距),一般来讲,我们知道三个点的坐标,两个坐标点形成得直线,求另一个坐标点到此直线的距离。

一般来讲,行列式算法,海伦公式,向量法,都可以求出面积;

这里介绍下行列式算法:

已知三个点坐标(x1,y1),(x2,y2),(x3,y3)

由此三角形构成的面积是|S|注意是S的绝对值,因为坐标的方向不确定。

下面是S的求值

 

求出面积后,用面积公式,得到高(垂距),找出所有点中垂距的最大值与极差D比较。

下面简要写出道格拉斯-普克算法的代码,由于是采用传统的迭代方法,存在一定的弊端

qt代码如下:

#include <QPoint>
#include <QVector>
/**
 * @brief CPointRarefyOprt::PerpendicularDistance 计算点到首尾连线间的距离(垂距)
 * @param Point1                                  首坐标
 * @param Point2                                  尾坐标
 * @param Point                                   要计算到直线距离的点
 * @return 
 */
double PerpendicularDistance(const QPoint &Point1, const QPoint &Point2, const QPoint &Point)
{
    //行列式算法的展开
    double area = fabsf(0.5 * (Point1.x * Point2.y + Point2.x * Point.y + Point.x * Point1.y - Point2.x * Point1.y - Point.x * Point2.y - Point1.x * Point.y));
    double bottom = sqrtf(pow(Point1.x - Point2.x, 2) + pow(Point1.y - Point2.y, 2));
    double height = area / bottom * 2;
    return height;
}
/**
 * @brief DouglasPeuckerReduction  道格拉斯——普克算法
 * @param Points                   要进行简化的曲线的点列
 * @param startPoint               进行此算法的曲线开始的点的数组下标
 * @param endPoint                 进行此算法的曲线结束的点的数组下标
 * @param tolerance                极差D(阈值)
 * @param resultPoints             得到的简化的曲线
 */
void DouglasPeuckerReduction(QVector<QPoint> Points,int startPoint
                             ,int endPoint, double tolerance,QVector<QPoint> resultPoints)
{
    double maxDistance = 0; //最远距离(垂距)
    int indexFarthest = -1;  //最远距离点的数组下标
    if(endPoint-startPoint<=1)
    {
        if(endPoint==startPoint)
        {
            //endPoint==startPoint相等时,只有一个点时,所以添加一个点
            resultPoints.append(points[startPoint]);
        }
        else
        {
            //有两个点时,添加两个点
            resultPoints.append(points[startPoint]);
            resultPoints.append(points[endPoint]);   
        }
        //当小于等于两个点时,直接返回
        return;
    }
 
   
    for (int index = startPoint; index < endPoint; index++)
    {
        double distance = PerpendicularDistance(points[startPoint],points[endPoint],points[index]);
        if (distance > maxDistance)
        {
            //当新得到的点的垂距大于之前的最大垂距时,更新最大垂距,并且更新最大垂距点的下标
            maxDistance = distance;
            indexFarthest = index;
        }
    }
 
   
    if (maxDistance > tolerance && indexFarthest != -1)
    {
        //对起始点到最大垂距点之间的点重复进行道格拉斯——普克算法
        DouglasPeuckerReduction(points, startPoint,indexFarthest-1, tolerance, resultPoints);
        //将最大垂距点保留
        resultPoints.append(points[indexFarthest]);
        //对最大垂距点到结束点之间的点重复进行道格拉斯——普克算法
        DouglasPeuckerReduction(points, indexFarthest+1,endPoint, tolerance, resultPoints);
    }
    
    return;
}

//第一次写博客,以前一直想写,但总不知道写些什么,或者感觉想写的东西太过于简单,或者感觉想写的东西自己无法合理的表示
//写这个东西用了两个多小时,借鉴了一些文档,但其中还是不免有许多错误,求指点,感激不尽
//或许很简单,或许很多错误,但是当我鼓足勇气写完之后,忽然发现,原来坚持写比什么都重要

转载于:https://www.cnblogs.com/wpch1993/p/6558086.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值