【问题标题】:Rotating a bit matrix旋转位矩阵
【发布时间】:2015-05-01 03:03:18
【问题描述】:

假设我使用大小为 8 的 char 数组来表示图像的碰撞掩码。 char 的每一位代表一个像素。实际上,我将使用 long[64] 数组作为 64x64 矩阵。

所以一个框会显示为:

00000000
01111110
01111110
01111110
01111110
01111110
01111110
00000000

45 度的示例输出应如下所示,但旋转可以是任意度数。对于 45 度旋转,此形状可能不准确,因为我是手动完成的。

00011000
00111100
01111110
11111111
11111111
01111110
00111100
00011000

还有一个向右旋转-10 度的示例输出?这些值可能是错误的,因为从数学上讲,我不知道它会如何旋转,但我认为可以安全地假设,如果每个位对旧形状的覆盖率超过 50%,那么它就是 1。

00000000
00111111
01111111
01111110
01111110
11111110
11111100
00000000

如果不进行旋转,使用此 stackoverflow 回复中定义的位移位,可以快速找到这些位掩码之间的冲突:https://stackoverflow.com/a/336615/4595736

现在,过去我使用过 Path2D、矩形、形状等...并使用 AffineTransform 来旋转对象。 Path2D 是唯一提供我想要的复杂形状的类,但它访问每个点的“类似链表”的行为并没有我希望的那么快。

在 Java 中旋转二进制地图的最佳方法是什么?

矩阵的 Java 库似乎也不是最快的。

【问题讨论】:

  • 你能用你想要达到的输出来更新这个问题吗?不知道你要做什么。
  • @aguibert 我添加了一些示例。 45 度是直接手动变换的,对于较小的旋转来说不是那么多,但我尝试手动模拟它。希望这将使我能够创建由位表示的更复杂的形状,使用 longs 而不是 chars。我将从 PNG 的 alpha 通道创建这些形状。例如,飞机精灵不是由矩形准确界定的,因此使用 Rectangle2D 对象进行碰撞检测并不理想。 Path2D 允许我创建更精确的碰撞框——但对于复杂的形状来说它非常慢。

标签: java game-engine affinetransform


【解决方案1】:

此解决方案基于prior answer。它不是将输入点映射到输出点,而是将输出点映射到输入矩阵空间中的某个位置。

在这个版本中,它只是使用最近的整数索引点的值。通过使用相邻点值的距离加权和的更复杂的值计算,它可能会获得更好的结果。

以下是一些结果:

Angle: 10.0 degrees
00000000 00000000
00000000 00000000
00111100 00011000
00111100 00011110
00111100 00111110
00111100 00111100
00000000 00001100
00000000 00000000

Angle: 45.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000001000000000
00000000000000000000 00000000011100000000
00000111111111100000 00000000111110000000
00000111111111100000 00000001111111000000
00000111111111100000 00000011111111100000
00000111111111100000 00000111111111110000
00000111111111100000 00001111111111111000
00000111111111100000 00011111111111111100
00000111111111100000 00001111111111111000
00000111111111100000 00000111111111110000
00000111111111100000 00000011111111100000
00000111111111100000 00000001111111000000
00000000000000000000 00000000111110000000
00000000000000000000 00000000011100000000
00000000000000000000 00000000001000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

Angle: 10.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000111111111100000 00000011111000000000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000111111111110000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000000000000000000 00000000001111100000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

Angle: 90.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

测试程序:

public class Test {
  public static void main(String args[]) {
    int[][] input1 = { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0 },
        { 0, 0, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } };

    testit(input1, 10);

    int[][] input2 = new int[20][20];
    for(int i=5; i<15; i++){
      for(int j = 5; j<15; j++){
        input2[i][j] = 1;
      }
    }

    testit(input2, 45);
    testit(input2, 10);
    testit(input2, 90);
  }

  private static void testit(int[][] input, double degrees) {
    int[][] output = rotate(input, degrees);
    System.out.println("Angle: "+degrees+" degrees");
    for (int i = 0; i < input.length; i++) {
      for (int j = 0; j < input[i].length; j++) {
        System.out.print(input[i][j]);
      }
      System.out.print(" ");
      for (int j = 0; j < output[i].length; j++) {
        System.out.print(output[i][j]);
      }
      System.out.println();
    }
    System.out.println();
  }

  private static int[][] rotate(int[][] input, double degrees) {

    double rad = Math.toRadians(degrees);
    double sin = Math.sin(-rad);
    double cos = Math.cos(-rad);

    int[][] output = new int[input.length][input[0].length];

    for (int i = 0; i < output.length; i++) {
      double oldX = i - output.length / 2.0; // move to center
      for (int j = 0; j < input[i].length; j++) {
        {
          double oldY = j - output[i].length / 2.0; // move to center
          double x = (int) (cos * oldX + sin * oldY + input.length / 2.0);
          double y = (int) (-sin * oldX + cos * oldY + input[i].length / 2.0);
          output[i][j] = getNearestVal(input, x, y);
        }
      }
    }
    return output;
  }

  private static int getNearestVal(int[][] input, double x, double y) {
    int xLow = (int) Math.floor(x);
    int xHigh = (int) Math.ceil(x);
    int yLow = (int) Math.floor(y);
    int yHigh = (int) Math.ceil(y);
    int[][] points = { { xLow, yLow }, { xLow, yHigh }, { xHigh, yLow },
        { xHigh, yHigh } };
    double minDistance = Double.POSITIVE_INFINITY;
    int minDistanceValue = 0;
    for (int[] point : points) {
      double distance = (point[0] - x) * (point[0] - x) + (point[1] - y)
          * (point[1] - y);
      if (distance < minDistance) {
        minDistance = distance;
        if (point[0] >= 0 && point[0] < input.length && point[1] >= 0
            && point[1] < input[point[0]].length) {
          minDistanceValue = input[point[0]][point[1]];
        } else {
          minDistanceValue = 0;
        }
      }
    }
    return minDistanceValue;
  }
}

【讨论】:

  • 将输出映射到输入是更好的方法
  • @RobAu 一般来说,是的更好,但如果你只想看看是否有碰撞,其他版本就足够了,而且对于稀疏矩阵来说肯定要快得多
  • 如果我错了请纠正我,但不应该移动到中心是 ... - output.length / 2.0 + 0.5 吗? oldY 也一样?
  • @maraca 这完全取决于如何计算旋转和存储矩阵。
  • @maraca 这种方式不会出现漏洞,这似乎是一个问题。如果你更关心稀疏矩阵的性能而不是空洞避免,那么原始解决方案会更好。
【解决方案2】:

我同意通常最好映射输出的条目,但这足以检测碰撞。并且您可以将内部点设置为 0 以使其更加稀疏(如果您没有可以跳入其他对象的非常小的对象):

...

// simple algorithm to remove inner 1s with a sliding window,
// here shown with 3x3 but I think it has to be 5x5 (you can omit the corners)
int[][] inputSparse = new int[input.length][input[0].length];
// assuming the border is 0 anyway
// not the best way to implement it, but it shows the idea and it only has to be done once
for (int i = 1; i < inputSparse.length - 1; i++) {
  for (int j = 1; j < inputSparse[0].length - 1; j++) {
    if (input[i-1][j-1] != 1 || input[i-1][j] != 1 || input[i-1][j+1] !=1 ||
        input[i][j-1] != 1 || input[i][j] != 1 || input[i][j+1] != 1 ||
        input[i+1][j-1] != 1 || input[i+1][j] != 1 || input[i+1][j+1] !=1) {
      inputSparse[i][j] = input[i][j];
    } else {
      inputSparse[i][j] = 0; // just to show that a one is removed, you don't need the else
    }
  }
}
...
output = rotate(inputSparse, 10); // example
...
private int[][] rotate(int[][] input, double degrees) {
  double rad = Math.toRadians(degrees);
  double sin = Math.sin(rad);
  double cos = Math.cos(rad);
  int[][] output = new int[input.length][input[0].length];
  for (int i = 0;  i < input.length; i++) {
    double oldY = i - (input.length - 1) / 2.0;
    for (int j = 0; j < input[0].length; j++) {
      if (input[i][j] == 1) { // <-- this is the big gain !!!
        double oldX = j - (input[0].length - 1) / 2.0;
        int x = (int)(cos * oldX + sin * oldY + input[0].length / 2.0);
        int y = (int)(-sin * oldX + cos * oldY + input.length / 2.0);
        output[y][x] = 1;
      }
    }
  }
  return output;
}

旧答案: 我不知道这是否足够好,但你只能改造那些,希望它有任何意义,我不知道你是否会得到“洞”(洞之间的0),而且这只适用于你有足够的 0 或者索引会超出范围,无论如何这是我的建议:

int[][] input = {{0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0}};

double rad = Math.toRadians(10); // 10 * Math.PI / 180;
double sin = Math.sin(rad);
double cos = Math.cos(rad);

int[][] output = new int[8][8];
// or: int[][] output = new int[input.lengh][input[0].lengh];

for (int i = 0;  i < 8; i++) {
  double oldX = i - 3.5; // move to center
  for (int j = 0; j < 8; j++) {
   if (input[i][j] == 1) {
     double oldY = j - 3.5; // move to center
     int x = (int)(cos * oldX + sin * oldY + 4); // + 3.5 to shift back, +0.5 to simulate rounding
     int y = (int)(-sin * oldX + cos * oldY + 4);
     output[x][y] = 1;
   }
  }
}

【讨论】:

  • 当 rad = Math.toRadians(45) 时,有一个洞。 00000000 00001000 00011000 00101100 01111110 00011000 00001000 00000000 这个洞似乎也是由于中心产生的一个洞,它是从圆角转置的。但对于小样本量,很难判断这是否是唯一的极端情况。我正在阅读旋转矩阵,因为这似乎是我应该解决这个问题的方法。
  • 当我将阵列扩大到 12x12 并移除舍入以更正确地对齐孔以进行调试时,由于所有孔都被舍入了 1 个单位,因此孔更多。 000001000000 000011100000 000110110000 001011101000 011111111100 110110110110 011111111100 001011101000 000110110000 000011100000 000001000000 000000000000
  • @AnthonyKorzan 对,但是这些孔对于碰撞检测真的很重要吗?我认为即使发生碰撞,你也不会像拼图一样神奇地拼在一起。跨度>
  • @AnthonyKorzan 更新了我的答案,向您展示我为什么这样做以及如何优化更多
  • 您甚至可以在其中存储第一行和最后一行以及带有 1 的列,然后在旋转时开始/结束迭代以进一步改进
猜你喜欢
  • 2014-01-13
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-08
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
相关资源
最近更新 更多