From e0181ab7052eeee9fbe6eba046c039fb561d2492 Mon Sep 17 00:00:00 2001 From: Meixner <andre.meixner@kit.edu> Date: Thu, 13 Jul 2023 20:28:20 +0200 Subject: [PATCH 1/2] Added filter to costmap --- .../armarx/navigation/algorithms/Costmap.cpp | 74 ++++++++++++++++++- source/armarx/navigation/algorithms/Costmap.h | 15 ++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/source/armarx/navigation/algorithms/Costmap.cpp b/source/armarx/navigation/algorithms/Costmap.cpp index 862dc263..ace775b0 100644 --- a/source/armarx/navigation/algorithms/Costmap.cpp +++ b/source/armarx/navigation/algorithms/Costmap.cpp @@ -172,6 +172,61 @@ namespace armarx::navigation::algorithms return {.value = minVal, .index = {row, col}, .position = toPositionGlobal({row, col})}; } + void + Costmap::filter(const Filter &filter) + { + ARMARX_TRACE; + + ARMARX_CHECK(filter.matrix.rows() % 2 == 1 and filter.matrix.cols() % 2 == 1); + + if (mask.has_value()) + { + ARMARX_CHECK_GREATER(mask->array().sum(), 0) + << "At least one element has to be valid. Here, all elements are masked out!"; + } + + Grid newGrid(grid.rows(), grid.cols()); + const double max = grid.maxCoeff(); + newGrid.setConstant(max); + for (int r = 0; r < grid.rows(); r++) + { + for (int c = 0; c < grid.cols(); c++) + { + if (mask.has_value()) + { + if (not mask.value()(r, c)) // skip invalid cells + { + continue; + } + + Eigen::MatrixXf valueMatrix(filter.matrix.rows(), filter.matrix.cols()); + const int radius_r = (filter.matrix.rows() - 1) / 2; + const int radius_c = (filter.matrix.cols() - 1) / 2; + valueMatrix.setConstant(max); + for (int i = -radius_r; i <= radius_r; i++) + { + for (int j = -radius_c; j <= radius_c; j++) + { + const int ri = r + i; + const int cj = c + j; + if (ri > 0 and ri < grid.rows() and cj > 0 and cj < grid.cols() and mask.value()(ri, cj)) + { + valueMatrix(radius_r + i, radius_c + j) = grid(ri, cj); + } + } + } + const double sum = filter.matrix.sum(); + if (sum > 0) + { + const float filteredValue = (valueMatrix.cwiseProduct(filter.matrix)).sum() / sum; + newGrid(r,c) = filter.useMinimum ? std::min(grid(r,c), filteredValue) : filteredValue; + } + } + } + } + grid = newGrid; + } + const Costmap::Parameters& Costmap::params() const noexcept { @@ -423,5 +478,20 @@ namespace armarx::navigation::algorithms return mergedCostmap; } - -} // namespace armarx::navigation::algorithms + Costmap::Filter Costmap::Filter::gaussian(int radius, float sigma) { + ARMARX_CHECK(radius > 0 and sigma > 0.); + Filter gaussianFilter + { + .matrix = Eigen::MatrixXf::Zero(radius * 2 + 1, radius * 2 + 1) + }; + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + gaussianFilter.matrix(radius + i, radius + j) = + std::exp(-0.5 * (std::pow(i / sigma, 2.0) + + std::pow(j / sigma, 2.0))) / + (2 * M_PI * sigma * sigma); + } + } + return gaussianFilter; + } + } // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/Costmap.h b/source/armarx/navigation/algorithms/Costmap.h index e7f4fc0c..35010af8 100644 --- a/source/armarx/navigation/algorithms/Costmap.h +++ b/source/armarx/navigation/algorithms/Costmap.h @@ -27,6 +27,19 @@ namespace armarx::navigation::algorithms float cellSize = 100.f; }; + struct Filter + { + Eigen::MatrixXf matrix; + bool useMinimum = false; + + inline Filter& setUseMinimum(bool value) + { + useMinimum = value; + return *this; + } + + static Filter gaussian(int radius = 2, float sigma = 1.); + }; using Index = Eigen::Array2i; using Position = Eigen::Vector2f; @@ -86,6 +99,8 @@ namespace armarx::navigation::algorithms Optimum optimum() const; + void filter(const Filter &filter); + bool add(const Costmap& other, float weight = 1.0); enum class AddMode -- GitLab From 76368fc7127c16e9fe570435652dd4f6d78efef8 Mon Sep 17 00:00:00 2001 From: Meixner <andre.meixner@kit.edu> Date: Thu, 13 Jul 2023 20:28:58 +0200 Subject: [PATCH 2/2] Partially fixed writing costmaps and improved debug images for cost maps --- .../navigation/algorithms/persistence.cpp | 85 ++++++++++++------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/source/armarx/navigation/algorithms/persistence.cpp b/source/armarx/navigation/algorithms/persistence.cpp index efcef663..ad21df29 100644 --- a/source/armarx/navigation/algorithms/persistence.cpp +++ b/source/armarx/navigation/algorithms/persistence.cpp @@ -31,6 +31,7 @@ #include <opencv2/core/eigen.hpp> #include <opencv2/core/mat.hpp> #include <opencv2/imgcodecs.hpp> +#include <opencv2/imgproc.hpp> #include <opencv2/opencv.hpp> #include <SimoxUtility/json/json.hpp> @@ -109,22 +110,28 @@ namespace armarx::navigation::algorithms std::filesystem::create_directories(directory); } - nlohmann::json j; - - // params - { - auto& jParam = j.at("params"); - jParam.at("binary_grid") = costmap.params().binaryGrid; - jParam.at("cell_size") = costmap.params().cellSize; - } - - // scene bounds + nlohmann::json j = { + { "params", { + {"binary_grid", costmap.params().binaryGrid }, + {"cell_size", costmap.params().cellSize } + }}, + {"scene_bounds", { + {"min", std::vector<float>{costmap.getLocalSceneBounds().min.x(), + costmap.getLocalSceneBounds().min.y()} }, + {"max", std::vector<float>{costmap.getLocalSceneBounds().max.x(), + costmap.getLocalSceneBounds().max.y()}} + }} + }; + + std::optional<cv::Mat1b> mask; + if (costmap.getMask().has_value()) { - auto& jSceneBounds = j.at("scene_bounds"); - jSceneBounds.at("min") = std::vector<float>{costmap.getLocalSceneBounds().min.x(), - costmap.getLocalSceneBounds().min.y()}; - jSceneBounds.at("max") = std::vector<float>{costmap.getLocalSceneBounds().max.x(), - costmap.getLocalSceneBounds().max.y()}; + // mask, if any + Eigen::Matrix<std::uint8_t, Eigen::Dynamic, Eigen::Dynamic> maskI = + costmap.getMask()->cast<std::uint8_t>(); + cv::Mat1b m; + cv::eigen2cv(maskI, m); + mask = m; } // grid @@ -137,32 +144,46 @@ namespace armarx::navigation::algorithms const std::string gridFilename = "grid.exr"; cv::imwrite((directory / gridFilename).string(), grid); - j.at("grid_filename") = gridFilename; + j["grid_filename"] = gridFilename; // for debugging purpose, also save a png image - const std::string gridDebuggingFilename = "grid.png"; - - cv::Mat gridDebug; - grid.convertTo(gridDebug, CV_16UC1); - cv::imwrite((directory / gridFilename).string(), gridDebug); - } + const std::string gridDebuggingFilename = "grid"; - // mask, if any - { - ARMARX_TRACE; + double min; + double max; + if (mask) + { + cv::minMaxIdx(grid, &min, &max, 0, 0, mask.value()); + } + else + { + cv::minMaxIdx(grid, &min, &max); + } + cv::Mat adjMap; + grid.convertTo(adjMap,CV_8UC1, 255 / (max-min), -255*min/(max-min)); - if (costmap.getMask().has_value()) + cv::Mat colorsMap; + applyColorMap(adjMap, colorsMap, cv::COLORMAP_JET); + cv::Mat maskedColorsMap; + if (mask) { - Eigen::Matrix<std::uint8_t, Eigen::Dynamic, Eigen::Dynamic> maskI = - costmap.getMask()->cast<std::uint8_t>(); + colorsMap.copyTo(maskedColorsMap, mask.value()); + } + else + { + maskedColorsMap = colorsMap; + } + cv::imwrite((directory / (gridDebuggingFilename + ".png")).string(), maskedColorsMap); - cv::Mat1b mask; - cv::eigen2cv(maskI, mask); + ARMARX_TRACE; + // mask if any + if (mask) + { const std::string maskFilename = "mask.ppm"; - cv::imwrite((directory / maskFilename).string(), mask); + cv::imwrite((directory / maskFilename).string(), mask.value()); - j.at("mask_filename") = maskFilename; + j["mask_filename"] = maskFilename; } } -- GitLab