mirror of https://github.com/koide3/small_gicp.git
init
This commit is contained in:
commit
3b0f422bcb
|
|
@ -0,0 +1,168 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: Google
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 180
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 0
|
||||||
|
ContinuationIndentWidth: 2
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<ext/.*\.h>'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^<.*\.h>'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Never
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Left
|
||||||
|
RawStringFormats:
|
||||||
|
- Language: Cpp
|
||||||
|
Delimiters:
|
||||||
|
- cc
|
||||||
|
- CC
|
||||||
|
- cpp
|
||||||
|
- Cpp
|
||||||
|
- CPP
|
||||||
|
- 'c++'
|
||||||
|
- 'C++'
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
|
- Language: TextProto
|
||||||
|
Delimiters:
|
||||||
|
- pb
|
||||||
|
- PB
|
||||||
|
- proto
|
||||||
|
- PROTO
|
||||||
|
EnclosingFunctions:
|
||||||
|
- EqualsProto
|
||||||
|
- EquivToProto
|
||||||
|
- PARSE_PARTIAL_TEXT_PROTO
|
||||||
|
- PARSE_TEST_PROTO
|
||||||
|
- PARSE_TEXT_PROTO
|
||||||
|
- ParseTextOrDie
|
||||||
|
- ParseTextProtoOrDie
|
||||||
|
CanonicalDelimiter: ''
|
||||||
|
BasedOnStyle: google
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
Standard: Auto
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 8
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
...
|
||||||
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
.vscode/*
|
||||||
|
build/*
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5.2)
|
||||||
|
project(small_gicp)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build." FORCE)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(BUILD_TESTS "Build test" OFF)
|
||||||
|
option(BUILD_WITH_MARCH_NATIVE "Build with -march=native" OFF)
|
||||||
|
|
||||||
|
if(BUILD_WITH_MARCH_NATIVE)
|
||||||
|
add_compile_options(-march=native)
|
||||||
|
set(CMAKE_C_FLAGS "-march=native ${CMAKE_C_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "-march=native ${CMAKE_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(PCL REQUIRED)
|
||||||
|
find_package(TBB REQUIRED)
|
||||||
|
find_package(Eigen3 REQUIRED)
|
||||||
|
find_package(Iridescence REQUIRED)
|
||||||
|
|
||||||
|
find_package(OpenMP)
|
||||||
|
if (OPENMP_FOUND)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
###########
|
||||||
|
## Build ##
|
||||||
|
###########
|
||||||
|
|
||||||
|
add_executable(small_gicp_test
|
||||||
|
src/small_gicp_test.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(small_gicp_test PUBLIC
|
||||||
|
include
|
||||||
|
${PCL_INCLUDE_DIRS}
|
||||||
|
${TBB_INCLUDE_DIRS}
|
||||||
|
${EIGEN3_INCLUDE_DIR}
|
||||||
|
${Iridescence_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
target_link_libraries(small_gicp_test
|
||||||
|
${PCL_LIBRARIES}
|
||||||
|
${TBB_LIBRARIES}
|
||||||
|
${Iridescence_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
find_path(GTSAM_INCLUDE_DIRS gtsam/inference/FactorGraph.h
|
||||||
|
HINTS /usr/local/include /usr/include
|
||||||
|
DOC "GTSAM include directories")
|
||||||
|
|
||||||
|
find_library(GTSAM_LIB NAMES gtsam
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "GTSAM libraries")
|
||||||
|
|
||||||
|
find_library(GTSAM_UNSTABLE_LIB NAMES gtsam_unstable
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "GTSAM_UNSTABLE libraries")
|
||||||
|
|
||||||
|
find_library(TBB_LIB NAMES tbb
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "TBB libraries")
|
||||||
|
|
||||||
|
find_library(TBB_MALLOC_LIB NAMES tbbmalloc
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "TBB malloc libraries")
|
||||||
|
|
||||||
|
if(GTSAM_LIB AND GTSAM_UNSTABLE_LIB AND TBB_LIB)
|
||||||
|
set(GTSAM_LIBRARIES ${GTSAM_LIB} ${GTSAM_UNSTABLE_LIB} ${TBB_LIB} ${TBB_MALLOC_LIB})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GTSAM DEFAULT_MSG GTSAM_INCLUDE_DIRS GTSAM_LIBRARIES)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
find_path(Iridescence_INCLUDE_DIRS glk/drawable.hpp
|
||||||
|
HINTS /usr/local/include/iridescence /usr/include/iridescence
|
||||||
|
DOC "Iridescence include directories")
|
||||||
|
|
||||||
|
find_library(Iridescence_LIBRARY NAMES iridescence
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "Iridescence libraries")
|
||||||
|
|
||||||
|
find_library(gl_imgui_LIBRARY NAMES gl_imgui
|
||||||
|
HINTS /usr/local/lib /usr/lib
|
||||||
|
DOC "Iridescence libraries")
|
||||||
|
|
||||||
|
set(Iridescence_LIBRARIES ${Iridescence_LIBRARY} ${gl_imgui_LIBRARY})
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Iridescence DEFAULT_MSG Iridescence_INCLUDE_DIRS Iridescence_LIBRARIES)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
find_path(TBB_INCLUDE_DIRS tbb/tbb.h
|
||||||
|
HINTS /usr/local/include /usr/include
|
||||||
|
DOC "oneTBB include directories")
|
||||||
|
|
||||||
|
find_library(TBB_LIB NAMES tbb
|
||||||
|
HINTS /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu
|
||||||
|
DOC "TBB libraries")
|
||||||
|
|
||||||
|
# if(GTSAM_LIB AND GTSAM_UNSTABLE_LIB AND TBB_LIB)
|
||||||
|
if(TBB_LIB)
|
||||||
|
set(TBB_LIBRARIES ${TBB_LIB})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_INCLUDE_DIRS TBB_LIBRARIES)
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <tbb/tbb.h>
|
||||||
|
#include <small_gicp/ann/kdtree.hpp>
|
||||||
|
#include <small_gicp/util/vector3i_hash.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
class CachedKdTree {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<CachedKdTree>;
|
||||||
|
using ConstPtr = std::shared_ptr<const CachedKdTree>;
|
||||||
|
|
||||||
|
CachedKdTree(const PointCloud& points, double leaf_size) : inv_leaf_size(1.0 / leaf_size), kdtree(points) {}
|
||||||
|
|
||||||
|
size_t knn_search(const Eigen::Vector4d& pt, size_t k, size_t* k_indices, double* k_sq_dists) const {
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().cast<int>().head<3>();
|
||||||
|
|
||||||
|
CacheTable::const_accessor ca;
|
||||||
|
if (cache.find(ca, coord)) {
|
||||||
|
std::ranges::copy(ca->second.first, k_indices);
|
||||||
|
std::ranges::copy(ca->second.second, k_sq_dists);
|
||||||
|
return ca->second.first.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t n = kdtree.knn_search(pt, k, k_indices, k_sq_dists);
|
||||||
|
std::vector<size_t> indices(k_indices, k_indices + n);
|
||||||
|
std::vector<double> sq_dists(k_sq_dists, k_sq_dists + n);
|
||||||
|
|
||||||
|
CacheTable::accessor a;
|
||||||
|
if (cache.insert(a, coord)) {
|
||||||
|
a->second.first = std::move(indices);
|
||||||
|
a->second.second = std::move(sq_dists);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const double inv_leaf_size;
|
||||||
|
|
||||||
|
using KnnResult = std::pair<std::vector<size_t>, std::vector<double>>;
|
||||||
|
using CacheTable = tbb::concurrent_hash_map<Eigen::Vector3i, KnnResult, XORVector3iHash>;
|
||||||
|
mutable CacheTable cache;
|
||||||
|
|
||||||
|
UnsafeKdTree<PointCloud> kdtree;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct Traits<CachedKdTree<PointCloud>> {
|
||||||
|
static size_t knn_search(const CachedKdTree<PointCloud>& tree, const Eigen::Vector4d& point, size_t k, size_t* k_indices, double* k_sq_dists) {
|
||||||
|
return tree.knn_search(point, k, k_indices, k_sq_dists);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/ann/nanoflann.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
class UnsafeKdTree {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<UnsafeKdTree>;
|
||||||
|
using ConstPtr = std::shared_ptr<const UnsafeKdTree>;
|
||||||
|
using Index = nanoflann::KDTreeSingleIndexAdaptor<nanoflann::L2_Simple_Adaptor<double, UnsafeKdTree<PointCloud>, double>, UnsafeKdTree<PointCloud>, 3, size_t>;
|
||||||
|
|
||||||
|
UnsafeKdTree(const PointCloud& points) : points(points), index(3, *this, nanoflann::KDTreeSingleIndexAdaptorParams(10)) { index.buildIndex(); }
|
||||||
|
~UnsafeKdTree() {}
|
||||||
|
|
||||||
|
size_t kdtree_get_point_count() const { return traits::size(points); }
|
||||||
|
double kdtree_get_pt(const size_t idx, const size_t dim) const { return traits::point(points, idx)[dim]; }
|
||||||
|
|
||||||
|
template <class BBox>
|
||||||
|
bool kdtree_get_bbox(BBox&) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t knn_search(const Eigen::Vector4d& pt, size_t k, size_t* k_indices, double* k_sq_dists) const { return index.knnSearch(pt.data(), k, k_indices, k_sq_dists); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const PointCloud& points;
|
||||||
|
Index index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
class KdTree {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<KdTree>;
|
||||||
|
using ConstPtr = std::shared_ptr<const KdTree>;
|
||||||
|
|
||||||
|
KdTree(const std::shared_ptr<const PointCloud>& points) : points(points), tree(*points) {}
|
||||||
|
~KdTree() {}
|
||||||
|
|
||||||
|
size_t knn_search(const Eigen::Vector4d& pt, size_t k, size_t* k_indices, double* k_sq_dists) const { return tree.knn_search(pt, k, k_indices, k_sq_dists); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<const PointCloud> points;
|
||||||
|
const UnsafeKdTree<PointCloud> tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct Traits<UnsafeKdTree<PointCloud>> {
|
||||||
|
static size_t knn_search(const UnsafeKdTree<PointCloud>& tree, const Eigen::Vector4d& point, size_t k, size_t* k_indices, double* k_sq_dists) {
|
||||||
|
return tree.knn_search(point, k, k_indices, k_sq_dists);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct Traits<KdTree<PointCloud>> {
|
||||||
|
static size_t knn_search(const KdTree<PointCloud>& tree, const Eigen::Vector4d& point, size_t k, size_t* k_indices, double* k_sq_dists) {
|
||||||
|
return tree.knn_search(point, k, k_indices, k_sq_dists);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Traits;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t knn_search(const T& tree, const Eigen::Vector4d& point, size_t k, size_t* k_indices, double* k_sq_dists) {
|
||||||
|
return Traits<T>::knn_search(tree, point, k, k_indices, k_sq_dists);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
#include <small_gicp/util/lie.hpp>
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct GICPFactor {
|
||||||
|
GICPFactor() : target_index(std::numeric_limits<size_t>::max()), source_index(std::numeric_limits<size_t>::max()), mahalanobis(Eigen::Matrix4d::Zero()) {}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector>
|
||||||
|
bool linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
size_t source_index,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
Eigen::Matrix<double, 6, 6>* H,
|
||||||
|
Eigen::Matrix<double, 6, 1>* b,
|
||||||
|
double* e) {
|
||||||
|
//
|
||||||
|
this->source_index = source_index;
|
||||||
|
this->target_index = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
const Eigen::Vector4d transed_source_pt = T * traits::point(source, source_index);
|
||||||
|
|
||||||
|
size_t k_index;
|
||||||
|
double k_sq_dist;
|
||||||
|
if (!traits::knn_search(target_tree, transed_source_pt, 1, &k_index, &k_sq_dist) || rejector(T, k_index, source_index, k_sq_dist)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_index = k_index;
|
||||||
|
|
||||||
|
const Eigen::Matrix4d RCR = traits::cov(target, target_index) + T.matrix() * traits::cov(source, source_index) * T.matrix().transpose();
|
||||||
|
mahalanobis.block<3, 3>(0, 0) = RCR.block<3, 3>(0, 0).inverse();
|
||||||
|
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - transed_source_pt;
|
||||||
|
|
||||||
|
Eigen::Matrix<double, 4, 6> J = Eigen::Matrix<double, 4, 6>::Zero();
|
||||||
|
J.block<3, 3>(0, 0) = T.linear() * skew(traits::point(source, source_index).template head<3>());
|
||||||
|
J.block<3, 3>(0, 3) = -T.linear();
|
||||||
|
|
||||||
|
*H = J.transpose() * mahalanobis * J;
|
||||||
|
*b = J.transpose() * mahalanobis * residual;
|
||||||
|
*e = 0.5 * residual.dot(mahalanobis * residual);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T) const {
|
||||||
|
if (target_index == std::numeric_limits<size_t>::max()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector4d transed_source_pt = T * traits::point(source, source_index);
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - transed_source_pt;
|
||||||
|
return 0.5 * residual.dot(mahalanobis * residual);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inlier() const { return target_index != std::numeric_limits<size_t>::max(); }
|
||||||
|
|
||||||
|
size_t target_index;
|
||||||
|
size_t source_index;
|
||||||
|
Eigen::Matrix4d mahalanobis;
|
||||||
|
};
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
#include <small_gicp/util/lie.hpp>
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct ICPFactor {
|
||||||
|
ICPFactor() : target_index(std::numeric_limits<size_t>::max()), source_index(std::numeric_limits<size_t>::max()) {}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector>
|
||||||
|
bool linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
size_t source_index,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
Eigen::Matrix<double, 6, 6>* H,
|
||||||
|
Eigen::Matrix<double, 6, 1>* b,
|
||||||
|
double* e) {
|
||||||
|
//
|
||||||
|
this->source_index = source_index;
|
||||||
|
this->target_index = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
const Eigen::Vector4d transed_source_pt = T * traits::point(source, source_index);
|
||||||
|
|
||||||
|
size_t k_index;
|
||||||
|
double k_sq_dist;
|
||||||
|
if (!traits::knn_search(target_tree, transed_source_pt, 1, &k_index, &k_sq_dist) || rejector(T, k_index, source_index, k_sq_dist)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_index = k_index;
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - transed_source_pt;
|
||||||
|
|
||||||
|
Eigen::Matrix<double, 4, 6> J = Eigen::Matrix<double, 4, 6>::Zero();
|
||||||
|
J.block<3, 3>(0, 0) = T.linear() * skew(traits::point(source, source_index).template head<3>());
|
||||||
|
J.block<3, 3>(0, 3) = -T.linear();
|
||||||
|
|
||||||
|
*H = J.transpose() * J;
|
||||||
|
*b = J.transpose() * residual;
|
||||||
|
*e = 0.5 * residual.squaredNorm();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T) const {
|
||||||
|
if (target_index == std::numeric_limits<size_t>::max()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - T * traits::point(source, source_index);
|
||||||
|
return 0.5 * residual.squaredNorm();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inlier() const { return target_index != std::numeric_limits<size_t>::max(); }
|
||||||
|
|
||||||
|
size_t target_index;
|
||||||
|
size_t source_index;
|
||||||
|
};
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
#include <small_gicp/util/lie.hpp>
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct PointToPlaneICPFactor {
|
||||||
|
PointToPlaneICPFactor() : target_index(std::numeric_limits<size_t>::max()), source_index(std::numeric_limits<size_t>::max()) {}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector>
|
||||||
|
bool linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
size_t source_index,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
Eigen::Matrix<double, 6, 6>* H,
|
||||||
|
Eigen::Matrix<double, 6, 1>* b,
|
||||||
|
double* e) {
|
||||||
|
//
|
||||||
|
this->source_index = source_index;
|
||||||
|
this->target_index = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
const Eigen::Vector4d transed_source_pt = T * traits::point(source, source_index);
|
||||||
|
|
||||||
|
size_t k_index;
|
||||||
|
double k_sq_dist;
|
||||||
|
if (!traits::knn_search(target_tree, transed_source_pt, 1, &k_index, &k_sq_dist) || rejector(T, k_index, source_index, k_sq_dist)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_index = k_index;
|
||||||
|
const auto& target_normal = traits::normal(target, target_index);
|
||||||
|
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - transed_source_pt;
|
||||||
|
const Eigen::Vector4d error = target_normal.array() * residual.array();
|
||||||
|
|
||||||
|
Eigen::Matrix<double, 4, 6> J = Eigen::Matrix<double, 4, 6>::Zero();
|
||||||
|
J.block<3, 3>(0, 0) = target_normal.template head<3>().asDiagonal() * T.linear() * skew(traits::point(source, source_index).template head<3>());
|
||||||
|
J.block<3, 3>(0, 3) = target_normal.template head<3>().asDiagonal() * (-T.linear());
|
||||||
|
|
||||||
|
*H = J.transpose() * J;
|
||||||
|
*b = J.transpose() * error;
|
||||||
|
*e = 0.5 * error.squaredNorm();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T) const {
|
||||||
|
if (target_index == std::numeric_limits<size_t>::max()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector4d transed_source_pt = T * traits::point(source, source_index);
|
||||||
|
const Eigen::Vector4d residual = traits::point(target, target_index) - transed_source_pt;
|
||||||
|
const Eigen::Vector4d error = traits::normal(target, target_index).array() * residual.array();
|
||||||
|
return 0.5 * error.squaredNorm();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inlier() const { return target_index != std::numeric_limits<size_t>::max(); }
|
||||||
|
|
||||||
|
size_t target_index;
|
||||||
|
size_t source_index;
|
||||||
|
};
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
#include <small_gicp/util/vector3i_hash.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct GaussianVoxel {
|
||||||
|
public:
|
||||||
|
GaussianVoxel(const Eigen::Vector3i& coord) : finalized(false), lru(0), num_points(0), coord(coord), mean(Eigen::Vector4d::Zero()), cov(Eigen::Matrix4d::Zero()) {}
|
||||||
|
~GaussianVoxel() {}
|
||||||
|
|
||||||
|
void add(const Eigen::Vector4d& mean, const Eigen::Matrix4d& cov, size_t lru) {
|
||||||
|
if (finalized) {
|
||||||
|
this->finalized = false;
|
||||||
|
this->mean *= num_points;
|
||||||
|
this->cov *= num_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_points++;
|
||||||
|
this->mean += mean;
|
||||||
|
this->cov += cov;
|
||||||
|
this->lru = lru;
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize() {
|
||||||
|
if (finalized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mean /= num_points;
|
||||||
|
cov /= num_points;
|
||||||
|
finalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool finalized;
|
||||||
|
size_t lru;
|
||||||
|
size_t num_points;
|
||||||
|
Eigen::Vector3i coord;
|
||||||
|
|
||||||
|
Eigen::Vector4d mean;
|
||||||
|
Eigen::Matrix4d cov;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GaussianVoxelMap {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<GaussianVoxelMap>;
|
||||||
|
using ConstPtr = std::shared_ptr<const GaussianVoxelMap>;
|
||||||
|
|
||||||
|
GaussianVoxelMap(double leaf_size) : inv_leaf_size(1.0 / leaf_size), lru_horizon(100), lru_clear_cycle(10), lru_counter(0) {}
|
||||||
|
~GaussianVoxelMap() {}
|
||||||
|
|
||||||
|
size_t size() const { return flat_voxels.size(); }
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void insert(const PointCloud& points, const Eigen::Isometry3d& T = Eigen::Isometry3d::Identity()) {
|
||||||
|
for (size_t i = 0; i < traits::size(points); i++) {
|
||||||
|
const Eigen::Vector4d pt = T * traits::point(points, i);
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().cast<int>().head<3>();
|
||||||
|
|
||||||
|
auto found = voxels.find(coord);
|
||||||
|
if (found == voxels.end()) {
|
||||||
|
found = voxels.emplace_hint(found, coord, flat_voxels.size());
|
||||||
|
flat_voxels.emplace_back(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& voxel = flat_voxels[found->second];
|
||||||
|
const Eigen::Matrix4d cov = T.matrix() * traits::cov(points, i) * T.matrix().transpose();
|
||||||
|
voxel.add(pt, cov, lru_counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lru_counter++) % lru_clear_cycle == 0) {
|
||||||
|
std::erase_if(flat_voxels, [&](const GaussianVoxel& voxel) { return voxel.lru + lru_horizon < lru_counter; });
|
||||||
|
voxels.clear();
|
||||||
|
for (size_t i = 0; i < flat_voxels.size(); i++) {
|
||||||
|
voxels[flat_voxels[i].coord] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& voxel : flat_voxels) {
|
||||||
|
voxel.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t knn_search(const Eigen::Vector4d& pt, size_t k, size_t* k_indices, double* k_sq_dists) const {
|
||||||
|
if (k != 1) {
|
||||||
|
std::cerr << "warning:!!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().cast<int>().head<3>();
|
||||||
|
const auto found = voxels.find(coord);
|
||||||
|
if (found == voxels.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GaussianVoxel& voxel = flat_voxels[found->second];
|
||||||
|
k_indices[0] = found->second;
|
||||||
|
k_sq_dists[0] = (voxel.mean - pt).squaredNorm();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const double inv_leaf_size;
|
||||||
|
size_t lru_horizon;
|
||||||
|
size_t lru_clear_cycle;
|
||||||
|
size_t lru_counter;
|
||||||
|
|
||||||
|
std::vector<GaussianVoxel> flat_voxels;
|
||||||
|
std::unordered_map<Eigen::Vector3i, size_t, XORVector3iHash> voxels;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<GaussianVoxelMap> {
|
||||||
|
static size_t size(const GaussianVoxelMap& voxelmap) { return voxelmap.size(); }
|
||||||
|
|
||||||
|
static bool has_points(const GaussianVoxelMap& voxelmap) { return !voxelmap.flat_voxels.empty(); }
|
||||||
|
static bool has_covs(const GaussianVoxelMap& voxelmap) { return !voxelmap.flat_voxels.empty(); }
|
||||||
|
|
||||||
|
static const Eigen::Vector4d& point(const GaussianVoxelMap& voxelmap, size_t i) { return voxelmap.flat_voxels[i].mean; }
|
||||||
|
static const Eigen::Matrix4d& cov(const GaussianVoxelMap& voxelmap, size_t i) { return voxelmap.flat_voxels[i].cov; }
|
||||||
|
|
||||||
|
static size_t knn_search(const GaussianVoxelMap& voxelmap, const Eigen::Vector4d& point, size_t k, size_t* k_indices, double* k_sq_dists) {
|
||||||
|
return voxelmap.knn_search(point, k, k_indices, k_sq_dists);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pcl/point_types.h>
|
||||||
|
|
||||||
|
namespace pcl {
|
||||||
|
|
||||||
|
using Matrix4fMap = Eigen::Map<Eigen::Matrix4f, Eigen::Aligned>;
|
||||||
|
using Matrix4fMapConst = const Eigen::Map<const Eigen::Matrix4f, Eigen::Aligned>;
|
||||||
|
|
||||||
|
struct EIGEN_ALIGN16 PointCovariance {
|
||||||
|
PCL_ADD_POINT4D;
|
||||||
|
Eigen::Matrix4f cov;
|
||||||
|
|
||||||
|
Matrix4fMap getCovariance4fMap() { return Matrix4fMap(cov.data()); }
|
||||||
|
Matrix4fMapConst getCovariance4fMap() const { return Matrix4fMapConst(cov.data()); }
|
||||||
|
|
||||||
|
PCL_MAKE_ALIGNED_OPERATOR_NEW
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EIGEN_ALIGN16 PointNormalCovariance {
|
||||||
|
PCL_ADD_POINT4D;
|
||||||
|
PCL_ADD_NORMAL4D
|
||||||
|
Eigen::Matrix4f cov;
|
||||||
|
|
||||||
|
Matrix4fMap getCovariance4fMap() { return Matrix4fMap(cov.data()); }
|
||||||
|
Matrix4fMapConst getCovariance4fMap() const { return Matrix4fMapConst(cov.data()); }
|
||||||
|
|
||||||
|
PCL_MAKE_ALIGNED_OPERATOR_NEW
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pcl
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pcl/point_types.h>
|
||||||
|
#include <pcl/point_cloud.h>
|
||||||
|
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <typename PointType>
|
||||||
|
struct Traits<pcl::PointCloud<PointType>> {
|
||||||
|
using Points = pcl::PointCloud<PointType>;
|
||||||
|
|
||||||
|
static size_t size(const Points& points) { return points.size(); }
|
||||||
|
static void resize(Points& points, size_t n) { points.resize(n); }
|
||||||
|
|
||||||
|
static bool has_points(const Points& points) { return !points.empty(); }
|
||||||
|
static bool has_normals(const Points& points) { return !points.empty(); }
|
||||||
|
static bool has_covs(const Points& points) { return !points.empty(); }
|
||||||
|
|
||||||
|
static void set_point(Points& points, size_t i, const Eigen::Vector4d& pt) { points.at(i).getVector4fMap() = pt.template cast<float>(); }
|
||||||
|
static void set_normal(Points& points, size_t i, const Eigen::Vector4d& pt) { points.at(i).getNormalVector4fMap() = pt.template cast<float>(); }
|
||||||
|
static void set_cov(Points& points, size_t i, const Eigen::Matrix4d& cov) { points.at(i).getCovariance4fMap() = cov.template cast<float>(); }
|
||||||
|
|
||||||
|
static Eigen::Vector4d point(const Points& points, size_t i) { return points.at(i).getVector4fMap().template cast<double>(); }
|
||||||
|
static Eigen::Vector4d normal(const Points& points, size_t i) { return points.at(i).getNormalVector4fMap().template cast<double>(); }
|
||||||
|
static Eigen::Matrix4d cov(const Points& points, size_t i) { return points.at(i).getCovariance4fMap().template cast<double>(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct PointCloud {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<PointCloud>;
|
||||||
|
using ConstPtr = std::shared_ptr<const PointCloud>;
|
||||||
|
|
||||||
|
PointCloud() {}
|
||||||
|
~PointCloud() {}
|
||||||
|
|
||||||
|
template <typename T, int D, typename Allocator>
|
||||||
|
PointCloud(const std::vector<Eigen::Matrix<T, D, 1>, Allocator>& points) {
|
||||||
|
this->resize(points.size());
|
||||||
|
for (size_t i = 0; i < points.size(); i++) {
|
||||||
|
this->point(i) << points[i].template cast<double>().template head<3>(), 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const { return points.size(); }
|
||||||
|
|
||||||
|
void resize(size_t n) {
|
||||||
|
points.resize(n);
|
||||||
|
normals.resize(n);
|
||||||
|
covs.resize(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector4d& point(size_t i) { return points[i]; }
|
||||||
|
Eigen::Vector4d& normal(size_t i) { return normals[i]; }
|
||||||
|
Eigen::Matrix4d& cov(size_t i) { return covs[i]; }
|
||||||
|
const Eigen::Vector4d& point(size_t i) const { return points[i]; }
|
||||||
|
const Eigen::Vector4d& normal(size_t i) const { return normals[i]; }
|
||||||
|
const Eigen::Matrix4d& cov(size_t i) const { return covs[i]; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<Eigen::Vector4d> points;
|
||||||
|
std::vector<Eigen::Vector4d> normals;
|
||||||
|
std::vector<Eigen::Matrix4d> covs;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Traits<PointCloud> {
|
||||||
|
using Points = PointCloud;
|
||||||
|
|
||||||
|
static size_t size(const Points& points) { return points.size(); }
|
||||||
|
|
||||||
|
static bool has_points(const Points& points) { return !points.points.empty(); }
|
||||||
|
static bool has_normals(const Points& points) { return !points.normals.empty(); }
|
||||||
|
static bool has_covs(const Points& points) { return !points.covs.empty(); }
|
||||||
|
|
||||||
|
static const Eigen::Vector4d& point(const Points& points, size_t i) { return points.point(i); }
|
||||||
|
static const Eigen::Vector4d& normal(const Points& points, size_t i) { return points.normal(i); }
|
||||||
|
static const Eigen::Matrix4d& cov(const Points& points, size_t i) { return points.cov(i); }
|
||||||
|
|
||||||
|
static void resize(Points& points, size_t n) { points.resize(n); }
|
||||||
|
static void set_point(Points& points, size_t i, const Eigen::Vector4d& pt) { points.point(i) = pt; }
|
||||||
|
static void set_normal(Points& points, size_t i, const Eigen::Vector4d& n) { points.normal(i) = n; }
|
||||||
|
static void set_cov(Points& points, size_t i, const Eigen::Matrix4d& cov) { points.cov(i) = cov; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Traits;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t size(const T& points) {
|
||||||
|
return Traits<T>::size(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool has_points(const T& points) {
|
||||||
|
return Traits<T>::has_points(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool has_normals(const T& points) {
|
||||||
|
return Traits<T>::has_normals(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool has_covs(const T& points) {
|
||||||
|
return Traits<T>::has_covs(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto point(const T& points, size_t i) {
|
||||||
|
return Traits<T>::point(points, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto normal(const T& points, size_t i) {
|
||||||
|
return Traits<T>::normal(points, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto cov(const T& points, size_t i) {
|
||||||
|
return Traits<T>::cov(points, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void resize(T& points, size_t n) {
|
||||||
|
Traits<T>::resize(points, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_point(T& points, size_t i, const Eigen::Vector4d& pt) {
|
||||||
|
Traits<T>::set_point(points, i, pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_normal(T& points, size_t i, const Eigen::Vector4d& pt) {
|
||||||
|
Traits<T>::set_normal(points, i, pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_cov(T& points, size_t i, const Eigen::Matrix4d& cov) {
|
||||||
|
Traits<T>::set_cov(points, i, cov);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <small_gicp/util/lie.hpp>
|
||||||
|
#include <small_gicp/registration/registration_result.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct GaussNewtonOptimizer {
|
||||||
|
GaussNewtonOptimizer() : verbose(false), max_iterations(20), lambda(1e-6) {}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename TargetPointCloud,
|
||||||
|
typename SourcePointCloud,
|
||||||
|
typename TargetTree,
|
||||||
|
typename CorrespondenceRejector,
|
||||||
|
typename TerminationCriteria,
|
||||||
|
typename Reduction,
|
||||||
|
typename Factor>
|
||||||
|
RegistrationResult optimize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const TerminationCriteria& criteria,
|
||||||
|
Reduction& reduction,
|
||||||
|
const Eigen::Isometry3d& init_T,
|
||||||
|
std::vector<Factor>& factors) {
|
||||||
|
//
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "--- GN optimization ---" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistrationResult result(init_T);
|
||||||
|
for (int i = 0; i < max_iterations && !result.converged; i++) {
|
||||||
|
const auto [H, b, e] = reduction.linearize(target, source, target_tree, rejector, result.T_target_source, factors);
|
||||||
|
const Eigen::Matrix<double, 6, 1> delta = (H + lambda * Eigen ::Matrix<double, 6, 6>::Identity()).ldlt().solve(-b);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "iter=" << i << " e=" << e << " lambda=" << lambda << " dt=" << delta.tail<3>().norm() << " dr=" << delta.head<3>().norm() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.converged = criteria.converged(delta);
|
||||||
|
result.T_target_source = result.T_target_source * se3_exp(delta);
|
||||||
|
result.iterations = i;
|
||||||
|
result.H = H;
|
||||||
|
result.b = b;
|
||||||
|
result.error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.num_inliers = std::ranges::count_if(factors, [](const auto& factor) { return factor.inlier(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verbose;
|
||||||
|
int max_iterations;
|
||||||
|
double lambda;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LevenbergMarquardtOptimizer {
|
||||||
|
LevenbergMarquardtOptimizer() : verbose(false), max_iterations(20), max_inner_iterations(10), init_lambda(1e-3), lambda_factor(10.0) {}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename TargetPointCloud,
|
||||||
|
typename SourcePointCloud,
|
||||||
|
typename TargetTree,
|
||||||
|
typename CorrespondenceRejector,
|
||||||
|
typename TerminationCriteria,
|
||||||
|
typename Reduction,
|
||||||
|
typename Factor>
|
||||||
|
RegistrationResult optimize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const TerminationCriteria& criteria,
|
||||||
|
Reduction& reduction,
|
||||||
|
const Eigen::Isometry3d& init_T,
|
||||||
|
std::vector<Factor>& factors) {
|
||||||
|
//
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "--- LM optimization ---" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
double lambda = init_lambda;
|
||||||
|
RegistrationResult result(init_T);
|
||||||
|
for (int i = 0; i < max_iterations && !result.converged; i++) {
|
||||||
|
const auto [H, b, e] = reduction.linearize(target, source, target_tree, rejector, result.T_target_source, factors);
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
for (int j = 0; j < max_inner_iterations; j++) {
|
||||||
|
const Eigen::Matrix<double, 6, 1> delta = (H + lambda * Eigen ::Matrix<double, 6, 6>::Identity()).ldlt().solve(-b);
|
||||||
|
const Eigen::Isometry3d new_T = result.T_target_source * se3_exp(delta);
|
||||||
|
const double new_e = reduction.error(target, source, new_T, factors);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "iter=" << i << " inner=" << j << " e=" << e << " new_e=" << new_e << " lambda=" << lambda << " dt=" << delta.tail<3>().norm()
|
||||||
|
<< " dr=" << delta.head<3>().norm() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_e < e) {
|
||||||
|
result.converged = criteria.converged(delta);
|
||||||
|
result.T_target_source = new_T;
|
||||||
|
lambda /= lambda_factor;
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
lambda *= lambda_factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.iterations = i;
|
||||||
|
result.H = H;
|
||||||
|
result.b = b;
|
||||||
|
result.error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.num_inliers = std::ranges::count_if(factors, [](const auto& factor) { return factor.inlier(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verbose;
|
||||||
|
int max_iterations;
|
||||||
|
int max_inner_iterations;
|
||||||
|
double init_lambda;
|
||||||
|
double lambda_factor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct SerialReduction {
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector, typename Factor>
|
||||||
|
std::tuple<Eigen::Matrix<double, 6, 6>, Eigen::Matrix<double, 6, 1>, double> linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
std::vector<Factor>& factors) {
|
||||||
|
Eigen::Matrix<double, 6, 6> sum_H = Eigen::Matrix<double, 6, 6>::Zero();
|
||||||
|
Eigen::Matrix<double, 6, 1> sum_b = Eigen::Matrix<double, 6, 1>::Zero();
|
||||||
|
double sum_e = 0.0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < factors.size(); i++) {
|
||||||
|
Eigen::Matrix<double, 6, 6> H;
|
||||||
|
Eigen::Matrix<double, 6, 1> b;
|
||||||
|
double e;
|
||||||
|
|
||||||
|
if (!factors[i].linearize(target, source, target_tree, T, i, rejector, &H, &b, &e)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_H += H;
|
||||||
|
sum_b += b;
|
||||||
|
sum_e += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {sum_H, sum_b, sum_e};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename Factor>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T, std::vector<Factor>& factors) {
|
||||||
|
double sum_e = 0.0;
|
||||||
|
for (size_t i = 0; i < factors.size(); i++) {
|
||||||
|
sum_e += factors[i].error(target, source, T);
|
||||||
|
}
|
||||||
|
return sum_e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct ParallelReductionOMP {
|
||||||
|
ParallelReductionOMP() : num_threads(8) {}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector, typename Factor>
|
||||||
|
std::tuple<Eigen::Matrix<double, 6, 6>, Eigen::Matrix<double, 6, 1>, double> linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
std::vector<Factor>& factors) {
|
||||||
|
std::vector<Eigen::Matrix<double, 6, 6>> Hs(num_threads, Eigen::Matrix<double, 6, 6>::Zero());
|
||||||
|
std::vector<Eigen::Matrix<double, 6, 1>> bs(num_threads, Eigen::Matrix<double, 6, 1>::Zero());
|
||||||
|
std::vector<double> es(num_threads, 0.0);
|
||||||
|
|
||||||
|
#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
|
||||||
|
for (size_t i = 0; i < factors.size(); i++) {
|
||||||
|
Eigen::Matrix<double, 6, 6> H;
|
||||||
|
Eigen::Matrix<double, 6, 1> b;
|
||||||
|
double e;
|
||||||
|
|
||||||
|
if (!factors[i].linearize(target, source, target_tree, T, i, rejector, &H, &b, &e)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int thread_id = omp_get_thread_num();
|
||||||
|
Hs[thread_id] += H;
|
||||||
|
bs[thread_id] += b;
|
||||||
|
es[thread_id] += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < num_threads; i++) {
|
||||||
|
Hs[0] += Hs[i];
|
||||||
|
bs[0] += bs[i];
|
||||||
|
es[0] += es[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {Hs[0], bs[0], es[0]};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename Factor>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T, std::vector<Factor>& factors) {
|
||||||
|
double sum_e = 0.0;
|
||||||
|
|
||||||
|
#pragma omp parallel for num_threads(num_threads) schedule(guided, 8) reduction(+ : sum_e)
|
||||||
|
for (size_t i = 0; i < factors.size(); i++) {
|
||||||
|
sum_e += factors[i].error(target, source, T);
|
||||||
|
}
|
||||||
|
return sum_e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_threads;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tbb/tbb.h>
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector, typename Factor>
|
||||||
|
struct LinearizeSum {
|
||||||
|
LinearizeSum(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
std::vector<Factor>& factors)
|
||||||
|
: target(target),
|
||||||
|
source(source),
|
||||||
|
target_tree(target_tree),
|
||||||
|
rejector(rejector),
|
||||||
|
T(T),
|
||||||
|
factors(factors),
|
||||||
|
H(Eigen::Matrix<double, 6, 6>::Zero()),
|
||||||
|
b(Eigen::Matrix<double, 6, 1>::Zero()),
|
||||||
|
e(0.0) {}
|
||||||
|
|
||||||
|
LinearizeSum(LinearizeSum& x, tbb::split)
|
||||||
|
: target(x.target),
|
||||||
|
source(x.source),
|
||||||
|
target_tree(x.target_tree),
|
||||||
|
rejector(x.rejector),
|
||||||
|
T(x.T),
|
||||||
|
factors(x.factors),
|
||||||
|
H(Eigen::Matrix<double, 6, 6>::Zero()),
|
||||||
|
b(Eigen::Matrix<double, 6, 1>::Zero()),
|
||||||
|
e(0.0) {}
|
||||||
|
|
||||||
|
void operator()(const tbb::blocked_range<size_t>& r) {
|
||||||
|
Eigen::Matrix<double, 6, 6> Ht = H;
|
||||||
|
Eigen::Matrix<double, 6, 1> bt = b;
|
||||||
|
double et = e;
|
||||||
|
|
||||||
|
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||||
|
Eigen::Matrix<double, 6, 6> Hi;
|
||||||
|
Eigen::Matrix<double, 6, 1> bi;
|
||||||
|
double ei;
|
||||||
|
|
||||||
|
if (!factors[i].linearize(target, source, target_tree, T, i, rejector, &Hi, &bi, &ei)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ht += Hi;
|
||||||
|
bt += bi;
|
||||||
|
et += ei;
|
||||||
|
}
|
||||||
|
|
||||||
|
H = Ht;
|
||||||
|
b = bt;
|
||||||
|
e = et;
|
||||||
|
}
|
||||||
|
|
||||||
|
void join(const LinearizeSum& y) {
|
||||||
|
H += y.H;
|
||||||
|
b += y.b;
|
||||||
|
e += y.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TargetPointCloud& target;
|
||||||
|
const SourcePointCloud& source;
|
||||||
|
const TargetTree& target_tree;
|
||||||
|
const CorrespondenceRejector& rejector;
|
||||||
|
const Eigen::Isometry3d& T;
|
||||||
|
std::vector<Factor>& factors;
|
||||||
|
|
||||||
|
Eigen::Matrix<double, 6, 6> H;
|
||||||
|
Eigen::Matrix<double, 6, 1> b;
|
||||||
|
double e;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename Factor>
|
||||||
|
struct ErrorSum {
|
||||||
|
ErrorSum(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T, std::vector<Factor>& factors)
|
||||||
|
: target(target),
|
||||||
|
source(source),
|
||||||
|
T(T),
|
||||||
|
factors(factors),
|
||||||
|
e(0.0) {}
|
||||||
|
|
||||||
|
ErrorSum(ErrorSum& x, tbb::split) : target(x.target), source(x.source), T(x.T), factors(x.factors), e(0.0) {}
|
||||||
|
|
||||||
|
void operator()(const tbb::blocked_range<size_t>& r) {
|
||||||
|
double et = e;
|
||||||
|
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||||
|
et += factors[i].error(target, source, T);
|
||||||
|
}
|
||||||
|
e = et;
|
||||||
|
}
|
||||||
|
|
||||||
|
void join(const ErrorSum& y) { e += y.e; }
|
||||||
|
|
||||||
|
const TargetPointCloud& target;
|
||||||
|
const SourcePointCloud& source;
|
||||||
|
const Eigen::Isometry3d& T;
|
||||||
|
std::vector<Factor>& factors;
|
||||||
|
|
||||||
|
double e;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParallelReductionTBB {
|
||||||
|
ParallelReductionTBB() {}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename CorrespondenceRejector, typename Factor>
|
||||||
|
std::tuple<Eigen::Matrix<double, 6, 6>, Eigen::Matrix<double, 6, 1>, double> linearize(
|
||||||
|
const TargetPointCloud& target,
|
||||||
|
const SourcePointCloud& source,
|
||||||
|
const TargetTree& target_tree,
|
||||||
|
const CorrespondenceRejector& rejector,
|
||||||
|
const Eigen::Isometry3d& T,
|
||||||
|
std::vector<Factor>& factors) {
|
||||||
|
//
|
||||||
|
LinearizeSum<TargetPointCloud, SourcePointCloud, TargetTree, CorrespondenceRejector, Factor> sum(target, source, target_tree, rejector, T, factors);
|
||||||
|
|
||||||
|
tbb::parallel_reduce(tbb::blocked_range<size_t>(0, factors.size()), sum);
|
||||||
|
|
||||||
|
return {sum.H, sum.b, sum.e};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename Factor>
|
||||||
|
double error(const TargetPointCloud& target, const SourcePointCloud& source, const Eigen::Isometry3d& T, std::vector<Factor>& factors) {
|
||||||
|
ErrorSum<TargetPointCloud, SourcePointCloud, Factor> sum(target, source, T, factors);
|
||||||
|
tbb::parallel_reduce(tbb::blocked_range<size_t>(0, factors.size()), sum);
|
||||||
|
return sum.e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <small_gicp/ann/traits.hpp>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
#include <small_gicp/registration/rejector.hpp>
|
||||||
|
#include <small_gicp/registration/reduction.hpp>
|
||||||
|
#include <small_gicp/registration/registration_result.hpp>
|
||||||
|
#include <small_gicp/registration/optimizer.hpp>
|
||||||
|
|
||||||
|
#include <guik/viewer/light_viewer.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct TerminationCriteria {
|
||||||
|
TerminationCriteria() : translation_eps(1e-3), rotation_eps(0.1 * M_PI / 180.0) {}
|
||||||
|
|
||||||
|
bool converged(const Eigen::Matrix<double, 6, 1>& delta) const { return delta.template head<3>().norm() < rotation_eps && delta.template tail<3>().norm() < translation_eps; }
|
||||||
|
|
||||||
|
double translation_eps;
|
||||||
|
double rotation_eps;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TargetPointCloud, typename SourcePointCloud, typename TargetTree, typename Factor, typename CorrespondenceRejector, typename Reduction, typename Optimizer>
|
||||||
|
struct Registration {
|
||||||
|
public:
|
||||||
|
RegistrationResult align(const TargetPointCloud& target, const SourcePointCloud& source, const TargetTree& target_tree, const Eigen::Isometry3d& init_T) {
|
||||||
|
std::vector<Factor> factors(traits::size(source));
|
||||||
|
return optimizer.optimize(target, source, target_tree, rejector, criteria, reduction, init_T, factors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TerminationCriteria criteria;
|
||||||
|
CorrespondenceRejector rejector;
|
||||||
|
Reduction reduction;
|
||||||
|
Optimizer optimizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct RegistrationResult {
|
||||||
|
RegistrationResult(const Eigen::Isometry3d& T)
|
||||||
|
: T_target_source(T),
|
||||||
|
converged(false),
|
||||||
|
iterations(0),
|
||||||
|
num_inliers(0),
|
||||||
|
H(Eigen::Matrix<double, 6, 6>::Zero()),
|
||||||
|
b(Eigen::Matrix<double, 6, 1>::Zero()),
|
||||||
|
error(0.0) {}
|
||||||
|
|
||||||
|
Eigen::Isometry3d T_target_source;
|
||||||
|
|
||||||
|
bool converged;
|
||||||
|
size_t iterations;
|
||||||
|
size_t num_inliers;
|
||||||
|
|
||||||
|
Eigen::Matrix<double, 6, 6> H;
|
||||||
|
Eigen::Matrix<double, 6, 1> b;
|
||||||
|
double error;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
struct NullRejector {
|
||||||
|
bool operator()(const Eigen::Isometry3d& T, size_t target_index, size_t source_index, double sq_dist) const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DistanceRejector {
|
||||||
|
DistanceRejector() : max_dist_sq(1.0) {}
|
||||||
|
DistanceRejector(double max_dist) : max_dist_sq(max_dist * max_dist) {}
|
||||||
|
|
||||||
|
bool operator()(const Eigen::Isometry3d& T, size_t target_index, size_t source_index, double sq_dist) const { return sq_dist > max_dist_sq; }
|
||||||
|
|
||||||
|
double max_dist_sq;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <random>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
#include <small_gicp/util/vector3i_hash.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename InputPointCloud, typename OutputPointCloud = InputPointCloud>
|
||||||
|
std::shared_ptr<OutputPointCloud> voxelgrid_sampling(const InputPointCloud& points, double leaf_size) {
|
||||||
|
const double inv_leaf_size = 1.0 / leaf_size;
|
||||||
|
|
||||||
|
std::unordered_map<Eigen::Vector3i, Eigen::Vector4d, XORVector3iHash> voxels;
|
||||||
|
for (size_t i = 0; i < traits::size(points); i++) {
|
||||||
|
const auto& pt = traits::point(points, i);
|
||||||
|
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().template cast<int>().template head<3>();
|
||||||
|
auto found = voxels.find(coord);
|
||||||
|
if (found == voxels.end()) {
|
||||||
|
found = voxels.emplace_hint(found, coord, Eigen::Vector4d::Zero());
|
||||||
|
}
|
||||||
|
found->second += pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto downsampled = std::make_shared<OutputPointCloud>();
|
||||||
|
traits::resize(*downsampled, voxels.size());
|
||||||
|
size_t i = 0;
|
||||||
|
for (const auto& v : voxels) {
|
||||||
|
traits::set_point(*downsampled, i++, v.second / v.second.w());
|
||||||
|
}
|
||||||
|
|
||||||
|
return downsampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputPointCloud, typename OutputPointCloud = InputPointCloud>
|
||||||
|
std::shared_ptr<OutputPointCloud> randomgrid_sampling(const InputPointCloud& points, double leaf_size, size_t target_num_points, std::mt19937& rng) {
|
||||||
|
if (traits::size(points) <= target_num_points) {
|
||||||
|
auto downsampled = std::make_shared<OutputPointCloud>();
|
||||||
|
traits::resize(*downsampled, traits::size(points));
|
||||||
|
for (size_t i = 0; i < traits::size(points); i++) {
|
||||||
|
traits::set_point(*downsampled, i, traits::point(points, i));
|
||||||
|
}
|
||||||
|
return downsampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double inv_leaf_size = 1.0 / leaf_size;
|
||||||
|
|
||||||
|
using Indices = std::shared_ptr<std::vector<size_t>>;
|
||||||
|
std::unordered_map<Eigen::Vector3i, Indices, XORVector3iHash> voxels;
|
||||||
|
for (size_t i = 0; i < traits::size(points); i++) {
|
||||||
|
const auto& pt = traits::point(points, i);
|
||||||
|
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().template cast<int>().template head<3>();
|
||||||
|
auto found = voxels.find(coord);
|
||||||
|
if (found == voxels.end()) {
|
||||||
|
found = voxels.emplace_hint(found, coord, std::make_shared<std::vector<size_t>>());
|
||||||
|
found->second->reserve(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
found->second->emplace_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t points_per_voxel = std::ceil(static_cast<double>(target_num_points) / voxels.size());
|
||||||
|
|
||||||
|
std::vector<size_t> indices;
|
||||||
|
indices.reserve(points_per_voxel * voxels.size());
|
||||||
|
|
||||||
|
for (const auto& voxel : voxels) {
|
||||||
|
const auto& voxel_indices = *voxel.second;
|
||||||
|
if (voxel_indices.size() <= points_per_voxel) {
|
||||||
|
indices.insert(indices.end(), voxel_indices.begin(), voxel_indices.end());
|
||||||
|
} else {
|
||||||
|
std::ranges::sample(voxel_indices, std::back_inserter(indices), points_per_voxel, rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::ranges::sort(indices);
|
||||||
|
|
||||||
|
auto downsampled = std::make_shared<OutputPointCloud>();
|
||||||
|
traits::resize(*downsampled, indices.size());
|
||||||
|
for (size_t i = 0; i < indices.size(); i++) {
|
||||||
|
traits::set_point(*downsampled, i, traits::point(points, indices[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return downsampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <tbb/tbb.h>
|
||||||
|
#include <small_gicp/points/traits.hpp>
|
||||||
|
#include <small_gicp/util/vector3i_hash.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Voxel grid downsampling using TBB.
|
||||||
|
* @note This TBB version brings only a minor speedup compared to the single-thread version (e.g., 32-threads -> 1.4x speedup), and is not worth using usually.
|
||||||
|
*/
|
||||||
|
template <typename InputPointCloud, typename OutputPointCloud = InputPointCloud>
|
||||||
|
std::shared_ptr<OutputPointCloud> voxelgrid_sampling_tbb(const InputPointCloud& points, double leaf_size) {
|
||||||
|
const double inv_leaf_size = 1.0 / leaf_size;
|
||||||
|
|
||||||
|
typedef tbb::concurrent_hash_map<Eigen::Vector3i, int, XORVector3iHash> VoxelMap;
|
||||||
|
|
||||||
|
std::atomic_uint64_t num_voxels = 0;
|
||||||
|
VoxelMap voxels;
|
||||||
|
std::vector<Eigen::Vector4d> voxel_values(traits::size(points), Eigen::Vector4d::Zero());
|
||||||
|
|
||||||
|
const int chunk_size = 8;
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, traits::size(points), chunk_size), [&](const tbb::blocked_range<size_t>& range) {
|
||||||
|
std::vector<Eigen::Vector3i> coords;
|
||||||
|
std::vector<Eigen::Vector4d> values;
|
||||||
|
coords.reserve(range.size());
|
||||||
|
values.reserve(range.size());
|
||||||
|
|
||||||
|
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||||
|
const Eigen::Vector4d pt = traits::point(points, i);
|
||||||
|
const Eigen::Vector3i coord = (pt * inv_leaf_size).array().floor().template cast<int>().template head<3>();
|
||||||
|
|
||||||
|
auto found = std::ranges::find(coords, coord);
|
||||||
|
if (found == coords.end()) {
|
||||||
|
coords.emplace_back(coord);
|
||||||
|
values.emplace_back(pt);
|
||||||
|
} else {
|
||||||
|
values[std::distance(coords.begin(), found)] += pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < coords.size(); i++) {
|
||||||
|
VoxelMap::accessor a;
|
||||||
|
if (voxels.insert(a, coords[i])) {
|
||||||
|
a->second = num_voxels++;
|
||||||
|
voxel_values[a->second] = values[i];
|
||||||
|
} else {
|
||||||
|
voxel_values[a->second] += values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const int N = num_voxels;
|
||||||
|
auto downsampled = std::make_shared<OutputPointCloud>();
|
||||||
|
traits::resize(*downsampled, N);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N; i++) {
|
||||||
|
const Eigen::Vector4d pt = voxel_values[i];
|
||||||
|
traits::set_point(*downsampled, i, pt / pt.w());
|
||||||
|
}
|
||||||
|
|
||||||
|
return downsampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
inline Eigen::Matrix3d skew(const Eigen::Vector3d& x) {
|
||||||
|
Eigen::Matrix3d skew = Eigen::Matrix3d::Zero();
|
||||||
|
skew(0, 1) = -x[2];
|
||||||
|
skew(0, 2) = x[1];
|
||||||
|
skew(1, 0) = x[2];
|
||||||
|
skew(1, 2) = -x[0];
|
||||||
|
skew(2, 0) = -x[1];
|
||||||
|
skew(2, 1) = x[0];
|
||||||
|
|
||||||
|
return skew;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SO3 expmap code taken from Sophus
|
||||||
|
* https://github.com/strasdat/Sophus/blob/593db47500ea1a2de5f0e6579c86147991509c59/sophus/so3.hpp#L585
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Hauke Strasdat
|
||||||
|
* 2012-2017 Steven Lovegrove
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
inline Eigen::Quaterniond so3_exp(const Eigen::Vector3d& omega) {
|
||||||
|
double theta_sq = omega.dot(omega);
|
||||||
|
|
||||||
|
double theta;
|
||||||
|
double imag_factor;
|
||||||
|
double real_factor;
|
||||||
|
if (theta_sq < 1e-10) {
|
||||||
|
theta = 0;
|
||||||
|
double theta_quad = theta_sq * theta_sq;
|
||||||
|
imag_factor = 0.5 - 1.0 / 48.0 * theta_sq + 1.0 / 3840.0 * theta_quad;
|
||||||
|
real_factor = 1.0 - 1.0 / 8.0 * theta_sq + 1.0 / 384.0 * theta_quad;
|
||||||
|
} else {
|
||||||
|
theta = std::sqrt(theta_sq);
|
||||||
|
double half_theta = 0.5 * theta;
|
||||||
|
imag_factor = std::sin(half_theta) / theta;
|
||||||
|
real_factor = std::cos(half_theta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Eigen::Quaterniond(real_factor, imag_factor * omega.x(), imag_factor * omega.y(), imag_factor * omega.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation-first
|
||||||
|
inline Eigen::Isometry3d se3_exp(const Eigen::Matrix<double, 6, 1>& a) {
|
||||||
|
using std::cos;
|
||||||
|
using std::sin;
|
||||||
|
const Eigen::Vector3d omega = a.head<3>();
|
||||||
|
|
||||||
|
double theta = std::sqrt(omega.dot(omega));
|
||||||
|
const Eigen::Quaterniond so3 = so3_exp(omega);
|
||||||
|
const Eigen::Matrix3d Omega = skew(omega);
|
||||||
|
const Eigen::Matrix3d Omega_sq = Omega * Omega;
|
||||||
|
Eigen::Matrix3d V;
|
||||||
|
|
||||||
|
if (theta < 1e-10) {
|
||||||
|
V = so3.matrix();
|
||||||
|
/// Note: That is an accurate expansion!
|
||||||
|
} else {
|
||||||
|
const double theta_sq = theta * theta;
|
||||||
|
V = (Eigen::Matrix3d::Identity() + (1.0 - cos(theta)) / (theta_sq)*Omega + (theta - sin(theta)) / (theta_sq * theta) * Omega_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Isometry3d se3 = Eigen::Isometry3d::Identity();
|
||||||
|
se3.linear() = so3.toRotationMatrix();
|
||||||
|
se3.translation() = V * a.tail<3>();
|
||||||
|
|
||||||
|
return se3;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Eigen>
|
||||||
|
#include <small_gicp/ann/kdtree.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct NormalSetter {
|
||||||
|
static void set_invalid(PointCloud& cloud, size_t i) { traits::set_normal(cloud, i, Eigen::Vector4d::Zero()); }
|
||||||
|
|
||||||
|
static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
|
||||||
|
const Eigen::Vector4d normal = (Eigen::Vector4d() << eigenvectors.col(0).normalized(), 0.0).finished();
|
||||||
|
if (traits::point(cloud, i).dot(normal) > 0) {
|
||||||
|
traits::set_normal(cloud, i, -normal);
|
||||||
|
} else {
|
||||||
|
traits::set_normal(cloud, i, normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct CovarianceSetter {
|
||||||
|
static void set_invalid(PointCloud& cloud, size_t i) {
|
||||||
|
Eigen::Matrix4d cov = Eigen::Matrix4d::Identity();
|
||||||
|
cov(3, 3) = 0.0;
|
||||||
|
traits::set_cov(cloud, i, cov);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
|
||||||
|
const Eigen::Vector3d values(1e-3, 1.0, 1.0);
|
||||||
|
Eigen::Matrix4d cov = Eigen::Matrix4d::Zero();
|
||||||
|
cov.block<3, 3>(0, 0) = eigenvectors * values.asDiagonal() * eigenvectors.transpose();
|
||||||
|
traits::set_cov(cloud, i, cov);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
struct NormalCovarianceSetter {
|
||||||
|
static void set_invalid(PointCloud& cloud, size_t i) {
|
||||||
|
NormalSetter<PointCloud>::set_invalid(cloud, i);
|
||||||
|
CovarianceSetter<PointCloud>::set_invalid(cloud, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
|
||||||
|
NormalSetter<PointCloud>::set(cloud, i, eigenvectors);
|
||||||
|
CovarianceSetter<PointCloud>::set(cloud, i, eigenvectors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PointCloud, typename Tree, typename Setter>
|
||||||
|
void estimate_local_features(PointCloud& cloud, Tree& kdtree, int num_neighbors, size_t point_index) {
|
||||||
|
std::vector<size_t> k_indices(num_neighbors);
|
||||||
|
std::vector<double> k_sq_dists(num_neighbors);
|
||||||
|
const size_t n = kdtree.knn_search(traits::point(cloud, point_index), num_neighbors, k_indices.data(), k_sq_dists.data());
|
||||||
|
|
||||||
|
if (n < 5) {
|
||||||
|
// Insufficient number of neighbors
|
||||||
|
Setter::set_invalid(cloud, point_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector4d sum_points = Eigen::Vector4d::Zero();
|
||||||
|
Eigen::Matrix4d sum_cross = Eigen::Matrix4d::Zero();
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
const auto& pt = traits::point(cloud, k_indices[i]);
|
||||||
|
sum_points += pt;
|
||||||
|
sum_cross += pt * pt.transpose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector4d mean = sum_points / n;
|
||||||
|
const Eigen::Matrix4d cov = (sum_cross - mean * sum_points.transpose()) / n;
|
||||||
|
|
||||||
|
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig;
|
||||||
|
eig.computeDirect(cov.block<3, 3>(0, 0));
|
||||||
|
|
||||||
|
Setter::set(cloud, point_index, eig.eigenvectors());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud, typename Setter>
|
||||||
|
void estimate_local_features(PointCloud& cloud, int num_neighbors) {
|
||||||
|
traits::resize(cloud, traits::size(cloud));
|
||||||
|
|
||||||
|
KdTree<PointCloud> kdtree(cloud);
|
||||||
|
for (size_t i = 0; i < traits::size(cloud); i++) {
|
||||||
|
estimate_local_features<PointCloud, KdTree<PointCloud>, Setter>(cloud, kdtree, num_neighbors, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features<PointCloud, NormalSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_covariances(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features<PointCloud, CovarianceSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals_covariances(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features<PointCloud, NormalCovarianceSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <small_gicp/util/normal_estimation.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename PointCloud, typename Setter>
|
||||||
|
void estimate_local_features_omp(PointCloud& cloud, int num_neighbors, int num_threads) {
|
||||||
|
traits::resize(cloud, traits::size(cloud));
|
||||||
|
|
||||||
|
UnsafeKdTree<PointCloud> kdtree(cloud);
|
||||||
|
|
||||||
|
#pragma omp parallel for num_threads(num_threads)
|
||||||
|
for (size_t i = 0; i < traits::size(cloud); i++) {
|
||||||
|
estimate_local_features<PointCloud, UnsafeKdTree<PointCloud>, Setter>(cloud, kdtree, num_neighbors, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals_omp(PointCloud& cloud, int num_neighbors = 20, int num_threads = 4) {
|
||||||
|
estimate_local_features_omp<PointCloud, NormalSetter<PointCloud>>(cloud, num_neighbors, num_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_covariances_omp(PointCloud& cloud, int num_neighbors = 20, int num_threads = 4) {
|
||||||
|
estimate_local_features_omp<PointCloud, CovarianceSetter<PointCloud>>(cloud, num_neighbors, num_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals_covariances_omp(PointCloud& cloud, int num_neighbors = 20, int num_threads = 4) {
|
||||||
|
estimate_local_features_omp<PointCloud, NormalCovarianceSetter<PointCloud>>(cloud, num_neighbors, num_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tbb/tbb.h>
|
||||||
|
#include <small_gicp/util/normal_estimation.hpp>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
template <typename PointCloud, typename Setter>
|
||||||
|
void estimate_local_features_tbb(PointCloud& cloud, int num_neighbors) {
|
||||||
|
traits::resize(cloud, traits::size(cloud));
|
||||||
|
|
||||||
|
UnsafeKdTree<PointCloud> kdtree(cloud);
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, traits::size(cloud)), [&](const tbb::blocked_range<size_t>& range) {
|
||||||
|
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||||
|
estimate_local_features<PointCloud, UnsafeKdTree<PointCloud>, Setter>(cloud, kdtree, num_neighbors, i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals_tbb(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features_tbb<PointCloud, NormalSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_covariances_tbb(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features_tbb<PointCloud, CovarianceSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PointCloud>
|
||||||
|
void estimate_normals_covariances_tbb(PointCloud& cloud, int num_neighbors = 20) {
|
||||||
|
estimate_local_features_tbb<PointCloud, NormalCovarianceSetter<PointCloud>>(cloud, num_neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
static std::vector<Eigen::Vector4f> read_points(const std::string& filename) {
|
||||||
|
std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!ifs) {
|
||||||
|
std::cerr << "error: failed to open " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize points_bytes = ifs.tellg();
|
||||||
|
size_t num_points = points_bytes / (sizeof(Eigen::Vector4f));
|
||||||
|
|
||||||
|
ifs.seekg(0, std::ios::beg);
|
||||||
|
std::vector<Eigen::Vector4f> points(num_points);
|
||||||
|
ifs.read(reinterpret_cast<char*>(points.data()), sizeof(Eigen::Vector4f) * num_points);
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
namespace small_gicp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Spatial hashing function
|
||||||
|
* Teschner et al., "Optimized Spatial Hashing for Collision Detection of Deformable Objects", VMV2003
|
||||||
|
*/
|
||||||
|
struct XORVector3iHash {
|
||||||
|
public:
|
||||||
|
size_t operator()(const Eigen::Vector3i& x) const {
|
||||||
|
const size_t p1 = 73856093;
|
||||||
|
const size_t p2 = 19349669; // 19349663 was not a prime number
|
||||||
|
const size_t p3 = 83492791;
|
||||||
|
return static_cast<size_t>((x[0] * p1) ^ (x[1] * p2) ^ (x[2] * p3));
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t hash(const Eigen::Vector3i& x) { return XORVector3iHash()(x); }
|
||||||
|
static bool equal(const Eigen::Vector3i& x1, const Eigen::Vector3i& x2) { return x1 == x2; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace small_gicp
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <small_gicp/util/read_points.hpp>
|
||||||
|
|
||||||
|
#include <small_gicp/points/pcl_point.hpp>
|
||||||
|
#include <small_gicp/points/pcl_point_traits.hpp>
|
||||||
|
#include <small_gicp/points/point_cloud.hpp>
|
||||||
|
#include <small_gicp/points/gaussian_voxelmap.hpp>
|
||||||
|
#include <small_gicp/ann/kdtree.hpp>
|
||||||
|
#include <small_gicp/ann/cached_kdtree.hpp>
|
||||||
|
#include <small_gicp/util/downsampling.hpp>
|
||||||
|
#include <small_gicp/util/downsampling_tbb.hpp>
|
||||||
|
#include <small_gicp/util/normal_estimation_omp.hpp>
|
||||||
|
#include <small_gicp/util/normal_estimation_tbb.hpp>
|
||||||
|
#include <small_gicp/registration/registration.hpp>
|
||||||
|
#include <small_gicp/registration/reduction_omp.hpp>
|
||||||
|
#include <small_gicp/registration/reduction_tbb.hpp>
|
||||||
|
#include <small_gicp/factors/icp_factor.hpp>
|
||||||
|
#include <small_gicp/factors/gicp_factor.hpp>
|
||||||
|
#include <small_gicp/factors/plane_icp_factor.hpp>
|
||||||
|
|
||||||
|
#include <glk/io/ply_io.hpp>
|
||||||
|
#include <glk/pointcloud_buffer_pcl.hpp>
|
||||||
|
#include <glk/normal_distributions.hpp>
|
||||||
|
#include <guik/viewer/light_viewer.hpp>
|
||||||
|
|
||||||
|
#include <easy_profiler.hpp>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
using namespace small_gicp;
|
||||||
|
|
||||||
|
const int num_threads = 32;
|
||||||
|
tbb::task_scheduler_init init(num_threads);
|
||||||
|
|
||||||
|
const std::string dataset_path = "/home/koide/datasets/kitti/velodyne_filtered";
|
||||||
|
std::vector<std::string> filenames;
|
||||||
|
for (const auto& path : std::filesystem::directory_iterator(dataset_path)) {
|
||||||
|
if (path.path().extension() != ".bin") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames.emplace_back(path.path().string());
|
||||||
|
}
|
||||||
|
std::ranges::sort(filenames);
|
||||||
|
|
||||||
|
auto viewer = guik::viewer();
|
||||||
|
viewer->disable_vsync();
|
||||||
|
|
||||||
|
GaussianVoxelMap::Ptr voxelmap;
|
||||||
|
|
||||||
|
Eigen::Isometry3d T = Eigen::Isometry3d::Identity();
|
||||||
|
for (const auto& filename : filenames) {
|
||||||
|
glim::EasyProfiler prof("prof");
|
||||||
|
prof.push("read_points");
|
||||||
|
const auto raw_points = read_points(filename);
|
||||||
|
|
||||||
|
prof.push("copy");
|
||||||
|
auto points = std::make_shared<PointCloud>(raw_points);
|
||||||
|
prof.push("downsample");
|
||||||
|
points = voxelgrid_sampling_tbb(*points, 0.1);
|
||||||
|
prof.push("estimate covs");
|
||||||
|
estimate_covariances_tbb(*points, 10);
|
||||||
|
std::cout << raw_points.size() << " => " << points->size() << std::endl;
|
||||||
|
|
||||||
|
if (voxelmap == nullptr) {
|
||||||
|
voxelmap = std::make_shared<GaussianVoxelMap>(1.0);
|
||||||
|
voxelmap->insert(*points, T);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
prof.push("create_tbb");
|
||||||
|
Registration<GaussianVoxelMap, PointCloud, GaussianVoxelMap, GICPFactor, NullRejector, ParallelReductionTBB, LevenbergMarquardtOptimizer> registration_tbb;
|
||||||
|
registration_tbb.optimizer.verbose = true;
|
||||||
|
prof.push("registration_tbb");
|
||||||
|
auto result = registration_tbb.align(*voxelmap, *points, *voxelmap, T);
|
||||||
|
|
||||||
|
prof.push("update");
|
||||||
|
T = result.T_target_source;
|
||||||
|
voxelmap->insert(*points, T);
|
||||||
|
|
||||||
|
prof.push("show");
|
||||||
|
viewer->update_points("current", points->points, guik::FlatOrange(T).set_point_scale(2.0f));
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector4d> means;
|
||||||
|
std::vector<Eigen::Matrix4d> covs;
|
||||||
|
for (const auto& voxel : voxelmap->flat_voxels) {
|
||||||
|
means.emplace_back(voxel.mean);
|
||||||
|
covs.emplace_back(voxel.cov);
|
||||||
|
}
|
||||||
|
viewer->update_normal_dists("target", means, covs, 0.5, guik::Rainbow());
|
||||||
|
|
||||||
|
if (!viewer->spin_once()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue