16 #include <mrpt/containers/yaml.h>
17 #include <mrpt/math/ops_containers.h>
18 #include <mrpt/random/RandomGenerators.h>
21 #include <mrpt/maps/CPointsMapXYZI.h>
22 #include <mrpt/maps/CPointsMapXYZIRT.h>
33 c.has(
"input_pointcloud_layer"),
34 "YAML configuration must have an entry `input_pointcloud_layer` with a "
35 "scalar or sequence.");
39 auto cfgIn = c[
"input_pointcloud_layer"];
48 "YAML configuration must have an entry `input_pointcloud_layer` "
49 "with a scalar or sequence.");
51 for (
const auto&
s : cfgIn.asSequence())
64 if (c.has(
"flatten_to"))
flatten_to = c[
"flatten_to"].as<
double>();
69 mrpt::system::COutputLogger::setLoggerName(
"FilterDecimateVoxels");
76 MRPT_LOG_DEBUG_STREAM(
"Loading these params:\n" << c);
101 std::vector<mrpt::maps::CPointsMap*> pcPtrs;
102 size_t reserveSize = 0;
105 if (
auto itLy = inOut.
layers.find(inputLayer);
106 itLy != inOut.
layers.end())
111 "Layer '%s' must be of point cloud type.",
114 pcPtrs.push_back(pcPtr);
115 reserveSize += pcPtr->size() / 10;
123 "Input layer '%s' not found on input map.",
134 ASSERT_(!pcPtrs.empty());
145 pcPtrs.at(0)->GetRuntimeClass()->className);
147 outPc->reserve(outPc->size() + reserveSize);
153 std::vector<size_t> idxsToRemove;
154 for (
size_t mapIdx = 0; mapIdx < pcPtrs.size(); mapIdx++)
162 idxsToRemove.push_back(mapIdx);
164 const auto& xs = pcPtrs[mapIdx]->getPointsBufferRef_x();
165 const auto& ys = pcPtrs[mapIdx]->getPointsBufferRef_y();
167 for (
size_t i = 0; i < xs.size(); i++)
174 outPc->insertPointFrom(*pcPtrs[mapIdx], i);
177 for (
auto it = idxsToRemove.rbegin(); it != idxsToRemove.rend(); ++it)
178 pcPtrs.erase(pcPtrs.begin() + *it);
183 size_t nonEmptyVoxels = 0;
189 "Has you called initialize() after updating/loading parameters?");
196 for (
const auto& pcPtr : pcPtrs)
198 const auto& pc = *pcPtr;
199 grid.processPointCloud(pc);
202 ASSERT_(sanityPassed);
215 if (!vxl.
pointIdx.has_value())
return;
221 const PointCloudToVoxelGridSingle::indices_t flattenIdx = {
222 idx.cx_, idx.cy_, 0};
225 if (flattenUsedBins.count(flattenIdx) != 0)
229 flattenUsedBins.insert(flattenIdx);
231 outPc->insertPointFast(
244 "Only one input layer allowed when requiring the non-single "
247 const auto& pc = *pcPtrs.at(0);
251 "Has you called initialize() after updating/loading parameters?");
257 grid.processPointCloud(pc);
259 const auto& xs = pc.getPointsBufferRef_x();
260 const auto& ys = pc.getPointsBufferRef_y();
261 const auto& zs = pc.getPointsBufferRef_z();
263 auto rng = mrpt::random::CRandomGenerator();
275 if (vxl.
indices.empty())
return;
278 std::optional<mrpt::math::TPoint3Df> insertPt;
285 auto mean = mrpt::math::TPoint3Df(0, 0, 0);
286 const float inv_n = (1.0f / vxl.indices.size());
287 for (size_t i = 0; i < vxl.indices.size(); i++)
289 const auto pt_idx = vxl.indices[i];
290 mean.x += xs[pt_idx];
291 mean.y += ys[pt_idx];
292 mean.z += zs[pt_idx];
299 std::optional<float> minSqrErr;
300 std::optional<size_t> bestIdx;
302 for (size_t i = 0; i < vxl.indices.size(); i++)
304 const auto pt_idx = vxl.indices[i];
306 mrpt::square(xs[pt_idx] - mean.x) +
307 mrpt::square(ys[pt_idx] - mean.y) +
308 mrpt::square(zs[pt_idx] - mean.z);
310 if (!minSqrErr.has_value() || sqrErr < *minSqrErr)
317 insertPtIdx = *bestIdx;
322 insertPt = {mean.x, mean.y, mean.z};
328 const auto idxInVoxel =
330 ? (rng.drawUniform64bit() % vxl.
indices.size())
333 const auto pt_idx = vxl.
indices.at(idxInVoxel);
334 insertPtIdx = pt_idx;
344 if (flattenUsedBins.count(flattenIdx) != 0)
348 flattenUsedBins.insert(flattenIdx);
352 xs[insertPtIdx], ys[insertPtIdx], zs[insertPtIdx]);
353 outPc->insertPointFast(
359 outPc->insertPointFast(
360 insertPt->x, insertPt->y, insertPt->z);
361 else { outPc->insertPointFrom(pc, insertPtIdx); }
367 outPc->mark_as_modified();
369 MRPT_LOG_DEBUG_STREAM(
370 "Voxel count=" << nonEmptyVoxels
371 <<
", output_layer=" << params_.output_pointcloud_layer
372 <<
" type=" << outPc->GetRuntimeClass()->className
374 << (useSingleGrid() ?
"Yes" :
"No"));