11 #include <Eigen/Dense>
12 #include <Eigen/Sparse>
22 const std::vector<int>& constraint_vertex_indices,
23 const std::vector<Eigen::Vector3d>& constraint_vertex_positions,
26 double smoothed_alpha)
const {
32 auto prime = std::make_shared<ccMesh>(baseVertices);
33 prime->addChild(baseVertices);
34 baseVertices->
addPoints(this->getVertices());
35 prime->addTriangles(this->getTriangles());
38 prime->ComputeAdjacencyList();
39 auto edges_to_vertices = prime->GetEdgeToVerticesMap();
41 prime->ComputeEdgeWeightsCot(edges_to_vertices, 0);
44 std::unordered_map<int, Eigen::Vector3d> constraints;
45 for (
size_t idx = 0; idx < constraint_vertex_indices.size() &&
46 idx < constraint_vertex_positions.size();
48 constraints[constraint_vertex_indices[idx]] =
49 constraint_vertex_positions[idx];
52 double surface_area = -1;
55 std::vector<Eigen::Matrix3d> Rs(getVerticeSize());
56 std::vector<Eigen::Matrix3d> Rs_old;
57 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed) {
58 surface_area = prime->GetSurfaceArea();
59 Rs_old.resize(getVerticeSize());
64 std::vector<Eigen::Triplet<double>> triplets;
65 for (
int i = 0; i < int(getVerticeSize()); ++i) {
66 if (constraints.count(i) > 0) {
67 triplets.push_back(Eigen::Triplet<double>(i, i, 1));
70 for (
int j : prime->adjacency_list_[i]) {
72 triplets.push_back(Eigen::Triplet<double>(i, j, -w));
76 triplets.push_back(Eigen::Triplet<double>(i, i, W));
80 Eigen::SparseMatrix<double> L(getVerticeSize(), getVerticeSize());
81 L.setFromTriplets(triplets.begin(), triplets.end());
83 "[DeformAsRigidAsPossible] done setting up system matrix L");
86 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
87 solver.analyzePattern(L);
89 if (solver.info() != Eigen::Success) {
91 "[DeformAsRigidAsPossible] Failed to build solver (factorize)");
94 "[DeformAsRigidAsPossible] done setting up sparse solver");
97 std::vector<Eigen::VectorXd> b = {Eigen::VectorXd(getVerticeSize()),
98 Eigen::VectorXd(getVerticeSize()),
99 Eigen::VectorXd(getVerticeSize())};
101 for (
size_t iter = 0; iter < max_iter; ++iter) {
102 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed) {
106 #pragma omp parallel for schedule(static) \
107 num_threads(utility::EstimateMaxThreads())
109 for (
int i = 0; i < int(getVerticeSize()); ++i) {
111 Eigen::Matrix3d S = Eigen::Matrix3d::Zero();
112 Eigen::Matrix3d R = Eigen::Matrix3d::Zero();
114 for (
int j : prime->adjacency_list_[i]) {
115 Eigen::Vector3d e0 = getVertice(
static_cast<size_t>(i)) -
116 getVertice(
static_cast<size_t>(j));
117 Eigen::Vector3d e1 = prime->getVertice(
static_cast<size_t>(i)) -
118 prime->getVertice(
static_cast<size_t>(j));
120 S += w * (e0 * e1.transpose());
121 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed) {
126 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed &&
127 iter > 0 && n_nbs > 0) {
129 (4 * smoothed_alpha * surface_area / n_nbs) * R.transpose();
131 Eigen::JacobiSVD<Eigen::Matrix3d> svd(
132 S, Eigen::ComputeFullU | Eigen::ComputeFullV);
133 Eigen::Matrix3d U = svd.matrixU();
134 Eigen::Matrix3d V = svd.matrixV();
135 Eigen::Vector3d D(1, 1, (V * U.transpose()).determinant());
138 Rs[i] = V * D.asDiagonal() * U.transpose();
139 if (Rs[i].determinant() <= 0) {
141 "[DeformAsRigidAsPossible] something went wrong with "
147 #pragma omp parallel for schedule(static) \
148 num_threads(utility::EstimateMaxThreads())
150 for (
int i = 0; i < int(getVerticeSize()); ++i) {
152 Eigen::Vector3d bi(0, 0, 0);
153 if (constraints.count(i) > 0) {
156 for (
int j : prime->adjacency_list_[i]) {
160 (getVertice(
static_cast<size_t>(i)) -
161 getVertice(
static_cast<size_t>(j))));
169 #pragma omp parallel for schedule(static) \
170 num_threads(utility::EstimateMaxThreads())
172 for (
int comp = 0; comp < 3; ++comp) {
173 Eigen::VectorXd p_prime = solver.solve(b[comp]);
174 if (solver.info() != Eigen::Success) {
176 "[DeformAsRigidAsPossible] Cholesky solve failed");
178 for (
int i = 0; i < int(getVerticeSize()); ++i) {
179 baseVertices->
getPointPtr(
static_cast<size_t>(i))->u[comp] =
187 for (
int i = 0; i < int(getVerticeSize()); ++i) {
188 for (
int j : prime->adjacency_list_[i]) {
190 Eigen::Vector3d e0 = getVertice(
static_cast<size_t>(i)) -
191 getVertice(
static_cast<size_t>(j));
192 Eigen::Vector3d e1 = prime->getVertice(
static_cast<size_t>(i)) -
193 prime->getVertice(
static_cast<size_t>(j));
194 Eigen::Vector3d diff = e1 - Rs[i] * e0;
195 energy += w * diff.squaredNorm();
196 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed) {
197 reg += (Rs[i] - Rs[j]).squaredNorm();
201 if (energy_model == DeformAsRigidAsPossibleEnergy::Smoothed) {
202 energy = energy + smoothed_alpha * surface_area * reg;
211 prime->shrinkToFit();
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Array of compressed 3D normals (single index)
std::shared_ptr< ccMesh > DeformAsRigidAsPossible(const std::vector< int > &constraint_vertex_indices, const std::vector< Eigen::Vector3d > &constraint_vertex_positions, size_t max_iter, DeformAsRigidAsPossibleEnergy energy=DeformAsRigidAsPossibleEnergy::Spokes, double smoothed_alpha=0.01) const
This function deforms the mesh using the method by Sorkine and Alexa, "As-Rigid-As-Possible Surface M...
virtual void setLocked(bool state)
Sets the "enabled" property.
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void shrinkToFit()
Removes unused capacity.
DeformAsRigidAsPossibleEnergy
CCVector3 * getPointPtr(size_t index)
void addPoints(const std::vector< CCVector3 > &points)
static Edge< T > GetOrderedEdge(T vidx0, T vidx1)
brief Helper function to get an edge with ordered vertex indices.
Generic file read and write utility for python interface.
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.