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.實驗效果
分別是前、后
在保持圖形和面積基本不變的前提下,多邊形的點變少,壓縮具有較好效果。