道格拉斯-普克算法
道格拉斯-普克算法 [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 }