查找像素区域质心的更快方法

时间:2019-04-23 07:38:07

标签: python numpy optimization centroid

在游戏中,我制作了一个由像素组成的区域地图,每个区域都有不同的颜色。从那里,我想在每个地区添加名称。

出于视觉目的,我想将名称放在该区域的质心上。因此,我使用PIL将图像转换为单个大矩阵。我建立了一个类来记录在字典中收集的每个区域的质心数据。然后,我遍历像素以处理质心。这种方法非常慢,对于2400 x 1100的地图来说大约需要一分钟。

#include <string>
#include <iostream>
#include <pybind11/pybind11.h>

namespace py = pybind11;

class SS {
  public:
    SS() = default;
    SS(const std::string& s) : data_(s.data()), size_(s.size()) {}
    const char* data() const { return data_; }

  private:
    const char* data_;
    size_t size_;
};

class PySS {
  public:
    PySS(const std::string& s) { m_data = s; m_SS = SS(m_data); }
    std::string get() { return std::string(m_SS.data()); }

  private:
    std::string m_data;
    SS m_SS;
};

PYBIND11_MODULE(example, m)
{
  py::class_<PySS>(m, "SS")
    .def(py::init<const std::string&>())
    .def("get", &PySS::get);
}

这应该打印以下内容:

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 个答案:

答案 0 :(得分:0)

颜色质心的每个坐标只是该颜色点的所有坐标的均值。因此,我们可以使用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)
相关问题