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>
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>
41#include <opencv2/core.hpp>
43#include <opencv2/highgui.hpp>
44#include <opencv2/imgproc.hpp>
55std::atomic_bool map_changed =
false;
58 Time(std::string
const& tag) : tag_(tag) {}
61 void add(T
const& start)
63 double dur = std::chrono::duration<double, std::milli>(
64 std::chrono::high_resolution_clock::now() - start)
69 min_ = std::min(min_, dur);
70 max_ = std::max(max_, dur);
74 [[nodiscard]]
double total()
const {
return total_; }
76 [[nodiscard]]
double mean()
const {
return num_ ? total_ / num_ : 0.0; }
78 [[nodiscard]]
double last()
const {
return last_; }
80 [[nodiscard]]
double min()
const {
return num_ ? min_ : 0.0; }
82 [[nodiscard]]
double max()
const {
return max_; }
84 [[nodiscard]] std::size_t count()
const {
return num_; }
86 [[nodiscard]] std::tuple<std::string, double, double, double, double, double,
90 return {tag_, last(), mean(), total(), min(), max(), count()};
97 double min_ = std::numeric_limits<double>::max();
103 Time reading{
"Reading"};
104 Time insert{
"Insert"};
105 Time propagate{
"Propagate"};
106 Time render{
"Render"};
107 Time upscale{
"Upscale"};
111 std::array data{reading.data(), insert.data(), propagate.data(), render.data(),
116 std::cout <<
"UFO Timings [ms]\n";
118 std::cout <<
"+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
119 std::cout <<
"| What | Last | Mean | Total | Min | Max | Count |\n";
120 std::cout <<
"+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
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";
132 std::cout <<
"+------------+------------+------------+-------------+-------------+-------------+---------------+\n";
137Transform3f deserializeTF(std::filesystem::path
const& file)
141 auto config = toml::parse_file(file.string());
143 auto const& rotation = *config[
"rotation"].as_table();
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);
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);
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)
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;
173 std::filesystem::path resource_dir = RESOURCE_DIR;
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);
182 std::sort(files.begin(), files.end());
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();
188 auto file = files[i];
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);
198 timings.reading.add(reading);
200 auto const insert = std::chrono::high_resolution_clock::now();
202 std::cout << tf.translation << std::endl;
204 integrator.insertRays(execution::par, map, cloud, tf.translation,
false);
207 timings.insert.add(insert);
209 auto const propagate = std::chrono::high_resolution_clock::now();
211 map.propagateModified(execution::par);
213 timings.propagate.add(propagate);
222std::optional<std::future<void>> kitti(
224 std::size_t max_number = std::numeric_limits<std::size_t>::max(),
225 std::size_t step = 1)
227 return std::async(std::launch::async,
228 [&]() { load<Map>(map, timings,
"kitti/ufo", max_number, step); });
232std::optional<std::future<void>> euroc(
234 std::size_t max_number = std::numeric_limits<std::size_t>::max(),
235 std::size_t step = 1)
237 return std::async(std::launch::async,
238 [&]() { load<Map>(map, timings,
"euroc/ufo", max_number, step); });
241int main(
int argc,
char* argv[])
246 map.occupancySetThres(0.8f, 0.5f);
247 map.occupancySetClampingThres(0.97f);
250 auto f = euroc(map, timings);
257 camera.vertical_fov =
radians(60.0f);
258 camera.near_clip = 0.01;
259 camera.far_clip = 100.0;
261 std::size_t rows = 720;
262 std::size_t cols = 1280;
264 std::size_t output_rows = rows / 1;
265 std::size_t output_cols = cols / 1;
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);
278 double ufo_total_ms{};
279 double interpolate_total_ms{};
280 std::size_t iterations{};
283 float angle_x = M_PI;
285 float angle_y = M_PI_2 - 0.8f;
296 Vec3f center(-10, 0, 10);
297 camera.lookAt(center,
Vec3f(0, 0, 0));
299 float linear_vel = 1.0f;
300 float angular_vel = 0.1f;
302 std::size_t iter_since_moved = 0;
303 bool show_free_space =
false;
304 bool show_free_space_changed =
false;
306 for (iterations = 1;
true; ++iterations) {
307 auto const render = std::chrono::high_resolution_clock::now();
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);
324 static_cast<Transform3f>(lookAt<float, true>(center + offset, center, camera.up));
334 std::size_t
const full_res_thres = 30;
335 std::size_t
const high_res_thres = 100;
338 full_res_thres <= iter_since_moved
339 ? (high_res_thres <= iter_since_moved ? image_high_res : image_full_res)
342 full_res_thres <= iter_since_moved
343 ? (high_res_thres <= iter_since_moved ? img_high_res : img_full_res)
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;
350 show_free_space_changed =
false;
353 execution::par, camera, image,
354 [&map, pred, show_free_space](
auto node,
Ray3 ,
356 if (show_free_space) {
357 return map.containsOccupied(node) || map.containsFree(node);
359 return map.containsOccupied(node);
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);
369 bool leaf_and_occupied = leaf && occupied;
370 bool only_free = (leaf && free) || (free && !occupied && !unknown);
373 if (leaf_and_occupied) {
375 }
else if (only_free) {
379 return std::make_pair(leaf_and_occupied || only_free,
382 bool occupied = map.containsOccupied(node);
383 bool leaf = map.isLeaf(node);
385 bool leaf_and_occupied = leaf && occupied;
388 if (leaf_and_occupied) {
392 return std::make_pair(leaf_and_occupied, std::make_pair(c,
distance));
395 std::make_pair(Color{0, 0, 0, 0}, std::numeric_limits<float>::infinity()));
397 timings.render.add(render);
400 auto const upscale = std::chrono::high_resolution_clock::now();
402 float min_distance = std::numeric_limits<float>::max();
403 float max_distance{};
404 for (
auto const& [_,
distance] : image) {
406 min_distance = std::min(min_distance,
distance);
407 max_distance = std::max(max_distance,
distance);
410 max_distance -= min_distance;
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;
420 img.at<cv::Vec3b>(i, image.cols() + j) = cv::Vec3b::all(inf ? 0 : dist_c);
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);
432#pragma omp parallel for
434 for (std::size_t i = 0; i < image.rows(); ++i) {
440 cv::resize(img, result, cv::Size(),
double(output_rows) /
double(image.rows()),
441 double(output_cols) /
double(image.cols()),
444 timings.upscale.add(upscale);
450 cv::imshow(
"UFOViz", result);
452 auto total = std::chrono::duration<double, std::milli>(
453 std::chrono::high_resolution_clock::now() - render)
455 std::cout <<
"Waiting for "
456 << std::max(1,
static_cast<int>(1000 * (1.0 / fps) - total)) <<
" ms\n";
458 int k = cv::waitKey(std::max(1,
static_cast<int>(1000 * (1.0 / fps) - total)));
467 iter_since_moved = 0;
472 iter_since_moved = 0;
476 iter_since_moved = 0;
481 iter_since_moved = 0;
485 iter_since_moved = 0;
489 iter_since_moved = 0;
493 iter_since_moved = 0;
496 angle_y += angular_vel;
497 iter_since_moved = 0;
501 angle_x += angular_vel;
502 iter_since_moved = 0;
505 angle_y += -angular_vel;
506 iter_since_moved = 0;
510 angle_x += -angular_vel;
511 iter_since_moved = 0;
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;
518 show_free_space = !show_free_space;
519 show_free_space_changed =
true;
530 std::cout <<
"Velocity: Linear: " << linear_vel <<
", Angular: " << angular_vel
531 <<
", Key Pressed: " << k <<
'\n';
Image class for storing and manipulating 2D pixel data.
constexpr T radians(T deg) noexcept
Converts degrees to radians.
T roll(Quat< T > const &q) noexcept
Extracts the roll angle (rotation about the X-axis) in radians.
All vision-related classes and functions.
T pitch(Quat< T > const &q) noexcept
Extracts the pitch angle (rotation about the Y-axis) in radians.
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.
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.
constexpr M inverse(M const &m) noexcept
Computes the inverse of a square floating-point matrix.
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.
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.
Unit quaternion representing an orientation or rotation in 3-D space.
T w
Quaternion components. Must satisfy w^2 + x^2 + y^2 + z^2 == 1 for a valid rotation.
Ray in Dim-dimensional space.