| 90 | } |
| 91 | |
| 92 | void LloydKMeansFilter::filter(PointView& view) |
| 93 | { |
| 94 | if (!view.size() || (view.size() < m_k)) |
| 95 | return; |
| 96 | |
| 97 | // come up with k random samples for initial cluster centers (based on |
| 98 | // spatial farthest point sampling) |
| 99 | PointIdList ids = Segmentation::farthestPointSampling(view, m_k); |
| 100 | |
| 101 | // setup table with at least XYZ as required by KDIndex, plus any |
| 102 | // additional dimensions as specified via filter options |
| 103 | ColumnPointTable table; |
| 104 | table.layout()->registerDims({Id::X, Id::Y, Id::Z}); |
| 105 | table.layout()->registerDims(m_dimIdList); |
| 106 | table.finalize(); |
| 107 | |
| 108 | // create view of initial cluster centers |
| 109 | PointViewPtr centers(new PointView(table)); |
| 110 | PointId i(0); |
| 111 | for (auto const& id : ids) |
| 112 | { |
| 113 | centers->setField(Id::X, i, view.getFieldAs<double>(Id::X, id)); |
| 114 | centers->setField(Id::Y, i, view.getFieldAs<double>(Id::Y, id)); |
| 115 | centers->setField(Id::Z, i, view.getFieldAs<double>(Id::Z, id)); |
| 116 | for (auto const& dimid : m_dimIdList) |
| 117 | { |
| 118 | centers->setField(dimid, i, view.getFieldAs<double>(dimid, id)); |
| 119 | } |
| 120 | i++; |
| 121 | } |
| 122 | |
| 123 | // construct KDFlexIndex for nearest neighbor search |
| 124 | KDFlexIndex kdi(*centers, m_dimIdList); |
| 125 | kdi.build(); |
| 126 | |
| 127 | // update cluster centers through specified number of iterations |
| 128 | for (int iter = 0; iter < m_maxiters; ++iter) |
| 129 | { |
| 130 | // initialize mean and count for each cluster |
| 131 | std::vector<std::vector<double>> M1(m_dimIdList.size(), |
| 132 | std::vector<double>(m_k, 0.0)); |
| 133 | std::vector<point_count_t> cnt(m_k, 0); |
| 134 | |
| 135 | // for every point, find closest cluster center and assign ClusterID |
| 136 | for (PointRef p : view) |
| 137 | { |
| 138 | PointId q = kdi.neighbor(p); |
| 139 | p.setField(Id::ClusterID, q); |
| 140 | |
| 141 | // update cluster size and mean |
| 142 | cnt[q]++; |
| 143 | point_count_t n(cnt[q]); |
| 144 | for (size_t i = 0; i < m_dimIdList.size(); ++i) |
| 145 | { |
| 146 | double delta = p.getFieldAs<double>(m_dimIdList[i]) - M1[i][q]; |
| 147 | double delta_n = delta / n; |
| 148 | M1[i][q] += delta_n; |
| 149 | } |
nothing calls this directly
no test coverage detected