├── .clang-format
├── .gitignore
├── CMakeLists.txt
├── README.md
├── cmake
└── functions.cmake
├── example
├── README.md
└── example.png
├── include
└── visualizer.hpp
└── src
└── main.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 | ColumnLimit: 120
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | # Folders
35 | build/
36 | build-vscode/
37 | PCL-Build-Action/
38 | .vscode
39 |
40 | # nektos/act secrets
41 | .secrets
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # #############################################################################
2 | # CMAKE CONFIGURATION
3 | # #############################################################################
4 | cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
5 |
6 | set(CMAKE_BUILD_TYPE_INIT Release)
7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
8 |
9 | include("${CMAKE_CURRENT_LIST_DIR}/cmake/functions.cmake")
10 |
11 | project(upsampling_cloud VERSION 1.1.0 LANGUAGES CXX)
12 |
13 | message("\n" "=========================================")
14 | message("Project: ${PROJECT_NAME} ")
15 | message("=========================================")
16 |
17 | # set the CMP0074 policy to old behavior (disable warnings) (CMake 3.12.0-rc1)
18 | if(${CMAKE_VERSION} MATCHES 3.12.0)
19 | cmake_policy(SET CMP0074 OLD)
20 |
21 | if(POLICY CMP0048)
22 | cmake_policy(SET CMP0048 NEW)
23 | endif(POLICY CMP0048)
24 | endif()
25 |
26 | # #############################################################################
27 | # PACKAGES
28 | # #############################################################################
29 | find_package(PCL 1.8 REQUIRED QUIET)
30 |
31 | if(PCL_FOUND)
32 | message(STATUS "PCL status:")
33 | message(STATUS " version: ${PCL_VERSION}")
34 | message(STATUS " directory: ${PCL_DIR}")
35 | else()
36 | message(FATAL_ERROR " ERROR: PCL minimum required version 1.8. Not found")
37 | endif()
38 |
39 | fetch_project(
40 | NAME cloudparse
41 | URL https://github.com/danielTobon43/cloudparse/archive/v0.2.1.tar.gz
42 | )
43 |
44 | fetch_project(
45 | NAME argparse
46 | URL https://github.com/p-ranav/argparse/archive/v2.6.tar.gz
47 | )
48 |
49 | # #############################################################################
50 | # SOURCE CODE
51 | # #############################################################################
52 | set(MAIN_SOURCE "src/main.cpp")
53 |
54 | # #############################################################################
55 | # EXECUTABLES
56 | # #############################################################################
57 | add_executable(${PROJECT_NAME} ${MAIN_SOURCE})
58 |
59 | # #############################################################################
60 | # HEADERS
61 | # #############################################################################
62 | target_include_directories(${PROJECT_NAME} PRIVATE
63 | ${PCL_INCLUDE_DIRS}
64 | include
65 | )
66 |
67 | # #############################################################################
68 | # TARGET LIBRARIES
69 | # #############################################################################
70 | target_link_libraries(${PROJECT_NAME} PRIVATE
71 | ${PCL_LIBRARIES}
72 | cloudparse
73 | argparse
74 | )
75 |
76 | # #############################################################################
77 | # COMPILATION FLAGS: MMX, SSE(1, 2, 3, 3S, 4.1, 4.2), CLMUL, RdRand, VT-x, x86-64
78 | # #############################################################################
79 | target_compile_options(${PROJECT_NAME} PRIVATE -Wno-cpp
80 | -mmmx
81 | -msse
82 | -msse2
83 | -msse3
84 | -mssse3
85 | -msse4.2
86 | -msse4.1
87 | -mno-sse4a
88 | -mno-avx
89 | -mno-avx2
90 | -mno-fma
91 | -mno-fma4
92 | -mno-f16c
93 | -mno-xop
94 | -mno-bmi
95 | -mno-bmi2
96 | -mrdrnd
97 | -mno-3dnow
98 | -mlzcnt
99 | -mfsgsbase
100 | -mpclmul
101 | )
102 |
103 | # #############################################################################
104 | # INSTALL DIRECTORY
105 | # #############################################################################
106 | install(TARGETS ${PROJECT_NAME}
107 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
108 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
109 | )
110 |
111 | message("=========================================")
112 | message("Project: ${PROJECT_NAME} COMPILED WITH CMAKE " ${CMAKE_VERSION})
113 | message("=========================================")
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # upsamplingCloudPCL
2 | Upsampling method for an input cloud using [MovingLeastSquares](https://pointclouds.org/documentation/classpcl_1_1_moving_least_squares.html) method of PCL
3 |
4 | ## Input file structure support
5 |
6 | | Format | Description |
7 | | ----------- | ----------- |
8 | | .pcd | Point Cloud Data file format |
9 | | .ply | Polygon file format |
10 | | .txt | Text file format |
11 | | .xyz | X Y Z Text file format |
12 |
13 | ## Output file structure (.pcd)
14 |
15 | * unsampled_cloud.pcd
16 |
17 | ## Example
18 |
19 | 
20 |
21 |
22 |
23 | 
24 |
25 |
26 |
27 | 
28 |
29 |
30 |
31 | ## Command line
32 | ```cpp
33 | Usage: ./upsampling_cloud [options]
34 |
35 | Optional arguments:
36 | -h --help shows help message and exits [default: false]
37 | -v --version prints version information and exits [default: false]
38 | --cloudfile input cloud file [required]
39 | --search-radius search radius value [default: 0.03]
40 | --sampling-radius sampling radius value [default: 0.005]
41 | --step-size step size [default: 0.005]
42 | -o --output-dir output dir to save upsampled cloud [default: "-"] (not configured)
43 | -d --display display upsampling in the pcl visualizer [default: false]
44 | ```
45 |
46 | ## Dependencies
47 | This projects depends on the Point Cloud Library (it works with version `1.8...1.12.1`) and its dependencies.
48 | | Package | Version | Description |
49 | | ----------- | ----------- | ----------- |
50 | | VTK | 9.0.0 | Visualization toolkit |
51 | | PCL | 1.12.1 | The Point Cloud Library (PCL) |
52 | | Eigen | 3.7.7 | Eigen is a library of template headers for linear algebra |
53 | | Flann | 1.9.1 | Fast Library for Approximate Nearest Neighbors |
54 | | Boost | 1.77.0 | Provides support for linear algebra, pseudorandom number generation, multithreading |
55 | | OpenGL | 21.2.6 | Programming interface for rendering 2D and 3D vector graphics. |
56 |
57 |
58 | ## Compilation
59 | ### Compile from source
60 |
61 | 1. Download source code
62 |
63 | ```bash
64 | git clone https://github.com/danielTobon43/upsamplingCloudPCL
65 | ```
66 |
67 | 2. Create a "build" folder at the top level of the upsamplingCloudPCL
68 |
69 | ```bash
70 | cd upsamplingCloudPCL/ && mkdir build
71 | ```
72 |
73 | 3. Compile with CMake
74 |
75 | ```bash
76 | cd build/ && cmake ../ && make
77 | ```
78 |
79 | ### Test
80 | ```bash
81 | cd /build
82 | ./upsampling_cloud --cloudfile
83 | ```
84 |
85 | ## Note
86 |
87 | You can modify the parameters to obtain better results [here](https://github.com/danielTobon43/upsamplingCloudPCL/blob/master/src/main.cpp#:~:text=void%20upsampling(pcl,Ptr%26%20output_cloud)%20%7B)
88 |
89 | ```cpp
90 | mls.setComputeNormals(true);
91 | mls.setInputCloud(input_cloud);
92 | mls.setSearchMethod(kd_tree);
93 | mls.setSearchRadius(search_radius);
94 | mls.setUpsamplingMethod(pcl::MovingLeastSquares::UpsamplingMethod::SAMPLE_LOCAL_PLANE);
95 | mls.setUpsamplingRadius(sampling_radius);
96 | mls.setUpsamplingStepSize(step_size);
97 | mls.setPolynomialOrder(pol_order);
98 | mls.setSqrGaussParam(gauss_param);// (the square of the search radius works best in general)
99 | mls.setCacheMLSResults(true);//Set whether the mls results should be stored for each point in the input cloud.
100 | mls.setNumberOfThreads(num_threats);
101 | ```
102 |
--------------------------------------------------------------------------------
/cmake/functions.cmake:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 |
3 | function(fetch_project)
4 | cmake_parse_arguments(FETCH_SOURCE "" "NAME;URL" "" ${ARGN})
5 | FetchContent_Declare(${FETCH_SOURCE_NAME}
6 | URL ${FETCH_SOURCE_URL}
7 | )
8 |
9 | FetchContent_MakeAvailable(${FETCH_SOURCE_NAME})
10 | endfunction()
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codebydant/upsamplingCloudPCL/bc523c214b67566ddcac32a1d8d5f89d5ea851b9/example/example.png
--------------------------------------------------------------------------------
/include/visualizer.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | void append_points_size_to_display(pcl::PointCloud::Ptr &cloud,
9 | pcl::visualization::PCLVisualizer::Ptr &viewer, int &PORT, std::string &name) {
10 | // add points label to visualizer
11 | std::string str = "Points: ";
12 | std::stringstream ss;
13 | ss << cloud->points.size();
14 | str += ss.str();
15 | int xpos = 10;
16 | int ypos = 25;
17 | int fontSize = 13;
18 | double r = 1.0;
19 | double g = 1.0;
20 | double b = 1.0;
21 | viewer->addText(str, xpos, ypos, fontSize, r, g, b, name, PORT);
22 | }
23 |
24 | void display_upsampling(pcl::PointCloud::Ptr &input_cloud,
25 | pcl::PointCloud::Ptr &output_cloud) {
26 | vtkObject::GlobalWarningDisplayOff(); // Disable vtk render warning
27 | pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("PCL VISUALIZER"));
28 |
29 | int PORT1 = 0;
30 | viewer->createViewPort(0.0, 0.0, 0.5, 1.0, PORT1);
31 | viewer->setBackgroundColor(0, 0, 0, PORT1);
32 | viewer->addText("ORIGINAL", 10, 10, "PORT1", PORT1);
33 |
34 | int PORT2 = 0;
35 | viewer->createViewPort(0.5, 0.0, 1.0, 1.0, PORT2);
36 | viewer->setBackgroundColor(0, 0, 0, PORT2);
37 | viewer->addText("UPSAMPLING", 10, 10, "PORT2", PORT2);
38 |
39 | std::string name1 = "points_cloud_1";
40 | std::string name2 = "points_cloud_2";
41 |
42 | append_points_size_to_display(input_cloud, viewer, PORT1, name1);
43 | append_points_size_to_display(output_cloud, viewer, PORT2, name2);
44 |
45 | viewer->removeAllPointClouds(0);
46 |
47 | if (input_cloud->points[0].r <= 0 and input_cloud->points[0].g <= 0 and input_cloud->points[0].b <= 0) {
48 | pcl::visualization::PointCloudColorHandlerCustom color_handler(input_cloud, 255, 255, 0);
49 | viewer->addPointCloud(input_cloud, color_handler, "Original", PORT1);
50 | } else {
51 | viewer->addPointCloud(input_cloud, "Original", PORT1);
52 | }
53 |
54 | if (output_cloud->points[0].r <= 0 and output_cloud->points[0].g <= 0 and output_cloud->points[0].b <= 0) {
55 | pcl::visualization::PointCloudColorHandlerCustom color_handler(output_cloud, 255, 255, 0);
56 | viewer->addPointCloud(output_cloud, color_handler, "transform1 rvec", PORT2);
57 | } else {
58 | viewer->addPointCloud(output_cloud, "transform1 rvec", PORT2);
59 | }
60 |
61 | pcl::PointXYZ p1, p2, p3;
62 | p1.getArray3fMap() << 1, 0, 0;
63 | p2.getArray3fMap() << 0, 1, 0;
64 | p3.getArray3fMap() << 0, 0.1, 1;
65 |
66 | viewer->addCoordinateSystem(1, "original_usc", PORT1);
67 | viewer->addText3D("x", p1, 0.2, 1, 0, 0, "x_", PORT1);
68 | viewer->addText3D("y", p2, 0.2, 0, 1, 0, "y_", PORT1);
69 | viewer->addText3D("z", p3, 0.2, 0, 0, 1, "z_", PORT1);
70 |
71 | viewer->addCoordinateSystem(1, "transform_ucs", PORT2);
72 | viewer->addText3D("x", p1, 0.2, 1, 0, 0, "x_", PORT2);
73 | viewer->addText3D("y", p2, 0.2, 0, 1, 0, "y_", PORT2);
74 | viewer->addText3D("z", p3, 0.2, 0, 0, 1, "z_", PORT2);
75 |
76 | viewer->setPosition(0, 0);
77 | viewer->initCameraParameters();
78 | viewer->resetCamera();
79 |
80 | std::cout << "\nPress [q] to exit" << std::endl;
81 |
82 | while (!viewer->wasStopped()) {
83 | viewer->spin();
84 | }
85 | }
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "argparse/argparse.hpp"
6 | #include "cloudparse/parser.hpp"
7 | #include "visualizer.hpp"
8 |
9 | void upsampling(pcl::PointCloud::Ptr& input_cloud, argparse::ArgumentParser& arg_parser,
10 | pcl::PointCloud::Ptr& output_cloud) {
11 | double search_radius = arg_parser.get("--search-radius");
12 | double sampling_radius = arg_parser.get("--sampling-radius");
13 | double step_size = arg_parser.get("--step-size");
14 | double gauss_param = (double)std::pow(search_radius, 2);
15 | int pol_order = 2;
16 | unsigned int num_threats = 1;
17 |
18 | // https://pointclouds.org/documentation/classpcl_1_1_moving_least_squares.html
19 | // check alternative https://pointclouds.org/documentation/classpcl_1_1_bilateral_upsampling.html
20 | pcl::PointCloud::Ptr dense_points(new pcl::PointCloud());
21 | pcl::search::KdTree::Ptr kd_tree(new pcl::search::KdTree);
22 | pcl::MovingLeastSquares mls;
23 |
24 | mls.setComputeNormals(true);
25 | mls.setInputCloud(input_cloud);
26 | mls.setSearchMethod(kd_tree);
27 | mls.setSearchRadius(search_radius);
28 | mls.setUpsamplingMethod(
29 | pcl::MovingLeastSquares::UpsamplingMethod::SAMPLE_LOCAL_PLANE);
30 | mls.setUpsamplingRadius(sampling_radius);
31 | mls.setUpsamplingStepSize(step_size);
32 | mls.setPolynomialOrder(pol_order);
33 | mls.setSqrGaussParam(gauss_param); // (the square of the search radius works best in general)
34 | mls.setCacheMLSResults(true); // Set whether the mls results should be stored for each point in the input cloud.
35 | mls.setNumberOfThreads(num_threats);
36 | // mls.setDilationVoxelSize();//Used only in the VOXEL_GRID_DILATION upsampling method
37 | // mls.setPointDensity(15); //15
38 | mls.process(*dense_points);
39 |
40 | *output_cloud = *input_cloud;
41 | *output_cloud += *dense_points;
42 |
43 | if (output_cloud->points.size() == input_cloud->points.size()) {
44 | pcl::console::print_warn("\ninput cloud could not be upsampled, change input parameters!");
45 | }
46 |
47 | pcl::console::print_info("\nNew points: ");
48 | pcl::console::print_value("%d", dense_points->points.size());
49 |
50 | pcl::console::print_info("\nOutput cloud points: ");
51 | pcl::console::print_value("%d", output_cloud->points.size());
52 | pcl::console::print_info("\n");
53 | }
54 |
55 | int main(int argc, char** argv) {
56 | // -----------------Command line interface -----------------
57 | argparse::ArgumentParser arg_parser(argv[0]);
58 |
59 | arg_parser.add_argument("--cloudfile").required().help("input cloud file");
60 | arg_parser.add_argument("--search-radius").default_value(double(0.03)).scan<'g', double>().help("epsilon value");
61 | arg_parser.add_argument("--sampling-radius").default_value(double(0.005)).scan<'g', double>().help("epsilon value");
62 | arg_parser.add_argument("--step-size").default_value(double(0.005)).scan<'g', double>().help("epsilon value");
63 | arg_parser.add_argument("-o", "--output-dir").default_value(std::string("-")).help("output dir to save clusters");
64 | arg_parser.add_argument("-d", "--display")
65 | .default_value(false)
66 | .implicit_value(true)
67 | .help("display clusters in the pcl visualizer");
68 |
69 | try {
70 | arg_parser.parse_args(argc, argv);
71 | } catch (const std::runtime_error& err) {
72 | std::cerr << err.what() << std::endl;
73 | std::cerr << arg_parser;
74 | std::exit(0);
75 | }
76 |
77 | // -----------------Read input cloud file -----------------
78 | pcl::PointCloud::Ptr input_cloud(new pcl::PointCloud());
79 |
80 | // cloud parser object
81 | CloudParserLibrary::ParserCloudFile cloud_parser;
82 | cloud_parser.load_cloudfile(arg_parser.get("--cloudfile"), input_cloud);
83 |
84 | // set cloud metadata
85 | input_cloud->width = (int)input_cloud->points.size();
86 | input_cloud->height = 1;
87 | input_cloud->is_dense = true;
88 |
89 | // -----------------Upsampling -----------------
90 | pcl::PointCloud::Ptr output_cloud(new pcl::PointCloud());
91 | upsampling(input_cloud, arg_parser, output_cloud);
92 | pcl::io::savePCDFile("upsampled_cloud.pcd", *output_cloud);
93 |
94 | // -----------------Visualize upsampling -----------------
95 | if (arg_parser["--display"] == true) {
96 | display_upsampling(input_cloud, output_cloud);
97 | }
98 |
99 | return 0;
100 | }
101 |
--------------------------------------------------------------------------------