道格拉斯-普克算法 [1] (Douglas–Peucker algorithm,亦稱為拉默-道格拉斯-普克算法、迭代適應點算法、分裂與合並算法)是將曲線近似表示為一系列點,並減少點的數量的一種算法。它的優點是具有平移和旋轉不變性,給定曲線與閾值后,抽樣結果一定。
算法步驟
- 連接曲線首尾兩點A、B形成一條直線AB;
- 計算曲線上離該直線段距離最大的點C,計算其與AB的距離d;
- 比較該距離與預先給定的閾值threshold的大小,如果小於threshold,則以該直線作為曲線的近似,該段曲線處理完畢。
- 如果距離大於閾值,則用點C將曲線分為兩段AC和BC,並分別對兩段曲線進行步驟[1~3]的處理。
- 當所有曲線都處理完畢后,依次連接各個分割點形成折線,作為原曲線的近似。

1 //DouglasPeucker.h 2 #pragma once 3 #include <vector> 4 #include <stdio.h> 5 #include <iostream> 6 7 using namespace std; 8 9 struct MyPointStruct//點雲結構體 10 { 11 public: 12 double X; 13 double Y; 14 double Z; 15 MyPointStruct() 16 { 17 this->X = 0; 18 this->Y = 0; 19 this->Z = 0; 20 }; 21 22 MyPointStruct(double x, double y, double z) 23 { 24 this->X = x; 25 this->Y = y; 26 this->Z = z; 27 }; 28 29 ~MyPointStruct() {}; 30 }; 31 32 33 34 class DouglasPeucker :public MyPointStruct 35 { 36 public: 37 //MyPointStruct pointXYZ; 38 vector<MyPointStruct> PointStruct; 39 vector<bool> myTag; // 標記特征點的一個bool數組 40 vector<int> PointNum;//離散化得到的點號 41 42 public: 43 DouglasPeucker(void); 44 DouglasPeucker(vector<MyPointStruct>& Points, int tolerance); 45 ~DouglasPeucker(); 46 47 void WriteData(const char *filename); 48 49 private: 50 void DouglasPeuckerReduction(int firstPoint, int lastPoint, double tolerance); 51 double PerpendicularDistance(MyPointStruct &point1, MyPointStruct &point2, MyPointStruct &point3); 52 MyPointStruct& myConvert(int index); 53 };

1 //DouglasPeucker.cpp 2 3 #include "DouglasPeucker.h" 4 #include <stdio.h> 5 6 DouglasPeucker::DouglasPeucker() 7 { 8 9 } 10 11 DouglasPeucker::DouglasPeucker(vector<MyPointStruct>& Points, int tolerance) 12 { 13 PointStruct = Points; 14 int totalPointNum = Points.size(); 15 16 myTag.resize(totalPointNum, 0); 17 18 DouglasPeuckerReduction(0, totalPointNum - 1, tolerance); 19 20 for (int index = 0; index < totalPointNum;index++) 21 { 22 if (myTag[index]) 23 { 24 PointNum.push_back(index); 25 } 26 } 27 } 28 29 30 DouglasPeucker::~DouglasPeucker() 31 { 32 33 } 34 35 void DouglasPeucker::WriteData(const char *filename) 36 { 37 FILE *fp = fopen(filename, "w"); 38 int pSize = PointNum.size(); 39 for (int index = 0; index < pSize; index++) 40 { 41 fprintf(fp, "%lf\t%lf\n", PointStruct[PointNum[index]].X, PointStruct[PointNum[index]].Y); 42 } 43 } 44 45 void DouglasPeucker::DouglasPeuckerReduction(int firstPoint, int lastPoint, double tolerance) 46 { 47 double maxDistance = 0; 48 int indexFarthest = 0; // 記錄最大值時點元素在數組中的下標 49 50 for (int index = firstPoint; index < lastPoint; index++) 51 { 52 double distance = PerpendicularDistance(myConvert(firstPoint), 53 54 myConvert(lastPoint), myConvert(index)); 55 56 if (distance > maxDistance) 57 { 58 maxDistance = distance; 59 indexFarthest = index; 60 } 61 } 62 if (maxDistance > tolerance && indexFarthest != 0) 63 { 64 myTag[indexFarthest] = true; // 記錄特征點的索引信息 65 66 DouglasPeuckerReduction(firstPoint, indexFarthest, tolerance); 67 DouglasPeuckerReduction(indexFarthest, lastPoint, tolerance); 68 } 69 } 70 71 double DouglasPeucker::PerpendicularDistance(MyPointStruct &point1, MyPointStruct &point2, MyPointStruct &point3) 72 { 73 // 點到直線的距離公式法 74 double A, B, C, maxDist = 0; 75 A = point2.Y - point1.Y; 76 B = point1.X - point2.X; 77 C = point2.X * point1.Y - point1.X * point2.Y; 78 maxDist = fabs((A * point3.X + B * point3.Y + C) / sqrt(A * A + B *B)); 79 return maxDist; 80 } 81 82 MyPointStruct& DouglasPeucker::myConvert(int index) 83 { 84 return PointStruct[index]; 85 }