[英]Algorithm to simplify a path
給定一條路徑,我想對其進行優化,以便可以刪除直線上的所有頂點。
例如: 路徑:
*******
* *
* *
***********
可以優化為:
*-----*
| \
| \
*---------*
但是我想控制與斜率的偏差,這樣它就不必完全在斜率上。
什么樣的算法可以做到這一點?
謝謝
我相信您可以通過簡單的迭代遍歷路徑上的點來做到這一點。 在每個點上跟蹤您遇到的最后三個點。 如果它們三個都是共線的,則從路徑中刪除中間點,因為從第一個節點到第三個節點的直線路徑將通過中間節點。 您可以通過使用一些術語來控制點必須接近共線的程度來控制偏差的大小。
如果您將點存儲在雙向鏈表之類的數據結構中,則可以通過簡單的數據傳遞在 O(n) 時間內實現。
希望這可以幫助!
您應該使用凸包算法(這取決於您的多邊形在內存中的存儲方式),然后在兩個相鄰點上以最小角度清理這些點。 然后你會有一個多邊形,只有末端的點。
這是: http://en.wikipedia.org/wiki/Convex_hull
它們有很多可能的實現。這取決於您使用的編程語言以及您使用的數據。
編輯:我當時不知道您已經擁有數據中的點。只需遍歷這些點並計算您所在位置、上一個和下一個之間的角度。 如果角度為 ~= 180,則刪除當前點。
這將是一個抽象的觀點,因為我不是一個 C++ 的人,但這里有......
現在讓我們來看一個點:
*******
* *
* *<- this one, lets call it X
***********
你要做的是慢慢決定每個點是否是必要的。 要確定一個點是否有效,必須使用其他點,即之前和之后的點:
*******
* *<- A
* *
***********<- B
如果從 A 到 X 的角度與從 X 到 B 的角度相同(或在您認為足夠准確的誤差范圍內),則 X 是不必要的。
這不會產生與 Convex Hull 算法相同的結果。 這只會降低路徑的分辨率。 如果您允許的錯誤太大,您可能會受到副作用,例如:
* *
* |
* |
* -> |
* |
* |
* *
或者如果你的錯誤太小,你可能根本不會改變路徑。
另請注意,凸包可以極大地改變路徑,例如:
* * *---*
* * * * / \
* * * -> * *
* * | |
********* *-------*
我已經在C++
中實現了@templatetypedef 的解決方案,用於由兩個x,y
向量描述的閉合多邊形鏈。 我遍歷多邊形,如果一個點與前一個點和下一個點共線,我將其刪除:
template<class T> void del_collinear_vanilla(std::vector<T> &x,
std::vector<T> &y) {
assert(x.size() == y.size());
size_t i = x.size();
size_t im1, ip1 = 0;
do {
i--;
im1 = i ? i - 1 : x.size() - 1;
if (are_collinear(x[ip1], y[ip1], x[i], y[i], x[im1], y[im1])) {
x.erase(x.begin() + i);
y.erase(y.begin() + i);
}
ip1 = i;
} while (i != 0);
}
其中實現取決於宏/模板are_collinear(x0,y0,x1,y1,x2,y2)
。
但是,在某些情況下,output 中仍然有一些共線點。 這是算法失敗的示例輸入:
本例中,P5 與 P0 重合,P4 與 P0 和 P1 的縱坐標相同; 我改變了一點他們的坐標來顯示所有的段。 該算法應該只返回一個頂點為 P1、P2、P3、P4 的矩形。
上圖,P6 與 P5 和 P0 共線。 那么,一旦 P6 被淘汰,P5 和 P0 就重合了,它們都與 P4 和 P1 共線。
事實證明,在每個點上進行簡單循環,如果它與前一個點和下一個點共線,則刪除一個點,並不能提供正確的結果。
(在這個例子中,假設你從 P0 開始,你發現它與 P6 之前的點和 P1 之后的點不共線。然后你移動到 P1,P2,......直到你到達 P6。P6 共線,你刪除它,循環結束。但是現在P0與P4和P1共線,它應該被刪除了!)
開放路徑也存在同樣的缺陷。 只要輸入路徑在某種程度上沒有崩潰,該算法就可以正常工作。
解決方案是每次刪除一個點時后退一步,以驗證前一個點現在是否共線:
template<class T> void del_collinear(std::vector<T> &x, std::vector<T> &y) {
assert(x.size() == y.size());
size_t target = x.size() - 1;
size_t i = x.size() - 1;
do {
size_t im1 = i ? i - 1 : x.size() - 1;
size_t ip1 = (i == x.size() - 1) ? 0 : i + 1;
if (are_collinear(x[ip1], y[ip1], x[i], y[i], x[im1], y[im1])) {
x.erase(x.begin() + i);
y.erase(y.begin() + i);
// I do not decrease i in this case, as the the previous (alread
// processed) point may now be a collinear point that must be
// deleted. I mod it because i may now exceed x.size()
i = i % x.size();
//Increment the target as well.
target = (i + 1 + x.size()) % x.size();
} else
//go for the next point.
i = i ? i - 1 : x.size() - 1;
} while (i != target);
}
set `D` to a maximum deviance of 10 degrees or so.
set `P` to the first point.
set `Q` to the point after `P`.
set `A` to the angle from `P` to `Q`.
while `Q` is not that last point in the list
if the angle from `P` to `Q` is within of `A` plus or minus `D`
remove `Q`
else
set `P` to `Q`
set `A` to the angle from `P` to `Q`.
set `Q` to the point after `P`
這比 templatetypedef 的答案稍微復雜一些,但它的優點是 forms 更適合大曲線。
更復雜的解決方案將涉及圖像處理技術。 您可以嘗試允許偏差的霍夫變換。 可以通過“模糊”參數空間來包含偏差。 然而算法並不簡單。 當每條線上的點數非常不同時,我也不知道它處理大量線的能力如何。 由於您的點是有序的,您可以嘗試查看參數空間並刪除所有產生匹配的點。 如果您首先使用 select 最佳匹配項,您可能會得到一個好的解決方案。
我認為這個頁面應該可以幫助你: Simplyfing Polygons (我也推薦這本書)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.