diff --git a/cpp/open3d/pipelines/color_map/ColorMapOptimization.cpp b/cpp/open3d/pipelines/color_map/ColorMapOptimization.cpp index 2cf184d202c..fb3e0e83280 100644 --- a/cpp/open3d/pipelines/color_map/ColorMapOptimization.cpp +++ b/cpp/open3d/pipelines/color_map/ColorMapOptimization.cpp @@ -171,9 +171,8 @@ static void OptimizeImageCoorRigid( auto f_lambda = [&](int i, Eigen::Vector6d& J_r, double& r, double& w) { - w = 1.0; jac.ComputeJacobianAndResidualRigid( - i, J_r, r, mesh, proxy_intensity, images_gray[c], + i, J_r, r, w, mesh, proxy_intensity, images_gray[c], images_dx[c], images_dy[c], intr, extrinsic, visibility_image_to_vertex[c], option.image_boundary_margin_); diff --git a/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.cpp b/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.cpp index fef90dd371b..a23a021283d 100644 --- a/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.cpp +++ b/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.cpp @@ -39,6 +39,7 @@ void ColorMapOptimizationJacobian::ComputeJacobianAndResidualRigid( int row, Eigen::Vector6d& J_r, double& r, + double& w, const geometry::TriangleMesh& mesh, const std::vector& proxy_intensity, const std::shared_ptr& images_gray, @@ -74,6 +75,7 @@ void ColorMapOptimizationJacobian::ComputeJacobianAndResidualRigid( J_r(4) = v1; J_r(5) = v2; r = (gray - proxy_intensity[vid]); + w = 1.0; } void ColorMapOptimizationJacobian::ComputeJacobianAndResidualNonRigid( diff --git a/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.h b/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.h index 32e15e601a4..df304a52813 100644 --- a/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.h +++ b/cpp/open3d/pipelines/color_map/ColorMapOptimizationJacobian.h @@ -53,6 +53,7 @@ class ColorMapOptimizationJacobian { int row, Eigen::Vector6d& J_r, double& r, + double& w, const geometry::TriangleMesh& mesh, const std::vector& proxy_intensity, const std::shared_ptr& images_gray, diff --git a/cpp/open3d/pipelines/odometry/Odometry.cpp b/cpp/open3d/pipelines/odometry/Odometry.cpp index 385746078df..cbaa31f1262 100644 --- a/cpp/open3d/pipelines/odometry/Odometry.cpp +++ b/cpp/open3d/pipelines/odometry/Odometry.cpp @@ -433,9 +433,9 @@ static std::tuple DoSingleIteration( auto f_lambda = [&](int i, std::vector &J_r, - std::vector &r) { + std::vector &w, std::vector &r) { jacobian_method.ComputeJacobianAndResidual( - i, J_r, r, source, target, source_xyz, target_dx, + i, J_r, r, w, source, target, source_xyz, target_dx, target_dy, intrinsic, extrinsic_initial, *correspondence); }; diff --git a/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.cpp b/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.cpp index cada9dd2e2d..deeac8eae7a 100644 --- a/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.cpp +++ b/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.cpp @@ -45,6 +45,7 @@ void RGBDOdometryJacobianFromColorTerm::ComputeJacobianAndResidual( int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, @@ -82,12 +83,15 @@ void RGBDOdometryJacobianFromColorTerm::ComputeJacobianAndResidual( J_r[0](5) = c2; r.resize(1); r[0] = diff; + w.resize(1); + w[0] = 1.0; } void RGBDOdometryJacobianFromHybridTerm::ComputeJacobianAndResidual( int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, @@ -133,6 +137,7 @@ void RGBDOdometryJacobianFromHybridTerm::ComputeJacobianAndResidual( J_r.resize(2); r.resize(2); + w.resize(2); J_r[0](0) = sqrt_lambda_img * (-p3d_trans(2) * c1 + p3d_trans(1) * c2); J_r[0](1) = sqrt_lambda_img * (p3d_trans(2) * c0 - p3d_trans(0) * c2); J_r[0](2) = sqrt_lambda_img * (-p3d_trans(1) * c0 + p3d_trans(0) * c1); @@ -141,6 +146,7 @@ void RGBDOdometryJacobianFromHybridTerm::ComputeJacobianAndResidual( J_r[0](5) = sqrt_lambda_img * (c2); double r_photo = sqrt_lambda_img * diff_photo; r[0] = r_photo; + w[0] = 1.0; J_r[1](0) = sqrt_lamba_dep * ((-p3d_trans(2) * d1 + p3d_trans(1) * d2) - p3d_trans(1)); @@ -152,6 +158,7 @@ void RGBDOdometryJacobianFromHybridTerm::ComputeJacobianAndResidual( J_r[1](5) = sqrt_lamba_dep * (d2 - 1.0f); double r_geo = sqrt_lamba_dep * diff_geo; r[1] = r_geo; + w[1] = 1.0; } } // namespace odometry diff --git a/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.h b/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.h index 2b94dd835ca..d138d080538 100644 --- a/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.h +++ b/cpp/open3d/pipelines/odometry/RGBDOdometryJacobian.h @@ -68,6 +68,7 @@ class RGBDOdometryJacobian { int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, @@ -99,6 +100,7 @@ class RGBDOdometryJacobianFromColorTerm : public RGBDOdometryJacobian { int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, @@ -129,6 +131,7 @@ class RGBDOdometryJacobianFromHybridTerm : public RGBDOdometryJacobian { int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, diff --git a/cpp/open3d/pipelines/registration/ColoredICP.cpp b/cpp/open3d/pipelines/registration/ColoredICP.cpp index a1096a3de68..d9ea684c928 100644 --- a/cpp/open3d/pipelines/registration/ColoredICP.cpp +++ b/cpp/open3d/pipelines/registration/ColoredICP.cpp @@ -32,6 +32,7 @@ #include "open3d/geometry/KDTreeFlann.h" #include "open3d/geometry/KDTreeSearchParam.h" #include "open3d/geometry/PointCloud.h" +#include "open3d/pipelines/registration/RobustKernel.h" #include "open3d/utility/Console.h" #include "open3d/utility/Eigen.h" @@ -39,43 +40,14 @@ namespace open3d { namespace pipelines { namespace registration { +namespace { + class PointCloudForColoredICP : public geometry::PointCloud { public: std::vector color_gradient_; }; -class TransformationEstimationForColoredICP : public TransformationEstimation { -public: - TransformationEstimationType GetTransformationEstimationType() - const override { - return type_; - }; - TransformationEstimationForColoredICP(double lambda_geometric = 0.968) - : lambda_geometric_(lambda_geometric) { - if (lambda_geometric_ < 0 || lambda_geometric_ > 1.0) - lambda_geometric_ = 0.968; - } - ~TransformationEstimationForColoredICP() override {} - -public: - double ComputeRMSE(const geometry::PointCloud &source, - const geometry::PointCloud &target, - const CorrespondenceSet &corres) const override; - Eigen::Matrix4d ComputeTransformation( - const geometry::PointCloud &source, - const geometry::PointCloud &target, - const CorrespondenceSet &corres) const override; - -public: - double lambda_geometric_; - -private: - const TransformationEstimationType type_ = - TransformationEstimationType::ColoredICP; -}; - -static std::shared_ptr -InitializePointCloudForColoredICP( +std::shared_ptr InitializePointCloudForColoredICP( const geometry::PointCloud &target, const geometry::KDTreeSearchParamHybrid &search_param) { utility::LogDebug("InitializePointCloudForColoredICP"); @@ -128,7 +100,7 @@ InitializePointCloudForColoredICP( A(nn - 1, 2) = (nn - 1) * nt(2); b(nn - 1, 0) = 0; // solving linear equation - bool is_success; + bool is_success = false; Eigen::MatrixXd x; std::tie(is_success, x) = utility::SolveLinearSystemPSD( A.transpose() * A, A.transpose() * b); @@ -140,13 +112,16 @@ InitializePointCloudForColoredICP( return output; } +} // namespace + Eigen::Matrix4d TransformationEstimationForColoredICP::ComputeTransformation( const geometry::PointCloud &source, const geometry::PointCloud &target, const CorrespondenceSet &corres) const { if (corres.empty() || !target.HasNormals() || !target.HasColors() || - !source.HasColors()) + !source.HasColors()) { return Eigen::Matrix4d::Identity(); + } double sqrt_lambda_geometric = sqrt(lambda_geometric_); double lambda_photometric = 1.0 - lambda_geometric_; @@ -157,7 +132,7 @@ Eigen::Matrix4d TransformationEstimationForColoredICP::ComputeTransformation( auto compute_jacobian_and_residual = [&](int i, std::vector &J_r, - std::vector &r) { + std::vector &r, std::vector &w) { size_t cs = corres[i][0]; size_t ct = corres[i][1]; const Eigen::Vector3d &vs = source.points_[cs]; @@ -166,10 +141,12 @@ Eigen::Matrix4d TransformationEstimationForColoredICP::ComputeTransformation( J_r.resize(2); r.resize(2); + w.resize(2); J_r[0].block<3, 1>(0, 0) = sqrt_lambda_geometric * vs.cross(nt); J_r[0].block<3, 1>(3, 0) = sqrt_lambda_geometric * nt; r[0] = sqrt_lambda_geometric * (vs - vt).dot(nt); + w[0] = kernel_->Weight(r[0]); // project vs into vt's tangential plane Eigen::Vector3d vs_proj = vs - (vs - vt).dot(nt) * nt; @@ -194,6 +171,7 @@ Eigen::Matrix4d TransformationEstimationForColoredICP::ComputeTransformation( sqrt_lambda_photometric * vs.cross(ditM); J_r[1].block<3, 1>(3, 0) = sqrt_lambda_photometric * ditM; r[1] = sqrt_lambda_photometric * (is - is0_proj); + w[1] = kernel_->Weight(r[1]); }; Eigen::Matrix6d JTJ; @@ -249,13 +227,14 @@ RegistrationResult RegistrationColoredICP( const geometry::PointCloud &target, double max_distance, const Eigen::Matrix4d &init /* = Eigen::Matrix4d::Identity()*/, - const ICPConvergenceCriteria &criteria /* = ICPConvergenceCriteria()*/, - double lambda_geometric /* = 0.968*/) { + const TransformationEstimationForColoredICP &estimation + /*TransformationEstimationForColoredICP()*/, + const ICPConvergenceCriteria + &criteria /* = ICPConvergenceCriteria()*/) { auto target_c = InitializePointCloudForColoredICP( target, geometry::KDTreeSearchParamHybrid(max_distance * 2.0, 30)); - return RegistrationICP( - source, *target_c, max_distance, init, - TransformationEstimationForColoredICP(lambda_geometric), criteria); + return RegistrationICP(source, *target_c, max_distance, init, estimation, + criteria); } } // namespace registration diff --git a/cpp/open3d/pipelines/registration/ColoredICP.h b/cpp/open3d/pipelines/registration/ColoredICP.h index 470e1b83df2..784e1eda44c 100644 --- a/cpp/open3d/pipelines/registration/ColoredICP.h +++ b/cpp/open3d/pipelines/registration/ColoredICP.h @@ -27,8 +27,11 @@ #pragma once #include +#include #include "open3d/pipelines/registration/Registration.h" +#include "open3d/pipelines/registration/RobustKernel.h" +#include "open3d/pipelines/registration/TransformationEstimation.h" namespace open3d { @@ -38,8 +41,45 @@ class PointCloud; namespace pipelines { namespace registration { + class RegistrationResult; +class TransformationEstimationForColoredICP : public TransformationEstimation { +public: + ~TransformationEstimationForColoredICP() override{}; + + TransformationEstimationType GetTransformationEstimationType() + const override { + return type_; + }; + explicit TransformationEstimationForColoredICP( + double lambda_geometric = 0.968, + std::shared_ptr kernel = std::make_shared()) + : lambda_geometric_(lambda_geometric), kernel_(std::move(kernel)) { + if (lambda_geometric_ < 0 || lambda_geometric_ > 1.0) { + lambda_geometric_ = 0.968; + } + } + +public: + double ComputeRMSE(const geometry::PointCloud &source, + const geometry::PointCloud &target, + const CorrespondenceSet &corres) const override; + Eigen::Matrix4d ComputeTransformation( + const geometry::PointCloud &source, + const geometry::PointCloud &target, + const CorrespondenceSet &corres) const override; + +public: + double lambda_geometric_ = 0.968; + /// shared_ptr to an Abstract RobustKernel that could mutate at runtime. + std::shared_ptr kernel_ = std::make_shared(); + +private: + const TransformationEstimationType type_ = + TransformationEstimationType::ColoredICP; +}; + /// \brief Function for Colored ICP registration. /// /// This is implementation of following paper @@ -52,14 +92,17 @@ class RegistrationResult; /// \param init Initial transformation estimation. /// Default value: array([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], /// [0., 0., 0., 1.]]). \param criteria Convergence criteria. \param -/// lambda_geometric lambda_geometric value. +/// \param estimation TransformationEstimationForColoredICP method. Can only +/// change the lambda_geometric value and the robust kernel used in the +/// optimization RegistrationResult RegistrationColoredICP( const geometry::PointCloud &source, const geometry::PointCloud &target, double max_distance, const Eigen::Matrix4d &init = Eigen::Matrix4d::Identity(), - const ICPConvergenceCriteria &criteria = ICPConvergenceCriteria(), - double lambda_geometric = 0.968); + const TransformationEstimationForColoredICP &estimation = + TransformationEstimationForColoredICP(), + const ICPConvergenceCriteria &criteria = ICPConvergenceCriteria()); } // namespace registration } // namespace pipelines diff --git a/cpp/open3d/utility/Eigen.cpp b/cpp/open3d/utility/Eigen.cpp index a7fa23e6a74..974778b402e 100644 --- a/cpp/open3d/utility/Eigen.cpp +++ b/cpp/open3d/utility/Eigen.cpp @@ -207,6 +207,7 @@ std::tuple ComputeJTJandJTr( std::function< void(int, std::vector> &, + std::vector &, std::vector &)> f, int iteration_num, bool verbose /*=true*/) { @@ -223,13 +224,14 @@ std::tuple ComputeJTJandJTr( JTJ_private.setZero(); JTr_private.setZero(); std::vector r; + std::vector w; std::vector> J_r; #pragma omp for nowait for (int i = 0; i < iteration_num; i++) { - f(i, J_r, r); + f(i, J_r, r, w); for (int j = 0; j < (int)r.size(); j++) { - JTJ_private.noalias() += J_r[j] * J_r[j].transpose(); - JTr_private.noalias() += J_r[j] * r[j]; + JTJ_private.noalias() += J_r[j] * w[j] * J_r[j].transpose(); + JTr_private.noalias() += J_r[j] * w[j] * r[j]; r2_sum_private += r[j] * r[j]; } } @@ -255,6 +257,7 @@ template std::tuple ComputeJTJandJTr( template std::tuple ComputeJTJandJTr( std::function &, + std::vector &, std::vector &)> f, int iteration_num, bool verbose); // clang-format on diff --git a/cpp/open3d/utility/Eigen.h b/cpp/open3d/utility/Eigen.h index 5608dcada24..05371c9c05f 100644 --- a/cpp/open3d/utility/Eigen.h +++ b/cpp/open3d/utility/Eigen.h @@ -113,6 +113,7 @@ std::tuple ComputeJTJandJTr( std::function< void(int, std::vector> &, + std::vector &, std::vector &)> f, int iteration_num, bool verbose = true); diff --git a/cpp/pybind/pipelines/odometry/odometry.cpp b/cpp/pybind/pipelines/odometry/odometry.cpp index 5736d5477b2..21741342d17 100644 --- a/cpp/pybind/pipelines/odometry/odometry.cpp +++ b/cpp/pybind/pipelines/odometry/odometry.cpp @@ -45,6 +45,7 @@ class PyRGBDOdometryJacobian : public RGBDOdometryJacobianBase { int row, std::vector &J_r, std::vector &r, + std::vector &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, diff --git a/cpp/pybind/pipelines/registration/registration.cpp b/cpp/pybind/pipelines/registration/registration.cpp index a0ea891bbb0..1b50b80681c 100644 --- a/cpp/pybind/pipelines/registration/registration.cpp +++ b/cpp/pybind/pipelines/registration/registration.cpp @@ -246,6 +246,50 @@ Sets :math:`c = 1` if ``with_scaling`` is ``False``. &TransformationEstimationPointToPlane::kernel_, "Robust Kernel used in the Optimization"); + // open3d.registration.TransformationEstimationForColoredICP : + py::class_< + TransformationEstimationForColoredICP, + PyTransformationEstimation, + TransformationEstimation> + te_col(m, "TransformationEstimationForColoredICP", + "Class to estimate a transformation between two point " + "clouds using color information"); + py::detail::bind_default_constructor( + te_col); + py::detail::bind_copy_functions( + te_col); + te_col.def(py::init([](double lambda_geometric, + std::shared_ptr kernel) { + return new TransformationEstimationForColoredICP( + lambda_geometric, std::move(kernel)); + }), + "lambda_geometric"_a, "kernel"_a) + .def(py::init([](double lambda_geometric) { + return new TransformationEstimationForColoredICP( + lambda_geometric); + }), + "lambda_geometric"_a) + .def(py::init([](std::shared_ptr kernel) { + auto te = TransformationEstimationForColoredICP(); + te.kernel_ = std::move(kernel); + return te; + }), + "kernel"_a) + .def("__repr__", + [](const TransformationEstimationForColoredICP &te) { + return std::string( + "TransformationEstimationForColoredICP " + "with lambda_geometric:") + + std::to_string(te.lambda_geometric_); + }) + .def_readwrite( + "lambda_geometric", + &TransformationEstimationForColoredICP::lambda_geometric_, + "lambda_geometric") + .def_readwrite("kernel", + &TransformationEstimationForColoredICP::kernel_, + "Robust Kernel used in the Optimization"); + // open3d.registration.CorrespondenceChecker py::class_> @@ -492,9 +536,12 @@ static const std::unordered_map "(``" "TransformationEstimationPointToPoint``, " "``" - "TransformationEstimationPointToPlane``)"}, + "TransformationEstimationPointToPlane``, " + "``" + "TransformationEstimationForColoredICP``)"}, {"init", "Initial transformation estimation"}, {"lambda_geometric", "lambda_geometric value"}, + {"kernel", "Robust Kernel used in the Optimization"}, {"max_correspondence_distance", "Maximum correspondence points-pair distance."}, {"option", "Registration option"}, @@ -527,8 +574,8 @@ void pybind_registration_methods(py::module &m) { "Function for Colored ICP registration", "source"_a, "target"_a, "max_correspondence_distance"_a, "init"_a = Eigen::Matrix4d::Identity(), - "criteria"_a = ICPConvergenceCriteria(), - "lambda_geometric"_a = 0.968); + "estimation_method"_a = TransformationEstimationForColoredICP(), + "criteria"_a = ICPConvergenceCriteria()); docstring::FunctionDocInject(m, "registration_colored_icp", map_shared_argument_docstrings); diff --git a/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromColorTerm.cpp b/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromColorTerm.cpp index 37598a3712e..cba77586d28 100644 --- a/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromColorTerm.cpp +++ b/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromColorTerm.cpp @@ -97,10 +97,11 @@ TEST(RGBDOdometryJacobianFromColorTerm, ComputeJacobianAndResidual) { for (int row = 0; row < rows; row++) { std::vector J_r; std::vector r; + std::vector w; jacobian_method.ComputeJacobianAndResidual( - row, J_r, r, source, target, *source_xyz, target_dx, target_dy, - intrinsic, extrinsic, corresps); + row, J_r, r, w, source, target, *source_xyz, target_dx, + target_dy, intrinsic, extrinsic, corresps); EXPECT_NEAR(ref_r[row], r[0], THRESHOLD_1E_6); ExpectEQ(ref_J_r[row], J_r[0], 1e-4); diff --git a/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromHybridTerm.cpp b/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromHybridTerm.cpp index 530ad24246e..9c63e51f159 100644 --- a/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromHybridTerm.cpp +++ b/cpp/tests/pipelines/odometry/RGBDOdometryJacobianFromHybridTerm.cpp @@ -115,10 +115,11 @@ TEST(RGBDOdometryJacobianFromHybridTerm, ComputeJacobianAndResidual) { for (int row = 0; row < rows; row++) { std::vector J_r; std::vector r; + std::vector w; jacobian_method.ComputeJacobianAndResidual( - row, J_r, r, source, target, *source_xyz, target_dx, target_dy, - intrinsic, extrinsic, corresps); + row, J_r, r, w, source, target, *source_xyz, target_dx, + target_dy, intrinsic, extrinsic, corresps); EXPECT_NEAR(ref_r[2 * row + 0], r[0], THRESHOLD_1E_6); EXPECT_NEAR(ref_r[2 * row + 1], r[1], THRESHOLD_1E_6); diff --git a/cpp/tests/utility/Eigen.cpp b/cpp/tests/utility/Eigen.cpp index 6d5fc7712ae..fc08fc87cf0 100644 --- a/cpp/tests/utility/Eigen.cpp +++ b/cpp/tests/utility/Eigen.cpp @@ -224,12 +224,13 @@ TEST(Eigen, ComputeJTJandJTr_vector) { auto testFunction = [&](int i, std::vector &J_r, - std::vector &r) { + std::vector &r, std::vector &w) { { size_t size = 10; J_r.resize(size); r.resize(size); + w.resize(size); std::vector v(6); for (size_t s = 0; s < size; s++) { @@ -238,6 +239,7 @@ TEST(Eigen, ComputeJTJandJTr_vector) { for (int k = 0; k < 6; k++) J_r[s](k) = v[k]; r[s] = (double)((i * s) % 6) / 6; + w[s] = 1.0; } } }; diff --git a/examples/python/pipelines/colored_pointcloud_registration.ipynb b/examples/python/pipelines/colored_pointcloud_registration.ipynb index 351340a00f9..99f8200f36d 100644 --- a/examples/python/pipelines/colored_pointcloud_registration.ipynb +++ b/examples/python/pipelines/colored_pointcloud_registration.ipynb @@ -166,6 +166,7 @@ " print(\"3-3. Applying colored point cloud registration\")\n", " result_icp = o3d.pipelines.registration.registration_colored_icp(\n", " source_down, target_down, radius, current_transformation,\n", + " o3d.pipelines.registration.TransformationEstimationForColoredICP(),\n", " o3d.pipelines.registration.ICPConvergenceCriteria(relative_fitness=1e-6,\n", " relative_rmse=1e-6,\n", " max_iteration=iter))\n", diff --git a/examples/python/reconstruction_system/refine_registration.py b/examples/python/reconstruction_system/refine_registration.py index 3fec353c1e0..0e6a54e84e9 100644 --- a/examples/python/reconstruction_system/refine_registration.py +++ b/examples/python/reconstruction_system/refine_registration.py @@ -79,6 +79,7 @@ def multiscale_icp(source, result_icp = o3d.pipelines.registration.registration_colored_icp( source_down, target_down, voxel_size[scale], current_transformation, + TransformationEstimationForColoredICP(), o3d.pipelines.registration.ICPConvergenceCriteria( relative_fitness=1e-6, relative_rmse=1e-6,