Skip to content

Extend and refactor log_point_cloud2 #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions rerun_bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ project(rerun_bridge)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
add_compile_options(-Wall -Wextra -Wpedantic)

# Avoid warning about CMP0135
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
Expand All @@ -22,11 +23,16 @@ find_package(OpenCV REQUIRED)
find_package(yaml-cpp REQUIRED)

include(FetchContent)
FetchContent_Declare(rerun_sdk URL https://github.com/rerun-io/rerun/releases/download/0.16.0/rerun_cpp_sdk.zip)
FetchContent_Declare(rerun_sdk URL https://github.com/rerun-io/rerun/releases/download/0.17.0/rerun_cpp_sdk.zip)
FetchContent_MakeAvailable(rerun_sdk)

# setup targets (has to be done before ament_package call)
add_library(${PROJECT_NAME} src/rerun_bridge/rerun_ros_interface.cpp)
add_library(${PROJECT_NAME}
src/rerun_bridge/rerun_ros_interface.cpp
src/rerun_bridge/color_maps.cpp
src/rerun_bridge/point_cloud_processor.cpp
src/rerun_bridge/color_mapper.cpp
)
add_executable(visualizer src/rerun_bridge/visualizer_node.cpp)

# add system dependencies
Expand Down
44 changes: 44 additions & 0 deletions rerun_bridge/include/rerun_bridge/color_mapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <optional>
#include <string>
#include <vector>

#include "sensor_msgs/point_cloud2_iterator.hpp"

#include "rerun_bridge/color_maps.hpp"
#include "rerun_bridge/rerun_ros_interface.hpp"

using pclConstIter = sensor_msgs::PointCloud2ConstIterator<float>;

struct PointCloud2Options {
std::optional<std::string> colormapName;
std::optional<std::string> colormapField;
std::optional<float> colormapMin;
std::optional<float> colormapMax;
};

class ColorMapper {
std::optional<colormapLUT> colormap_{};
std::vector<float> fieldValues_{};
std::vector<rerun::Color> colors_{};

std::pair<float, float> getMinMax(const std::vector<float>& values) const;
std::size_t calculateIdx(const float& value, const float& minValue, const float& maxValue)
const;
void setColormap();
std::string toUppercase(std::string str) const;
void convertPclMsgToColorapFieldVec(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg);
void remapValuesToColors();

public:
// data from yaml file
const PointCloud2Options options_{};

ColorMapper(const PointCloud2Options& options);
void calculateRerunColors(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg);
bool isValid() const;
void reserve(const size_t& size);
void clear();
const std::vector<rerun::components::Color>& getColors() const;
};
13 changes: 13 additions & 0 deletions rerun_bridge/include/rerun_bridge/color_maps.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <array>
#include <unordered_map>

using colormapLUT = std::array<std::array<float, 3>, 256>;

namespace ColormapsLUT {
extern const std::unordered_map<std::string, colormapLUT> supportedColormaps;

extern const colormapLUT Turbo;
extern const colormapLUT Rainbow;
} // namespace ColormapsLUT
46 changes: 46 additions & 0 deletions rerun_bridge/include/rerun_bridge/point_cloud_processor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <algorithm>
#include <optional>
#include <vector>

#include "rerun.hpp"
#include "rerun_bridge/color_mapper.hpp" // PointCloud2Options
#include "rerun_bridge/rerun_ros_interface.hpp"

class PointCloudProcessor {
ColorMapper colorMapper_;
std::size_t maxNumPerMsg_{};
std::vector<rerun::Position3D> points_{};

std::pair<std::optional<float>, std::optional<float>> getMinMax(const std::vector<float>& values
) const;

void reserve(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg);
bool isMsgLonger(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg) const;
const std::vector<rerun::components::Position3D>& convertPclMsgToPosition3DVec(
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg
);
void convertPclMsgToColorMapFieldVec(
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg, std::vector<float>& values
) const;
void convertPclMsgToPosition3DVecIter(
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg,
std::vector<rerun::components::Position3D>& points
) const;
bool areFieldNamesSupported(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg
) const;
bool isFieldTypeFloat(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::PointField& field
) const;

public:
PointCloudProcessor(const PointCloud2Options& options);
void logPointCloud2(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg
);
};
23 changes: 6 additions & 17 deletions rerun_bridge/include/rerun_bridge/rerun_ros_interface.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <map>
#include <optional>
#include <string>

#include <geometry_msgs/msg/pose_stamped.hpp>
Expand All @@ -14,16 +15,16 @@

#include <rerun.hpp>

struct ImageOptions {
std::optional<float> min_depth{};
std::optional<float> max_depth{};
};

void log_imu(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::Imu::ConstSharedPtr& msg
);

struct ImageOptions {
std::optional<float> min_depth;
std::optional<float> max_depth;
};

void log_image(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::Image::ConstSharedPtr& msg, const ImageOptions& options = ImageOptions{}
Expand Down Expand Up @@ -54,15 +55,3 @@ void log_transform(
const rerun::RecordingStream& rec, const std::string& entity_path,
const geometry_msgs::msg::TransformStamped::ConstSharedPtr& msg
);

struct PointCloud2Options {
std::optional<std::string> colormap;
std::optional<std::string> colormap_field;
std::optional<float> colormap_min;
std::optional<float> colormap_max;
};

void log_point_cloud2(
const rerun::RecordingStream& rec, const std::string& entity_path,
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg, const PointCloud2Options& options
);
100 changes: 100 additions & 0 deletions rerun_bridge/src/rerun_bridge/color_mapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <algorithm>
#include <cctype> // std::toupper
#include <iostream>
#include <optional>
#include <string>

#include "rerun_bridge/color_mapper.hpp"
#include "rerun_bridge/color_maps.hpp"

ColorMapper::ColorMapper(const PointCloud2Options& options) : options_{options} {
setColormap();
}

bool ColorMapper::isValid() const {
return (colormap_.has_value() && options_.colormapField.has_value());
}

void ColorMapper::calculateRerunColors(const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg) {
convertPclMsgToColorapFieldVec(msg);
remapValuesToColors();
}

std::size_t ColorMapper::calculateIdx(
const float& value, const float& minValue, const float& maxValue
) const {
return static_cast<size_t>(255 * (value - minValue) / (maxValue - minValue));
}

std::pair<float, float> ColorMapper::getMinMax(const std::vector<float>& values) const {
float min_value{0}, max_value{0};
if (!options_.colormapMin) {
min_value = *std::min_element(values.begin(), values.end());
} else {
min_value = *options_.colormapMin;
}

if (!options_.colormapMax) {
max_value = *std::max_element(values.begin(), values.end());
} else {
max_value = *options_.colormapMax;
}
return {min_value, max_value};
}

void ColorMapper::reserve(const size_t& size) {
colors_.reserve(size);
fieldValues_.reserve(size);
}

void ColorMapper::clear() {
colors_.clear();
fieldValues_.clear();
}

std::string ColorMapper::toUppercase(std::string str) const {
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {
return std::toupper(c);
});
return str;
}

void ColorMapper::setColormap() {
if (!options_.colormapName.has_value()) {
return;
}

// name from YAML file
auto colormapName = toUppercase(*options_.colormapName);
auto it = ColormapsLUT::supportedColormaps.find(colormapName);
if (it != ColormapsLUT::supportedColormaps.end()) {
colormap_ = it->second;
} else {
std::cout << "Colormap: " << colormapName << " is not supported" << std::endl;
}
}

void ColorMapper::convertPclMsgToColorapFieldVec(
const sensor_msgs::msg::PointCloud2::ConstSharedPtr& msg
) {
auto iterColorField = pclConstIter(*msg, options_.colormapField.value());
for (; iterColorField != iterColorField.end(); ++iterColorField) {
fieldValues_.emplace_back(*iterColorField);
}
}

void ColorMapper::remapValuesToColors() {
if (!options_.colormapName || !colormap_) {
return;
}

const auto [min, max] = getMinMax(fieldValues_);
for (const auto& value : fieldValues_) {
const auto idx = calculateIdx(value, min, max);
colors_.emplace_back((*colormap_)[idx][0], (*colormap_)[idx][1], (*colormap_)[idx][2]);
}
}

const std::vector<rerun::components::Color>& ColorMapper::getColors() const {
return colors_;
}
84 changes: 84 additions & 0 deletions rerun_bridge/src/rerun_bridge/color_maps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <array>
#include <string>
#include <tuple>
#include <unordered_map>

#include "rerun_bridge/color_maps.hpp"

constexpr colormapLUT reverse(const colormapLUT& input) {
colormapLUT reversed{};
constexpr std::size_t N = std::tuple_size<colormapLUT>::value;
for (std::size_t i = 0; i < N; ++i) {
reversed[i] = input[N - 1 - i];
}
return reversed;
}

namespace ColormapsLUT {
const std::unordered_map<std::string, colormapLUT> supportedColormaps = {
{"TURBO", Turbo}, {"RAINBOW", Rainbow}};

// Turbo colormap lookup table
// from: https://gist.github.com/mikhailov-work/6a308c20e494d9e0ccc29036b28faa7a
// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0
//
// Author: Anton Mikhailov

constexpr colormapLUT Turbo = {
{{48, 18, 59}, {50, 21, 67}, {51, 24, 74}, {52, 27, 81}, {53, 30, 88},
{54, 33, 95}, {55, 36, 102}, {56, 39, 109}, {57, 42, 115}, {58, 45, 121},
{59, 47, 128}, {60, 50, 134}, {61, 53, 139}, {62, 56, 145}, {63, 59, 151},
{63, 62, 156}, {64, 64, 162}, {65, 67, 167}, {65, 70, 172}, {66, 73, 177},
{66, 75, 181}, {67, 78, 186}, {68, 81, 191}, {68, 84, 195}, {68, 86, 199},
{69, 89, 203}, {69, 92, 207}, {69, 94, 211}, {70, 97, 214}, {70, 100, 218},
{70, 102, 221}, {70, 105, 224}, {70, 107, 227}, {71, 110, 230}, {71, 113, 233},
{71, 115, 235}, {71, 118, 238}, {71, 120, 240}, {71, 123, 242}, {70, 125, 244},
{70, 128, 246}, {70, 130, 248}, {70, 133, 250}, {70, 135, 251}, {69, 138, 252},
{69, 140, 253}, {68, 143, 254}, {67, 145, 254}, {66, 148, 255}, {65, 150, 255},
{64, 153, 255}, {62, 155, 254}, {61, 158, 254}, {59, 160, 253}, {58, 163, 252},
{56, 165, 251}, {55, 168, 250}, {53, 171, 248}, {51, 173, 247}, {49, 175, 245},
{47, 178, 244}, {46, 180, 242}, {44, 183, 240}, {42, 185, 238}, {40, 188, 235},
{39, 190, 233}, {37, 192, 231}, {35, 195, 228}, {34, 197, 226}, {32, 199, 223},
{31, 201, 221}, {30, 203, 218}, {28, 205, 216}, {27, 208, 213}, {26, 210, 210},
{26, 212, 208}, {25, 213, 205}, {24, 215, 202}, {24, 217, 200}, {24, 219, 197},
{24, 221, 194}, {24, 222, 192}, {24, 224, 189}, {25, 226, 187}, {25, 227, 185},
{26, 228, 182}, {28, 230, 180}, {29, 231, 178}, {31, 233, 175}, {32, 234, 172},
{34, 235, 170}, {37, 236, 167}, {39, 238, 164}, {42, 239, 161}, {44, 240, 158},
{47, 241, 155}, {50, 242, 152}, {53, 243, 148}, {56, 244, 145}, {60, 245, 142},
{63, 246, 138}, {67, 247, 135}, {70, 248, 132}, {74, 248, 128}, {78, 249, 125},
{82, 250, 122}, {85, 250, 118}, {89, 251, 115}, {93, 252, 111}, {97, 252, 108},
{101, 253, 105}, {105, 253, 102}, {109, 254, 98}, {113, 254, 95}, {117, 254, 92},
{121, 254, 89}, {125, 255, 86}, {128, 255, 83}, {132, 255, 81}, {136, 255, 78},
{139, 255, 75}, {143, 255, 73}, {146, 255, 71}, {150, 254, 68}, {153, 254, 66},
{156, 254, 64}, {159, 253, 63}, {161, 253, 61}, {164, 252, 60}, {167, 252, 58},
{169, 251, 57}, {172, 251, 56}, {175, 250, 55}, {177, 249, 54}, {180, 248, 54},
{183, 247, 53}, {185, 246, 53}, {188, 245, 52}, {190, 244, 52}, {193, 243, 52},
{195, 241, 52}, {198, 240, 52}, {200, 239, 52}, {203, 237, 52}, {205, 236, 52},
{208, 234, 52}, {210, 233, 53}, {212, 231, 53}, {215, 229, 53}, {217, 228, 54},
{219, 226, 54}, {221, 224, 55}, {223, 223, 55}, {225, 221, 55}, {227, 219, 56},
{229, 217, 56}, {231, 215, 57}, {233, 213, 57}, {235, 211, 57}, {236, 209, 58},
{238, 207, 58}, {239, 205, 58}, {241, 203, 58}, {242, 201, 58}, {244, 199, 58},
{245, 197, 58}, {246, 195, 58}, {247, 193, 58}, {248, 190, 57}, {249, 188, 57},
{250, 186, 57}, {251, 184, 56}, {251, 182, 55}, {252, 179, 54}, {252, 177, 54},
{253, 174, 53}, {253, 172, 52}, {254, 169, 51}, {254, 167, 50}, {254, 164, 49},
{254, 161, 48}, {254, 158, 47}, {254, 155, 45}, {254, 153, 44}, {254, 150, 43},
{254, 147, 42}, {254, 144, 41}, {253, 141, 39}, {253, 138, 38}, {252, 135, 37},
{252, 132, 35}, {251, 129, 34}, {251, 126, 33}, {250, 123, 31}, {249, 120, 30},
{249, 117, 29}, {248, 114, 28}, {247, 111, 26}, {246, 108, 25}, {245, 105, 24},
{244, 102, 23}, {243, 99, 21}, {242, 96, 20}, {241, 93, 19}, {240, 91, 18},
{239, 88, 17}, {237, 85, 16}, {236, 83, 15}, {235, 80, 14}, {234, 78, 13},
{232, 75, 12}, {231, 73, 12}, {229, 71, 11}, {228, 69, 10}, {226, 67, 10},
{225, 65, 9}, {223, 63, 8}, {221, 61, 8}, {220, 59, 7}, {218, 57, 7},
{216, 55, 6}, {214, 53, 6}, {212, 51, 5}, {210, 49, 5}, {208, 47, 5},
{206, 45, 4}, {204, 43, 4}, {202, 42, 4}, {200, 40, 3}, {197, 38, 3},
{195, 37, 3}, {193, 35, 2}, {190, 33, 2}, {188, 32, 2}, {185, 30, 2},
{183, 29, 2}, {180, 27, 1}, {178, 26, 1}, {175, 24, 1}, {172, 23, 1},
{169, 22, 1}, {167, 20, 1}, {164, 19, 1}, {161, 18, 1}, {158, 16, 1},
{155, 15, 1}, {152, 14, 1}, {149, 13, 1}, {146, 11, 1}, {142, 10, 1},
{139, 9, 2}, {136, 8, 2}, {133, 7, 2}, {129, 6, 2}, {126, 5, 2},
{122, 4, 3}}};

constexpr colormapLUT Rainbow = reverse(Turbo);

} // namespace ColormapsLUT
Loading