给定一个路径,我想优化它,以便可以删除所有直线上的顶点。
例如: 路径:
*******
* *
* *
***********
可以优化为:
*-----*
| \
| \
*---------*
然而,我想控制偏离坡度的方向,使其不必完全在坡度上。
什么样的算法可以做到这一点?
由于
答案 0 :(得分:7)
我相信你可以通过简单的迭代遍历路径上的点来做到这一点。在每个点跟踪您遇到的最后三个点。如果它们中的所有三个都是共线的,则从路径中移除中间点,因为从第一个节点到第三个节点的直线路径将通过中间节点。您可以通过控制一些用于控制点的共线距离的术语来控制偏差的大小。
如果您将点存储在双链表等数据结构中,则可以在O(n)时间内通过简单的数据传递来实现。
希望这有帮助!
答案 1 :(得分:3)
您应该使用凸包算法(它取决于您的多边形在内存中的存储方式),然后在两个相邻点上以最小角度清洁点。那么你将得到一个只有极点处的多边形。
这是: http://en.wikipedia.org/wiki/Convex_hull
它们是很多可能的实现方式。它取决于您正在编程的语言以及您使用的数据..
编辑:我当时并不知道你已经拥有了数据中的分数。只需迭代这些分数并计算你所在的那个,即上一个和下一个之间的角度。如果角度为〜= 180,则擦除当前点。
答案 2 :(得分:3)
这将是一个抽象的视图,因为我不是一个C ++人,但是这里......
现在让我们来看一点:
*******
* *
* *<- this one, lets call it X
***********
你要做的是慢慢决定每一点是否必要。要确定一个点是否有效,必须使用其他点,紧接在之前和之后的点:
*******
* *<- A
* *
***********<- B
如果从A到X的角度与从X到B的角度相同(或在您认为足够准确的误差范围内),则X是不必要的。
这不会导致与Convex Hull算法相同的结果。这将简单地降低路径的分辨率。如果允许的错误太大,你可以得到副作用:
* *
* |
* |
* -> |
* |
* |
* *
或者如果你的错误太小,你可能根本不会改变路径。
另请注意,凸壳可以大大改变路径,例如:
* * *---*
* * * * / \
* * * -> * *
* * | |
********* *-------*
答案 3 :(得分:0)
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的答案稍微复杂一点,但它的优点是它更适合大曲线。
答案 4 :(得分:0)
更复杂的解决方案将涉及来自图像处理的技术。您可以尝试允许偏差的Hough transform。通过“模糊”参数空间可以包括偏差。然而,算法并不简单。此外,当每条线上的点数非常不同时,我不知道处理大量线条的效果如何。由于您的积分是有序的,您可以尝试查看参数空间并删除所有产生匹配的点。如果您首先选择最佳匹配,您可能会留下一个很好的解决方案。
答案 5 :(得分:0)
我认为此页面可以为您提供帮助:Simplyfing Polygons(我还建议the book)。
答案 6 :(得分:0)
我在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)
。
但是,在某些情况下,我仍然在输出中有一些共线点。这是算法失败的示例输入:
在该示例中,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);
}