UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
render.cpp
1#ifdef UFO_TBB
2// #undef UFO_TBB
3#endif
4#ifdef UFO_OMP
5// #undef UFO_OMP
6#endif
7
8// UFO
9#include <ufo/map/color/map.hpp>
10#include <ufo/map/occupancy/map.hpp>
11#include <ufo/map/ufomap.hpp>
12#include <ufo/numeric/transform3.hpp>
13#include <ufo/vision/image.hpp>
14// #include <ufo/util/timing.hpp>
15#include <ufo/execution/execution.hpp>
16#include <ufo/map/distance/map.hpp>
17#include <ufo/map/integrator/integrator.hpp>
18#include <ufo/pcl/cloud.hpp>
19#include <ufo/pcl/ply.hpp>
20#include <ufo/pcl/ufo.hpp>
21#include <ufo/utility/index_iterator.hpp>
22#include <ufo/vision/camera.hpp>
23
24// STL
25#include <bitset>
26#include <chrono>
27#include <cstddef>
28#include <cstdint>
29#include <cstdio>
30#include <filesystem>
31#include <fstream>
32#include <future>
33#include <iostream>
34#include <limits>
35#include <random>
36#include <string>
37#include <thread>
38#include <vector>
39
40// OpenCV
41#include <opencv2/core.hpp>
42// #include <opencv2/dnn_superres.hpp>
43#include <opencv2/highgui.hpp>
44#include <opencv2/imgproc.hpp>
45
46// PCL
47// #include <pcl/io/ply_io.h>
48// #include <pcl/kdtree/kdtree_flann.h>
49
50// TOML
51#include "toml.hpp"
52
53using namespace ufo;
54
55std::atomic_bool map_changed = false;
56
57struct Time {
58 Time(std::string const& tag) : tag_(tag) {}
59
60 template <class T>
61 void add(T const& start)
62 {
63 double dur = std::chrono::duration<double, std::milli>(
64 std::chrono::high_resolution_clock::now() - start)
65 .count();
66
67 total_ += dur;
68 last_ = dur;
69 min_ = std::min(min_, dur);
70 max_ = std::max(max_, dur);
71 ++num_;
72 }
73
74 [[nodiscard]] double total() const { return total_; }
75
76 [[nodiscard]] double mean() const { return num_ ? total_ / num_ : 0.0; }
77
78 [[nodiscard]] double last() const { return last_; }
79
80 [[nodiscard]] double min() const { return num_ ? min_ : 0.0; }
81
82 [[nodiscard]] double max() const { return max_; }
83
84 [[nodiscard]] std::size_t count() const { return num_; }
85
86 [[nodiscard]] std::tuple<std::string, double, double, double, double, double,
87 std::size_t>
88 data() const
89 {
90 return {tag_, last(), mean(), total(), min(), max(), count()};
91 }
92
93 private:
94 std::string tag_;
95 double total_{};
96 double last_{};
97 double min_ = std::numeric_limits<double>::max();
98 double max_{};
99 std::size_t num_{};
100};
101
102struct Timings {
103 Time reading{"Reading"};
104 Time insert{"Insert"};
105 Time propagate{"Propagate"};
106 Time render{"Render"};
107 Time upscale{"Upscale"};
108
109 void print()
110 {
111 std::array data{reading.data(), insert.data(), propagate.data(), render.data(),
112 upscale.data()};
113
114 // clang-format off
115 std::cout << '\n';
116 std::cout << "UFO Timings [ms]\n";
117
118 std::cout << "+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
119 std::cout << "| What | Last | Mean | Total | Min | Max | Count |\n";
120 std::cout << "+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
121
122 for (auto const& [what, last, mean, total, min, max, count] : data) {
123 std::cout << "| " << std::left << std::setw(10) << what << " " << std::right
124 << "| " << std::setw(10) << std::fixed << std::setprecision(4) << last << " "
125 << "| " << std::setw(10) << std::fixed << std::setprecision(4) << mean << " "
126 << "| " << std::setw(11) << std::fixed << std::setprecision(4) << total << " "
127 << "| " << std::setw(11) << std::fixed << std::setprecision(4) << min << " "
128 << "| " << std::setw(11) << std::fixed << std::setprecision(4) << max << " "
129 << "| " << std::setw(13) << std::fixed << std::setprecision(4) << count << " |\n";
130 }
131
132 std::cout << "+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
133 // clang-format on
134 }
135};
136
137Transform3f deserializeTF(std::filesystem::path const& file)
138{
140
141 auto config = toml::parse_file(file.string());
142
143 auto const& rotation = *config["rotation"].as_table();
144 Quatf q;
145 q.w = rotation["w"].value_or(0.0f);
146 q.x = rotation["x"].value_or(0.0f);
147 q.y = rotation["y"].value_or(0.0f);
148 q.z = rotation["z"].value_or(0.0f);
149 transform.rotation = static_cast<Mat3x3f>(q);
150
151 auto const& translation = *config["translation"].as_table();
152 transform.translation.x = translation["x"].value_or(0.0f);
153 transform.translation.y = translation["y"].value_or(0.0f);
154 transform.translation.z = translation["z"].value_or(0.0f);
155
156 return transform;
157}
158
159template <class Map>
160void load(Map& map, Timings& timings, std::filesystem::path const& path,
161 std::size_t max_number = std::numeric_limits<std::size_t>::max(),
162 std::size_t step = 1)
163{
164 Integrator integrator;
165 integrator.sample_method = DownSamplingMethod::FIRST;
166 integrator.occupancy_hit = 0.7f;
167 integrator.occupancy_miss = 0.35f;
168 integrator.miss_depth = 0;
169 integrator.max_distance = 10.0f;
170 // integrator.counted = true;
171 // integrator.distance_offset = 0.2f;
172
173 std::filesystem::path resource_dir = RESOURCE_DIR;
174
175 std::vector<std::filesystem::path> files;
176 for (auto const& e : std::filesystem::directory_iterator(resource_dir / path)) {
177 auto path = e.path();
178 if (e.is_regular_file() && ".ufo" == path.extension()) {
179 files.push_back(path);
180 }
181 }
182 std::sort(files.begin(), files.end());
183
184 max_number = std::min(files.size(), max_number);
185 for (std::size_t i{}; max_number > i; i += step) {
186 auto const reading = std::chrono::high_resolution_clock::now();
187
188 auto file = files[i];
189
191 readCloudUFO(file, cloud);
192 auto stem = file.stem().string();
193 stem = stem.substr(0, stem.find('_'));
194 file.replace_filename(stem + "_pose.toml");
195 auto tf = deserializeTF(file);
196 transformInPlace(execution::par, tf, cloud);
197
198 timings.reading.add(reading);
199
200 auto const insert = std::chrono::high_resolution_clock::now();
201
202 std::cout << tf.translation << std::endl;
203
204 integrator.insertRays(execution::par, map, cloud, tf.translation, false);
205 // integrator.insertPoints(execution::par, map, cloud, false);
206
207 timings.insert.add(insert);
208
209 auto const propagate = std::chrono::high_resolution_clock::now();
210
211 map.propagateModified(execution::par);
212
213 timings.propagate.add(propagate);
214
215 map_changed = true;
216
217 timings.print();
218 }
219}
220
221template <class Map>
222std::optional<std::future<void>> kitti(
223 Map& map, Timings& timings,
224 std::size_t max_number = std::numeric_limits<std::size_t>::max(),
225 std::size_t step = 1)
226{
227 return std::async(std::launch::async,
228 [&]() { load<Map>(map, timings, "kitti/ufo", max_number, step); });
229}
230
231template <class Map>
232std::optional<std::future<void>> euroc(
233 Map& map, Timings& timings,
234 std::size_t max_number = std::numeric_limits<std::size_t>::max(),
235 std::size_t step = 1)
236{
237 return std::async(std::launch::async,
238 [&]() { load<Map>(map, timings, "euroc/ufo", max_number, step); });
239}
240
241int main(int argc, char* argv[])
242{
243 Timings timings;
244
245 Map3D<OccupancyMap, ColorMap> map(0.05, 19);
246 map.occupancySetThres(0.8f, 0.5f);
247 map.occupancySetClampingThres(0.97f);
248
249 // auto f = kitti(map, timings);
250 auto f = euroc(map, timings);
251
252 float yaw = M_PI_4;
253 float pitch = 0;
254 float roll = 0.0f; // M_PI_2;
255
256 Camera camera;
257 camera.vertical_fov = radians(60.0f);
258 camera.near_clip = 0.01;
259 camera.far_clip = 100.0;
260
261 std::size_t rows = 720; // 360; // 720; // 1080;
262 std::size_t cols = 1280; // 640; // 1280; // 1920;
263
264 std::size_t output_rows = rows / 1;
265 std::size_t output_cols = cols / 1;
266
267 rows /= 2;
268 cols /= 2;
269
270 double fps = 30.0;
271
272 cv::Mat img_low_res(rows, cols * 2, CV_8UC3);
273 cv::Mat img_full_res(output_rows, output_cols * 2, CV_8UC3);
274 cv::Mat img_high_res(output_rows * 4, output_cols * 4 * 2, CV_8UC3);
275 // cv::Mat rgb(rows, cols, CV_8UC3);
276 // cv::Mat mask(rows, cols, CV_8UC3);
277
278 double ufo_total_ms{};
279 double interpolate_total_ms{};
280 std::size_t iterations{};
281
282 // float angle_x = 0.0f;
283 float angle_x = M_PI;
284 // float angle_y = M_PI_2;
285 float angle_y = M_PI_2 - 0.8f;
286 // float angle_y = M_PI_2 + 0.4f;
287 float zoom = -0.0f;
288
289 Image<std::pair<Color, float>> image_low_res(rows, cols);
290 Image<std::pair<Color, float>> image_full_res(output_rows, output_cols);
291 Image<std::pair<Color, float>> image_high_res(output_rows * 4, output_cols * 4);
292
293 // f->wait();
294
295 // Vec3f center(-0.85f, 16.1f, 200.84f);
296 Vec3f center(-10, 0, 10);
297 camera.lookAt(center, Vec3f(0, 0, 0));
298
299 float linear_vel = 1.0f;
300 float angular_vel = 0.1f;
301
302 std::size_t iter_since_moved = 0;
303 bool show_free_space = false;
304 bool show_free_space_changed = false;
305
306 for (iterations = 1; true; ++iterations) {
307 auto const render = std::chrono::high_resolution_clock::now();
308
309 // Vec3f center(5.0f, 4.0f, 1.5f);
310 // Vec3f center(4.59316f, -43.7646f, 36.0135f);
311 // Vec3f center(-0.85f, 16.1f, 200.84f);
312
313 // camera.pose.translation = position; // (max - min) / 2.0f;
314 float cx = std::cos(angle_x);
315 float cy = std::cos(angle_y);
316 float sx = std::sin(angle_x);
317 float sy = std::sin(angle_y);
318 Vec3f offset = Vec3f(sy * cx, sy * sx, cy) * std::exp(-zoom);
319 // camera.lookAt(position + offset);
320 // auto roll_mat = rotate(Mat4f(), roll, normalize(offset));
321 // roll += 0.1f;
322 // camera.up = Mat3f(roll_mat) * Vec3f(0.0f, 0.0f, 1.0f);
323 camera.pose =
324 static_cast<Transform3f>(lookAt<float, true>(center + offset, center, camera.up));
325 yaw -= 0.01;
326 // angle_x += 0.01f;
327 // angle_y += 0.05f;
328 // pitch = 0;
329 // roll = 0; // M_PI_2;
330 // camera.pose.orientation = Quat(yaw, roll, pitch);
331
332 auto pred = pred::Z() > 0.0 && pred::Z() < 2.0;
333
334 std::size_t const full_res_thres = 30;
335 std::size_t const high_res_thres = 100;
336
337 auto& image =
338 full_res_thres <= iter_since_moved
339 ? (high_res_thres <= iter_since_moved ? image_high_res : image_full_res)
340 : image_low_res;
341 cv::Mat& img =
342 full_res_thres <= iter_since_moved
343 ? (high_res_thres <= iter_since_moved ? img_high_res : img_full_res)
344 : img_low_res;
345
346 if (map_changed || 0 == iter_since_moved || full_res_thres == iter_since_moved ||
347 high_res_thres == iter_since_moved || show_free_space_changed) {
348 iter_since_moved = map_changed ? full_res_thres : iter_since_moved;
349 map_changed = false;
350 show_free_space_changed = false;
351
352 map.render(
353 execution::par, camera, image,
354 [&map, pred, show_free_space](auto node, Ray3 /* ray */,
355 float /* distance */) -> bool {
356 if (show_free_space) {
357 return map.containsOccupied(node) || map.containsFree(node);
358 } else {
359 return map.containsOccupied(node);
360 }
361 },
362 [&map, pred, show_free_space](auto node, Ray3 ray, float distance) {
363 if (show_free_space) {
364 bool unknown = map.containsUnknown(node);
365 bool free = map.containsFree(node);
366 bool occupied = map.containsOccupied(node);
367 bool leaf = map.isLeaf(node);
368
369 bool leaf_and_occupied = leaf && occupied;
370 bool only_free = (leaf && free) || (free && !occupied && !unknown);
371
372 Color c;
373 if (leaf_and_occupied) {
374 c = map.color(node);
375 } else if (only_free) {
376 c.green = 255;
377 }
378
379 return std::make_pair(leaf_and_occupied || only_free,
380 std::make_pair(c, distance));
381 } else {
382 bool occupied = map.containsOccupied(node);
383 bool leaf = map.isLeaf(node);
384
385 bool leaf_and_occupied = leaf && occupied;
386
387 Color c;
388 if (leaf_and_occupied) {
389 c = map.color(node);
390 }
391
392 return std::make_pair(leaf_and_occupied, std::make_pair(c, distance));
393 }
394 },
395 std::make_pair(Color{0, 0, 0, 0}, std::numeric_limits<float>::infinity()));
396
397 timings.render.add(render);
398 }
399
400 auto const upscale = std::chrono::high_resolution_clock::now();
401
402 float min_distance = std::numeric_limits<float>::max();
403 float max_distance{};
404 for (auto const& [_, distance] : image) {
405 if (!std::isinf(distance)) {
406 min_distance = std::min(min_distance, distance);
407 max_distance = std::max(max_distance, distance);
408 }
409 }
410 max_distance -= min_distance;
411
412 auto f = [&](std::size_t i) {
413 for (std::size_t j{}; j < image.cols(); ++j) {
414 auto ufo_c = image(i, j).first;
415 img.at<cv::Vec3b>(i, j) = {ufo_c.blue, ufo_c.green, ufo_c.red};
416 bool inf = std::isinf(image(i, j).second);
417 auto distance = image(i, j).second - min_distance;
418 auto dist_c = 255 * (max_distance - distance) / max_distance;
419 // dist_c = std::pow(dist_c, 1.0f / 2.2f);
420 img.at<cv::Vec3b>(i, image.cols() + j) = cv::Vec3b::all(inf ? 0 : dist_c);
421
422 // rgb.at<cv::Vec3b>(i, j) = img.at<cv::Vec3b>(i, j);
423 // mask.at<cv::Vec3b>(i, j) = cv::Vec3b::all(inf ? 0 : 255);
424 }
425 };
426
427#if defined(UFO_TBB)
428 IndexIterator<std::size_t> row_it(0, image.rows());
429 std::for_each(std::execution::par_unseq, row_it.begin(), row_it.end(), f);
430#else
431#if defined(UFO_OMP)
432#pragma omp parallel for
433#endif
434 for (std::size_t i = 0; i < image.rows(); ++i) {
435 f(i);
436 }
437#endif
438
439 cv::Mat result;
440 cv::resize(img, result, cv::Size(), double(output_rows) / double(image.rows()),
441 double(output_cols) / double(image.cols()),
442 cv::INTER_AREA); // INTER_LANCZOS4
443
444 timings.upscale.add(upscale);
445
446 timings.print();
447
448 // cv::imwrite(RESOURCE_DIR "/ufo_highres.png", img);
449
450 cv::imshow("UFOViz", result);
451
452 auto total = std::chrono::duration<double, std::milli>(
453 std::chrono::high_resolution_clock::now() - render)
454 .count();
455 std::cout << "Waiting for "
456 << std::max(1, static_cast<int>(1000 * (1.0 / fps) - total)) << " ms\n";
457 // Wait for a keystroke in the window
458 int k = cv::waitKey(std::max(1, static_cast<int>(1000 * (1.0 / fps) - total)));
459
460 // cv::waitKey();
461
462 ++iter_since_moved;
463
464 switch (k) {
465 case 'h':
466 center += inverse(camera.pose.rotation) * Vec3f(0, 0, -linear_vel * 10);
467 iter_since_moved = 0;
468 break;
469 case 82:
470 case 'w':
471 center += inverse(camera.pose.rotation) * Vec3f(0, 0, -linear_vel);
472 iter_since_moved = 0;
473 break;
474 case 'a':
475 center += inverse(camera.pose.rotation) * Vec3f(-linear_vel, 0, 0);
476 iter_since_moved = 0;
477 break;
478 case 84:
479 case 's':
480 center += inverse(camera.pose.rotation) * Vec3f(0, 0, linear_vel);
481 iter_since_moved = 0;
482 break;
483 case 'd':
484 center += inverse(camera.pose.rotation) * Vec3f(linear_vel, 0, 0);
485 iter_since_moved = 0;
486 break;
487 case 'r':
488 center += inverse(camera.pose.rotation) * Vec3f(0, linear_vel, 0);
489 iter_since_moved = 0;
490 break;
491 case 'f':
492 center += inverse(camera.pose.rotation) * Vec3f(0, -linear_vel, 0);
493 iter_since_moved = 0;
494 break;
495 case 'i':
496 angle_y += angular_vel;
497 iter_since_moved = 0;
498 break;
499 case 81:
500 case 'j':
501 angle_x += angular_vel;
502 iter_since_moved = 0;
503 break;
504 case 'k':
505 angle_y += -angular_vel;
506 iter_since_moved = 0;
507 break;
508 case 83:
509 case 'l':
510 angle_x += -angular_vel;
511 iter_since_moved = 0;
512 break;
513 case 'q': linear_vel /= 1.1f; break;
514 case 'e': linear_vel *= 1.1f; break;
515 case 'u': angular_vel /= 1.1f; break;
516 case 'o': angular_vel *= 1.1f; break;
517 case 'g':
518 show_free_space = !show_free_space;
519 show_free_space_changed = true;
520 break;
521 default: break;
522 }
523
524 // for (auto node : map.query(pred::Occupied() && pred::Intersects<BS<3, float>>(
525 // BS<3, float>(center, 1.0f)))) {
526 // std::cout << "We are collision\n";
527 // break;
528 // }
529
530 std::cout << "Velocity: Linear: " << linear_vel << ", Angular: " << angular_vel
531 << ", Key Pressed: " << k << '\n';
532 }
533 return 0;
534}
Image class for storing and manipulating 2D pixel data.
Definition image.hpp:78
constexpr T radians(T deg) noexcept
Converts degrees to radians.
Definition math.hpp:86
T roll(Quat< T > const &q) noexcept
Extracts the roll angle (rotation about the X-axis) in radians.
Definition quat.hpp:905
STL namespace.
All vision-related classes and functions.
Definition cloud.hpp:49
T pitch(Quat< T > const &q) noexcept
Extracts the pitch angle (rotation about the Y-axis) in radians.
Definition quat.hpp:918
constexpr Vec< Geometry::dimension(), typename Geometry::value_type > max(Geometry const &g)
Returns the maximum coordinate of the minimum spanning axis-aligned bounding box of a geometry.
Definition fun.hpp:72
void transformInPlace(Transform< Dim, T > const &t, PointCloud< Dim, T, Rest... > &pc)
Applies a rigid transform to every point position in-place.
T yaw(Quat< T > const &q) noexcept
Extracts the yaw angle (rotation about the Z-axis) in radians.
Definition quat.hpp:931
constexpr M inverse(M const &m) noexcept
Computes the inverse of a square floating-point matrix.
Definition mat.hpp:1097
constexpr Vec< Geometry::dimension(), typename Geometry::value_type > min(Geometry const &g)
Returns the minimum coordinate of the minimum spanning axis-aligned bounding box of a geometry.
Definition fun.hpp:58
PointCloud< Dim, T, Rest... > transform(Transform< Dim, T > const &t, PointCloud< Dim, T, Rest... > pc)
Applies a rigid transform to every point position and returns the result.
constexpr auto distance(A const &a, B const &b)
Computes the minimum distance between two shapes.
Definition distance.hpp:61
Unit quaternion representing an orientation or rotation in 3-D space.
Definition quat.hpp:76
T w
Quaternion components. Must satisfy w^2 + x^2 + y^2 + z^2 == 1 for a valid rotation.
Definition quat.hpp:84
Ray in Dim-dimensional space.
Definition ray.hpp:66
Rigid-body transform: a rotation matrix plus a translation vector.
Definition transform.hpp:81
Mat< Dim, Dim, T > rotation
Rotation component of the transform, represented as a Dim × Dim matrix.
Definition transform.hpp:88