1.算法描述
經典的Douglas-Peucker算法(簡稱DP法)描述如下:
(1)在曲線首尾兩點A,B之間連接一條直線AB,該直線為曲線的弦;
(2)得到曲線上離該直線段距離最大的點C,計算其與AB的距離d;
(3)比較該距離與預先給定的閾值threshold的大小,如果小於threshold,則該直線段作為曲線的近似,該段曲線處理完畢。
(4)如果距離大於閾值,則用C將曲線分為兩段AC和BC,並分別對兩段取信進行1~3的處理。
(5)當所有曲線都處理完畢時,依次連接各個分割點形成的折線,即可以作為曲線的近似
2.算法分析
①顯然,整個過程是一個迭代過程,第四步時迭代,再次回到第一步。
②由於計算開方耗時,所以直接取d²作為評判值更加方便。
③DP法一般是化簡一條曲線,本次化簡的是多邊形,實質是一條首尾相連的多邊形,意味着曲線首尾兩點的坐標相等。如果兩點坐標相等,則第二步計算距離時會出現分母為0的問題。因此要換一個就近的點。
3.算法實現
①計算某點到已知兩點的距離。
// 計算一點到一條直線(已知兩點)的距離
double disP2L(CMyPoint* first, CMyPoint* last, CMyPoint* third) //first和last分別為線的兩端,third是第三點
//CMyPoint是點的類型,可以換成CPoint
{
double x0 = first->Getx();
double y0 = first->Gety();
double x1 = last->Getx();
double y1 = last->Gety();
double x = third->Getx();
double y = third->Gety();
//diSquare是d²,不開方,耗時更短。
double disSuqare = ((y0 - y1)*x + (x1 - x0)*y + (x0*y1 - x1*y0))*((y0 - y1)*x + (x1 - x0)*y + (x0*y1 - x1*y0)) / ((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0));
return disSuqare;
}
②壓縮算法
// Douglas–Peucker法,20190220,壓縮,zf
void DP(vector<CMyPoint*> inputLine) //輸入是包含指針的數組,vector類型
{
if (inputLine.size() <= 2) //若少於兩點,直接返回
return;
int size = inputLine.size();
CMyPoint *first = inputLine[0]; //定義首點
CMyPoint *last = inputLine[size - 1]; //定義尾點
while (last->Getx() == first->Getx() && last->Gety() == first->Gety()) {//若首尾相同,則換點
size = size - 1;
last = inputLine[size - 1];
}
int flag = 0; //標記距離最大的點的下標
double disSquare = 0;
for (int i = 1; i<inputLine.size() - 1; i++) {
double temp = disP2L(first, last, inputLine[i]);
if (temp>disSquare) { //記錄最大距離及編號
disSquare = temp;
flag = i;
}
}
if (disSquare<4) { //判斷值與閾值的關系,閾值自己設定
out_DP.push_back(first); //如果小於閾值,則保留首尾點
out_DP.push_back(last); //out_DP是一個全局變量,vector<CMyPoint*> out_DP
//用於存儲留下來的點,是最后的成果
}
else { //否則分成兩段
vector<CMyPoint*> head, rear;
for (int j = 0; j<inputLine.size(); j++) {
if (j <= flag) head.push_back(inputLine[j]);
if (j >= flag) rear.push_back(inputLine[j]);
}
DP(head); //迭代進行
DP(rear);
}
}
4.實驗效果
分別是前、后

在保持圖形和面積基本不變的前提下,多邊形的點變少,壓縮具有較好效果。
