{"id":13436171,"url":"https://github.com/koide3/small_gicp","last_synced_at":"2025-05-16T07:05:47.009Z","repository":{"id":231060457,"uuid":"775939573","full_name":"koide3/small_gicp","owner":"koide3","description":"Efficient and parallel algorithms for point cloud registration [C++, Python]","archived":false,"fork":false,"pushed_at":"2025-05-07T05:51:09.000Z","size":22819,"stargazers_count":620,"open_issues_count":15,"forks_count":74,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-14T03:42:59.719Z","etag":null,"topics":["cpp","icp","multi-threading","open3d","pcl","point-cloud-regstration","pointcloud","python","registration","scan-matching"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/koide3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-22T10:54:45.000Z","updated_at":"2025-05-09T09:03:04.000Z","dependencies_parsed_at":"2024-05-18T15:47:11.294Z","dependency_job_id":"8bf85070-d900-4eea-9480-1c8a9bf7c27e","html_url":"https://github.com/koide3/small_gicp","commit_stats":null,"previous_names":["koide3/small_gicp"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koide3%2Fsmall_gicp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koide3%2Fsmall_gicp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koide3%2Fsmall_gicp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koide3%2Fsmall_gicp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/koide3","download_url":"https://codeload.github.com/koide3/small_gicp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254485062,"owners_count":22078767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","icp","multi-threading","open3d","pcl","point-cloud-regstration","pointcloud","python","registration","scan-matching"],"created_at":"2024-07-31T03:00:45.016Z","updated_at":"2025-05-16T07:05:41.999Z","avatar_url":"https://github.com/koide3.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"# small_gicp\n\n**small_gicp** is a header-only C++ library providing efficient and parallelized algorithms for fine point cloud registration (ICP, Point-to-Plane ICP, GICP, VGICP, etc.). It is a refined and optimized version of its predecessor, [fast_gicp](https://github.com/SMRT-AIST/fast_gicp), re-written from scratch with the following features.\n\n- **Highly Optimized** : The core registration algorithm implementation has been further optimized from fast_gicp, achieving up to **2x speed gain**.\n- **Fully parallerized** : small_gicp offers parallel implementations of several preprocessing algorithms, making the entire registration process parallelized (e.g., Downsampling, KdTree construction, Normal/Covariance estimation). It supports [OpenMP](https://www.openmp.org/) and [Intel TBB](https://github.com/oneapi-src/oneTBB) as parallelism backends.\n- **Minimum dependencies** : The library requires only [Eigen](https://eigen.tuxfamily.org/) along with the bundled [nanoflann](https://github.com/jlblancoc/nanoflann) and [Sophus](https://github.com/strasdat/Sophus). Optionally, it supports a [PCL](https://pointclouds.org/) registration interface for use as a drop-in replacement\n- **Customizable** : small_gicp allows the integration of any custom point cloud class into the registration algorithm via traits. Its template-based implementation enables customization of the registration process with original correspondence estimators and registration factors.\n- **Python bindings** : By being isolated from PCL, small_gicp's Python bindings are more portable and can be used seamlessly with other libraries such as Open3D.\n\nNote that GPU-based implementations are NOT included in this package.\n\nIf you find this package useful for your project, please consider leaving a comment [here](https://github.com/koide3/small_gicp/issues/3). It would help the author receive recognition in his organization and keep working on this project. Please also cite [the corresponding software paper](https://joss.theoj.org/papers/10.21105/joss.06948) if you use this software in an academic work.\n\n\n[![status](https://joss.theoj.org/papers/059b017c823ed9fd211fc91373cdc2cc/status.svg)](https://joss.theoj.org/papers/059b017c823ed9fd211fc91373cdc2cc) [![Build(Linux)](https://github.com/koide3/small_gicp/actions/workflows/build-linux.yml/badge.svg)](https://github.com/koide3/small_gicp/actions/workflows/build-linux.yml) [![macos](https://github.com/koide3/small_gicp/actions/workflows/build-macos.yml/badge.svg)](https://github.com/koide3/small_gicp/actions/workflows/build-macos.yml) [![Build(Windows)](https://github.com/koide3/small_gicp/actions/workflows/build-windows.yml/badge.svg)](https://github.com/koide3/small_gicp/actions/workflows/build-windows.yml) [![Test](https://github.com/koide3/small_gicp/actions/workflows/test.yml/badge.svg)](https://github.com/koide3/small_gicp/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/koide3/small_gicp/graph/badge.svg?token=PCVIUP2Z33)](https://codecov.io/gh/koide3/small_gicp)\n\n## Requirements\n\nThis library uses C++17 features. The PCL interface is not compatible with PCL older than 1.11 that uses `boost::shared_ptr`.\n\n## Dependencies\n\n- [Mandatory] [Eigen](https://eigen.tuxfamily.org/), [nanoflann](https://github.com/jlblancoc/nanoflann) ([bundled](include/small_gicp/ann/kdtree.hpp)), [Sophus](https://github.com/strasdat/Sophus) ([bundled](include/small_gicp/util/lie.hpp))\n- [Optional] [OpenMP](https://www.openmp.org/), [Intel TBB](https://www.intel.com/content/www/us/en/developer/tools/oneapi/onetbb.html), [PCL](https://pointclouds.org/)\n\n## Installation\n\n### C++\n\nsmall_gicp is a header-only library. You can just download and drop it in your project directory to use it.\n\nIf you need only basic point cloud registration functions, you can build and install the helper library as follows.\n\n```bash\nsudo apt-get install libeigen3-dev libomp-dev\n\ncd small_gicp\nmkdir build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release \u0026\u0026 make -j\nsudo make install\n```\n\n### Python (Linux / Windows / MacOS)\n\n#### Install from [PyPI](https://pypi.org/project/small-gicp/)\n\n```bash\npip install small_gicp\n```\n\n#### Install from source\n\n```bash\ncd small_gicp\npip install .\n\n# [Optional (linux)] Install stubs for autocomplete (If you know a better way, let me know...)\npip install pybind11-stubgen\ncd ~/.local/lib/python3.10/site-packages\npybind11-stubgen -o . --ignore-invalid=all small_gicp\n```\n\n## Documentation\n\n- Auto-generated docs: [C++](https://koide3.github.io/small_gicp/doc_cpp/index.html) [Python](https://koide3.github.io/small_gicp/doc_py/index.html)\n\n\n\n## Usage (C++)\n\nThe following examples assume `using namespace small_gicp` is placed somewhere.\n\n### Using helper library ([01_basic_registration.cpp](src/example/01_basic_registration.cpp))\n\nThe helper library (`registration_helper.hpp`) enables easily processing point clouds represented as `std::vector\u003cEigen::Vector(3|4)(f|d)\u003e`.\n\u003cdetails\u003e\u003csummary\u003eExpand\u003c/summary\u003e\n\n`small_gicp::align` takes two point clouds (`std::vectors` of `Eigen::Vector(3|4)(f|d)`) and returns a registration result (estimated transformation and some information on the optimization result). This is the easiest way to use small_gicp but causes an overhead for duplicated preprocessing.\n\n```cpp\n#include \u003csmall_gicp/registration/registration_helper.hpp\u003e\n\nstd::vector\u003cEigen::Vector3d\u003e target_points = ...;   // Any of Eigen::Vector(3|4)(f|d) can be used\nstd::vector\u003cEigen::Vector3d\u003e source_points = ...;   // \n\nRegistrationSetting setting;\nsetting.num_threads = 4;                    // Number of threads to be used\nsetting.downsampling_resolution = 0.25;     // Downsampling resolution\nsetting.max_correspondence_distance = 1.0;  // Maximum correspondence distance between points (e.g., triming threshold)\n\nEigen::Isometry3d init_T_target_source = Eigen::Isometry3d::Identity();\nRegistrationResult result = align(target_points, source_points, init_T_target_source, setting);\n\nEigen::Isometry3d T = result.T_target_source;  // Estimated transformation\nsize_t num_inliers = result.num_inliers;       // Number of inlier source points\nEigen::Matrix\u003cdouble, 6, 6\u003e H = result.H;      // Final Hessian matrix (6x6)\n```\n\nThere is also a way to perform preprocessing and registration separately. This enables saving time for preprocessing in case registration is performed several times for the same point cloud (e.g., typical odometry estimation based on scan-to-scan matching).\n\n```cpp\n#include \u003csmall_gicp/registration/registration_helper.hpp\u003e\n\nstd::vector\u003cEigen::Vector3d\u003e target_points = ...;   // Any of Eigen::Vector(3|4)(f|d) can be used\nstd::vector\u003cEigen::Vector3d\u003e source_points = ...;   // \n\nint num_threads = 4;                    // Number of threads to be used\ndouble downsampling_resolution = 0.25;  // Downsampling resolution\nint num_neighbors = 10;                 // Number of neighbor points used for normal and covariance estimation\n\n// std::pair\u003cPointCloud::Ptr, KdTree\u003cPointCloud\u003e::Ptr\u003e\nauto [target, target_tree] = preprocess_points(target_points, downsampling_resolution, num_neighbors, num_threads);\nauto [source, source_tree] = preprocess_points(source_points, downsampling_resolution, num_neighbors, num_threads);\n\nRegistrationSetting setting;\nsetting.num_threads = num_threads;\nsetting.max_correspondence_distance = 1.0;  // Maximum correspondence distance between points (e.g., triming threshold)\n\nEigen::Isometry3d init_T_target_source = Eigen::Isometry3d::Identity();\nRegistrationResult result = align(*target, *source, *target_tree, init_T_target_source, setting);\n\nEigen::Isometry3d T = result.T_target_source;  // Estimated transformation\nsize_t num_inliers = result.num_inliers;       // Number of inlier source points\nEigen::Matrix\u003cdouble, 6, 6\u003e H = result.H;      // Final Hessian matrix (6x6)\n```\n\n\u003c/details\u003e\n\n### Using PCL interface ([02_basic_registration_pcl.cpp](src/example/02_basic_registration_pcl.cpp))\n\nThe PCL interface allows using small_gicp as a drop-in replacement for `pcl::Registration`. It is also possible to directly feed `pcl::PointCloud` to algorithms implemented in small_gicp.\n\n\u003cdetails\u003e\u003csummary\u003eExpand\u003c/summary\u003e\n\n```cpp\n#include \u003csmall_gicp/pcl/pcl_registration.hpp\u003e\n\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr raw_target = ...;\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr raw_source = ...;\n\n// small_gicp::voxelgrid_downsampling can directly operate on pcl::PointCloud.\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr target = voxelgrid_sampling_omp(*raw_target, 0.25);\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr source = voxelgrid_sampling_omp(*raw_source, 0.25);\n\n// RegistrationPCL is derived from pcl::Registration and has mostly the same interface as pcl::GeneralizedIterativeClosestPoint.\nRegistrationPCL\u003cpcl::PointXYZ, pcl::PointXYZ\u003e reg;\nreg.setNumThreads(4);\nreg.setCorrespondenceRandomness(20);\nreg.setMaxCorrespondenceDistance(1.0);\nreg.setVoxelResolution(1.0);\nreg.setRegistrationType(\"VGICP\");  // or \"GICP\" (default = \"GICP\")\n\n// Set input point clouds.\nreg.setInputTarget(target);\nreg.setInputSource(source);\n\n// Align point clouds.\nauto aligned = pcl::make_shared\u003cpcl::PointCloud\u003cpcl::PointXYZ\u003e\u003e();\nreg.align(*aligned);\n\n// Swap source and target and align again.\n// This is useful when you want to re-use preprocessed point clouds for successive registrations (e.g., odometry estimation).\nreg.swapSourceAndTarget();\nreg.align(*aligned);\n```\n\nIt is also possible to directly feed `pcl::PointCloud` to `small_gicp::Registration`. Because all preprocessed data are exposed in this way, you can easily re-use them to obtain the best efficiency.\n\n```cpp\n#include \u003csmall_gicp/pcl/pcl_point.hpp\u003e\n#include \u003csmall_gicp/pcl/pcl_point_traits.hpp\u003e\n\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr raw_target = ...;\npcl::PointCloud\u003cpcl::PointXYZ\u003e::Ptr raw_source = ...;\n\n// Downsample points and convert them into pcl::PointCloud\u003cpcl::PointCovariance\u003e.\npcl::PointCloud\u003cpcl::PointCovariance\u003e::Ptr target = voxelgrid_sampling_omp\u003cpcl::PointCloud\u003cpcl::PointXYZ\u003e, pcl::PointCloud\u003cpcl::PointCovariance\u003e\u003e(*raw_target, 0.25);\npcl::PointCloud\u003cpcl::PointCovariance\u003e::Ptr source = voxelgrid_sampling_omp\u003cpcl::PointCloud\u003cpcl::PointXYZ\u003e, pcl::PointCloud\u003cpcl::PointCovariance\u003e\u003e(*raw_source, 0.25);\n\n// Estimate covariances of points.\nconst int num_threads = 4;\nconst int num_neighbors = 20;\nestimate_covariances_omp(*target, num_neighbors, num_threads);\nestimate_covariances_omp(*source, num_neighbors, num_threads);\n\n// Create KdTree for target and source.\nauto target_tree = std::make_shared\u003cKdTree\u003cpcl::PointCloud\u003cpcl::PointCovariance\u003e\u003e\u003e(target, KdTreeBuilderOMP(num_threads));\nauto source_tree = std::make_shared\u003cKdTree\u003cpcl::PointCloud\u003cpcl::PointCovariance\u003e\u003e\u003e(source, KdTreeBuilderOMP(num_threads));\n\nRegistration\u003cGICPFactor, ParallelReductionOMP\u003e registration;\nregistration.reduction.num_threads = num_threads;\nregistration.rejector.max_dist_sq = 1.0;\n\n// Align point clouds. Note that the input point clouds are pcl::PointCloud\u003cpcl::PointCovariance\u003e.\nauto result = registration.align(*target, *source, *target_tree, Eigen::Isometry3d::Identity());\n```\n\n\u003c/details\u003e\n\n### Using `Registration` template ([03_registration_template.cpp](src/example/03_registration_template.cpp))\n\nIf you want to fine-control and customize the registration process, use `small_gicp::Registration` template that allows modifying the inner algorithms and parameters.\n\u003cdetails\u003e\u003csummary\u003eExpand\u003c/summary\u003e\n\n```cpp\n#include \u003csmall_gicp/ann/kdtree_omp.hpp\u003e\n#include \u003csmall_gicp/points/point_cloud.hpp\u003e\n#include \u003csmall_gicp/factors/gicp_factor.hpp\u003e\n#include \u003csmall_gicp/util/normal_estimation_omp.hpp\u003e\n#include \u003csmall_gicp/registration/reduction_omp.hpp\u003e\n#include \u003csmall_gicp/registration/registration.hpp\u003e\n\nstd::vector\u003cEigen::Vector3d\u003e target_points = ...;   // Any of Eigen::Vector(3|4)(f|d) can be used\nstd::vector\u003cEigen::Vector3d\u003e source_points = ...;   // \n\nint num_threads = 4;\ndouble downsampling_resolution = 0.25;\nint num_neighbors = 10;\ndouble max_correspondence_distance = 1.0;\n\n// Convert to small_gicp::PointCloud\nauto target = std::make_shared\u003cPointCloud\u003e(target_points);\nauto source = std::make_shared\u003cPointCloud\u003e(source_points);\n\n// Downsampling\ntarget = voxelgrid_sampling_omp(*target, downsampling_resolution, num_threads);\nsource = voxelgrid_sampling_omp(*source, downsampling_resolution, num_threads);\n\n// Create KdTree\nauto target_tree = std::make_shared\u003cKdTree\u003cPointCloud\u003e\u003e(target, KdTreeBuilderOMP(num_threads));\nauto source_tree = std::make_shared\u003cKdTree\u003cPointCloud\u003e\u003e(source, KdTreeBuilderOMP(num_threads));\n\n// Estimate point covariances\nestimate_covariances_omp(*target, *target_tree, num_neighbors, num_threads);\nestimate_covariances_omp(*source, *source_tree, num_neighbors, num_threads);\n\n// GICP + OMP-based parallel reduction\nRegistration\u003cGICPFactor, ParallelReductionOMP\u003e registration;\nregistration.reduction.num_threads = num_threads;\nregistration.rejector.max_dist_sq = max_correspondence_distance * max_correspondence_distance;\n\n// Align point clouds\nEigen::Isometry3d init_T_target_source = Eigen::Isometry3d::Identity();\nauto result = registration.align(*target, *source, *target_tree, init_T_target_source);\n\nEigen::Isometry3d T = result.T_target_source;  // Estimated transformation\nsize_t num_inliers = result.num_inliers;       // Number of inlier source points\nEigen::Matrix\u003cdouble, 6, 6\u003e H = result.H;      // Final Hessian matrix (6x6)\n```\n\nSee [03_registration_template.cpp](src/example/03_registration_template.cpp)  for more detailed customization examples.\n\n\u003c/details\u003e\n\n### Cookbook\n\n- [Standard scan-to-scan GICP matching odometry](src/benchmark/odometry_benchmark_small_gicp_omp.cpp)\n- [Extremely scalable scan-to-scan matching odometry with data flow graph](src/benchmark/odometry_benchmark_small_gicp_tbb_flow.cpp)\n- [Scan-to-model matching odometry with incremental voxelmap (GICP + iVox)](src/benchmark/odometry_benchmark_small_gicp_model_tbb.cpp)\n- [Scan-to-model matching odometry with incremental Gaussian voxelmap (VGICP)](src/benchmark/odometry_benchmark_small_vgicp_model_tbb.cpp)\n\n## Usage (Python) [basic_registration.py](src/example/basic_registration.py)\n\n\u003cdetails\u003e\u003csummary\u003eExpand\u003c/summary\u003e\n\nExample A : Perform registration with numpy arrays\n\n```python\n# Align two point clouds using various ICP-like algorithms.\n# \n# Parameters\n# ----------\n# target_points : NDArray[np.float64]\n#     Nx3 or Nx4 matrix representing the target point cloud.\n# source_points : NDArray[np.float64]\n#     Nx3 or Nx4 matrix representing the source point cloud.\n# init_T_target_source : np.ndarray[np.float64]\n#     4x4 matrix representing the initial transformation from target to source.\n# registration_type : str = 'GICP'\n#     Type of registration algorithm to use ('ICP', 'PLANE_ICP', 'GICP', 'VGICP').\n# voxel_resolution : float = 1.0\n#     Resolution of voxels used for correspondence search (used only in VGICP).\n# downsampling_resolution : float = 0.25\n#     Resolution for downsampling the point clouds.\n# max_correspondence_distance : float = 1.0\n#     Maximum distance for matching points between point clouds.\n# num_threads : int = 1\n#     Number of threads to use for parallel processing.\n# \n# Returns\n# -------\n# RegistrationResult\n#     Object containing the final transformation matrix and convergence status.\nresult = small_gicp.align(target_raw_numpy, source_raw_numpy, downsampling_resolution=0.25)\n\nresult.T_target_source  # Estimated transformation (4x4 numpy array)\nresult.converged        # If true, the optimization converged successfully\nresult.iterations       # Number of iterations the optimization took\nresult.num_inliers      # Number of inlier points\nresult.H                # Final Hessian matrix (6x6 matrix)\nresult.b                # Final information vector (6D vector)\nresult.e                # Final error (float)\n```\n\nExample B : Perform preprocessing and registration separately\n\n```python\n# Preprocess point cloud (downsampling, kdtree construction, and normal/covariance estimation)\n#\n# Parameters\n# ----------\n# points : NDArray[np.float64]\n#     Nx3 or Nx4 matrix representing the point cloud.\n# downsampling_resolution : float = 0.1\n#     Resolution for downsampling the point clouds.\n# num_neighbors : int = 20\n#     Number of neighbor points to usefor point normal/covariance estimation.\n# num_threads : int = 1\n#     Number of threads to use for parallel processing.\n# \n# Returns\n# -------\n# PointCloud\n#     Downsampled point cloud with estimated normals and covariances.\n# KdTree\n#     KdTree for the downsampled point cloud\ntarget, target_tree = small_gicp.preprocess_points(target_raw_numpy, downsampling_resolution=0.25)\nsource, source_tree = small_gicp.preprocess_points(source_raw_numpy, downsampling_resolution=0.25)\n\n# `target` and `source` are small_gicp.PointCloud with the following methods\ntarget.size()           # Number of points\ntarget.points()         # Nx4 numpy array   [x, y, z, 1] x N\ntarget.normals()        # Nx4 numpy array   [nx, ny, nz, 0] x N\ntarget.covs()           # Array of 4x4 covariance matrices\n\n#  Align two point clouds using specified ICP-like algorithms, utilizing point cloud and KD-tree inputs.\n#\n#  Parameters\n#  ----------\n#  target : PointCloud::ConstPtr\n#      Pointer to the target point cloud.\n#  source : PointCloud::ConstPtr\n#      Pointer to the source point cloud.\n#  target_tree : KdTree\u003cPointCloud\u003e::ConstPtr, optional\n#      Pointer to the KD-tree of the target for nearest neighbor search. If nullptr, a new tree is built.\n#  init_T_target_source : NDArray[np.float64]\n#      4x4 matrix representing the initial transformation from target to source.\n#  registration_type : str = 'GICP'\n#      Type of registration algorithm to use ('ICP', 'PLANE_ICP', 'GICP').\n#  max_correspondence_distance : float = 1.0\n#      Maximum distance for corresponding point pairs.\n#  num_threads : int = 1\n#      Number of threads to use for computation.\n# \n#  Returns\n#  -------\n#  RegistrationResult\n#      Object containing the final transformation matrix and convergence status.\nresult = small_gicp.align(target, source, target_tree)\n```\n\nExample C : Perform each of preprocessing steps one-by-one\n\n```python\n# Convert numpy arrays (Nx3 or Nx4) to small_gicp.PointCloud\ntarget_raw = small_gicp.PointCloud(target_raw_numpy)\nsource_raw = small_gicp.PointCloud(source_raw_numpy)\n\n# Downsampling\ntarget = small_gicp.voxelgrid_sampling(target_raw, 0.25)\nsource = small_gicp.voxelgrid_sampling(source_raw, 0.25)\n\n# KdTree construction\ntarget_tree = small_gicp.KdTree(target)\nsource_tree = small_gicp.KdTree(source)\n\n# Estimate covariances\nsmall_gicp.estimate_covariances(target, target_tree)\nsmall_gicp.estimate_covariances(source, source_tree)\n\n# Align point clouds\nresult = small_gicp.align(target, source, target_tree)\n```\n\nExample D: Example with Open3D\n\n```python\ntarget_o3d = open3d.io.read_point_cloud('small_gicp/data/target.ply').paint_uniform_color([0, 1, 0])\nsource_o3d = open3d.io.read_point_cloud('small_gicp/data/source.ply').paint_uniform_color([0, 0, 1])\n\ntarget, target_tree = small_gicp.preprocess_points(numpy.asarray(target_o3d.points), downsampling_resolution=0.25)\nsource, source_tree = small_gicp.preprocess_points(numpy.asarray(source_o3d.points), downsampling_resolution=0.25)\nresult = small_gicp.align(target, source, target_tree)\n\nsource_o3d.transform(result.T_target_source)\nopen3d.visualization.draw_geometries([target_o3d, source_o3d])\n```\n\n\u003c/details\u003e\n\n\n### Cookbook\n\n- [Scan-to-scan and scan-to-model GICP matching odometry on KITTI](src/example/kitti_odometry.py)\n\n## Running examples\n\n### C++\n\n```bash\ncd small_gicp\nmkdir build \u0026\u0026 cd build\ncmake .. -DBUILD_EXAMPLES=ON \u0026\u0026 make -j\n\ncd ..\n./build/01_basic_registration\n./build/02_basic_registration_pcl\n./build/03_registration_template\n```\n\n\n### Python\n\n```bash\ncd small_gicp\npip install .\n\npython3 src/example/basic_registration.py\n```\n\n## [Benchmark](BENCHMARK.md)\n\nProcessing speed comparison between small_gicp and Open3D ([youtube]((https://youtu.be/LNESzGXPr4c?feature=shared))).\n[![small_comp](https://github.com/koide3/small_gicp/assets/31344317/7959edd6-f0e4-4318-b4c1-a3f8755c407f)](https://youtu.be/LNESzGXPr4c?feature=shared)\n\n### Downsampling\n\n- Single-threaded `small_gicp::voxelgrid_sampling` is about **1.3x faster** than `pcl::VoxelGrid`.\n- Multi-threaded `small_gicp::voxelgrid_sampling_tbb` (6 threads) is about **3.2x faster** than `pcl::VoxelGrid`.\n- `small_gicp::voxelgrid_sampling` provides accurate downsampling results that are nearly identical to those of `pcl::VoxelGrid`, while `pcl::ApproximateVoxelGrid` can produce spurious points (up to 2x more points).\n- `small_gicp::voxelgrid_sampling` can handle larger point clouds with finer voxel resolutions compared to `pcl::VoxelGrid`. For a point cloud with a width of 1000m, the minimum voxel resolution can be **0.5 mm**.\n\n![downsampling_comp](docs/assets/downsampling_comp.png)\n\n### KdTree construction\n\n- Multi-threaded implementation (TBB and OMP) can be up to **6x faster** than the single-threaded version. The single-thread version performs almost equivalently to nanoflann.\n- The new KdTree implementation demonstrates good scalability due to its well-balanced task assignment.\n- This benchmark compares only the construction time (query time is not included). Nearest neighbor queries are included and evaluated in the following odometry estimation evaluation.\n\n![kdtree_time](docs/assets/kdtree_time.png)\n\n### Odometry estimation\n\n- Single-thread `small_gicp::GICP` is about **2.4x and 1.9x faster** than `pcl::GICP` and `fast_gicp::GICP`, respectively.\n- `small_gicp::(GICP|VGICP)` demonstrates better multi-threaded scalability compared to `fast_gicp::(GICP|VGICP)`.\n- `small_gicp::GICP` parallelized with [TBB flow graph](src/benchmark/odometry_benchmark_small_gicp_tbb_flow.cpp) shows excellent scalability in many-threads scenarios (**~128 threads**), though with some latency degradation.\n- Outputs of `small_gicp::GICP` are almost identical to those of `fast_gicp::GICP`.\n\n![odometry_time](docs/assets/odometry_time.png)\n\n## License\nThis package is released under the MIT license.\n\nIf you find this package useful for your project, please consider leaving a comment [here](https://github.com/koide3/small_gicp/issues/3). It would help the author receive recognition in his organization and keep working on this project.\n\nPlease also cite [the following article](https://joss.theoj.org/papers/10.21105/joss.06948) if you use this software in an academic work.\n\n```\n@article{small_gicp,\nauthor = {Kenji Koide},\ntitle = {{small\\_gicp: Efficient and parallel algorithms for point cloud registration}},\njournal = {Journal of Open Source Software},\nmonth = aug,\nnumber = {100},\npages = {6948},\nvolume = {9},\nyear = {2024},\ndoi = {10.21105/joss.06948}\n}\n```\n\n## Contact\n\n[Kenji Koide](https://staff.aist.go.jp/k.koide/), National Institute of Advanced Industrial Science and Technology (AIST)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoide3%2Fsmall_gicp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoide3%2Fsmall_gicp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoide3%2Fsmall_gicp/lists"}