Skip to content

Commit 02720e1

Browse files
committed
Refactors and fixes Symmetric ICP
Refactors the Symmetric ICP implementation for better readability and consistency. Addresses minor issues in the symmetric transformation estimation and improves code formatting.
1 parent 5a94059 commit 02720e1

File tree

11 files changed

+97
-101
lines changed

11 files changed

+97
-101
lines changed

cpp/open3d/pipelines/registration/SymmetricICP.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,23 @@ Eigen::Matrix4d TransformationEstimationSymmetric::ComputeTransformation(
8888

8989
std::tuple<std::shared_ptr<const geometry::PointCloud>,
9090
std::shared_ptr<const geometry::PointCloud>>
91-
TransformationEstimationSymmetric::
92-
InitializePointCloudsForTransformation(
93-
const geometry::PointCloud &source,
94-
const geometry::PointCloud &target,
95-
double max_correspondence_distance) const {
91+
TransformationEstimationSymmetric::InitializePointCloudsForTransformation(
92+
const geometry::PointCloud &source,
93+
const geometry::PointCloud &target,
94+
double max_correspondence_distance) const {
9695
if (!target.HasNormals() || !source.HasNormals()) {
97-
utility::LogError("SymmetricICP requires both source and target to "
98-
"have normals.");
96+
utility::LogError(
97+
"SymmetricICP requires both source and target to "
98+
"have normals.");
9999
}
100100
std::shared_ptr<const geometry::PointCloud> source_initialized_c(
101101
&source, [](const geometry::PointCloud *) {});
102102
std::shared_ptr<const geometry::PointCloud> target_initialized_c(
103103
&target, [](const geometry::PointCloud *) {});
104104
if (!source_initialized_c || !target_initialized_c) {
105-
utility::LogError("Internal error: InitializePointCloudsFor"
106-
"Transformation returns nullptr.");
105+
utility::LogError(
106+
"Internal error: InitializePointCloudsFor"
107+
"Transformation returns nullptr.");
107108
}
108109
return std::make_tuple(source_initialized_c, target_initialized_c);
109110
}
@@ -122,4 +123,3 @@ RegistrationResult RegistrationSymmetricICP(
122123
} // namespace registration
123124
} // namespace pipelines
124125
} // namespace open3d
125-

cpp/open3d/pipelines/registration/SymmetricICP.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,3 @@ RegistrationResult RegistrationSymmetricICP(
7474
} // namespace registration
7575
} // namespace pipelines
7676
} // namespace open3d
77-

cpp/open3d/t/pipelines/kernel/Registration.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,11 @@ core::Tensor ComputePoseSymmetric(const core::Tensor &source_points,
6363
int inlier_count = 0;
6464

6565
if (source_points.IsCPU()) {
66-
ComputePoseSymmetricCPU(source_points.Contiguous(),
67-
target_points.Contiguous(),
68-
source_normals.Contiguous(),
69-
target_normals.Contiguous(),
70-
correspondence_indices.Contiguous(), pose,
71-
residual, inlier_count, source_points.GetDtype(),
72-
device, kernel);
66+
ComputePoseSymmetricCPU(
67+
source_points.Contiguous(), target_points.Contiguous(),
68+
source_normals.Contiguous(), target_normals.Contiguous(),
69+
correspondence_indices.Contiguous(), pose, residual,
70+
inlier_count, source_points.GetDtype(), device, kernel);
7371
} else {
7472
utility::LogError("Unimplemented device.");
7573
}

cpp/open3d/t/pipelines/kernel/RegistrationCPU.cpp

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,14 @@ void ComputePosePointToPlaneCPU(const core::Tensor &source_points,
128128
}
129129

130130
template <typename scalar_t, typename funct_t>
131-
static void ComputePoseSymmetricKernelCPU(
132-
const scalar_t *source_points_ptr,
133-
const scalar_t *target_points_ptr,
134-
const scalar_t *source_normals_ptr,
135-
const scalar_t *target_normals_ptr,
136-
const int64_t *correspondence_indices,
137-
const int n,
138-
scalar_t *global_sum,
139-
funct_t GetWeightFromRobustKernel) {
131+
static void ComputePoseSymmetricKernelCPU(const scalar_t *source_points_ptr,
132+
const scalar_t *target_points_ptr,
133+
const scalar_t *source_normals_ptr,
134+
const scalar_t *target_normals_ptr,
135+
const int64_t *correspondence_indices,
136+
const int n,
137+
scalar_t *global_sum,
138+
funct_t GetWeightFromRobustKernel) {
140139
std::vector<scalar_t> A_1x29(29, 0.0);
141140
#ifdef _WIN32
142141
std::vector<scalar_t> zeros_29(29, 0.0);
@@ -174,27 +173,27 @@ static void ComputePoseSymmetricKernelCPU(
174173
scalar_t r1 = dx * ntx + dy * nty + dz * ntz;
175174
scalar_t r2 = dx * nsx + dy * nsy + dz * nsz;
176175

177-
scalar_t J1[6] = { -sz * nty + sy * ntz,
178-
sz * ntx - sx * ntz,
179-
-sy * ntx + sx * nty,
180-
ntx,
181-
nty,
182-
ntz };
183-
scalar_t J2[6] = { -sz * nsy + sy * nsz,
184-
sz * nsx - sx * nsz,
185-
-sy * nsx + sx * nsy,
186-
nsx,
187-
nsy,
188-
nsz };
176+
scalar_t J1[6] = {-sz * nty + sy * ntz,
177+
sz * ntx - sx * ntz,
178+
-sy * ntx + sx * nty,
179+
ntx,
180+
nty,
181+
ntz};
182+
scalar_t J2[6] = {-sz * nsy + sy * nsz,
183+
sz * nsx - sx * nsz,
184+
-sy * nsx + sx * nsy,
185+
nsx,
186+
nsy,
187+
nsz};
189188

190189
scalar_t w1 = GetWeightFromRobustKernel(r1);
191190
scalar_t w2 = GetWeightFromRobustKernel(r2);
192191

193192
int i = 0;
194193
for (int j = 0; j < 6; ++j) {
195194
for (int k = 0; k <= j; ++k) {
196-
A_reduction[i] += J1[j] * w1 * J1[k] +
197-
J2[j] * w2 * J2[k];
195+
A_reduction[i] +=
196+
J1[j] * w1 * J1[k] + J2[j] * w2 * J2[k];
198197
++i;
199198
}
200199
A_reduction[21 + j] +=
@@ -221,16 +220,16 @@ static void ComputePoseSymmetricKernelCPU(
221220
}
222221

223222
void ComputePoseSymmetricCPU(const core::Tensor &source_points,
224-
const core::Tensor &target_points,
225-
const core::Tensor &source_normals,
226-
const core::Tensor &target_normals,
227-
const core::Tensor &correspondence_indices,
228-
core::Tensor &pose,
229-
float &residual,
230-
int &inlier_count,
231-
const core::Dtype &dtype,
232-
const core::Device &device,
233-
const registration::RobustKernel &kernel) {
223+
const core::Tensor &target_points,
224+
const core::Tensor &source_normals,
225+
const core::Tensor &target_normals,
226+
const core::Tensor &correspondence_indices,
227+
core::Tensor &pose,
228+
float &residual,
229+
int &inlier_count,
230+
const core::Dtype &dtype,
231+
const core::Device &device,
232+
const registration::RobustKernel &kernel) {
234233
int n = source_points.GetLength();
235234

236235
core::Tensor global_sum = core::Tensor::Zeros({29}, dtype, device);

cpp/open3d/t/pipelines/registration/TransformationEstimation.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ double TransformationEstimationSymmetric::ComputeRMSE(
174174
}
175175
if (!target.HasPointNormals() || !source.HasPointNormals()) {
176176
utility::LogError(
177-
"SymmetricICP requires both source and target to have normals.");
177+
"SymmetricICP requires both source and target to have "
178+
"normals.");
178179
}
179180

180181
core::AssertTensorDtype(target.GetPointPositions(),
@@ -199,11 +200,12 @@ double TransformationEstimationSymmetric::ComputeRMSE(
199200
core::Tensor diff = source_points_indexed - target_points_indexed;
200201
core::Tensor r1 = diff.Mul(target_normals_indexed).Sum({1});
201202
core::Tensor r2 = diff.Mul(source_normals_indexed).Sum({1});
202-
203+
203204
// Compute symmetric error
204205
core::Tensor error_t = r1.Mul(r1) + r2.Mul(r2);
205206
double error = error_t.Sum({0}).To(core::Float64).Item<double>();
206-
return std::sqrt(error / static_cast<double>(neighbour_indices.GetLength()));
207+
return std::sqrt(error /
208+
static_cast<double>(neighbour_indices.GetLength()));
207209
}
208210

209211
core::Tensor TransformationEstimationSymmetric::ComputeTransformation(
@@ -217,7 +219,8 @@ core::Tensor TransformationEstimationSymmetric::ComputeTransformation(
217219
}
218220
if (!target.HasPointNormals() || !source.HasPointNormals()) {
219221
utility::LogError(
220-
"SymmetricICP requires both source and target to have normals.");
222+
"SymmetricICP requires both source and target to have "
223+
"normals.");
221224
}
222225

223226
core::AssertTensorDtypes(source.GetPointPositions(),
@@ -235,8 +238,8 @@ core::Tensor TransformationEstimationSymmetric::ComputeTransformation(
235238
// Get pose {6} of type Float64.
236239
core::Tensor pose = pipelines::kernel::ComputePoseSymmetric(
237240
source.GetPointPositions(), target.GetPointPositions(),
238-
source.GetPointNormals(), target.GetPointNormals(),
239-
correspondences, this->kernel_);
241+
source.GetPointNormals(), target.GetPointNormals(), correspondences,
242+
this->kernel_);
240243

241244
// Get rigid transformation tensor of {4, 4} of type Float64 on CPU:0
242245
// device, from pose {6}.

cpp/pybind/pipelines/registration/registration.cpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
#include "open3d/pipelines/registration/FastGlobalRegistration.h"
1717
#include "open3d/pipelines/registration/Feature.h"
1818
#include "open3d/pipelines/registration/GeneralizedICP.h"
19-
#include "open3d/pipelines/registration/SymmetricICP.h"
2019
#include "open3d/pipelines/registration/RobustKernel.h"
20+
#include "open3d/pipelines/registration/SymmetricICP.h"
2121
#include "open3d/pipelines/registration/TransformationEstimation.h"
2222
#include "open3d/utility/Logging.h"
2323
#include "pybind/docstring.h"
@@ -109,10 +109,9 @@ void pybind_registration_declarations(py::module &m) {
109109
py::class_<TransformationEstimationSymmetric,
110110
PyTransformationEstimation<TransformationEstimationSymmetric>,
111111
TransformationEstimation>
112-
te_sym(m_registration,
113-
"TransformationEstimationSymmetric",
114-
"Class to estimate a transformation for symmetric "
115-
"point to plane distance.");
112+
te_sym(m_registration, "TransformationEstimationSymmetric",
113+
"Class to estimate a transformation for symmetric "
114+
"point to plane distance.");
116115
py::class_<
117116
TransformationEstimationForColoredICP,
118117
PyTransformationEstimation<TransformationEstimationForColoredICP>,
@@ -318,8 +317,8 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``.
318317
auto te_sym = static_cast<py::class_<
319318
TransformationEstimationSymmetric,
320319
PyTransformationEstimation<TransformationEstimationSymmetric>,
321-
TransformationEstimation>>(m_registration.attr(
322-
"TransformationEstimationSymmetric"));
320+
TransformationEstimation>>(
321+
m_registration.attr("TransformationEstimationSymmetric"));
323322
py::detail::bind_default_constructor<TransformationEstimationSymmetric>(
324323
te_sym);
325324
py::detail::bind_copy_functions<TransformationEstimationSymmetric>(te_sym);
@@ -332,10 +331,9 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``.
332331
[](const TransformationEstimationSymmetric &te) {
333332
return std::string("TransformationEstimationSymmetric");
334333
})
335-
.def_readwrite(
336-
"kernel",
337-
&TransformationEstimationSymmetric::kernel_,
338-
"Robust Kernel used in the Optimization");
334+
.def_readwrite("kernel",
335+
&TransformationEstimationSymmetric::kernel_,
336+
"Robust Kernel used in the Optimization");
339337

340338
// open3d.registration.TransformationEstimationForColoredICP :
341339
auto te_col = static_cast<py::class_<
@@ -655,11 +653,11 @@ must hold true for all edges.)");
655653
{"criteria", "Convergence criteria"},
656654
{"estimation_method",
657655
"Estimation method. One of "
658-
"(``TransformationEstimationPointToPoint``, "
659-
"``TransformationEstimationPointToPlane``, "
660-
"``TransformationEstimationSymmetric``, "
661-
"``TransformationEstimationForGeneralizedICP``, "
662-
"``TransformationEstimationForColoredICP``)"},
656+
"(``TransformationEstimationPointToPoint``, "
657+
"``TransformationEstimationPointToPlane``, "
658+
"``TransformationEstimationSymmetric``, "
659+
"``TransformationEstimationForGeneralizedICP``, "
660+
"``TransformationEstimationForColoredICP``)"},
663661
{"init", "Initial transformation estimation"},
664662
{"lambda_geometric", "lambda_geometric value"},
665663
{"epsilon", "epsilon value"},
@@ -706,8 +704,7 @@ must hold true for all edges.)");
706704
"Function for symmetric ICP registration", "source"_a, "target"_a,
707705
"max_correspondence_distance"_a,
708706
"init"_a = Eigen::Matrix4d::Identity(),
709-
"estimation_method"_a =
710-
TransformationEstimationSymmetric(),
707+
"estimation_method"_a = TransformationEstimationSymmetric(),
711708
"criteria"_a = ICPConvergenceCriteria());
712709
docstring::FunctionDocInject(m_registration, "registration_symmetric_icp",
713710
map_shared_argument_docstrings);

cpp/pybind/t/pipelines/registration/registration.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,7 @@ void pybind_registration_definitions(py::module &m) {
494494
core::Tensor::Eye(4, core::Float64, core::Device("CPU:0")),
495495
"estimation_method"_a = TransformationEstimationSymmetric(),
496496
"criteria"_a = ICPConvergenceCriteria());
497-
docstring::FunctionDocInject(m_registration,
498-
"registration_symmetric_icp",
497+
docstring::FunctionDocInject(m_registration, "registration_symmetric_icp",
499498
map_shared_argument_docstrings);
500499

501500
m_registration.def(

cpp/tests/pipelines/registration/SymmetricICP.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ TEST(SymmetricICP, TransformationEstimationSymmetricComputeTransformation) {
8484
registration::TransformationEstimationSymmetric estimation;
8585

8686
Eigen::Matrix4d transformation =
87-
estimation.ComputeTransformation(source, target, corres);
87+
estimation.ComputeTransformation(source, target, corres);
8888

8989
// Should be close to identity for perfect correspondence
9090
EXPECT_TRUE(transformation.isApprox(Eigen::Matrix4d::Identity(), 1e-3));
@@ -98,7 +98,7 @@ TEST(SymmetricICP,
9898
registration::TransformationEstimationSymmetric estimation;
9999

100100
Eigen::Matrix4d transformation =
101-
estimation.ComputeTransformation(source, target, corres);
101+
estimation.ComputeTransformation(source, target, corres);
102102

103103
EXPECT_TRUE(transformation.isApprox(Eigen::Matrix4d::Identity()));
104104
}
@@ -116,7 +116,7 @@ TEST(SymmetricICP,
116116
registration::TransformationEstimationSymmetric estimation;
117117

118118
Eigen::Matrix4d transformation =
119-
estimation.ComputeTransformation(source, target, corres);
119+
estimation.ComputeTransformation(source, target, corres);
120120

121121
EXPECT_TRUE(transformation.isApprox(Eigen::Matrix4d::Identity()));
122122
}
@@ -130,18 +130,17 @@ TEST(SymmetricICP, RegistrationSymmetricICP) {
130130
source.normals_ = {{0, 0, 1}, {0, 0, 1}, {0, 0, 1}};
131131

132132
// Target is slightly translated
133-
target.points_ = {{0.05, 0.05, 0.05},
134-
{1.05, 0.05, 0.05},
135-
{0.05, 1.05, 0.05}};
133+
target.points_ = {
134+
{0.05, 0.05, 0.05}, {1.05, 0.05, 0.05}, {0.05, 1.05, 0.05}};
136135
target.normals_ = {{0, 0, 1}, {0, 0, 1}, {0, 0, 1}};
137136

138137
registration::TransformationEstimationSymmetric estimation;
139138
registration::ICPConvergenceCriteria criteria;
140139

141140
registration::RegistrationResult result =
142-
registration::RegistrationSymmetricICP(
143-
source, target, 0.1, Eigen::Matrix4d::Identity(),
144-
estimation, criteria);
141+
registration::RegistrationSymmetricICP(source, target, 0.1,
142+
Eigen::Matrix4d::Identity(),
143+
estimation, criteria);
145144

146145
EXPECT_GT(result.correspondence_set_.size(), 0);
147146
EXPECT_GT(result.fitness_, 0.0);
@@ -164,13 +163,13 @@ TEST(SymmetricICP, RegistrationSymmetricICPConvergence) {
164163
double z = rand_gen();
165164

166165
source.points_.push_back({x, y, z});
167-
source.normals_.push_back({0, 0, 1}); // Simple normal for testing
166+
source.normals_.push_back({0, 0, 1}); // Simple normal for testing
168167
}
169168

170169
// Create target by transforming source with known transformation
171170
Eigen::Matrix4d true_transformation = Eigen::Matrix4d::Identity();
172-
true_transformation(0, 3) = 0.1; // Small translation in x
173-
true_transformation(1, 3) = 0.05; // Small translation in y
171+
true_transformation(0, 3) = 0.1; // Small translation in x
172+
true_transformation(1, 3) = 0.05; // Small translation in y
174173

175174
target = source;
176175
target.Transform(true_transformation);
@@ -179,9 +178,9 @@ TEST(SymmetricICP, RegistrationSymmetricICPConvergence) {
179178
registration::ICPConvergenceCriteria criteria(1e-6, 1e-6, 30);
180179

181180
registration::RegistrationResult result =
182-
registration::RegistrationSymmetricICP(
183-
source, target, 0.5, Eigen::Matrix4d::Identity(),
184-
estimation, criteria);
181+
registration::RegistrationSymmetricICP(source, target, 0.5,
182+
Eigen::Matrix4d::Identity(),
183+
estimation, criteria);
185184

186185
// Check that registration converged to reasonable result
187186
EXPECT_GT(result.fitness_, 0.5);

0 commit comments

Comments
 (0)