在2D位图上查找质心

时间:2009-01-03 00:05:00

标签: actionscript-3 algorithm language-agnostic

我正在为游戏编码,我希望能够在黑白位图上找到任意形状的质心,如下所示:

 012345678
0.XX......
1..XXX....
2...XXX...
3..XXXXXXX
4...XXX...

所有“细胞”具有相同的重量。对角相邻的单元格不被认为是连接的,并且形状将始终是单个单元格,因为在此之前它已经被另一个函数拆分。

它仅用于合理的低分辨率(最多50x50)图像,并且它不需要超精确,速度更可取。

我觉得有一种正确的方法可以做到这一点,但我真的不知道谷歌的用途。

我在Actionscript 3中编写了这个代码,但是如果人们理解它们,那么任何语言的例子都会受到赞赏。

编辑:您可以随意假设数据存储在您认为最适合您示例的数据结构中。我正在使用位图,但是二维数组甚至单个数组也都可以!

编辑:这是我最终使用的代码,它很可能会更快地完成,但我发现它非常易读:

// _bmp is a private BitmapData instance
public function getCenterOfMass():Point {
    var avg     :Point  = new Point(0, 0);
    var points  :uint   = 0;

    for (var ix:uint = 0; ix < _bmp.width; ix++) {
        for (var iy:uint = 0; iy < _bmp.height; iy++) {
            if (_bmp.getPixel(ix, iy) == ACTIVE_COLOR) {
                avg.x += ix;
                avg.y += iy;
                points++;
            }
        }
    }

    avg.x /= points;
    avg.y /= points;

    return avg;
}

3 个答案:

答案 0 :(得分:19)

这个算法(伪代码)如何基于类似于你的例子中的布尔矩阵:

xSum = 0
ySum = 0
points = 0

for point in matrix
    if point is marked
        xSum += pointX
        ySum += pointY
        points++

return (xSum/points, ySum/points)

没有什么太复杂,计算X最大的存在,Y的相同,除以你计算的点数,你得到质量中心。你可以通过在平均值中给出不同的权重来进一步使这一点复杂化,但这应该是你的主要方向。


这个问题让我想到了这个问题的扩展,我找不到一个好的答案。我在这里发布了这个问题:Finding clusters of mass in a matrix/bitmap

答案 1 :(得分:2)

您可以计算相邻单元格的数量,然后以具有最高邻居计数的单元格为中心。

 012345678
0 XX      |
1  XXX    |
2   XXX   |
3  XXXXXXX|
4   XXX   |

 012345678
0 23      |
1  454    |
2   775   |
3  3686421|
4   454   |

在此示例中,您可以使用8的唯一单元格作为中心点。

如果您想出了具有相同数量邻居的多个单元格,您只需再次运行计数例程,但这次只使用高数字单元格。

例如,让我们假设拥有678的单元格都有八个邻居。

 012345678
0 23      |
1  454    |
2   885   |
3  3888421|
4   454   |

 012345678
0 --      |
1  ---    |
2   XX-   |
3  -XXX---|
4   ---   |

 012345678
0 --      |
1  ---    |
2   34-   |
3  -342---|
4   ---   |

 012345678
0 --      |
1  ---    |
2   -X-   |
3  --X----|
4   ---   |

在简单领带的情况下,我会选择离中心较近的那个。在这种情况下,它将是两者中较高的一个。

注意:我假设您没有使用它进行精确的物理模拟。

答案 2 :(得分:1)

根据动作脚本解释器的性质和对形状进行的预处理,您可以通过最初创建对角镜像的位图/数组的第二个副本来看到速度改进(通过Yuval指出的直接方法)然后使用行或字符串操作函数在单个步骤中对每行和每列上的点求和。这将是O(2m n)而不是O(n n)[见下文],但是有更多的开销。

xSum = 0
ySum = 0
points = 0

for row in matrix
  ySum += rowX * countpoints(row)
  points += countpoints(row)
for row in mirroredmatrix
  xSum += rowX * countpoints(row)

return (xSum/points, ySum/points)

其中countpoints()计算连续的点数。这依赖于计数点(其具有O(n)运行时)具有比原始方法的运行时更低的常数乘数(因此,上面,'m'是计数点的运行时,'n'是解释器循环行的时间)。 countpoints()的性质取决于您的存储方法,可能涉及计算字符串中的字符或位域中的位。