3

我正在开发一个涉及在游戏中获取相机角度的应用程序。角度可以是 0-359 之间的任何位置。0 是北,90 是东,180 是南,等等。我正在使用一个 API,它在 Camera 类中有一个 getAngle() 方法。

我如何找到不同摄像机角度之间的平均值。0 和 359 的实际平均值为 179.5。作为一个摄像机角度,那将是南方,但显然 0 和 359 都非常接近北方。

4

4 回答 4

6

您可以从向量的角度来考虑它。让和成为你的两个角度,用弧度表示。然后我们可以确定在这些角度的单位向量的 x 和 y 分量:θ1θ2

x 1 = sin(θ 1 )
y 1 = cos(θ 1 )

x 2 = sin(θ 2 )
y 2 = cos(θ 2 )

然后,您可以添加这两个向量,并确定结果的 x 和 y 分量:

x * = x 1 + x 2 
y * = y 1 + y 2

最后,您可以确定这个结果向量的角度:

θ avg = tan -1 (y * /x * )

或者,更好的是,使用atan2(许多语言支持的功能):

θ avg = atan2(y * , x * )

您可能必须分别处理 where的情况,因为这意味着这两个向量指向完全相反的方向(那么“平均值”应该是多少?)。y* = 0 x* = 0

于 2013-08-06T01:05:34.803 回答
2

这取决于您所说的“平均”是什么意思。但正常的定义是夹锐角的平分线。您必须将两者置于 180 度以内。有很多方法可以做到这一点,但一种简单的方法是增加或减少一个角度。如果角度是ab,那么这将做到:

if (a < b)
  while (abs(a - b) > 180) a = a + 360
else
  while (abs(a - b) > 180) a = a - 360

现在您可以计算简单平均值:

avg = (a + b) / 2

当然,您可能还想再标准化一次:

while (avg < 0) avg  = avg + 360
while (avg >= 360) avg = avg - 360

在您的示例中,您将有 a=0,b=359。第一个循环将 a 增加到 360。平均值为 359.5。当然,如果您愿意,您可以将其四舍五入为整数。如果四舍五入到 360,那么最后一组循环将递减为 0。

请注意,如果您的角度始终归一化为 [0..360),则这些循环都不会执行超过一次。但它们可能是一种很好的做法,因此狂野的论点不会导致您的代码失败。

于 2013-08-06T00:52:45.267 回答
1

你想平分角度而不是平均它们。首先获取它们之间的距离,采用最短的方式,然后将其分成两半并添加到其中一个角度。例如:

A = 355
B = 5

if (abs(A - B) < 180) {
    Distance = abs(A - B)
    if (A < B) {
        Bisect = A + Distance / 2
    }
    else {
        Bisect = B + Distance / 2
    }
}
else {
    Distance = 360 - abs(A - B)
    if (A < B) {
        Bisect = A - Distance / 2
    }
    else {
        Bisect = B - Distance / 2
    }
}

或者类似的东西——对于给定的输入,“Bisect”应该为零。可能有一些聪明的方法可以使算术产生更少的 if 和 abs 操作。

于 2013-08-06T01:13:33.437 回答
0

在评论中,您提到要平均的所有“角度”都在 90 度以内。我猜实际上只有一个相机,但它移动很多,并且您正在为相机 POV 创建某种图像稳定性机制。

无论如何,只有相机可能在270-359象限和0-89象限的特殊情况。对于所有其他情况,您可以取一个简单的平均值。因此,您只需要检测这种特殊情况,当它发生时,将 270-359 象限中的角度视为 -90 到 -1。然后,在计算简单平均值后,如有必要,将其调整回 270-359 象限。

在 C 代码中:

int quadrant (int a) {
    assert(0 <= a && a < 360);
    return a/90;
}

double avg_rays (int rays[], int num) {
    int i;
    int quads[4] = { 0, 0, 0, 0 };
    double sum = 0;
    /* trivial case */
    if (num == 1) return rays[0];
    for (i = 0; i < num; ++i) ++quads[quadrant(rays[i])];
    if (quads[0] == 0 || quads[3] == 0) {
        /* simple case */
        for (i = 0; i < num; ++i) sum += rays[i];
        return sum/num;
    }
    /* special case */
    for (i = 0; i < num; ++i) {
        if (quadrant(rays[i]) == 3) rays[i] -= 360;
        sum += rays[i];
    }
    return sum/num + (sum < 0) * 360;
}

可以以牺牲目的明确性为代价来优化此代码。当您检测到特殊情况时,您可以在sum事后修复。因此,您可以计算sum并找出特殊情况,并一次性完成修复。

double avg_rays_opt (int rays[], int num) {
    int i;
    int quads[4] = { 0, 0, 0, 0 };
    double sum = 0;
    /* trivial case */
    if (num == 1) return rays[0];
    for (i = 0; i < num; ++i) {
        ++quads[quadrant(rays[i])];
        sum += rays[i];
    }
    if (quads[0] == 0 || quads[3] == 0) {
        /* simple case */
        return sum/num;
    }
    /* special case */
    sum -= quads[3]*360;
    return sum/num + (sum < 0) * 360;
}

我相信它可以进一步优化,但它应该给你一个开始。

于 2013-08-06T03:24:24.210 回答