更快地找到像素区域的质心
Faster way to find centroids of pixel areas
为了一个游戏,我制作了一张领土地图,由像素组成,每个领土都有不同的颜色。从那里开始,我想在每个地区添加名称。
出于视觉目的,我想将名称放在该区域的质心上。因此,我使用 PIL 将图像转换为单个大矩阵。我建立了一个class来记录每个领土的质心数据,收集在字典中。然后,我遍历像素以处理质心。此方法非常慢,对于 2400 x 1100 地图大约需要一分钟。
territory_map = numpy.array([
[0, 0, 0, 1, 0, 0, 0],
[0, 2, 2, 1, 0, 0, 0],
[2, 2, 1, 1, 3, 3, 3],
[2, 0, 0, 1, 3, 0, 0],
])
centroid_data = {}
class CentroidRecord(object):
def __init__(self, x, y):
super(CentroidRecord, self).__init__()
self.x = float(x)
self.y = float(y)
self.volume = 1
def add_mass(self, x, y):
# new_x = (old_x * old_volume + x) / (old_volume + 1),
# therefore new_x = old_x + (x - old_x) / v,
# for v = volume + 1.
self.volume += 1
self.x += (x - self.x) / self.volume
self.y += (y - self.y) / self.volume
for y in range(territory_map.shape[0]):
for x in range(territory_map.shape[1]):
cell = territory_map[y][x]
if cell == 0:
continue
if cell not in centroid_data:
centroid_data[cell] = CentroidRecord(x, y)
else:
centroid_data[cell].add_mass(x, y)
for area in centroid_data:
data = centroid_data[area]
print(f"{area}: ({data.x}, {data.y})")
这应该打印以下内容:
1: (2.8, 1.6)
2: (0.8, 1.8)
3: (4.75, 2.25)
有什么更快的方法吗?
一种颜色的质心的每个坐标只是该颜色点的所有坐标的平均值。因此,我们可以使用 dict
理解:
import numpy as np
n_colours = territory_map.max()
{i: tuple(c.mean() for c in np.where(territory_map.T == i))
for i in range(1, n_colours + 1)}
输出:
{1: (2.8, 1.6),
2: (0.8, 1.8),
3: (4.75, 2.25)}
请注意,我们需要进行转置,因为在 numpy
中,行(y 坐标)位于列(x 坐标)之前。
随机生成数据所花费的时间:
81.6 ms ± 5.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
为了一个游戏,我制作了一张领土地图,由像素组成,每个领土都有不同的颜色。从那里开始,我想在每个地区添加名称。
出于视觉目的,我想将名称放在该区域的质心上。因此,我使用 PIL 将图像转换为单个大矩阵。我建立了一个class来记录每个领土的质心数据,收集在字典中。然后,我遍历像素以处理质心。此方法非常慢,对于 2400 x 1100 地图大约需要一分钟。
territory_map = numpy.array([
[0, 0, 0, 1, 0, 0, 0],
[0, 2, 2, 1, 0, 0, 0],
[2, 2, 1, 1, 3, 3, 3],
[2, 0, 0, 1, 3, 0, 0],
])
centroid_data = {}
class CentroidRecord(object):
def __init__(self, x, y):
super(CentroidRecord, self).__init__()
self.x = float(x)
self.y = float(y)
self.volume = 1
def add_mass(self, x, y):
# new_x = (old_x * old_volume + x) / (old_volume + 1),
# therefore new_x = old_x + (x - old_x) / v,
# for v = volume + 1.
self.volume += 1
self.x += (x - self.x) / self.volume
self.y += (y - self.y) / self.volume
for y in range(territory_map.shape[0]):
for x in range(territory_map.shape[1]):
cell = territory_map[y][x]
if cell == 0:
continue
if cell not in centroid_data:
centroid_data[cell] = CentroidRecord(x, y)
else:
centroid_data[cell].add_mass(x, y)
for area in centroid_data:
data = centroid_data[area]
print(f"{area}: ({data.x}, {data.y})")
这应该打印以下内容:
1: (2.8, 1.6)
2: (0.8, 1.8)
3: (4.75, 2.25)
有什么更快的方法吗?
一种颜色的质心的每个坐标只是该颜色点的所有坐标的平均值。因此,我们可以使用 dict
理解:
import numpy as np
n_colours = territory_map.max()
{i: tuple(c.mean() for c in np.where(territory_map.T == i))
for i in range(1, n_colours + 1)}
输出:
{1: (2.8, 1.6),
2: (0.8, 1.8),
3: (4.75, 2.25)}
请注意,我们需要进行转置,因为在 numpy
中,行(y 坐标)位于列(x 坐标)之前。
随机生成数据所花费的时间:
81.6 ms ± 5.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)