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
總結
以上是生活随笔為你收集整理的170316.道格拉斯-普克算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu 安装Jdk(apt-get
- 下一篇: libvirtError: 无效参数:c