2#include <ufo/map/integration/integration.hpp>
3#include <ufo/map/integration/integration_parameters.hpp>
4#include <ufo/map/node.hpp>
5#include <ufo/map/point.hpp>
6#include <ufo/map/point_cloud.hpp>
7#include <ufo/map/points/points_predicate.hpp>
8#include <ufo/map/predicate/satisfies.hpp>
9#include <ufo/map/predicate/spatial.hpp>
10#include <ufo/map/types.hpp>
11#include <ufo/map/ufomap.hpp>
12#include <ufo/numeric/pose6.hpp>
13#include <ufo/util/timing.hpp>
29#include <unordered_map>
38 std::size_t first = 0;
39 std::size_t last = -1;
44 ufo::node_size_t resolution = 0.1;
45 ufo::depth_t levels = 17;
50 float max_distance = 0.2f;
51 std::size_t min_points = 0;
52 ufo::depth_t depth = 0;
61 std::string filename =
"output";
62 bool has_color =
false;
63 bool raycasting =
false;
69 ufo::IntegrationParams integration;
70 bool propagate =
false;
75 void read(toml::table tbl)
77 dataset.first = read(tbl[
"dataset"][
"first"], dataset.first);
78 dataset.last = read(tbl[
"dataset"][
"last"], dataset.last);
79 dataset.num = read(tbl[
"dataset"][
"num"], dataset.num);
81 map.resolution = read(tbl[
"map"][
"resolution"], map.resolution);
82 map.levels = read(tbl[
"map"][
"levels"], map.levels);
84 auto dsm = read(tbl[
"integration"][
"down_sampling_method"], std::string(
"none"));
85 integration.down_sampling_method =
87 ? ufo::DownSamplingMethod::NONE
88 : (
"centroid" == dsm ? ufo::DownSamplingMethod::CENTROID
89 : (
"uniform" == dsm ? ufo::DownSamplingMethod::UNIFORM
90 : ufo::DownSamplingMethod::CENTER));
91 integration.hit_depth = read(tbl[
"integration"][
"hit_depth"], integration.hit_depth);
92 integration.miss_depth =
93 read(tbl[
"integration"][
"miss_depth"], integration.miss_depth);
94 integration.min_range = read(tbl[
"integration"][
"min_range"], integration.min_range);
95 integration.max_range = read(tbl[
"integration"][
"max_range"], integration.max_range);
96 integration.inflate_unknown =
97 read(tbl[
"integration"][
"inflate_unknown"], integration.inflate_unknown);
98 integration.inflate_unknown_compensation =
99 read(tbl[
"integration"][
"inflate_unknown_compensation"],
100 integration.inflate_unknown_compensation);
101 integration.ray_passthrough_hits = read(tbl[
"integration"][
"ray_passthrough_hits"],
102 integration.ray_passthrough_hits);
103 integration.inflate_hits_dist =
104 read(tbl[
"integration"][
"inflate_hits_dist"], integration.inflate_hits_dist);
105 integration.early_stop_distance =
106 read(tbl[
"integration"][
"early_stop_distance"], integration.early_stop_distance);
107 integration.ray_casting_method = read(tbl[
"integration"][
"simple_ray_casting"],
true)
108 ? ufo::RayCastingMethod::SIMPLE
109 : ufo::RayCastingMethod::PROPER;
110 integration.simple_ray_casting_factor =
111 read(tbl[
"integration"][
"simple_ray_casting_factor"],
112 integration.simple_ray_casting_factor);
113 integration.parallel = tbl[
"integration"][
"parallel"].value_or(integration.parallel);
114 integration.num_threads =
115 read(tbl[
"integration"][
"num_threads"], integration.num_threads);
116 integration.only_valid =
117 read(tbl[
"integration"][
"only_valid"], integration.only_valid);
118 integration.sliding_window_size =
119 read(tbl[
"integration"][
"sliding_window_size"], integration.sliding_window_size);
120 propagate = read(tbl[
"integration"][
"propagate"], propagate);
122 clustering.cluster = read(tbl[
"clustering"][
"cluster"], clustering.cluster);
123 clustering.max_distance =
124 read(tbl[
"clustering"][
"max_distance"], clustering.max_distance);
125 clustering.min_points = read(tbl[
"clustering"][
"min_points"], clustering.min_points);
126 clustering.depth = read(tbl[
"clustering"][
"depth"], clustering.depth);
128 printing.verbose = read(tbl[
"printing"][
"verbose"], printing.verbose);
129 printing.debug = read(tbl[
"printing"][
"debug"], printing.debug);
131 output.filename = read(tbl[
"output"][
"filename"], output.filename);
132 output.has_color = read(tbl[
"output"][
"has_color"], output.has_color);
133 output.raycasting = read(tbl[
"output"][
"raycasting"], output.raycasting);
142 template <
typename T>
143 std::remove_cv_t<std::remove_reference_t<T>> read(toml::node_view<toml::node> node,
153 return node.value_or(default_value);
157 bool missing_config{
false};
158 bool wrong_config{
false};
161std::ostream& operator<<(std::ostream& out,
Config const& config)
165 out <<
"\tDataset\n";
166 out <<
"\t\tFirst: " << config.dataset.first <<
'\n';
168 if (-1 == config.dataset.last) {
171 out << config.dataset.last <<
'\n';
174 if (-1 == config.dataset.num) {
177 out << config.dataset.num <<
'\n';
181 out <<
"\t\tResolution: " << config.map.resolution <<
'\n';
182 out <<
"\t\tLevels: " << +config.map.levels <<
'\n';
184 out <<
"\tIntegration\n";
185 out <<
"\t\tDown sampling method: "
186 << (ufo::DownSamplingMethod::NONE == config.integration.down_sampling_method
188 : (ufo::DownSamplingMethod::CENTER ==
189 config.integration.down_sampling_method
191 : (ufo::DownSamplingMethod::CENTROID ==
192 config.integration.down_sampling_method
196 out <<
"\t\tHit depth: " << +config.integration.hit_depth <<
'\n';
197 out <<
"\t\tMiss depth: " << +config.integration.miss_depth <<
'\n';
198 out <<
"\t\tMin range: " << config.integration.min_range <<
'\n';
199 out <<
"\t\tMax range: " << config.integration.max_range <<
'\n';
200 out <<
"\t\tInflate unknown " << config.integration.inflate_unknown
202 out <<
"\t\tInflate unknown compensation "
203 << config.integration.inflate_unknown_compensation <<
'\n';
204 out <<
"\t\tRay passthrough hits " << config.integration.ray_passthrough_hits
206 out <<
"\t\tInflate hits dist " << config.integration.inflate_hits_dist
208 out <<
"\t\tEarly stop distance: " << config.integration.early_stop_distance
210 out <<
"\t\tSimple ray casting: " << std::boolalpha
211 << (ufo::RayCastingMethod::SIMPLE == config.integration.ray_casting_method ? true
214 out <<
"\t\tSimple ray casting factor: "
215 << config.integration.simple_ray_casting_factor <<
'\n';
216 out <<
"\t\tParallel: " << config.integration.parallel <<
'\n';
217 out <<
"\t\tNum threads: " << config.integration.num_threads <<
'\n';
218 out <<
"\t\tOnly valid: " << config.integration.only_valid <<
'\n';
219 out <<
"\t\tSliding window size: " << config.integration.sliding_window_size
221 out <<
"\t\tPropagate: " << std::boolalpha << config.propagate
224 out <<
"\tClustering\n";
225 out <<
"\t\tCluster: " << std::boolalpha << config.clustering.cluster <<
'\n';
226 out <<
"\t\tMax distance: " << config.clustering.max_distance <<
'\n';
227 out <<
"\t\tMin points: " << config.clustering.min_points <<
'\n';
228 out <<
"\t\tDepth: " << +config.clustering.depth <<
'\n';
230 out <<
"\tPrinting\n";
231 out <<
"\t\tVerbose: " << std::boolalpha << config.printing.verbose <<
'\n';
232 out <<
"\t\tDebug: " << std::boolalpha << config.printing.debug <<
'\n';
235 out <<
"\t\tFilename: " << config.output.filename <<
'\n';
236 out <<
"\t\tHas color: " << std::boolalpha << config.output.has_color <<
'\n';
237 out <<
"\t\tRaycasting: " << config.output.raycasting <<
'\n';
242Config readConfig(std::filesystem::path path)
246 if (std::filesystem::exists(path /
"dufomap.toml")) {
249 tbl = toml::parse_file((path /
"dufomap.toml").
string());
250 }
catch (toml::parse_error
const& err) {
251 std::cerr <<
"Configuration parsing failed:\n" << err <<
'\n';
256 if (config.printing.verbose) {
257 std::cout <<
"Found: " << (path /
"dufomap.toml") <<
'\n';
262 if (!path.has_parent_path()) {
263 std::cout <<
"Did not find configuration file, using default.\n";
266 path = path.parent_path();
269 if (config.printing.verbose) {
270 std::cout << config <<
'\n';
278 static std::random_device rd;
279 static std::mt19937 gen(rd());
280 static std::uniform_int_distribution<ufo::color_t> dis(0, -1);
281 return {dis(gen), dis(gen), dis(gen)};
287 std::unordered_set<ufo::Node> seen;
288 std::vector<ufo::Sphere> queue;
290 auto depth = clustering.depth;
291 auto max_distance = clustering.max_distance;
292 auto min_points = clustering.min_points;
295 for (
auto node : map.query(
ufo::pred::LeafOrDepth(depth) &&
ufo::pred::SeenEmpty() &&
296 ufo::pred::HitsMin(1) &&
ufo::pred::Label(0))) {
297 if (map.label(node.index())) {
302 queue.assign(1,
ufo::Sphere(map.center(node), max_distance));
304 map.setLabel(node, l,
false);
306 while (!queue.empty()) {
309 for (
auto const& node : map.query(
310 ufo::pred::LeafOrDepth(depth) &&
ufo::pred::SeenEmpty() &&
311 ufo::pred::HitsMin(1) &&
ufo::pred::Label(0) &&
std::move(p) &&
312 ufo::pred::Satisfies([&seen](auto n) {
return seen.insert(n).second; }))) {
313 queue.emplace_back(map.center(node), max_distance);
314 map.setLabel(node, l,
false);
318 if (seen.size() < min_points) {
319 for (
auto e : seen) {
320 if (l == map.label(e)) {
321 map.setLabel(e, -1,
false);
326 l += seen.size() >= min_points;
328 map.propagateModified();
332int main(
int argc,
char* argv[])
335 std::cout <<
"Usage: ./dufomap PATH\n";
339 std::filesystem::path path(argv[1]);
341 auto config = readConfig(path);
346 config.map.resolution, config.map.levels);
347 map.reserve(100'000'000);
349 std::vector<std::filesystem::path> pcds;
350 for (
auto const& entry :
std::filesystem::directory_iterator(path /
"pcd")) {
351 if (!entry.is_regular_file()) {
354 std::size_t i = std::stoul(entry.path().stem());
355 if (config.dataset.first <= i && config.dataset.last >= i) {
356 pcds.push_back(entry.path().filename());
359 std::sort(std::begin(pcds), std::end(pcds));
361 pcds.resize(std::min(pcds.size(), config.dataset.num));
364 timing.start(
"Total");
366 ufo::PointCloudColor cloud_acc;
369 for (std::string filename : pcds) {
371 timing.setTag(
"Total " + std::to_string(i) +
" of " + std::to_string(pcds.size()) +
372 " (" + std::to_string(100 * i / pcds.size()) +
"%)");
374 ufo::PointCloudColor cloud;
375 ufo::Pose6f viewpoint;
376 timing[1].start(
"Read");
377 ufo::readPointCloudPCD(path /
"pcd" / filename, cloud, viewpoint);
380 cloud_acc.insert(std::end(cloud_acc), std::cbegin(cloud), std::cend(cloud));
382 ufo::insertPointCloud(map, cloud, viewpoint.translation, config.integration,
385 if (config.printing.verbose) {
386 timing[2] = config.integration.timing;
387 timing.print(
true,
true, 2, 4);
391 if (!config.propagate) {
392 timing[3].start(
"Propagate");
393 map.propagateModified();
397 timing[4].start(
"Cluster");
398 if (config.clustering.cluster) {
399 cluster(map, config.clustering);
403 timing[5].start(
"Query");
404 ufo::PointCloudColor cloud_static;
405 ufo::PointCloudColor cloud_dynamic;
406 ufo::PointCloudColor cloud_raycasting;
408 cloud_everything.reserve(cloud_acc.size());
410 for (
auto& p : cloud_acc) {
411 auto node = map.index(p);
412 cloud_everything.emplace_back(p, p, map.seenEmpty(node), map.label(node));
413 if (!config.clustering.cluster) {
414 cloud_everything.back().label = cloud_everything.back().dynamic;
416 if (cloud_everything.back().dynamic) {
417 cloud_dynamic.push_back(p);
419 cloud_static.push_back(p);
431 timing[6].start(
"write");
433 std::async(std::launch::async,
435 ufo::writePointCloudPCD(path / (config.output.filename +
".pcd"),
438 std::async(std::launch::async,
440 ufo::writePointCloudPCD(
441 path / (config.output.filename +
"_static.pcd"), cloud_static);
443 std::async(std::launch::async,
445 ufo::writePointCloudPCD(
446 path / (config.output.filename +
"_dynamic.pcd"), cloud_dynamic);
448 std::async(std::launch::async, [&] {
449 ufo::writePointCloudPCD(path / (config.output.filename +
"_raycasting.pcd"),
459 timing[2] = config.integration.timing;
460 timing.print(
true,
true, 2, 4);
Identifies any UFO color instantiation (Gray, Lab, Lch, Lrgb, Rgb).
All vision-related classes and functions.
Sphere in Dim-dimensional space.