1 #include "surface_approx.h"
3 #include <DGtal/dec/DiscreteExteriorCalculusFactory.h>
5 bool ends_with(
const std::string& value,
const std::string& ending)
7 if (ending.size() > value.size())
return false;
8 return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
11 template <
typename Value>
13 format_to(
const std::string& str)
22 std::tuple<Calculus, FlatVector>
23 initCalculusAndNormalsFromSurfelNormalsCSV(
const std::string& filename)
29 trace.beginBlock(
"loading surfels and normals from csv file");
31 trace.info() <<
"filename=" << filename << endl;
33 typedef DGtal::Z3i::RealVector RealVector;
34 typedef std::map<SCell, RealVector> Normals;
35 typedef std::vector<SCell> SCells;
39 static const std::string float_re =
"([-+]?([[:digit:]]*\\.?[[:digit:]]+|[[:digit:]]+\\.?[[:digit:]]*)([eE][-+]?[[:digit:]]+)?)";
40 static const std::regex line_re(
"^ *(-?[[:digit:]]+) +(-?[[:digit:]]+) +(-?[[:digit:]]+) +([0-1]) +"+float_re+
" +"+float_re+
" +"+float_re+
" *$", std::regex::extended);
47 std::ifstream handle(filename);
52 std::getline(handle, line);
53 if (!handle.good())
break;
55 if (std::regex_match(line, what, line_re) <= 0)
56 throw std::ios_base::failure(
"bad csv format");
58 ASSERT( what.size() == 14 );
60 const std::string xx_string(what[1].first, what[1].second);
61 const std::string yy_string(what[2].first, what[2].second);
62 const std::string zz_string(what[3].first, what[3].second);
63 const std::string sign_string(what[4].first, what[4].second);
64 const std::string nx_string(what[5].first, what[5].second);
65 const std::string ny_string(what[8].first, what[8].second);
66 const std::string nz_string(what[11].first, what[11].second);
69 point[0] = format_to<Point::Coordinate>(xx_string);
70 point[1] = format_to<Point::Coordinate>(yy_string);
71 point[2] = format_to<Point::Coordinate>(zz_string);
73 const bool positive = format_to<bool>(sign_string);
75 const SCell cell = kspace.sCell(point, positive);
77 cells.push_back(cell);
80 normal[0] = format_to<RealPoint::Coordinate>(nx_string);
81 normal[1] = format_to<RealPoint::Coordinate>(ny_string);
82 normal[2] = format_to<RealPoint::Coordinate>(nz_string);
84 normals.insert(std::make_pair(cell, normal));
87 catch (std::ios_base::failure &fail)
89 trace.error() << fail.what() << endl;
94 trace.info() <<
"cells_size=" << cells.size() << endl;
95 trace.info() <<
"normals_size=" << normals.size() << endl;
97 ASSERT( normals.size() == cells.size() );
99 Calculus calculus = DGtal::DiscreteExteriorCalculusFactory<Backend, int32_t>::createFromNSCells<2>(cells.begin(), cells.end(),
true);
101 const auto buildFlatVector = [&kspace, &calculus](
const Normals& real_vectors)
103 ASSERT( real_vectors.size() == calculus.kFormLength(2, PRIMAL) );
104 const int nsurfels = (
const int)real_vectors.size();
105 FlatVector vectors(3*real_vectors.size());
106 for (
const std::pair<SCell, RealVector>& cell_pair : real_vectors)
108 const int index = (
const int)calculus.getCellIndex(kspace.unsigns(cell_pair.first));
109 for (
int dim=0; dim<3; dim++)
110 vectors[index+nsurfels*dim] = cell_pair.second[dim];
117 return std::make_tuple(calculus, buildFlatVector(normals));
121 exportOBJ(
const Calculus& calculus,
const FlatVector& positions,
const std::string& filename)
126 ASSERT( !filename.empty() );
128 const KSpace& kspace = calculus.myKSpace;
129 const int nvertices = (
const int)calculus.kFormLength(0, PRIMAL);
131 ASSERT( positions.size() == 3*nvertices );
132 typedef Calculus::Index Index;
134 std::ofstream handle(filename);
136 for (
int index=0; index<nvertices; index++)
139 for (
int dim=0; dim<3; dim++)
140 handle <<
" " << positions[index+dim*nvertices];
144 for (
const Calculus::ConstIterator::value_type& pair : calculus)
146 const Cell& face = pair.first;
147 if (kspace.uDim(face) != 2)
continue;
149 std::vector<Cell> vertices;
151 std::set<Cell> vertices_set;
152 for (
const Cell& edge : kspace.uLowerIncident(face))
153 for (
const Cell& vertex : kspace.uLowerIncident(edge))
154 if (calculus.containsCell(vertex))
155 vertices_set.insert(vertex);
156 ASSERT( vertices_set.size() == 4 );
157 std::copy(vertices_set.begin(), vertices_set.end(), std::back_inserter(vertices));
158 std::swap(vertices[2], vertices[3]);
161 if (pair.second.flipped)
163 std::swap(vertices[0], vertices[1]);
164 std::swap(vertices[2], vertices[3]);
168 for (
const Cell& cell : vertices)
170 const Index index = calculus.getCellIndex(cell);
171 ASSERT( index >= 0 && index < nvertices );
172 handle <<
" " << (index+1);
179 vertexNormals(
const Calculus& calculus,
const FlatVector& face_normals)
183 const KSpace& kspace = calculus.myKSpace;
184 const int nfaces = (
const int)calculus.kFormLength(2, PRIMAL);
185 const int nvertices = (
const int)calculus.kFormLength(0, PRIMAL);
187 ASSERT( face_normals.size() == 3*nfaces );
189 typedef Calculus::Index Index;
191 FlatVector vertex_normals = FlatVector::Zero(3*nvertices);
193 Index face_index = 0;
194 for (
const SCell& signed_face : calculus.getIndexedSCells<2, PRIMAL>())
196 const Cell& face = kspace.unsigns(signed_face);
198 std::set<Cell> face_vertices;
199 for (
const Cell& edge : kspace.uLowerIncident(face))
200 for (
const Cell& vertex : kspace.uLowerIncident(edge))
201 if (calculus.containsCell(vertex))
202 face_vertices.insert(vertex);
203 ASSERT( face_vertices.size() == 4 );
205 for (
const Cell& vertex : face_vertices)
207 const Index vertex_index = calculus.getCellIndex(vertex);
208 for (
int dim=0; dim<3; dim++)
209 vertex_normals[vertex_index+dim*nvertices] += face_normals[face_index+dim*nfaces];
216 for (Index vertex_index=0; vertex_index<nvertices; vertex_index++)
219 for (
int dim=0; dim<3; dim++)
221 const double foo = vertex_normals[vertex_index+dim*nvertices];
225 if (norm == 0)
continue;
226 for (
int dim=0; dim<3; dim++)
227 vertex_normals[vertex_index+dim*nvertices] /= norm;
230 return vertex_normals;
234 checkOperatorSymmetry(
const OperatorMatrix& matrix,
const double tol)
236 typedef std::vector<Triplet> Triplets;
238 Triplets matrix_transpose_triplets;
239 for (
int kk=0; kk<matrix.outerSize(); kk++)
240 for(OperatorMatrix::InnerIterator ii(matrix, kk); ii; ++ii)
241 matrix_transpose_triplets.push_back(Triplet(ii.col(), ii.row(), ii.value()));
243 OperatorMatrix matrix_transpose(matrix.cols(), matrix.rows());
244 matrix_transpose.setFromTriplets(matrix_transpose_triplets.begin(), matrix_transpose_triplets.end());
245 const OperatorMatrix foo = matrix - matrix_transpose;
247 for (
int kk=0; kk<foo.outerSize(); kk++)
248 for(OperatorMatrix::InnerIterator ii(foo, kk); ii; ++ii)
249 if (std::abs(ii.value()) > tol)
255 std::tuple<double, double>
256 approximateSurfaceEnergies(
const Calculus& calculus,
const FlatVector& normals,
const FlatVector& positions)
260 typedef Calculus::Index Index;
262 const KSpace& kspace = calculus.myKSpace;
263 const CellEmbedder embedder(kspace);
264 const int nfaces = (
const int)calculus.kFormLength(2, PRIMAL);
265 const int nvertices = (
const int)calculus.kFormLength(0, PRIMAL);
267 ASSERT( normals.size() == 3*nfaces );
268 ASSERT( positions.size() == 3*nvertices );
270 double position_energy = 0;
271 for (
const Calculus::ConstIterator::value_type& pair : calculus)
273 const Cell& vertex = pair.first;
274 if (kspace.uDim(vertex) != 0)
continue;
275 const Index vertex_index = calculus.getCellIndex(vertex);
277 ASSERT( pair.second.primal_size == 1 );
278 const double area = pair.second.dual_size;
280 const RealPoint original_position = embedder(vertex);
282 for (
int dim=0; dim<3; dim++)
283 position[dim] = positions[vertex_index+dim*nvertices];
285 for (
int dim=0; dim<3; dim++)
286 position_energy += area*pow(position[dim]-original_position[dim], 2);
289 double align_energy = 0;
290 for (
const Calculus::ConstIterator::value_type& pair : calculus)
292 const Cell& face = pair.first;
293 if (kspace.uDim(face) != 2)
continue;
295 ASSERT( pair.second.dual_size == 1 );
296 const double area = pair.second.primal_size;
299 RealPoint face_normal;
300 for (
int dim=0; dim<3; dim++)
301 face_normal[dim] = normals[pair.second.index+dim*nfaces];
303 std::vector<Cell> vertices;
305 std::set<Cell> vertices_set;
306 for (
const Cell& edge : kspace.uLowerIncident(face))
307 for (
const Cell& vertex : kspace.uLowerIncident(edge))
308 if (calculus.containsCell(vertex))
309 vertices_set.insert(vertex);
310 ASSERT( vertices_set.size() == 4 );
311 std::copy(vertices_set.begin(), vertices_set.end(), std::back_inserter(vertices));
312 std::swap(vertices[2], vertices[3]);
315 std::vector<RealPoint> vertex_positions;
316 for (
const Cell& vertex : vertices)
318 const Index vertex_index = calculus.getCellIndex(vertex);
320 for (
int dim=0; dim<3; dim++)
321 position[dim] = positions[vertex_index+dim*nvertices];
322 vertex_positions.push_back(position);
325 for (
int kk=0; kk<4; kk++)
327 const int kk_next = (kk+1)%4;
328 align_energy += area*pow(face_normal.dot(vertex_positions[kk]-vertex_positions[kk_next]), 2);
332 return std::make_tuple(position_energy, align_energy);
335 std::tuple<FlatVector, FlatVector, FlatVector, FlatVector>
336 approximateSurface(
const Calculus& calculus,
const FlatVector& normals,
const ApproxParams& params)
342 trace.beginBlock(
"approximating surface");
344 typedef Calculus::Index Index;
345 typedef std::vector<Triplet> Triplets;
347 const KSpace& kspace = calculus.myKSpace;
348 auto nfaces = calculus.kFormLength(2, PRIMAL);
349 auto nedges = calculus.kFormLength(1, PRIMAL);
350 auto nvertices = calculus.kFormLength(0, PRIMAL);
351 trace.info() <<
"nfaces=" << nfaces << endl;
352 trace.info() <<
"nedges=" << nedges << endl;
353 trace.info() <<
"nvertices=" << nvertices << endl;
355 trace.info() <<
"computing operator" << endl;
357 FlatVector original_centers(3*nfaces);
358 OperatorMatrix big_primal_hodge_2(3*nfaces, 3*nfaces);
359 OperatorMatrix vertex_positions_to_face_centers(3*nfaces, 3*nvertices);
360 OperatorMatrix align_normals(3*nvertices, 3*nvertices);
361 OperatorMatrix fairness_operator(3*nvertices, 3*nvertices);
363 Triplets big_primal_hodge_2_triplets;
364 Triplets vertex_positions_to_face_centers_triplets;
365 Triplets align_normals_triplets;
366 Triplets fairness_operator_triplets;
367 for (
const Calculus::ConstIterator::value_type& pair : calculus)
369 const Cell& face = pair.first;
370 if (kspace.uDim(face) != 2)
continue;
371 const Index face_index = pair.second.index;
372 const double face_area = pair.second.dual_size/pair.second.primal_size;
373 ASSERT( face_area == 1 );
376 const double hodge_coeff = pair.second.dual_size/pair.second.primal_size;
377 ASSERT( hodge_coeff == 1 );
378 for (
int dim=0; dim<3; dim++)
379 big_primal_hodge_2_triplets.push_back(Triplet(face_index+dim*nfaces, face_index+dim*nfaces, hodge_coeff));
382 std::set<Cell> face_vertices;
383 for (
const Cell& edge : kspace.uLowerIncident(face))
384 for (
const Cell& vertex : kspace.uLowerIncident(edge))
385 if (calculus.containsCell(vertex))
386 face_vertices.insert(vertex);
387 ASSERT( face_vertices.size() == 4 );
391 std::set<Cell>::const_iterator aa = face_vertices.begin();
392 std::set<Cell>::const_iterator bb = face_vertices.begin();
393 ASSERT( bb != face_vertices.end() );
395 ASSERT( bb != face_vertices.end() );
396 std::vector<double> lengths;
397 while (aa != face_vertices.end())
399 const double length = (kspace.uKCoords(*aa)-kspace.uKCoords(*bb)).norm();
400 lengths.push_back(length);
403 if (bb == face_vertices.end()) bb= face_vertices.begin();
405 ASSERT( lengths == std::vector<double>({2,sqrt(8),2,sqrt(8)}) );
409 std::vector<Cell> ordered_face_vertices;
410 std::copy(face_vertices.begin(), face_vertices.end(), std::back_inserter(ordered_face_vertices));
411 std::swap(ordered_face_vertices[2], ordered_face_vertices[3]);
414 for (
int normal_dim_ii=0; normal_dim_ii<3; normal_dim_ii++)
415 for (
int normal_dim_jj=0; normal_dim_jj<3; normal_dim_jj++)
417 const double normal_coeff = face_area*normals(face_index+normal_dim_ii*nfaces)*normals(face_index+normal_dim_jj*nfaces);
419 for (
int kk_vertex=0; kk_vertex<4; kk_vertex++)
421 const int kk_vertex_next = (kk_vertex+1)%4;
422 const int kk_vertex_prev = (kk_vertex+3)%4;
423 ASSERT( (kspace.uKCoords(ordered_face_vertices[kk_vertex])-kspace.uKCoords(ordered_face_vertices[kk_vertex_next])).norm() == 2 );
424 ASSERT( (kspace.uKCoords(ordered_face_vertices[kk_vertex])-kspace.uKCoords(ordered_face_vertices[kk_vertex_prev])).norm() == 2 );
425 const Index index_vertex = calculus.getCellIndex(ordered_face_vertices[kk_vertex]);
426 const Index index_vertex_next = calculus.getCellIndex(ordered_face_vertices[kk_vertex_next]);
427 const Index index_vertex_prev = calculus.getCellIndex(ordered_face_vertices[kk_vertex_prev]);
428 align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex, 2*normal_coeff));
429 align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex_prev, -normal_coeff));
430 align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex_next, -normal_coeff));
436 for (
int kk_vertex=0; kk_vertex<4; kk_vertex++)
438 const Index kk_index = calculus.getCellIndex(ordered_face_vertices[kk_vertex]);
439 for (
int ll_vertex=0; ll_vertex<4; ll_vertex++)
441 const Index ll_index = calculus.getCellIndex(ordered_face_vertices[ll_vertex]);
442 const double coeff = kk_vertex%2 == ll_vertex%2 ? face_area : -face_area;
443 for (
int dim_kk=0; dim_kk<3; dim_kk++)
444 for (
int dim_ll=0; dim_ll<3; dim_ll++)
445 fairness_operator_triplets.push_back(Triplet(kk_index+dim_kk*nvertices, ll_index+dim_kk*nvertices, coeff));
451 const CellEmbedder embedder(kspace);
452 RealPoint original_center;
453 for (
const Cell& face_vertex : face_vertices)
455 original_center += embedder(face_vertex);
456 const Index vertex_index = calculus.getCellIndex(face_vertex);
457 for (
int dim=0; dim<3; dim++)
458 vertex_positions_to_face_centers_triplets.push_back(Triplet(face_index+dim*nfaces, vertex_index+dim*nvertices, 1./4));
460 original_center /= 4;
462 for (
int dim=0; dim<3; dim++) original_centers[face_index+dim*nfaces] = original_center[dim];
465 big_primal_hodge_2.setFromTriplets(big_primal_hodge_2_triplets.begin(), big_primal_hodge_2_triplets.end());
466 vertex_positions_to_face_centers.setFromTriplets(vertex_positions_to_face_centers_triplets.begin(), vertex_positions_to_face_centers_triplets.end());
467 align_normals.setFromTriplets(align_normals_triplets.begin(), align_normals_triplets.end());
468 fairness_operator.setFromTriplets(fairness_operator_triplets.begin(), fairness_operator_triplets.end());
472 FlatVector original_positions(3*nvertices);
473 OperatorMatrix big_primal_hodge_0(3*nvertices, 3*nvertices);
474 OperatorMatrix vertex_barycenter(3*nvertices, 3*nvertices);
476 const CellEmbedder embedder(kspace);
477 Triplets big_primal_hodge_0_triplets;
478 Triplets vertex_barycenter_triplets;
479 for (
const Calculus::ConstIterator::value_type& pair : calculus)
481 const Cell& vertex = pair.first;
482 if (kspace.uDim(vertex) != 0)
continue;
483 const Index vertex_index = calculus.getCellIndex(vertex);
486 ASSERT( pair.second.primal_size == 1 );
487 const double hodge_coeff = pair.second.dual_size/pair.second.primal_size;
488 for (
int dim=0; dim<3; dim++)
489 big_primal_hodge_0_triplets.push_back(Triplet(vertex_index+dim*nvertices, vertex_index+dim*nvertices, hodge_coeff));
493 const RealPoint coords = embedder(vertex);
494 for (
int dim=0; dim<3; dim++) original_positions[pair.second.index+dim*nvertices] = coords[dim];
498 std::set<Cell> neighbor_vertices;
499 std::set<Cell> neighbor_edges;
500 std::set<Cell> neighbor_faces;
501 for (
const Cell& edge : kspace.uUpperIncident(vertex))
502 if (calculus.containsCell(edge))
504 neighbor_edges.insert(edge);
505 for (
const Cell& neighbor_vertex : kspace.uLowerIncident(edge))
506 if (neighbor_vertex != vertex && calculus.containsCell(neighbor_vertex))
507 neighbor_vertices.insert(neighbor_vertex);
508 for (
const Cell& neighbor_face : kspace.uUpperIncident(edge))
509 if (calculus.containsCell(neighbor_face))
510 neighbor_faces.insert(neighbor_face);
513 const bool on_border = (neighbor_edges.size()-1 == neighbor_faces.size());
517 ASSERT( neighbor_vertices.size() >= 3 );
518 ASSERT( neighbor_vertices.size() <= 6 );
519 if (neighbor_vertices.size() != pair.second.dual_size*4) trace.warning() <<
"non 2-manifold!!!!" << endl;
521 const double coeff = 1./neighbor_vertices.size();
522 for (
const Cell& neighbor_vertex : neighbor_vertices)
524 const Index neighbor_vertex_index = calculus.getCellIndex(neighbor_vertex);
525 for (
int dim=0; dim<3; dim++)
526 vertex_barycenter_triplets.push_back(Triplet(vertex_index+dim*nvertices, neighbor_vertex_index+dim*nvertices, coeff));
529 for (
int dim=0; dim<3; dim++)
530 vertex_barycenter_triplets.push_back(Triplet(vertex_index+dim*nvertices, vertex_index+dim*nvertices, -1));
534 big_primal_hodge_0.setFromTriplets(big_primal_hodge_0_triplets.begin(), big_primal_hodge_0_triplets.end());
535 vertex_barycenter.setFromTriplets(vertex_barycenter_triplets.begin(), vertex_barycenter_triplets.end());
538 ASSERT( (vertex_positions_to_face_centers*original_positions-original_centers).array().maxCoeff() < 1e-5 );
541 typedef Backend::SolverSimplicialLDLT LinearSolver;
544 const double gamma = params.
align;
545 const double beta = params.
fairness;
547 trace.info() <<
"lambda=" << lambda <<
" (vertex regularization)" << endl;
548 trace.info() <<
"alpha=" << alpha <<
" (face center regularization)" << endl;
549 trace.info() <<
"gamma=" << gamma <<
" (normal align)" << endl;
550 trace.info() <<
"beta=" << beta <<
" (fairness)" << endl;
551 trace.info() <<
"rau=" << rau <<
" (barycenter)" << endl;
553 const OperatorMatrix ope =
554 lambda*2*big_primal_hodge_0 +
555 alpha*2*vertex_positions_to_face_centers.transpose()*big_primal_hodge_2*vertex_positions_to_face_centers +
556 gamma*align_normals +
557 beta*2*fairness_operator +
558 rau*2*vertex_barycenter.transpose()*big_primal_hodge_0*vertex_barycenter;
559 const FlatVector rht =
560 lambda*2*big_primal_hodge_0*original_positions +
561 alpha*2*vertex_positions_to_face_centers.transpose()*big_primal_hodge_2*original_centers;
563 using namespace DGtal;
565 trace.info() <<
"factorizing " << (checkOperatorSymmetry(ope) ?
"" :
"non-") <<
"symmetric operator ";
566 LinearSolver linear_solver;
567 linear_solver.compute(ope);
568 std::cout << linear_solver.info() << endl;
570 if (linear_solver.info() == Eigen::Success)
572 trace.info() <<
"solving problem ";
573 const FlatVector regularized_positions = linear_solver.solve(rht);
574 std::cout << linear_solver.info() << endl;
576 if (linear_solver.info() == Eigen::Success)
579 return std::make_tuple(original_positions, regularized_positions, original_centers, FlatVector(vertex_positions_to_face_centers*regularized_positions));
584 return std::make_tuple(FlatVector(), FlatVector(), FlatVector(), FlatVector());
double regularization_position
double regularization_center