2#include <ufo/cloud/point_cloud.hpp>
3#include <ufo/map/integrator/angular_integrator.hpp>
4#include <ufo/map/ufomap.hpp>
5#include <ufo/numeric/transform3.hpp>
6#include <ufo/vision/camera.hpp>
27#include <libobsensor/ObSensor.hpp>
28#include <libobsensor/hpp/Utils.hpp>
31#include <toml++/toml.hpp>
34#define STB_IMAGE_WRITE_IMPLEMENTATION
35#include "stb_image_write.h"
38#include <pcl/filters/crop_box.h>
39#include <pcl/filters/uniform_sampling.h>
40#include <pcl/filters/voxel_grid.h>
41#include <pcl/io/pcd_io.h>
42#include <pcl/point_types.h>
43#include <pcl/registration/gicp6d.h>
44#include <pcl/registration/icp.h>
46#define RESET "\033[0m"
47#define BLACK "\033[30m"
49#define GREEN "\033[32m"
50#define YELLOW "\033[33m"
51#define BLUE "\033[34m"
52#define MAGENTA "\033[35m"
53#define CYAN "\033[36m"
54#define WHITE "\033[37m"
55#define BOLDBLACK "\033[1m\033[30m"
56#define BOLDRED "\033[1m\033[31m"
57#define BOLDGREEN "\033[1m\033[32m"
58#define BOLDYELLOW "\033[1m\033[33m"
59#define BOLDBLUE "\033[1m\033[34m"
60#define BOLDMAGENTA "\033[1m\033[35m"
61#define BOLDCYAN "\033[1m\033[36m"
62#define BOLDWHITE "\033[1m\033[37m"
71 std::uint32_t width = 640, std::uint32_t height = 400, std::uint32_t fps = 15,
72 OBFormat color_format = OB_FORMAT_RGB, OBFormat depth_format = OB_FORMAT_Y16)
73 : name_(name), pose_(pose)
76 auto devices = ctx.queryDeviceList();
77 device_ = devices->getDeviceBySN(serial_number);
78 if (
nullptr == device_) {
80 <<
"Could not find a device with serial number: " << serial_number <<
'\n'
85 config_ = std::make_shared<ob::Config>();
86 config_->enableVideoStream(OB_STREAM_COLOR, width, height, fps, color_format);
87 config_->enableVideoStream(OB_STREAM_DEPTH, width, height, fps, depth_format);
88 config_->setFrameAggregateOutputMode(
90 OB_FRAME_AGGREGATE_OUTPUT_ALL_TYPE_FRAME_REQUIRE);
91 config_->setAlignMode(ALIGN_D2C_HW_MODE);
93 pipeline_ = std::make_shared<ob::Pipeline>(device_);
94 pipeline_->enableFrameSync();
95 pipeline_->start(config_);
97 point_cloud_ = std::make_shared<ob::PointCloudFilter>();
100 ~Camera() { pipeline_->stop(); }
104 auto frame_set = pipeline_->waitForFrameset(100);
106 if (
nullptr == frame_set) {
110 point_cloud_->setCreatePointFormat(OB_FORMAT_RGB_POINT);
112 frame_ = point_cloud_->process(frame_set);
117 [[nodiscard]]
Cloud cloud()
const
119 std::uint32_t num_points = frame_->dataSize() /
sizeof(OBColorPoint);
120 OBColorPoint
const* point =
static_cast<OBColorPoint const*
>(frame_->data());
123 cloud.reserve(num_points);
125 for (std::uint32_t i{}; num_points > i; ++i) {
128 ufo::convert<ufo::FineLab>(ufo::FineRGB{point->r, point->g, point->b}));
139 std::shared_ptr<ob::Device> device_;
140 std::shared_ptr<ob::Config> config_;
141 std::shared_ptr<ob::Pipeline> pipeline_;
142 std::shared_ptr<ob::PointCloudFilter> point_cloud_;
143 std::shared_ptr<ob::Frame> frame_;
151 std::vector<char> rgb_image;
152 std::vector<char> depth_image;
153 std::filesystem::path save_dir;
155 std::size_t image_index{};
157 ufo::SmallRGB background_color;
158 float min_dist = 0.01;
159 float max_dist = 4.6;
162 ufo::SmallRGB background_color)
165 , raw_rgb_image(rays.
rows(), rays.
cols())
166 , raw_depth_image(rays.
rows(), rays.
cols())
168 , named_pipe(named_pipe)
169 , background_color(background_color)
172 mkfifo(
"ufo_manipulation", 0666);
173 fd = open(
"ufo_manipulation", O_WRONLY);
179 void render(
Map const& map)
181 pcl::PointCloud<pcl::PointXYZRGB>::Ptr pcl_cloud(
182 new pcl::PointCloud<pcl::PointXYZRGB>);
184 auto coord = map.
center(node.index);
185 auto color = map.colorRGB(node.index);
186 pcl_cloud->push_back(pcl::PointXYZRGB(coord.x, coord.y, coord.z, color.red,
187 color.green, color.blue));
198 pcl::io::savePCDFile(
199 "/home/dduberg/Downloads/manipulation/src/ufo/lib/map/apps/manipulation/renders/"
203 map.trace(ufo::execution::par, rays.
begin(), rays.
end(), nodes.
begin(),
207 [
this, &map](
auto hit) {
208 return map.valid(hit.node) ? map.colorRGB(hit.node)
212 double max_depth = 0;
213 for (
auto const& n : nodes) {
214 double depth = map.
valid(n.node) ? n.distance : max_dist;
215 max_depth = std::max(max_depth, depth);
219 raw_depth_image.
begin(), [
this, &map, max_depth](
auto hit) {
220 double depth = map.valid(hit.node) ? hit.distance : max_depth;
221 depth = std::round(depth * 255 / max_depth);
230 return static_cast<std::uint8_t>(depth);
234 std::cout << raw_rgb_image(92, 95) <<
"\n";
235 std::cout << raw_rgb_image(95, 92) <<
"\n";
236 std::set<std::pair<int, int>> open;
237 for (
int iter{}; 1 > iter; ++iter) {
238 for (
int r{}; raw_rgb_image.
rows() > r; ++r) {
239 for (
int c{}; raw_rgb_image.
cols() > c; ++c) {
240 if (255 == raw_depth_image(r, c)) {
245 open.insert(std::pair(c, r));
247 while (!open.empty()) {
248 auto cur = *open.
begin();
250 for (
int y = -1; 1 >= y; ++y) {
251 for (
int x = -1; 1 >= x; ++x) {
252 int ry = cur.second + y;
253 int cx = cur.first + x;
254 if (0 > ry || raw_rgb_image.
rows() <= ry || 0 > cx ||
255 raw_rgb_image.
cols() <= cx || 0 < seen(ry, cx)) {
259 if (255 == raw_depth_image(ry, cx)) {
265 open.insert(std::pair(cx, ry));
270 std::size_t num_white{};
271 std::size_t num_green{};
273 for (
int r{}; raw_rgb_image.
rows() > r; ++r) {
274 for (
int c{}; raw_rgb_image.
cols() > c; ++c) {
275 if (1 != seen(r, c)) {
279 auto color = raw_rgb_image(r, c);
281 if (130 < color.red && 160 > color.red && 190 < color.green &&
282 210 > color.green && 130 < color.blue && 150 > color.blue) {
284 }
else if (220 < color.red && 220 < color.green && 220 < color.blue) {
290 if (5 > num_green && 30 > num_white) {
291 for (
int r{}; raw_rgb_image.
rows() > r; ++r) {
292 for (
int c{}; raw_rgb_image.
cols() > c; ++c) {
293 if (0 == seen(r, c)) {
297 raw_rgb_image(r, c) = background_color;
298 raw_depth_image(r, c) = 255;
303 std::fill(seen.
begin(), seen.
end(), 0);
309 auto f = [
this](std::string
const& name,
auto raw_image,
auto image,
int comp) {
310 if (0 != stbi_write_jpg_to_func(
311 [](
void* context,
void* data,
int size) {
312 std::vector<char>* v =
static_cast<std::vector<char>*
>(context);
313 std::size_t s = v->size();
315 std::memcpy(v->data() + s, data, size);
317 &image, raw_image.cols(), raw_image.rows(),
318 sizeof(
typename decltype(raw_image)::value_type) /
sizeof(char),
319 raw_image.data(), 95)) {
320 std::cerr << BOLDRED <<
"Error writing jpg image\n" << RESET;
329 std::filesystem::path path =
330 save_dir / (name +
"_" + std::to_string(image_index) +
".jpg");
331 std::cout <<
"Saving file " << path <<
'\n';
332 std::ofstream file(path, std::ios::binary | std::ios::out);
333 file.write(image.data(), image.size());
339 f(
"rgb", raw_rgb_image, rgb_image, 3);
340 f(
"depth", raw_depth_image, depth_image, 1);
348[[nodiscard]] std::vector<Camera> createCameras(toml::table
const& config)
350 std::cout <<
"Creating cameras\n";
352 std::uint32_t default_width = 640;
353 std::uint32_t default_height = 400;
354 std::uint32_t default_fps = 15;
356 if (
auto v = config[
"width"].value<std::uint32_t>(); v) {
359 std::cout << YELLOW <<
"\tMissing default camera width, using '" << default_width
363 if (
auto v = config[
"height"].value<std::uint32_t>(); v) {
366 std::cout << YELLOW <<
"\tMissing default camera height, using '" << default_height
370 if (
auto v = config[
"fps"].value<std::uint32_t>(); v) {
373 std::cout << YELLOW <<
"\tMissing default camera fps, using '" << default_fps
377 std::vector<Camera> cameras;
379 for (
auto const& [name, value] : config) {
380 if (!value.is_table()) {
384 std::cout <<
"\tCamera '" << name <<
"'\n";
386 std::string_view serial_number;
388 std::uint32_t width = default_width;
389 std::uint32_t height = default_height;
390 std::uint32_t fps = default_fps;
392 auto params = *value.as_table();
394 if (
auto v = params[
"serial_number"].value<std::string_view>(); v) {
397 std::cerr << BOLDRED <<
"\t - Missing serial_number.\n" << RESET;
401 if (
"" == serial_number) {
402 std::cerr << BOLDRED <<
"\t - Empty serial_number.\n" << RESET;
406 if (
auto v = params[
"pose"].as_array(); v) {
407 std::vector<float> components;
408 v->for_each([&components](
auto&& e) {
409 if constexpr (toml::is_number<
decltype(e)>) {
410 components.push_back(*e);
413 <<
"\t - 'pose' should consist of 7 floats in the format [x, y, z, "
415 "(e.g., [0, 0, 0, 1, 0, 0, 0])\n"
420 if (7 != components.size()) {
422 <<
"\t - 'pose' should be in the format [x, y, z, qw, qx, qy, qz] "
423 "(e.g., [0, 0, 0, 1, 0, 0, 0])\n"
429 components[5], components[6])),
430 ufo::Vec3f(components[0], components[1], components[2]));
432 std::cerr << BOLDRED <<
"\t - Missing pose.\n" << RESET;
436 if (
auto v = params[
"width"].value<std::uint32_t>(); v) {
439 std::cout << YELLOW <<
"\t - Missing width, using default width of '" << width
444 if (
auto v = params[
"height"].value<std::uint32_t>(); v) {
447 std::cout << YELLOW <<
"\t - Missing height, using default height of '" << height
452 if (
auto v = params[
"fps"].value<std::uint32_t>(); v) {
455 std::cout << YELLOW <<
"\t - Missing fps, using default fps of '" << fps
460 std::cout <<
"\t - Serial number: " << serial_number <<
'\n';
461 std::cout <<
"\t - Pose: " << pose <<
'\n';
462 std::cout <<
"\t - Width: " << width <<
'\n';
463 std::cout <<
"\t - Height: " << height <<
'\n';
464 std::cout <<
"\t - FPS: " << fps <<
'\n';
466 cameras.emplace_back(name.str(), pose, serial_number.data(), width, height, fps);
469 std::cout <<
"Created " << cameras.size() <<
" cameras\n";
474[[nodiscard]]
Map createMap(toml::table
const& config)
476 std::cout <<
"Creating UFOMap\n";
478 float resolution = 0.1f;
479 if (
auto v = config[
"resolution"].value<float>(); v) {
482 std::cout << YELLOW <<
" - Missing UFOMap resolution, using '" << resolution
486 std::cout <<
" - Resolution: " << resolution <<
" m\n";
488 return Map(resolution);
493 std::cout <<
"Creating UFOMap integrator\n";
497 if (
auto v = config[
"angular_resolution"].value<float>(); v) {
500 std::cout << YELLOW <<
" - Missing 'angular_resolution', using '"
501 <<
ufo::degrees(integrator.angularResolution()) <<
"'.\n";
504 if (
auto v = config[
"min_distance"].value<float>(); v) {
505 integrator.min_distance = *v;
507 std::cout << YELLOW <<
" - Missing 'min_distance', using '" << integrator.min_distance
511 if (
auto v = config[
"max_distance"].value<float>(); v) {
512 integrator.max_distance = *v;
514 std::cout << YELLOW <<
" - Missing 'max_distance', using '" << integrator.max_distance
518 if (
auto v = config[
"sensor_angular_resolution"].value<float>(); v) {
519 integrator.sensor_angular_resolution =
ufo::radians(*v);
521 std::cout << YELLOW <<
" - Missing 'sensor_angular_resolution', using '"
522 <<
ufo::degrees(integrator.sensor_angular_resolution) <<
"'.\n";
525 if (
auto v = config[
"translation_error"].value<float>(); v) {
526 integrator.translation_error = *v;
528 std::cout << YELLOW <<
" - Missing 'translation_error', using '"
529 << integrator.translation_error <<
"'.\n";
532 if (
auto v = config[
"orientation_error"].value<float>(); v) {
533 integrator.orientation_error = *v;
535 std::cout << YELLOW <<
" - Missing 'orientation_error', using '"
536 << integrator.orientation_error <<
"'.\n";
539 if (
auto v = config[
"occupancy_hit"].value<float>(); v) {
540 integrator.occupancy_hit = *v;
542 std::cout << YELLOW <<
" - Missing 'occupancy_hit', using '"
543 << integrator.occupancy_hit <<
"'.\n";
546 if (
auto v = config[
"occupancy_miss"].value<float>(); v) {
547 integrator.occupancy_miss = *v;
549 std::cout << YELLOW <<
" - Missing 'occupancy_miss', using '"
550 << integrator.occupancy_miss <<
"'.\n";
553 std::cout <<
" - Angular resolution: " <<
ufo::degrees(integrator.angularResolution())
555 std::cout <<
" - Min. distance: " << integrator.min_distance <<
" m\n";
556 std::cout <<
" - Max. distance: " << integrator.max_distance <<
" m\n";
557 std::cout <<
" - Sensor angular resolution: "
558 <<
ufo::degrees(integrator.sensor_angular_resolution) <<
"°\n";
559 std::cout <<
" - Translation error: " << integrator.translation_error <<
" m\n";
560 std::cout <<
" - Orientation error: " <<
ufo::degrees(integrator.orientation_error)
562 std::cout <<
" - Occupancy hit: " << (100 * integrator.occupancy_hit) <<
"%\n";
563 std::cout <<
" - Occupancy miss: " << (100 * integrator.occupancy_miss) <<
"%\n";
568[[nodiscard]]
Renderer createRenderer(toml::table
const& config)
570 std::cout <<
"Creating renderer\n";
572 bool named_pipe =
false;
573 if (
auto v = config[
"named_pipe"].value<bool>(); v) {
576 std::cerr << BOLDRED <<
" - Missing named_pipe, using default value of '"
577 << std::boolalpha << named_pipe <<
"'\n"
581 std::filesystem::path save_dir;
583 if (
auto v = config[
"output_dir"].value<std::string_view>(); v) {
586 std::cerr << BOLDRED <<
" - Missing output_dir.\n" << RESET;
590 if (
"" == save_dir) {
591 std::cerr << BOLDRED <<
" - Empty output_dir.\n" << RESET;
595 unsigned width = 160;
596 unsigned height = 160;
598 if (
auto v = config[
"width"].value<std::uint32_t>(); v) {
601 std::cout << YELLOW <<
" - Missing width, using default width of '" << width
606 if (
auto v = config[
"height"].value<std::uint32_t>(); v) {
609 std::cout << YELLOW <<
" - Missing height, using default height of '" << height
614 float elevation = 5.0f;
616 if (
auto v = config[
"elevation"].value<std::uint32_t>(); v) {
619 std::cout << YELLOW <<
" - Missing elevation, using default elevation of '"
620 << elevation <<
"' instead.\n"
624 auto coord_f = [&config, elevation](std::string
const& name,
626 if (
auto v = config[name].as_array(); v) {
627 std::vector<float> components;
628 v->for_each([&name, &components](
auto&& e) {
629 if constexpr (toml::is_number<
decltype(e)>) {
630 components.push_back(*e);
632 std::cerr << BOLDRED <<
" - coordinate '" + name
633 <<
"' should consist of 2 floats in the format [x, y] (e.g., [0, "
639 if (2 != components.size()) {
640 std::cerr << BOLDRED <<
" - coordinate '" + name
641 <<
"' should consist of 2 floats in the format [x, y] (e.g., [0, 0])\n"
645 return ufo::Vec3f(components[0], components[1], elevation);
647 std::cerr << BOLDRED <<
" - Missing coordinate '" + name
648 <<
"', using default coordinate '" << default_val <<
"' instead.\n"
660 for (std::size_t r{}; rays.rows() > r; ++r) {
661 for (std::size_t c{}; rays.cols() > c; ++c) {
662 auto top =
ufo::lerp(top_left, top_right,
float(c) / rays.cols());
663 auto bottom =
ufo::lerp(bottom_left, bottom_right,
float(c) / rays.cols());
664 rays(r, c).origin =
ufo::lerp(top, bottom,
float(r) / rays.rows());
669 ufo::SmallRGB background_color;
670 if (
auto v = config[
"background_color"].as_array(); v) {
671 std::vector<std::uint8_t> components;
672 v->for_each([&components](
auto&& e) {
673 if constexpr (toml::is_number<
decltype(e)>) {
674 components.push_back(*e);
677 <<
" - background_color should consist of 3 8-bit unsigned integers in "
678 "the format [r, g, b] (e.g., [0, 32, 255])\n"
683 if (3 != components.size()) {
686 <<
" - background_color should consist of 3 8-bit unsigned integers in the "
687 "format [r, g, b] (e.g., [0, 32, 255])\n"
691 background_color = ufo::SmallRGB{components[0], components[1], components[2]};
693 std::cerr << BOLDRED <<
" - Missing background_color, using default color '"
694 << background_color <<
"' instead.\n"
698 return Renderer(rays, save_dir, named_pipe, background_color);
803[[nodiscard]] std::vector<std::pair<std::filesystem::path, ufo::Transform3f>> loadDataset(
804 toml::table
const& config)
806 std::cout <<
"Loading dataset\n";
808 if (
auto v = config[
"use_dataset"].value<bool>(); v) {
810 std::cout <<
" - dataset disabled.\n";
811 return std::vector<std::pair<std::filesystem::path, ufo::Transform3f>>();
814 std::cerr << BOLDRED <<
" - Missing use_dataset.\n" << RESET;
818 std::filesystem::path dir;
819 if (
auto v = config[
"dataset_path"].value<std::string_view>(); v) {
822 std::cerr << BOLDRED <<
" - Missing dataset_path.\n" << RESET;
827 std::cerr << BOLDRED <<
" - Empty dataset_path.\n" << RESET;
831 std::vector<std::filesystem::path> clouds;
832 std::vector<ufo::Transform3f> poses;
833 for (
auto const& entry :
std::filesystem::directory_iterator(dir)) {
834 if (!entry.is_regular_file()) {
838 auto const& path = entry.path();
840 if (
".pcd" == path.extension()) {
841 clouds.push_back(path);
842 }
else if (
".tsv" == path.extension()) {
843 if (!poses.empty()) {
844 std::cerr << BOLDRED <<
" - Multiple poses files (i.e., '.tsv' files).\n"
849 std::ifstream data(path, std::ios::in | std::ios::binary);
851 while (std::getline(data, s)) {
852 std::stringstream ss(s);
853 std::vector<float> elements;
856 while (getline(ss, tmp,
'\t')) {
857 elements.push_back(std::stof(tmp));
862 ufo::Vec3f(elements[0], elements[1], elements[2]));
870 std::cout <<
" - Found " + std::to_string(clouds.size()) +
" clouds and " +
871 std::to_string(poses.size()) +
" poses.\n";
873 if (clouds.size() != poses.size()) {
874 std::cerr << BOLDRED <<
" - Number of clouds is not the same as number of poses\n"
879 assert(clouds.size() == poses.size());
881 std::sort(clouds.begin(), clouds.end());
883 std::vector<std::pair<std::filesystem::path, ufo::Transform3f>> ret;
884 ret.reserve(clouds.size());
886 std::transform(clouds.begin(), clouds.end(), poses.begin(), std::back_inserter(ret),
887 [](
auto const& a,
auto const& b) { return std::make_pair(a, b); });
892int main(
int argc,
char* argv[])
895 std::cerr << BOLDRED <<
"Run the program like: `./UFOManipulation config.toml`\n"
902 tbl = toml::parse_file(argv[1]);
903 }
catch (toml::parse_error
const& err) {
904 std::cerr << BOLDRED <<
"Error parsing file '" << *err.source().path <<
"':\n"
905 << err.description() <<
"\n (" << err.source().begin <<
")\n"
910 Map map = createMap(*tbl[
"map"].as_table());
912 auto integrator = createIntegrator(*tbl[
"integrator"].as_table());
913 auto cameras = createCameras(*tbl[
"cameras"].as_table());
914 auto renderer = createRenderer(*tbl[
"renderer"].as_table());
916 auto dataset = loadDataset(*tbl[
"dataset"].as_table());
917 if (!dataset.empty()) {
919 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr pcl_first(
920 new pcl::PointCloud<pcl::PointXYZRGBA>);
921 Eigen::Matrix4f camera_to_world{
929 for (
auto const& [cloud_file, pose] : dataset) {
930 std::cout <<
"\rIntegrating dataset [" << ++i <<
"/" << dataset.size() <<
"]"
933 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr pcl_cloud(
934 new pcl::PointCloud<pcl::PointXYZRGBA>);
936 pcl::io::loadPCDFile<pcl::PointXYZRGBA>(cloud_file.string(), *pcl_cloud)) {
937 std::cerr << BOLDRED <<
"Could not read file " + cloud_file.string() +
".\n"
943 for (
auto& p : *pcl_cloud) {
951 Eigen::Matrix4f pcl_tf{
953 {tf[0][0], tf[1][0], tf[2][0], tf[3][0]},
954 {tf[0][1], tf[1][1], tf[2][1], tf[3][1]},
955 {tf[0][2], tf[1][2], tf[2][2], tf[3][2]},
956 {tf[0][3], tf[1][3], tf[2][3], tf[3][3]},
960 pcl_tf *= camera_to_world;
962 pcl::transformPointCloud(*pcl_cloud, *pcl_cloud, pcl_tf);
964 pcl::CropBox<pcl::PointXYZRGBA> filter;
965 filter.setMin(Eigen::Vector4f(-2.7, -2.7, -1.0, 0.0));
966 filter.setMax(Eigen::Vector4f(2.7, 2.7, 10.0, 0.0));
967 filter.setInputCloud(pcl_cloud);
968 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr ds(
969 new pcl::PointCloud<pcl::PointXYZRGBA>);
973 pcl::UniformSampling<pcl::PointXYZRGBA> sampler;
975 sampler.setInputCloud(pcl_cloud);
977 double sample_size = 0.02;
979 double std_ratio = 1.5;
980 sampler.setRadiusSearch(sample_size);
987 if (!pcl_first->empty()) {
988 pcl::GeneralizedIterativeClosestPoint6D icp;
990 icp.setInputSource(ds);
991 icp.setInputTarget(pcl_first);
993 icp.setMaximumIterations(50);
994 icp.setMaxCorrespondenceDistance(0.3);
995 icp.setRANSACOutlierRejectionThreshold(sample_size * 6);
998 icp.setEuclideanFitnessEpsilon(1e-07);
999 icp.setTransformationEpsilon(1e-09);
1000 icp.setTransformationRotationEpsilon(1e-07);
1013 std::cout <<
'\n' << pcl_tf <<
'\n';
1016 if (pcl_first->empty()) {
1023 cloud.reserve(pcl_cloud->size());
1024 for (
auto const& point : *pcl_cloud) {
1027 ufo::convert<ufo::FineLab>(ufo::SmallRGB{point.r, point.g, point.b}));
1032 auto nodes = map.create(ufo::execution::par, cloud.view<
ufo::Vec3f>());
1034 for (std::size_t i{}; cloud.size() > i; ++i) {
1038 map.occupancyUpdate(n, 0.6f,
false);
1039 map.colorAdd(n, c,
false);
1055 map.propagate(ufo::execution::par);
1056 map.modifiedReset();
1058 std::vector<std::future<bool>> res;
1085 renderer.render(map);
1091 return EXIT_SUCCESS;
Image class for storing and manipulating 2D pixel data.
constexpr size_type rows() const noexcept
Returns the number of rows (height) in the image.
auto end(this auto &self) noexcept
Returns an iterator one past the last pixel.
auto begin(this auto &self) noexcept
Returns an iterator to the first pixel.
constexpr size_type cols() const noexcept
Returns the number of columns (width) in the image.
Coord center() const
Returns the center of the tree (/ root node).
bool valid(pos_type block) const
Checks if a block is valid.
constexpr T radians(T deg) noexcept
Converts degrees to radians.
constexpr T degrees(T rad) noexcept
Converts radians to degrees.
constexpr Quat< T > normalize(Quat< T > const &q) noexcept
Returns a unit quaternion in the same direction as q.
constexpr Quat< T > lerp(Quat< T > const &x, Quat< T > const &y, T a) noexcept
Normalised linear interpolation (NLERP) between two quaternions.
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.
Unit quaternion representing an orientation or rotation in 3-D space.