Skip to content
Merged
Show file tree
Hide file tree
Changes from 100 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
73cf239
Add Generalized ICP draft
nachovizzo Oct 13, 2020
614af95
Add weighting function for the per-row version of ComputeJTJandJTr.
nachovizzo Oct 16, 2020
76fe599
wip
nachovizzo Oct 16, 2020
2e6ee5d
Add weighting function for the per-row version of ComputeJTJandJTr.
nachovizzo Oct 16, 2020
5cf368c
Add weight vector to match ComputeJTJandJr new API.
nachovizzo Oct 16, 2020
c25e8d4
Expose weight parameter for ColorMapOptimizationJacobian
nachovizzo Oct 16, 2020
04a0653
Finish ColorizeICP re-binding in python
nachovizzo Oct 16, 2020
6ab6709
Move private function to unnamed namespace
nachovizzo Oct 16, 2020
0c9bcfc
Update python examples
nachovizzo Oct 16, 2020
974ddcc
Fix generic->geomtric
nachovizzo Oct 16, 2020
2d14b8f
Merge branch 'nacho/robust_kernel_colorized_icp' into nacho/generaliz…
nachovizzo Oct 16, 2020
bbe608b
Finish draft of the optimization pipeline
nachovizzo Oct 16, 2020
9351d59
Change naming
nachovizzo Oct 18, 2020
a5afa48
Fix tests.
nachovizzo Oct 18, 2020
f9ed528
Add Python bindings for Genrealized ICP
nachovizzo Oct 18, 2020
377c96d
Add draft for ComputeRMSE
nachovizzo Oct 18, 2020
eb06b96
Fix unit tests
nachovizzo Oct 19, 2020
a96a75d
Fix GenerelaizedICP draft
nachovizzo Oct 19, 2020
c15d1c5
Log also correspondence size for each ICP iteration
nachovizzo Oct 19, 2020
49a3415
Fix optimizaiton calculation
nachovizzo Oct 19, 2020
e987a17
Attempt to fix optimization problem
nachovizzo Oct 19, 2020
f284d71
Fix source/target swap.
nachovizzo Oct 19, 2020
8d26519
One step closer to gicp
nachovizzo Oct 19, 2020
b3a8b5c
Complete copy-paste from Registartion.cpp
nachovizzo Oct 19, 2020
d34d3e1
Finish gicp implentation (hopefully)
nachovizzo Oct 19, 2020
acccff7
Turn gicp into an ugly polymorphic code
nachovizzo Oct 19, 2020
e49c5e7
Use existing normal in the PointCloud instead of re-compute
nachovizzo Oct 19, 2020
37396c1
Fix stupid bug
nachovizzo Oct 19, 2020
df2c7f0
Add small chec
nachovizzo Oct 19, 2020
880a229
fix
nachovizzo Oct 19, 2020
b6587c1
Another huge bugfix. M -> M-1
nachovizzo Oct 19, 2020
8d5a2ee
last bugfix of the night
nachovizzo Oct 19, 2020
4eaf6d4
Another bugfix. I can now obtain the same results as the original
nachovizzo Oct 19, 2020
e81d802
Don't apply incremental rotation to Source Covariance Matrix.
nachovizzo Oct 20, 2020
a0def56
Compute source covariance for each iteration.
nachovizzo Oct 20, 2020
a494361
Add comments and fix mintor details
nachovizzo Oct 20, 2020
51a1eda
Merge remote-tracking branch 'origin/master' into nacho/generalized_icp
nachovizzo Oct 20, 2020
466840b
Fix style
nachovizzo Oct 20, 2020
b962b3d
Update CHANGELOG.md
nachovizzo Oct 20, 2020
c8a636a
Fix missing sign. Epsilon must be 0.001
nachovizzo Oct 20, 2020
6a6e344
Merge branch 'master' into nacho/generalized_icp
nachovizzo Nov 13, 2020
029dea2
Add covariance-point information to geometry::PointCloud type
nachovizzo Nov 13, 2020
7f3f11b
Improve the GeneralizedICP initialization step
nachovizzo Nov 13, 2020
18168b9
Unused variables cleanup
nachovizzo Nov 13, 2020
16b318b
Cleanup a bit
nachovizzo Nov 13, 2020
cf3d669
Add covariance data member to PointCloud class
nachovizzo Nov 13, 2020
040c99b
Add Variance propagation methods
nachovizzo Nov 13, 2020
23b062c
Propagate Covariance information to VoxelDownSample methods
nachovizzo Nov 13, 2020
149d1cc
Update PointCloud pybindings
nachovizzo Nov 13, 2020
7c11438
Reuse CovarianceEstimate code for Normal Computation
nachovizzo Nov 13, 2020
fc38942
Add matrix3dvector pybind type to expose PointCloud::covariances_
nachovizzo Nov 13, 2020
8ecbb8d
Expose HasCovariances() method to Python API
nachovizzo Nov 13, 2020
3ae4478
Expose the PointCloud::EstimateCovariances() method to Python API
nachovizzo Nov 13, 2020
85687aa
Opaque the covariance type
nachovizzo Nov 13, 2020
fb5e936
Sort things out
nachovizzo Nov 13, 2020
6705716
Fix matrix3dvector pybind.
nachovizzo Nov 14, 2020
1c12d28
Merge branch 'nacho/add_covariance_member_pointcloud' into nacho/gene…
nachovizzo Nov 14, 2020
582d49d
Start migrating towards the new PointCloud API
nachovizzo Nov 14, 2020
f1e97f3
Quick implementation of the SVD
nachovizzo Nov 14, 2020
fd5874d
Initialize with zeros not with identity
nachovizzo Nov 15, 2020
de1570b
Initialize with zeros not with identity
nachovizzo Nov 15, 2020
774237e
Remove unused variable
nachovizzo Nov 15, 2020
1ac3e8b
Add unit tests for new covariance member
nachovizzo Nov 15, 2020
6f5e669
Covariance Matrix always positive-semi definitive
nachovizzo Nov 15, 2020
53561d3
Covariance Matrix always positive-semi definitive
nachovizzo Nov 15, 2020
d9dd9b5
Simplify
nachovizzo Nov 16, 2020
b92c736
Clear temp covariances buffer for normal estimation.
nachovizzo Nov 16, 2020
46befb6
Merge branch 'nacho/add_covariance_member_pointcloud' into nacho/gene…
nachovizzo Nov 16, 2020
e410aa3
Fix bug
nachovizzo Nov 16, 2020
e80533b
Still not working as expected
nachovizzo Nov 16, 2020
0d599fc
Merge remote-tracking branch 'origin/master' into nacho/generalized_icp
nachovizzo Mar 12, 2021
9ca874e
Merge remote-tracking branch 'origin/master' into nacho/generalized_icp
nachovizzo Mar 16, 2021
7533433
Add GeneralizedICP back to build
nachovizzo Mar 16, 2021
442d511
Add static method to compute covariances.
nachovizzo Mar 16, 2021
b1eb35a
Make method static
nachovizzo Mar 16, 2021
300bb53
Revert log change
nachovizzo Mar 16, 2021
4055d93
Already checked insided RegistrationICP
nachovizzo Mar 16, 2021
2bba4fb
Fix some comments
nachovizzo Mar 16, 2021
b3f5594
Bring branch closer to master
nachovizzo Mar 16, 2021
334812f
Happy new year
nachovizzo Mar 16, 2021
890c5c7
Expose epsilon parameter for GeneralizedICP
nachovizzo Mar 16, 2021
15542e3
Two changes in this branch
nachovizzo Mar 16, 2021
bf38b27
Make it more similar to ColorizedICP
nachovizzo Mar 16, 2021
4e26c0d
Improve some comments
nachovizzo Mar 16, 2021
61ac60d
Fix some docs:
nachovizzo Mar 16, 2021
ef4997f
Add some tests
nachovizzo Mar 16, 2021
1f1852b
Add missing unit-test (disabled for now)
nachovizzo Mar 17, 2021
9308b3b
Improve a bit the comments
nachovizzo Mar 17, 2021
1ad3fbc
Check for covariances instead of normals
nachovizzo Mar 17, 2021
09cdf8a
Simplify wrapper
nachovizzo Mar 17, 2021
9f389a4
Prevent GeneralizedICP for running if no covariance are there
nachovizzo Mar 17, 2021
b89e1c4
Fix EstimateCovariance logic
nachovizzo Mar 17, 2021
50b5fa5
OpenMP static schedule
nachovizzo Mar 17, 2021
caa0cb6
It's faster to use the fast-normal-computation rather than SVD
nachovizzo Mar 17, 2021
e10a143
Put back comment
nachovizzo Mar 17, 2021
4f1247c
Add simple tutorial
nachovizzo Mar 17, 2021
35b9763
Improve a bit the tutorial
nachovizzo Mar 17, 2021
339cd18
Change titles
nachovizzo Mar 17, 2021
8d61d4f
Fix notebook style
nachovizzo Mar 17, 2021
6a3d12d
Fix unit tests
nachovizzo Mar 17, 2021
50ea9dd
fix conflicts
theNded Jul 22, 2021
ecd7192
remove jupytor example temporarily
theNded Jul 22, 2021
a1622fa
add generalized ICP cpp example
theNded Jul 22, 2021
aadb225
support/test generalized icp in reconstruction system
theNded Jul 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
* Updated Open3D.h.in to add certain missing header files
* Add Open3D-ML to Open3D wheel
* Fix a bug in PointCloud file format, use `float` instead of `float_t`
* Add per-point covariance member for geometry::PointCloud class.
* Add Generalized ICP implementation.

## 0.9.0

Expand Down
56 changes: 23 additions & 33 deletions cpp/open3d/geometry/EstimateNormals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,14 @@ Eigen::Vector3d ComputeEigenvector1(const Eigen::Matrix3d &A,
}
}

Eigen::Vector3d FastEigen3x3(Eigen::Matrix3d &A) {
Eigen::Vector3d FastEigen3x3(const Eigen::Matrix3d &covariance) {
// Previous version based on:
// https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
// Current version based on
// https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf
// which handles edge cases like points on a plane

Eigen::Matrix3d A = covariance;
double max_coeff = A.maxCoeff();
if (max_coeff == 0) {
return Eigen::Vector3d::Zero();
Expand Down Expand Up @@ -221,22 +222,14 @@ Eigen::Vector3d FastEigen3x3(Eigen::Matrix3d &A) {
}
}

Eigen::Vector3d ComputeNormal(const PointCloud &cloud,
const std::vector<int> &indices,
Eigen::Vector3d ComputeNormal(const Eigen::Matrix3d &covariance,
bool fast_normal_computation) {
if (indices.size() == 0) {
return Eigen::Vector3d::Zero();
}
Eigen::Matrix3d covariance =
utility::ComputeCovariance(cloud.points_, indices);

if (fast_normal_computation) {
return FastEigen3x3(covariance);
} else {
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver;
solver.compute(covariance, Eigen::ComputeEigenvectors);
return solver.eigenvectors().col(0);
}
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver;
solver.compute(covariance, Eigen::ComputeEigenvectors);
return solver.eigenvectors().col(0);
}

// Disjoint set data structure to find cycles in graphs
Expand Down Expand Up @@ -317,29 +310,26 @@ void PointCloud::EstimateNormals(
if (!has_normal) {
normals_.resize(points_.size());
}
KDTreeFlann kdtree;
kdtree.SetGeometry(*this);
std::vector<Eigen::Matrix3d> covariances;
if (!HasCovariances()) {
covariances = EstimatePerPointCovariances(*this, search_param);
} else {
covariances = covariances_;
}
#pragma omp parallel for schedule(static)
for (int i = 0; i < (int)points_.size(); i++) {
std::vector<int> indices;
std::vector<double> distance2;
Eigen::Vector3d normal;
if (kdtree.Search(points_[i], search_param, indices, distance2) >= 3) {
normal = ComputeNormal(*this, indices, fast_normal_computation);
if (normal.norm() == 0.0) {
if (has_normal) {
normal = normals_[i];
} else {
normal = Eigen::Vector3d(0.0, 0.0, 1.0);
}
}
if (has_normal && normal.dot(normals_[i]) < 0.0) {
normal *= -1.0;
for (int i = 0; i < (int)covariances.size(); i++) {
auto normal = ComputeNormal(covariances[i], fast_normal_computation);
if (normal.norm() == 0.0) {
if (has_normal) {
normal = normals_[i];
} else {
normal = Eigen::Vector3d(0.0, 0.0, 1.0);
}
normals_[i] = normal;
} else {
normals_[i] = Eigen::Vector3d(0.0, 0.0, 1.0);
}
if (has_normal && normal.dot(normals_[i]) < 0.0) {
normal *= -1.0;
}
normals_[i] = normal;
}
}

Expand Down
17 changes: 17 additions & 0 deletions cpp/open3d/geometry/Geometry3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ void Geometry3D::TransformNormals(const Eigen::Matrix4d& transformation,
}
}

void Geometry3D::TransformCovariances(
const Eigen::Matrix4d& transformation,
std::vector<Eigen::Matrix3d>& covariances) const {
RotateCovariances(transformation.block<3, 3>(0, 0), covariances);
}

void Geometry3D::TranslatePoints(const Eigen::Vector3d& translation,
std::vector<Eigen::Vector3d>& points,
bool relative) const {
Expand Down Expand Up @@ -148,6 +154,17 @@ void Geometry3D::RotateNormals(const Eigen::Matrix3d& R,
}
}

/// The only part that affects the covariance is the rotation part. For more
/// information on variance propagation please visit:
/// https://en.wikipedia.org/wiki/Propagation_of_uncertainty
void Geometry3D::RotateCovariances(
const Eigen::Matrix3d& R,
std::vector<Eigen::Matrix3d>& covariances) const {
for (auto& covariance : covariances) {
covariance = R * covariance * R.transpose();
}
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromXYZ(
const Eigen::Vector3d& rotation) {
return open3d::utility::RotationMatrixX(rotation(0)) *
Expand Down
16 changes: 16 additions & 0 deletions cpp/open3d/geometry/Geometry3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <vector>

#include "open3d/geometry/Geometry.h"
#include "open3d/utility/Eigen.h"
Expand Down Expand Up @@ -155,6 +156,14 @@ class Geometry3D : public Geometry {
/// \param normals A list of normals to be transformed.
void TransformNormals(const Eigen::Matrix4d& transformation,
std::vector<Eigen::Vector3d>& normals) const;

/// \brief Transforms all covariance matrices with the transformation.
///
/// \param transformation 4x4 matrix for transformation.
/// \param covariances A list of covariance matrices to be transformed.
void TransformCovariances(const Eigen::Matrix4d& transformation,
std::vector<Eigen::Matrix3d>& covariances) const;

/// \brief Apply translation to the geometry coordinates.
///
/// \param translation A 3D vector to transform the geometry.
Expand Down Expand Up @@ -192,6 +201,13 @@ class Geometry3D : public Geometry {
/// \param normals A list of normals to be transformed.
void RotateNormals(const Eigen::Matrix3d& R,
std::vector<Eigen::Vector3d>& normals) const;

/// \brief Rotate all covariance matrices with the rotation matrix \p R.
///
/// \param R A 3x3 rotation matrix
/// \param covariances A list of covariance matrices to be transformed.
void RotateCovariances(const Eigen::Matrix3d& R,
std::vector<Eigen::Matrix3d>& covariances) const;
};

} // namespace geometry
Expand Down
82 changes: 71 additions & 11 deletions cpp/open3d/geometry/PointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PointCloud &PointCloud::Clear() {
points_.clear();
normals_.clear();
colors_.clear();
covariances_.clear();
return *this;
}

Expand All @@ -71,6 +72,7 @@ OrientedBoundingBox PointCloud::GetOrientedBoundingBox() const {
PointCloud &PointCloud::Transform(const Eigen::Matrix4d &transformation) {
TransformPoints(transformation, points_);
TransformNormals(transformation, normals_);
TransformCovariances(transformation, covariances_);
return *this;
}

Expand All @@ -90,6 +92,7 @@ PointCloud &PointCloud::Rotate(const Eigen::Matrix3d &R,
const Eigen::Vector3d &center) {
RotatePoints(R, points_, center);
RotateNormals(R, normals_);
RotateCovariances(R, covariances_);
return *this;
}

Expand All @@ -114,6 +117,13 @@ PointCloud &PointCloud::operator+=(const PointCloud &cloud) {
} else {
colors_.clear();
}
if ((!HasPoints() || HasCovariances()) && cloud.HasCovariances()) {
covariances_.resize(new_vert_num);
for (size_t i = 0; i < add_vert_num; i++)
covariances_[old_vert_num + i] = cloud.covariances_[i];
} else {
covariances_.clear();
}
points_.resize(new_vert_num);
for (size_t i = 0; i < add_vert_num; i++)
points_[old_vert_num + i] = cloud.points_[i];
Expand Down Expand Up @@ -149,6 +159,7 @@ PointCloud &PointCloud::RemoveNonFinitePoints(bool remove_nan,
bool remove_infinite) {
bool has_normal = HasNormals();
bool has_color = HasColors();
bool has_covariance = HasCovariances();
size_t old_point_num = points_.size();
size_t k = 0; // new index
for (size_t i = 0; i < old_point_num; i++) { // old index
Expand All @@ -162,12 +173,14 @@ PointCloud &PointCloud::RemoveNonFinitePoints(bool remove_nan,
points_[k] = points_[i];
if (has_normal) normals_[k] = normals_[i];
if (has_color) colors_[k] = colors_[i];
if (has_covariance) covariances_[k] = covariances_[i];
k++;
}
}
points_.resize(k);
if (has_normal) normals_.resize(k);
if (has_color) colors_.resize(k);
if (has_covariance) covariances_.resize(k);
utility::LogDebug(
"[RemoveNonFinitePoints] {:d} nan points have been removed.",
(int)(old_point_num - k));
Expand All @@ -179,6 +192,7 @@ std::shared_ptr<PointCloud> PointCloud::SelectByIndex(
auto output = std::make_shared<PointCloud>();
bool has_normals = HasNormals();
bool has_colors = HasColors();
bool has_covariance = HasCovariances();

std::vector<bool> mask = std::vector<bool>(points_.size(), invert);
for (size_t i : indices) {
Expand All @@ -190,6 +204,7 @@ std::shared_ptr<PointCloud> PointCloud::SelectByIndex(
output->points_.push_back(points_[i]);
if (has_normals) output->normals_.push_back(normals_[i]);
if (has_colors) output->colors_.push_back(colors_[i]);
if (has_covariance) output->covariances_.push_back(covariances_[i]);
}
}
utility::LogDebug(
Expand All @@ -201,13 +216,6 @@ std::shared_ptr<PointCloud> PointCloud::SelectByIndex(
// helper classes for VoxelDownSample and VoxelDownSampleAndTrace
namespace {
class AccumulatedPoint {
public:
AccumulatedPoint()
: num_of_points_(0),
point_(0.0, 0.0, 0.0),
normal_(0.0, 0.0, 0.0),
color_(0.0, 0.0, 0.0) {}

public:
void AddPoint(const PointCloud &cloud, int index) {
point_ += cloud.points_[index];
Expand All @@ -221,6 +229,9 @@ class AccumulatedPoint {
if (cloud.HasColors()) {
color_ += cloud.colors_[index];
}
if (cloud.HasCovariances()) {
covariance_ += cloud.covariances_[index];
}
num_of_points_++;
}

Expand All @@ -237,11 +248,16 @@ class AccumulatedPoint {
return color_ / double(num_of_points_);
}

Eigen::Matrix3d GetAverageCovariance() const {
return covariance_ / double(num_of_points_);
}

public:
int num_of_points_;
Eigen::Vector3d point_;
Eigen::Vector3d normal_;
Eigen::Vector3d color_;
int num_of_points_ = 0;
Eigen::Vector3d point_ = Eigen::Vector3d::Zero();
Eigen::Vector3d normal_ = Eigen::Vector3d::Zero();
Eigen::Vector3d color_ = Eigen::Vector3d::Zero();
Eigen::Matrix3d covariance_ = Eigen::Matrix3d::Zero();
};

class point_cubic_id {
Expand Down Expand Up @@ -275,6 +291,9 @@ class AccumulatedPointForTrace : public AccumulatedPoint {
color_ += cloud.colors_[index];
}
}
if (cloud.HasCovariances()) {
covariance_ += cloud.covariances_[index];
}
point_cubic_id new_id;
new_id.point_id = index;
new_id.cubic_id = cubic_index;
Expand Down Expand Up @@ -331,6 +350,7 @@ std::shared_ptr<PointCloud> PointCloud::VoxelDownSample(
}
bool has_normals = HasNormals();
bool has_colors = HasColors();
bool has_covariances = HasCovariances();
for (auto accpoint : voxelindex_to_accpoint) {
output->points_.push_back(accpoint.second.GetAveragePoint());
if (has_normals) {
Expand All @@ -339,6 +359,10 @@ std::shared_ptr<PointCloud> PointCloud::VoxelDownSample(
if (has_colors) {
output->colors_.push_back(accpoint.second.GetAverageColor());
}
if (has_covariances) {
output->covariances_.emplace_back(
accpoint.second.GetAverageCovariance());
}
}
utility::LogDebug(
"Pointcloud down sampled from {:d} points to {:d} points.",
Expand Down Expand Up @@ -386,6 +410,7 @@ PointCloud::VoxelDownSampleAndTrace(double voxel_size,
}
bool has_normals = HasNormals();
bool has_colors = HasColors();
bool has_covariances = HasCovariances();
int cnt = 0;
cubic_id.resize(voxelindex_to_accpoint.size(), 8);
cubic_id.setConstant(-1);
Expand All @@ -403,6 +428,10 @@ PointCloud::VoxelDownSampleAndTrace(double voxel_size,
output->colors_.push_back(accpoint.second.GetAverageColor());
}
}
if (has_covariances) {
output->covariances_.emplace_back(
accpoint.second.GetAverageCovariance());
}
auto original_id = accpoint.second.GetOriginalID();
for (int i = 0; i < (int)original_id.size(); i++) {
size_t pid = original_id[i].point_id;
Expand Down Expand Up @@ -549,6 +578,37 @@ PointCloud::RemoveStatisticalOutliers(size_t nb_neighbors,
return std::make_tuple(SelectByIndex(indices), indices);
}

std::vector<Eigen::Matrix3d> PointCloud::EstimatePerPointCovariances(
const PointCloud &input,
const KDTreeSearchParam &search_param /* = KDTreeSearchParamKNN()*/) {
const auto &points = input.points_;
std::vector<Eigen::Matrix3d> covariances;
covariances.resize(points.size());

KDTreeFlann kdtree;
kdtree.SetGeometry(input);
#pragma omp parallel for schedule(static)
for (int i = 0; i < (int)points.size(); i++) {
std::vector<int> indices;
std::vector<double> distance2;
if (kdtree.Search(points[i], search_param, indices, distance2) >= 3) {
auto covariance = utility::ComputeCovariance(points, indices);
if (input.HasCovariances() && covariance.isIdentity(1e-4)) {
covariances[i] = input.covariances_[i];
} else {
covariances[i] = covariance;
}
} else {
covariances[i] = Eigen::Matrix3d::Identity();
}
}
return covariances;
}
void PointCloud::EstimateCovariances(
const KDTreeSearchParam &search_param /* = KDTreeSearchParamKNN()*/) {
this->covariances_ = EstimatePerPointCovariances(*this, search_param);
}

std::tuple<Eigen::Vector3d, Eigen::Matrix3d>
PointCloud::ComputeMeanAndCovariance() const {
if (IsEmpty()) {
Expand Down
Loading