计算无向段平均方向的算法

时间:2016-04-12 10:16:20

标签: algorithm geometry

我有一组2D无向段,由两个端点组成。据统计,他们中的大多数人或多或少都处于相同的方向。

我想要计算的是段集的平均方向(例如,如果集合是全局N / S,它将返回〜0°等等...) 。请注意,我不关心返回哪个实际方向(0°或180°同样如此)。

在[0..180°范围内夹住每个线段的方向并且取平均值不起作用(例如两个线段,一个1°,另一个-1°:第二个将夹紧到179°并且平均值是错误的,这里是90°,应该是0°)。

我还想到将两组中的“归一化段”端点聚类,并计算由2个聚类中点组成的段的方向,但这对于任务来说似乎有点复杂。 “归一化段”是指在单位圆上有两个终点而在原点上有中间点的段。

是否有已知的算法/公式?

5 个答案:

答案 0 :(得分:3)

据我了解,细分的位置并不重要,只有他们的方向。

所以我们可以稍微改变一下这个问题:我们有一组向量,我们想在它们上面加一条线。

我们可以采用不同的标准。常用的是最小二乘法。

对于此标准,解决方案是:

double dvx=0,dvy=0;
for(const auto &direction:directions)
{
    dvx+=2*direction.dx*direction.dy;
    dvy+=squared(directions.dx)-squared(directions.dy);
}
return std::atan2(dvx,dvy)/2;//or may be +pi/2

注意:对于此实现,方向将按其长度加权,如果要分配相同的权重,方向向量应该被标准化。

此方法有时用于确定指纹识别中的线条方向:http://jmit.us.edu.pl/cms/jmitjrn/22/28_Wieclaw_4.pdf

有几种方法可以理解这种方法。其中一个是几何的:

我们有一组X轴角度为alpha[i]的矢量。我们不平均这些向量。相反,我们构建角度为2*alpha[i]的向量,对它们求平均值并取得所得角度的一半。诀窍在于,如果相反的方向相差pi,并且在加倍之后它们将相差2*pi,这完全没有区别。

答案 1 :(得分:2)

is method找到角度的平均值(在完整的圆形范围内)

MeanAngle = ArcTan2(Sum{i=1..n}(Sin(Alpha[i])), Sum{i}(Cos(Alpha[i])))

似乎对于你的情况,你可以计算方向向量的余弦的平均值(因为Cos(-alpha)= Cos(alpha)),并获得ArcCos(范围0..Pi)

MeanAngleWithoutDir = ArcCos(1/n * Sum{i=1..n}(Cos(Alpha[i])))

可能应将角度钳制到(0..Pi)或(-Pi / 2..Pi / 2)范围以避免歧义。

答案 2 :(得分:2)

元注释:此答案计算给定行的“中位数”。 MBo的other answer计算给定行的“平均值”。

让我们以下列方式将问题正式化。 我们给出了一组线,我们想要找到一条线 p ,使得 p 和所有给定线之间的角度之和尽可能小。 这里,两条线之间的角度是它们交叉点处的角度的最小值,或者如果它们是平行的或重合的则为0。 因此,两条线之间的角度始终为0到90度。

为了使事情更容易理解,翻译线条以使它们都通过原点。 显然,这不会影响答案。

为了解决这个问题,让我们研究一下这个总和的衍生物。 假设我们有一个答案行 p 。 让 x 线从 p 顺时针旋转0-90度, y 线从0开始逆时针旋转0-90度em> p ( x + y = n ,给出的总行数)。

现在,顺时针旋转 p 一个小角度α。 答案将减少 x *α并增加 y *α。 所以,如果 x> y ,答案会减少,如果 x<是,它会增加。

有两种情况,数量 x y 会发生变化。

  1. p 行符合其中一行。

  2. q 行与其中一条给定行正交。

  3. 在圆上任意两个这样的连续点之间,我们的和的导数将是常数 x - y 。 因此,最小值将是“感兴趣的角度”之一:与某些给定线平行或正交。 这导致 O(n ^ 2)算法:对于每个感兴趣的 O(n)角,只计算 O(n)<中的和/ em>,并选择给出最小总和的角度。

    这可以进一步加速到 O(n log n)

    1. O(n)中生成 2 n 感兴趣的角度。

    2. O(n log n)中对它们进行排序。

    3. 计算答案,以及 x y ,为 O(n)中的第一个角度。

    4. 沿着圆圈移动,保持当前答案以及值 x y 。 在每个 O(n)步骤中,可以在 O(1)中进行计算。

答案 3 :(得分:2)

  

统计上,他们中的大多数人或多或少都是一样的   方向。

这一关键信息对于算法的设计至关重要。如果你知道所有的矢量都在90度锥形内,你可以使用一种非常简单的方法:

  • 取第一个归一化方向向量的点积与所有其余
  • 翻转任何返回负点积的向量
  • 照常平均得到的矢量

如果您需要处理更广泛的发行版,可以稍微修改一下:

  • 一次一个地对您的归一化方向向量求和
  • 在添加矢量之前,使用当前总和计算其点积
  • 如果点积为负,则在将矢量添加到总和之前翻转矢量
  • 规范您的金额

这是一种串行算法,但如果您需要更高的性能,可以很容易地将其表示为并行缩减:

  • 对于缩小树中的每个归一化向量对
    • 取该对的点积
    • 如果它是负向翻转两个向量中的一个并将它们相加
    • 将总和转移到还原的下一阶段
  • 规范化您的最终金额

任何这些方法都可以轻松加权,因为您只关心点积的符号。

答案 4 :(得分:1)

O(n)解决方案下方,还可以为每个细分受众群提供可选的权重。

我们用X轴(a)与重量(w)的角度对每个段进行建模。此时段的方向并不重要,任何模数为180°的值都可以。我们的想法是循环每个细分并跟踪到目前为止计算的平均方向;并以模数180的方向调整此平均值,该方向更接近平均值。

伪码(以度为单位的所有角度):

aa = 0
ww = 0
for a, w in segments:
    // Compute delta between angles in range [-180°..+180°[
    da = a - aa
    if da < -180:
       da += 360
    if da >= 180:
       da -= 360
    // Optional direction swap, delta in [-90°..+90°[
    if da < -90:
       da += 180
    if da >= 90:
       da -= 180
    // The following formula also make sure aa = a mod 180
    // when ww = 0 (first iteration).
    aa += da * w / (w + ww)
    ww += w
    // Clamp result to [0°..+360°[
    if aa >= 360:
       aa -= 360
    if aa < 0:
       aa += 360
// Clamp final result aa to [0..+180°[ (optional step)
if aa > 180:
    aa -= 180

我没有证明结果与迭代顺序无关,但乍一看算法应该是。

关于此算法与输入迭代顺序的依赖关系

对于表现良好的输入数据,无论迭代顺序如何,算法都非常稳定。

然而,一旦输入数据没有明确的主方向,这个算法的结果将很大程度上取决于迭代次序,在难以预测的混沌模式中。 / p>

数值模拟显示,对于标准偏差小于20°(中位数左右)的随机方向,算法似乎总是稳定的。当标准偏差大于20°时,数值不稳定性开始出现,结果很大程度上取决于迭代次序(在20°和30°之间,差异可能小到足以忽略,超过30°的大差异开始出现)。

我没有精确计算出精确的混沌/稳定标准偏差截止值,因此将此20°值作为初始猜测。一个精确的数学解决方案留给读者练习。

在数值模拟结果下(对于0到45°的每个标准偏差,在给定标准偏差的各种随机数据上运行1000倍算法,并测量10次运行之间的平均差值)。

Average deviation between runs vs input data standard deviation around mean

因此,为了获得最佳效果,如果您的输入数据不能保证具有较小的标准偏差,则最好在稳定键上输入数据(首先是更大的重量,或者根据您的输入选择任何其他键)。