├── .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 | --------------------------------------------------------------------------------