Skip to content

Commit 4214247

Browse files
committed
feat: add plane_segmentation (ROS 2) packages
1 parent 37585be commit 4214247

File tree

89 files changed

+5756
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+5756
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(convex_plane_decomposition)
3+
4+
find_package(ament_cmake REQUIRED)
5+
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
6+
find_package(OpenCV REQUIRED)
7+
8+
find_package(CGAL REQUIRED)
9+
find_package(GMP REQUIRED)
10+
find_package(MPFR REQUIRED)
11+
12+
find_package(grid_map_core REQUIRED)
13+
find_package(grid_map_cv REQUIRED)
14+
find_package(grid_map_filters_rsl REQUIRED)
15+
16+
###########
17+
## Build ##
18+
###########
19+
20+
add_library(${PROJECT_NAME}
21+
src/contour_extraction/ContourExtraction.cpp
22+
src/contour_extraction/Upsampling.cpp
23+
src/ransac/RansacPlaneExtractor.cpp
24+
src/sliding_window_plane_extraction/SlidingWindowPlaneExtractor.cpp
25+
src/ConvexRegionGrowing.cpp
26+
src/Draw.cpp
27+
src/GridMapPreprocessing.cpp
28+
src/LoadGridmapFromImage.cpp
29+
src/PlanarRegion.cpp
30+
src/PlaneDecompositionPipeline.cpp
31+
src/Postprocessing.cpp
32+
src/SegmentedPlaneProjection.cpp
33+
)
34+
35+
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
36+
target_compile_definitions(${PROJECT_NAME} PUBLIC CGAL_HAS_THREADS)
37+
38+
target_include_directories(${PROJECT_NAME}
39+
PUBLIC
40+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
41+
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>
42+
)
43+
44+
ament_target_dependencies(${PROJECT_NAME}
45+
grid_map_core
46+
grid_map_cv
47+
grid_map_filters_rsl
48+
)
49+
target_link_libraries(${PROJECT_NAME}
50+
Eigen3::Eigen
51+
${OpenCV_LIBRARIES}
52+
CGAL::CGAL
53+
gmp
54+
mpfr
55+
)
56+
57+
#############
58+
## Install ##
59+
#############
60+
61+
install(
62+
TARGETS ${PROJECT_NAME}
63+
EXPORT export_${PROJECT_NAME}
64+
ARCHIVE DESTINATION lib
65+
LIBRARY DESTINATION lib
66+
RUNTIME DESTINATION bin
67+
INCLUDES DESTINATION include/${PROJECT_NAME}
68+
)
69+
install(DIRECTORY include/ DESTINATION include/${PROJECT_NAME})
70+
71+
ament_export_include_directories("include/${PROJECT_NAME}")
72+
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
73+
ament_export_dependencies(
74+
CGAL
75+
Eigen3
76+
grid_map_core
77+
grid_map_cv
78+
grid_map_filters_rsl
79+
)
80+
ament_package()
81+
82+
#############
83+
## Testing ##
84+
#############
85+
86+
if(BUILD_TESTING)
87+
find_package(ament_cmake_gtest REQUIRED)
88+
find_package(Boost REQUIRED COMPONENTS filesystem system)
89+
ament_add_gtest(test_${PROJECT_NAME}
90+
test/testConvexApproximation.cpp
91+
test/testPipeline.cpp
92+
test/testPlanarRegion.cpp
93+
test/testRegionGrowing.cpp
94+
test/testUpsampling.cpp
95+
)
96+
target_link_libraries(test_${PROJECT_NAME}
97+
${PROJECT_NAME}
98+
Boost::filesystem
99+
Boost::system
100+
)
101+
endif()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Created by rgrandia on 09.06.20.
3+
//
4+
5+
#pragma once
6+
7+
#include "PolygonTypes.h"
8+
9+
namespace convex_plane_decomposition {
10+
11+
CgalPolygon2d createRegularPolygon(const CgalPoint2d& center, double radius, int numberOfVertices);
12+
13+
CgalPolygon2d growConvexPolygonInsideShape(const CgalPolygon2d& parentShape, CgalPoint2d center, int numberOfVertices, double growthFactor);
14+
15+
CgalPolygon2d growConvexPolygonInsideShape(const CgalPolygonWithHoles2d& parentShape, CgalPoint2d center, int numberOfVertices,
16+
double growthFactor);
17+
18+
void updateMean(CgalPoint2d& mean, const CgalPoint2d& oldValue, const CgalPoint2d& updatedValue, int N);
19+
20+
} // namespace convex_plane_decomposition
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Created by rgrandia on 09.06.20.
3+
//
4+
5+
#pragma once
6+
7+
#include <opencv2/core/core.hpp>
8+
#include <opencv2/highgui/highgui.hpp>
9+
#include <opencv2/imgcodecs.hpp>
10+
#include "opencv2/imgproc/imgproc.hpp"
11+
12+
#include "PolygonTypes.h"
13+
14+
namespace convex_plane_decomposition {
15+
16+
cv::Vec3b randomColor();
17+
18+
std::vector<cv::Point> toCv(const CgalPolygon2d& polygon);
19+
20+
void drawContour(cv::Mat& img, const CgalPoint2d& point, double radius = 1, const cv::Vec3b* color = nullptr);
21+
void drawContour(cv::Mat& img, const CgalPolygon2d& polygon, const cv::Vec3b* color = nullptr);
22+
void drawContour(cv::Mat& img, const CgalPolygonWithHoles2d& polygonWithHoles2d, const cv::Vec3b* color = nullptr);
23+
24+
CgalPolygon2d scaleShape(const CgalPolygon2d& polygon, double scale);
25+
CgalPolygonWithHoles2d scaleShape(const CgalPolygonWithHoles2d& polygonWithHoles, double scale);
26+
27+
} // namespace convex_plane_decomposition
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//
2+
// Created by rgrandia on 09.06.20.
3+
//
4+
5+
#pragma once
6+
7+
#include "PlanarRegion.h"
8+
#include "PolygonTypes.h"
9+
10+
namespace convex_plane_decomposition {
11+
12+
inline bool doEdgesIntersect(const CgalSegment2d& line, const CgalPolygon2d& contour) {
13+
for (auto edgeIt = contour.edges_begin(); edgeIt != contour.edges_end(); ++edgeIt) {
14+
if (CGAL::do_intersect(line, *edgeIt)) {
15+
return true;
16+
}
17+
}
18+
return false;
19+
}
20+
21+
inline bool doEdgesIntersect(const CgalSegment2d& line, const CgalPolygonWithHoles2d& parentShape) {
22+
if (doEdgesIntersect(line, parentShape.outer_boundary())) {
23+
return true;
24+
} else {
25+
for (const auto& hole : parentShape.holes()) {
26+
if (doEdgesIntersect(line, hole)) {
27+
return true;
28+
}
29+
}
30+
}
31+
return false;
32+
}
33+
34+
inline double squaredDistance(const CgalPoint2d& point, const CgalPolygon2d& polygon) {
35+
double minDistSquared = std::numeric_limits<double>::max();
36+
for (auto edgeIt = polygon.edges_begin(); edgeIt != polygon.edges_end(); ++edgeIt) {
37+
double distSquare = CGAL::squared_distance(point, *edgeIt);
38+
minDistSquared = std::min(distSquare, minDistSquared);
39+
}
40+
return minDistSquared;
41+
}
42+
43+
inline double squaredDistance(const CgalPoint2d& point, const CgalPolygonWithHoles2d& parentShape) {
44+
double minDistSquared = squaredDistance(point, parentShape.outer_boundary());
45+
for (const auto& hole : parentShape.holes()) {
46+
double distSquare = squaredDistance(point, hole);
47+
minDistSquared = std::min(distSquare, minDistSquared);
48+
}
49+
return minDistSquared;
50+
}
51+
52+
inline double squaredDistance(const CgalPoint2d& point, const CgalCircle2d& circle) {
53+
auto dx = (point.x() - circle.center().x());
54+
auto dy = (point.y() - circle.center().y());
55+
return dx * dx + dy * dy;
56+
}
57+
58+
template <typename T>
59+
double distance(const CgalPoint2d& point, const T& shape) {
60+
double distanceSquared = squaredDistance(point, shape);
61+
return (distanceSquared > 0.0) ? std::sqrt(distanceSquared) : 0.0;
62+
}
63+
64+
inline bool isInside(const CgalPoint2d& point, const CgalCircle2d& circle) {
65+
return squaredDistance(point, circle) <= circle.squared_radius();
66+
}
67+
68+
inline bool isInside(const CgalPoint2d& point, const CgalPolygon2d& polygon) {
69+
const auto boundedSide = CGAL::bounded_side_2(polygon.begin(), polygon.end(), point);
70+
return boundedSide == CGAL::ON_BOUNDED_SIDE || boundedSide == CGAL::ON_BOUNDARY;
71+
}
72+
73+
inline bool isInside(const CgalPoint2d& point, const CgalPolygonWithHoles2d& polygonWithHoles) {
74+
if (isInside(point, polygonWithHoles.outer_boundary())) {
75+
// Inside the outer contour -> return false if the point is inside any of the holes
76+
for (const auto& hole : polygonWithHoles.holes()) {
77+
const auto boundedSide = CGAL::bounded_side_2(hole.begin(), hole.end(), point);
78+
if (boundedSide == CGAL::ON_BOUNDED_SIDE) { // The edge of the hole is considered part of the polygon
79+
return false;
80+
}
81+
}
82+
return true;
83+
} else {
84+
return false;
85+
}
86+
}
87+
88+
inline CgalPoint2d getPointOnLine(const CgalPoint2d& start, const CgalPoint2d& end, double factor) {
89+
return {factor * (end.x() - start.x()) + start.x(), factor * (end.y() - start.y()) + start.y()};
90+
}
91+
92+
inline CgalPoint2d projectToClosestPoint(const CgalPoint2d& point, const CgalSegment2d& segment) {
93+
// The segment as a vector, with the source being the origin
94+
const Eigen::Vector2d sourceToTarget{segment.target().x() - segment.source().x(), segment.target().y() - segment.source().y()};
95+
const double sourceToTargetDistance = sourceToTarget.norm();
96+
const Eigen::Vector2d n = sourceToTarget / sourceToTargetDistance;
97+
98+
// Vector from source to the query point
99+
const Eigen::Vector2d sourceToPoint{point.x() - segment.source().x(), point.y() - segment.source().y()};
100+
101+
// Projection to the line, clamped to be between source and target points
102+
const double coeff = std::min(std::max(0.0, n.dot(sourceToPoint)), sourceToTargetDistance);
103+
104+
return {coeff * n.x() + segment.source().x(), coeff * n.y() + segment.source().y()};
105+
}
106+
107+
inline CgalPoint2d projectToClosestPoint(const CgalPoint2d& point, const CgalPolygon2d& polygon) {
108+
double minDistSquared = CGAL::squared_distance(point, *polygon.edges_begin());
109+
auto closestEdge = polygon.edges_begin();
110+
for (auto edgeIt = std::next(polygon.edges_begin()); edgeIt != polygon.edges_end(); ++edgeIt) {
111+
double distSquare = CGAL::squared_distance(point, *edgeIt);
112+
if (distSquare < minDistSquared) {
113+
closestEdge = edgeIt;
114+
minDistSquared = distSquare;
115+
}
116+
}
117+
return projectToClosestPoint(point, *closestEdge);
118+
}
119+
120+
inline void transformInPlace(CgalPolygon2d& polygon, const std::function<void(CgalPoint2d&)>& f) {
121+
for (auto& point : polygon) {
122+
f(point);
123+
}
124+
}
125+
126+
inline void transformInPlace(CgalPolygonWithHoles2d& polygonWithHoles, const std::function<void(CgalPoint2d&)>& f) {
127+
transformInPlace(polygonWithHoles.outer_boundary(), f);
128+
for (auto& hole : polygonWithHoles.holes()) {
129+
transformInPlace(hole, f);
130+
}
131+
}
132+
133+
inline void transformInPlace(BoundaryWithInset& boundaryWithInset, const std::function<void(CgalPoint2d&)>& f) {
134+
transformInPlace(boundaryWithInset.boundary, f);
135+
for (auto& inset : boundaryWithInset.insets) {
136+
transformInPlace(inset, f);
137+
}
138+
}
139+
140+
} // namespace convex_plane_decomposition
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <Eigen/Core>
4+
5+
#include <grid_map_core/GridMap.hpp>
6+
7+
namespace convex_plane_decomposition {
8+
9+
struct PreprocessingParameters {
10+
/// Resample to this resolution, set to negative values to skip
11+
double resolution = 0.04;
12+
/// Kernel size of the median filter, either 3 or 5
13+
int kernelSize = 3;
14+
/// Number of times the image is filtered
15+
int numberOfRepeats = 2;
16+
};
17+
18+
class GridMapPreprocessing {
19+
public:
20+
GridMapPreprocessing(const PreprocessingParameters& parameters);
21+
22+
void preprocess(grid_map::GridMap& gridMap, const std::string& layer) const;
23+
24+
private:
25+
void denoise(grid_map::GridMap& gridMap, const std::string& layer) const;
26+
void changeResolution(grid_map::GridMap& gridMap, const std::string& layer) const;
27+
void inpaint(grid_map::GridMap& gridMap, const std::string& layer) const;
28+
29+
PreprocessingParameters parameters_;
30+
};
31+
32+
/**
33+
* @return true if any of the elements in the map are finite
34+
*/
35+
bool containsFiniteValue(const grid_map::Matrix& map);
36+
37+
} // namespace convex_plane_decomposition
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// Created by rgrandia on 16.03.22.
3+
//
4+
5+
#pragma once
6+
7+
#include <string>
8+
9+
#include <grid_map_core/GridMap.hpp>
10+
11+
namespace convex_plane_decomposition {
12+
13+
/**
14+
* Load an elevation map from a grey scale image.
15+
*
16+
* @param filePath : absolute path to file
17+
* @param elevationLayer : name of the elevation layer
18+
* @param frameId : frame assigned to loaded map
19+
* @param resolution : map resolution [m/pixel]
20+
* @param scale : distance [m] between lowest and highest point in the map
21+
* @return Gridmap with the loaded image as elevation layer.
22+
*/
23+
grid_map::GridMap loadGridmapFromImage(const std::string& filePath, const std::string& elevationLayer, const std::string& frameId,
24+
double resolution, double scale);
25+
26+
} // namespace convex_plane_decomposition

0 commit comments

Comments
 (0)