ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
meshing.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "mvs/meshing.h"
33 
34 #include <fstream>
35 #include <unordered_map>
36 #include <vector>
37 
38 #ifdef CGAL_ENABLED
39 #include <CGAL/Delaunay_triangulation_3.h>
40 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
41 #endif // CGAL_ENABLED
42 
43 #include "PoissonRecon/PoissonRecon.h"
44 #include "PoissonRecon/SurfaceTrimmer.h"
45 #include "base/graph_cut.h"
46 #include "base/reconstruction.h"
47 #include "util/endian.h"
48 #include "util/logging.h"
49 #include "util/misc.h"
50 #include "util/option_manager.h"
51 #include "util/ply.h"
52 #include "util/random.h"
53 #include "util/threading.h"
54 #include "util/timer.h"
55 
56 #ifdef CGAL_ENABLED
57 
58 typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
59 typedef CGAL::Delaunay_triangulation_3<K, CGAL::Fast_location> Delaunay;
60 
61 namespace std {
62 
63 template <>
64 struct hash<Delaunay::Vertex_handle> {
65  std::size_t operator()(const Delaunay::Vertex_handle& handle) const {
66  return reinterpret_cast<std::size_t>(&*handle);
67  }
68 };
69 
70 template <>
71 struct hash<const Delaunay::Vertex_handle> {
72  std::size_t operator()(const Delaunay::Vertex_handle& handle) const {
73  return reinterpret_cast<std::size_t>(&*handle);
74  }
75 };
76 
77 template <>
78 struct hash<Delaunay::Cell_handle> {
79  std::size_t operator()(const Delaunay::Cell_handle& handle) const {
80  return reinterpret_cast<std::size_t>(&*handle);
81  }
82 };
83 
84 template <>
85 struct hash<const Delaunay::Cell_handle> {
86  std::size_t operator()(const Delaunay::Cell_handle& handle) const {
87  return reinterpret_cast<std::size_t>(&*handle);
88  }
89 };
90 
91 } // namespace std
92 
93 #endif // CGAL_ENABLED
94 
95 namespace colmap {
96 namespace mvs {
97 
102  CHECK_OPTION_GE(trim, 0);
105  return true;
106 }
107 
120  return true;
121 }
122 
124  const std::string& input_path,
125  const std::string& output_path) {
126  CHECK(options.Check());
127 
128  std::vector<std::string> args;
129 
130  args.push_back("./binary");
131 
132  args.push_back("--in");
133  args.push_back(input_path);
134 
135  args.push_back("--out");
136  args.push_back(output_path);
137 
138  args.push_back("--pointWeight");
139  args.push_back(std::to_string(options.point_weight));
140 
141  args.push_back("--depth");
142  args.push_back(std::to_string(options.depth));
143 
144  if (options.color > 0) {
145  args.push_back("--color");
146  args.push_back(std::to_string(options.color));
147  }
148 
149 #ifdef OPENMP_ENABLED
150  if (options.num_threads > 0) {
151  args.push_back("--threads");
152  args.push_back(std::to_string(options.num_threads));
153  }
154 #endif // OPENMP_ENABLED
155 
156  if (options.trim > 0) {
157  args.push_back("--density");
158  }
159 
160  std::vector<const char*> args_cstr;
161  args_cstr.reserve(args.size());
162  for (const auto& arg : args) {
163  args_cstr.push_back(arg.c_str());
164  }
165 
166  if (PoissonRecon(args_cstr.size(), const_cast<char**>(args_cstr.data())) !=
167  EXIT_SUCCESS) {
168  return false;
169  }
170 
171  if (options.trim == 0) {
172  return true;
173  }
174 
175  args.clear();
176  args_cstr.clear();
177 
178  args.push_back("./binary");
179 
180  args.push_back("--in");
181  args.push_back(output_path);
182 
183  args.push_back("--out");
184  args.push_back(output_path);
185 
186  args.push_back("--trim");
187  args.push_back(std::to_string(options.trim));
188 
189  args_cstr.reserve(args.size());
190  for (const auto& arg : args) {
191  args_cstr.push_back(arg.c_str());
192  }
193 
194  return SurfaceTrimmer(args_cstr.size(),
195  const_cast<char**>(args_cstr.data())) == EXIT_SUCCESS;
196 }
197 
198 #ifdef CGAL_ENABLED
199 
200 K::Point_3 EigenToCGAL(const Eigen::Vector3f& point) {
201  return K::Point_3(point.x(), point.y(), point.z());
202 }
203 
204 Eigen::Vector3f CGALToEigen(const K::Point_3& point) {
205  return Eigen::Vector3f(point.x(), point.y(), point.z());
206 }
207 
208 class DelaunayMeshingInput {
209  public:
210  struct Image {
211  camera_t camera_id = kInvalidCameraId;
212  Eigen::Matrix3x4f proj_matrix = Eigen::Matrix3x4f::Identity();
213  Eigen::Vector3f proj_center = Eigen::Vector3f::Zero();
214  std::vector<size_t> point_idxs;
215  };
216 
217  struct Point {
218  Eigen::Vector3f position = Eigen::Vector3f::Zero();
219  uint32_t num_visible_images = 0;
220  };
221 
222  std::unordered_map<camera_t, Camera> cameras;
223  std::vector<Image> images;
224  std::vector<Point> points;
225 
226  void ReadSparseReconstruction(const std::string& path) {
227  Reconstruction reconstruction;
228  reconstruction.Read(path);
229  CopyFromSparseReconstruction(reconstruction);
230  }
231 
232  void CopyFromSparseReconstruction(const Reconstruction& reconstruction) {
233  images.reserve(reconstruction.NumRegImages());
234  points.reserve(reconstruction.NumPoints3D());
235 
236  cameras = reconstruction.Cameras();
237 
238  std::unordered_map<point3D_t, size_t> point_id_to_idx;
239  point_id_to_idx.reserve(reconstruction.NumPoints3D());
240  for (const auto& point3D : reconstruction.Points3D()) {
241  point_id_to_idx.emplace(point3D.first, points.size());
242  DelaunayMeshingInput::Point input_point;
243  input_point.position = point3D.second.XYZ().cast<float>();
244  input_point.num_visible_images = point3D.second.Track().Length();
245  points.push_back(input_point);
246  }
247 
248  for (const auto image_id : reconstruction.RegImageIds()) {
249  const auto& image = reconstruction.Image(image_id);
250  DelaunayMeshingInput::Image input_image;
251  input_image.camera_id = image.CameraId();
252  input_image.proj_matrix = image.ProjectionMatrix().cast<float>();
253  input_image.proj_center = image.ProjectionCenter().cast<float>();
254  input_image.point_idxs.reserve(image.NumPoints3D());
255  for (const auto& point2D : image.Points2D()) {
256  if (point2D.HasPoint3D()) {
257  input_image.point_idxs.push_back(
258  point_id_to_idx.at(point2D.Point3DId()));
259  }
260  }
261  images.push_back(input_image);
262  }
263  }
264 
265  void ReadDenseReconstruction(const std::string& path) {
266  {
267  Reconstruction reconstruction;
268  reconstruction.Read(JoinPaths(path, "sparse"));
269 
270  cameras = reconstruction.Cameras();
271 
272  images.reserve(reconstruction.NumRegImages());
273  for (const auto& image_id : reconstruction.RegImageIds()) {
274  const auto& image = reconstruction.Image(image_id);
275  DelaunayMeshingInput::Image input_image;
276  input_image.camera_id = image.CameraId();
277  input_image.proj_matrix = image.ProjectionMatrix().cast<float>();
278  input_image.proj_center = image.ProjectionCenter().cast<float>();
279  images.push_back(input_image);
280  }
281  }
282 
283  const auto& ply_points = ReadPly(JoinPaths(path, "fused.ply"));
284 
285  const std::string vis_path = JoinPaths(path, "fused.ply.vis");
286  std::fstream vis_file(vis_path, std::ios::in | std::ios::binary);
287  CHECK(vis_file.is_open()) << vis_path;
288 
289  const size_t vis_num_points = ReadBinaryLittleEndian<uint64_t>(&vis_file);
290  CHECK_EQ(vis_num_points, ply_points.size());
291 
292  points.reserve(ply_points.size());
293  for (const auto& ply_point : ply_points) {
294  const int point_idx = points.size();
295  DelaunayMeshingInput::Point input_point;
296  input_point.position =
297  Eigen::Vector3f(ply_point.x, ply_point.y, ply_point.z);
298  input_point.num_visible_images =
299  ReadBinaryLittleEndian<uint32_t>(&vis_file);
300  for (uint32_t i = 0; i < input_point.num_visible_images; ++i) {
301  const int image_idx = ReadBinaryLittleEndian<uint32_t>(&vis_file);
302  images.at(image_idx).point_idxs.push_back(point_idx);
303  }
304  points.push_back(input_point);
305  }
306  }
307 
308  Delaunay CreateDelaunayTriangulation() const {
309  std::vector<Delaunay::Point> delaunay_points(points.size());
310  for (size_t i = 0; i < points.size(); ++i) {
311  delaunay_points[i] =
312  Delaunay::Point(points[i].position.x(), points[i].position.y(),
313  points[i].position.z());
314  }
315  return Delaunay(delaunay_points.begin(), delaunay_points.end());
316  }
317 
318  Delaunay CreateSubSampledDelaunayTriangulation(
319  const float max_proj_dist, const float max_depth_dist) const {
320  CHECK_GE(max_proj_dist, 0);
321 
322  if (max_proj_dist == 0) {
323  return CreateDelaunayTriangulation();
324  }
325 
326  std::vector<std::vector<uint32_t>> points_visible_image_idxs(points.size());
327  for (size_t image_idx = 0; image_idx < images.size(); ++image_idx) {
328  for (const auto& point_idx : images[image_idx].point_idxs) {
329  points_visible_image_idxs[point_idx].push_back(image_idx);
330  }
331  }
332 
333  std::vector<size_t> point_idxs(points.size());
334  std::iota(point_idxs.begin(), point_idxs.end(), 0);
335  Shuffle(point_idxs.size(), &point_idxs);
336 
337  Delaunay triangulation;
338 
339  const float max_squared_proj_dist = max_proj_dist * max_proj_dist;
340  const float min_depth_ratio = 1.0f - max_depth_dist;
341  const float max_depth_ratio = 1.0f + max_depth_dist;
342 
343  for (const auto point_idx : point_idxs) {
344  const auto& point = points[point_idx];
345  const auto& visible_image_idxs = points_visible_image_idxs[point_idx];
346 
347  const K::Point_3 point_position = EigenToCGAL(point.position);
348 
349  // Insert point into triangulation until there is one cell.
350  if (triangulation.number_of_vertices() < 4) {
351  triangulation.insert(point_position);
352  continue;
353  }
354 
355  const Delaunay::Cell_handle cell = triangulation.locate(point_position);
356 
357  // If the point is outside the current hull, then extend the hull.
358  if (triangulation.is_infinite(cell)) {
359  triangulation.insert(point_position);
360  continue;
361  }
362 
363  // Project point and located cell vertices to all visible images and
364  // determine reprojection error.
365 
366  bool insert_point = false;
367 
368  for (const auto& image_idx : visible_image_idxs) {
369  const auto& image = images[image_idx];
370  const auto& camera = cameras.at(image.camera_id);
371 
372  for (int i = 0; i < 4; ++i) {
373  const Eigen::Vector3f cell_point =
374  CGALToEigen(cell->vertex(i)->point());
375 
376  const Eigen::Vector3f point_local =
377  image.proj_matrix * point.position.homogeneous();
378  const Eigen::Vector3f cell_point_local =
379  image.proj_matrix * cell_point.homogeneous();
380 
381  // Ensure that both points are infront of camera.
382  if (point_local.z() <= 0 || cell_point_local.z() <= 0) {
383  insert_point = true;
384  break;
385  }
386 
387  // Check depth ratio between the two points.
388  const float depth_ratio = point_local.z() / cell_point_local.z();
389  if (depth_ratio < min_depth_ratio || depth_ratio > max_depth_ratio) {
390  insert_point = true;
391  break;
392  }
393 
394  // Check reprojection error between the two points.
395  const Eigen::Vector2f point_proj =
396  camera.WorldToImage(point_local.hnormalized().cast<double>())
397  .cast<float>();
398  const Eigen::Vector2f cell_point_proj =
399  camera.WorldToImage(cell_point_local.hnormalized().cast<double>())
400  .cast<float>();
401  const float squared_proj_dist =
402  (point_proj - cell_point_proj).squaredNorm();
403  if (squared_proj_dist > max_squared_proj_dist) {
404  insert_point = true;
405  break;
406  }
407  }
408 
409  if (insert_point) {
410  break;
411  }
412  }
413 
414  if (insert_point) {
415  triangulation.insert(point_position);
416  }
417  }
418 
419  std::cout << StringPrintf("Triangulation has %d using %d points.",
420  triangulation.number_of_vertices(), points.size())
421  << std::endl;
422 
423  return triangulation;
424  }
425 };
426 
427 struct DelaunayMeshingEdgeWeightComputer {
428  DelaunayMeshingEdgeWeightComputer(const Delaunay& triangulation,
429  const double visibility_sigma,
430  const double distance_sigma_factor)
431  : visibility_threshold_(5 * visibility_sigma),
432  visibility_normalization_(-0.5 /
433  (visibility_sigma * visibility_sigma)) {
434  std::vector<float> edge_lengths;
435  edge_lengths.reserve(triangulation.number_of_finite_edges());
436 
437  for (auto it = triangulation.finite_edges_begin();
438  it != triangulation.finite_edges_end(); ++it) {
439  edge_lengths.push_back((it->first->vertex(it->second)->point() -
440  it->first->vertex(it->third)->point())
441  .squared_length());
442  }
443 
444  distance_sigma_ = distance_sigma_factor *
445  std::max(std::sqrt(Percentile(edge_lengths, 25)), 1e-7f);
446  distance_threshold_ = 5 * distance_sigma_;
447  distance_normalization_ = -0.5 / (distance_sigma_ * distance_sigma_);
448  }
449 
450  double DistanceSigma() const { return distance_sigma_; }
451 
452  double ComputeVisibilityProb(const double visibility_squared) const {
453  if (visibility_squared < visibility_threshold_) {
454  return std::max(
455  0.0, 1.0 - std::exp(visibility_squared * visibility_normalization_));
456  } else {
457  return 1.0;
458  }
459  }
460 
461  double ComputeDistanceProb(const double distance_squared) const {
462  if (distance_squared < distance_threshold_) {
463  return std::max(
464  0.0, 1.0 - std::exp(distance_squared * distance_normalization_));
465  } else {
466  return 1.0;
467  }
468  }
469 
470  private:
471  double visibility_threshold_;
472  double visibility_normalization_;
473  double distance_sigma_;
474  double distance_threshold_;
475  double distance_normalization_;
476 };
477 
478 // Ray caster through the cells of a Delaunay triangulation. The tracing locates
479 // the cell of the ray origin and then iteratively intersects the ray with all
480 // facets of the current cell and advances to the neighboring cell of the
481 // intersected facet. Note that the ray can also pass through outside of the
482 // hull of the triangulation, i.e. lie within the infinite cells/facets.
483 // The ray caster collects the intersected facets along the ray.
484 struct DelaunayTriangulationRayCaster {
485  struct Intersection {
486  Delaunay::Facet facet;
487  double target_distance_squared = 0.0;
488  };
489 
490  DelaunayTriangulationRayCaster(const Delaunay& triangulation)
491  : triangulation_(triangulation) {
492  FindHullFacets();
493  }
494 
495  void CastRaySegment(const K::Segment_3& ray_segment,
496  std::vector<Intersection>* intersections) const {
497  intersections->clear();
498 
499  Delaunay::Cell_handle next_cell =
500  triangulation_.locate(ray_segment.start());
501 
502  bool next_cell_found = true;
503  while (next_cell_found) {
504  next_cell_found = false;
505 
506  if (triangulation_.is_infinite(next_cell)) {
507  // Linearly check all hull facets for intersection.
508 
509  for (const auto& hull_facet : hull_facets_) {
510  // Check if the ray origin is infront of the facet.
511  const K::Triangle_3 triangle = triangulation_.triangle(hull_facet);
512  if (CGAL::orientation(triangle[0], triangle[1], triangle[2],
513  ray_segment.start()) ==
514  K::Orientation::NEGATIVE) {
515  continue;
516  }
517 
518  // Check if the segment intersects the facet.
519  K::Point_3 intersection_point;
520  if (!CGAL::assign(intersection_point,
521  CGAL::intersection(ray_segment, triangle))) {
522  continue;
523  }
524 
525  // Make sure the next intersection is closer to target than previous.
526  const double target_distance_squared =
527  (intersection_point - ray_segment.end()).squared_length();
528  if (!intersections->empty() &&
529  intersections->back().target_distance_squared <
530  target_distance_squared) {
531  continue;
532  }
533 
534  Intersection intersection;
535  intersection.facet =
536  Delaunay::Facet(hull_facet.first, hull_facet.second);
537  intersection.target_distance_squared = target_distance_squared;
538  intersections->push_back(intersection);
539 
540  next_cell = hull_facet.first->neighbor(hull_facet.second);
541  next_cell_found = true;
542 
543  break;
544  }
545  } else {
546  // Check all neighboring finite facets for intersection.
547 
548  for (int i = 0; i < 4; ++i) {
549  // Check if the ray origin is infront of the facet.
550  const K::Triangle_3 triangle = triangulation_.triangle(next_cell, i);
551  if (CGAL::orientation(triangle[0], triangle[1], triangle[2],
552  ray_segment.start()) ==
553  K::Orientation::NEGATIVE) {
554  continue;
555  }
556 
557  // Check if the segment intersects the facet.
558  K::Point_3 intersection_point;
559  if (!CGAL::assign(intersection_point,
560  CGAL::intersection(ray_segment, triangle))) {
561  continue;
562  }
563 
564  // Make sure the next intersection is closer to target than previous.
565  const double target_distance_squared =
566  (intersection_point - ray_segment.end()).squared_length();
567  if (!intersections->empty() &&
568  intersections->back().target_distance_squared <
569  target_distance_squared) {
570  continue;
571  }
572 
573  Intersection intersection;
574  intersection.facet = Delaunay::Facet(next_cell, i);
575  intersection.target_distance_squared = target_distance_squared;
576  intersections->push_back(intersection);
577 
578  next_cell = next_cell->neighbor(i);
579  next_cell_found = true;
580 
581  break;
582  }
583  }
584  }
585  }
586 
587  private:
588  // Find all finite facets of infinite cells.
589  void FindHullFacets() {
590  for (auto it = triangulation_.all_cells_begin();
591  it != triangulation_.all_cells_end(); ++it) {
592  if (triangulation_.is_infinite(it)) {
593  for (int i = 0; i < 4; ++i) {
594  if (!triangulation_.is_infinite(it, i)) {
595  hull_facets_.emplace_back(it, i);
596  }
597  }
598  }
599  }
600  }
601 
602  const Delaunay& triangulation_;
603  std::vector<Delaunay::Facet> hull_facets_;
604 };
605 
606 // Implementation of geometry visualized in Figure 9 in P. Labatut, J‐P. Pons,
607 // and R. Keriven. "Robust and efficient surface reconstruction from range
608 // data." Computer graphics forum, 2009.
609 double ComputeCosFacetCellAngle(const Delaunay& triangulation,
610  const Delaunay::Facet& facet) {
611  if (triangulation.is_infinite(facet.first)) {
612  return 1.0;
613  }
614 
615  const K::Triangle_3 triangle = triangulation.triangle(facet);
616 
617  const K::Vector_3 facet_normal =
618  CGAL::cross_product(triangle[1] - triangle[0], triangle[2] - triangle[0]);
619  const double facet_normal_length_squared = facet_normal.squared_length();
620  if (facet_normal_length_squared == 0.0) {
621  return 0.5;
622  }
623 
624  const K::Vector_3 co_tangent = facet.first->circumcenter() - triangle[0];
625  const float co_tangent_length_squared = co_tangent.squared_length();
626  if (co_tangent_length_squared == 0.0) {
627  return 0.5;
628  }
629 
630  return (facet_normal * co_tangent) /
631  std::sqrt(facet_normal_length_squared * co_tangent_length_squared);
632 }
633 
634 void WriteDelaunayTriangulationPly(const std::string& path,
635  const Delaunay& triangulation) {
636  std::fstream file(path, std::ios::out);
637  CHECK(file.is_open());
638 
639  file << "ply" << std::endl;
640  file << "format ascii 1.0" << std::endl;
641  file << "element vertex " << triangulation.number_of_vertices() << std::endl;
642  file << "property float x" << std::endl;
643  file << "property float y" << std::endl;
644  file << "property float z" << std::endl;
645  file << "element edge " << triangulation.number_of_finite_edges()
646  << std::endl;
647  file << "property int vertex1" << std::endl;
648  file << "property int vertex2" << std::endl;
649  file << "element face " << triangulation.number_of_finite_facets()
650  << std::endl;
651  file << "property list uchar int vertex_index" << std::endl;
652  file << "end_header" << std::endl;
653 
654  std::unordered_map<const Delaunay::Vertex_handle, size_t> vertex_indices;
655  vertex_indices.reserve(triangulation.number_of_vertices());
656  for (auto it = triangulation.finite_vertices_begin();
657  it != triangulation.finite_vertices_end(); ++it) {
658  vertex_indices.emplace(it, vertex_indices.size());
659  file << it->point().x() << " " << it->point().y() << " " << it->point().z()
660  << std::endl;
661  }
662 
663  for (auto it = triangulation.finite_edges_begin();
664  it != triangulation.finite_edges_end(); ++it) {
665  file << vertex_indices.at(it->first->vertex(it->second)) << " "
666  << vertex_indices.at(it->first->vertex(it->third)) << std::endl;
667  }
668 
669  for (auto it = triangulation.finite_facets_begin();
670  it != triangulation.finite_facets_end(); ++it) {
671  file << "3 "
672  << vertex_indices.at(it->first->vertex(
673  triangulation.vertex_triple_index(it->second, 0)))
674  << " "
675  << vertex_indices.at(it->first->vertex(
676  triangulation.vertex_triple_index(it->second, 1)))
677  << " "
678  << vertex_indices.at(it->first->vertex(
679  triangulation.vertex_triple_index(it->second, 2)))
680  << std::endl;
681  }
682 }
683 
684 struct DelaunayCellData {
685  DelaunayCellData() : DelaunayCellData(-1) {}
686  DelaunayCellData(const int index)
687  : index(index),
688  source_weight(0),
689  sink_weight(0),
690  edge_weights({{0, 0, 0, 0}}) {}
691  int index;
692  float source_weight;
693  float sink_weight;
694  std::array<float, 4> edge_weights;
695 };
696 
697 PlyMesh DelaunayMeshing(const DelaunayMeshingOptions& options,
698  const DelaunayMeshingInput& input_data) {
699  CHECK(options.Check());
700 
701  // Create a delaunay triangulation of all input points.
702  std::cout << "Triangulating points..." << std::endl;
703  const auto triangulation = input_data.CreateSubSampledDelaunayTriangulation(
704  options.max_proj_dist, options.max_depth_dist);
705 
706  // Helper class to efficiently trace rays through the triangulation.
707  std::cout << "Initializing ray tracer..." << std::endl;
708  const DelaunayTriangulationRayCaster ray_caster(triangulation);
709 
710  // Helper class to efficiently compute edge weights in the s-t graph.
711  const DelaunayMeshingEdgeWeightComputer edge_weight_computer(
712  triangulation, options.visibility_sigma, options.distance_sigma_factor);
713 
714  // Initialize the s-t graph with cells as nodes and oriented facets as edges.
715 
716  std::cout << "Initializing graph optimization..." << std::endl;
717 
718  typedef std::unordered_map<const Delaunay::Cell_handle, DelaunayCellData>
719  CellGraphData;
720 
721  CellGraphData cell_graph_data;
722  cell_graph_data.reserve(triangulation.number_of_cells());
723  for (auto it = triangulation.all_cells_begin();
724  it != triangulation.all_cells_end(); ++it) {
725  cell_graph_data.emplace(it, DelaunayCellData(cell_graph_data.size()));
726  }
727 
728  // Spawn threads for parallelized integration of images.
729  const int num_threads = GetEffectiveNumThreads(options.num_threads);
730  ThreadPool thread_pool(num_threads);
731  JobQueue<CellGraphData> result_queue(num_threads);
732 
733  // Function that accumulates edge weights in the s-t graph for a single image.
734  auto IntegreateImage = [&](const size_t image_idx) {
735  // Accumulated weights for the current image only.
736  CellGraphData image_cell_graph_data;
737 
738  // Image that is integrated into s-t graph.
739  const auto& image = input_data.images[image_idx];
740  const K::Point_3 image_position = EigenToCGAL(image.proj_center);
741 
742  // Intersections between viewing rays and Delaunay triangulation.
743  std::vector<DelaunayTriangulationRayCaster::Intersection> intersections;
744 
745  // Iterate through all image observations and integrate them into the graph.
746  for (const auto& point_idx : image.point_idxs) {
747  const auto& point = input_data.points[point_idx];
748 
749  // Likelihood of the point observation.
750  const double alpha = edge_weight_computer.ComputeVisibilityProb(
751  point.num_visible_images * point.num_visible_images);
752 
753  const K::Point_3 point_position = EigenToCGAL(point.position);
754  const K::Ray_3 viewing_ray = K::Ray_3(image_position, point_position);
755  const K::Vector_3 viewing_direction = point_position - image_position;
756  const K::Vector_3 viewing_direction_normalized =
757  viewing_direction / std::sqrt(viewing_direction.squared_length());
758  const K::Vector_3 viewing_direction_epsilon =
759  0.001 * edge_weight_computer.DistanceSigma() *
760  viewing_direction_normalized;
761 
762  // Find intersected facets between image and point.
763  ray_caster.CastRaySegment(
764  K::Segment_3(image_position,
765  point_position - viewing_direction_epsilon),
766  &intersections);
767 
768  // Accumulate source weights for cell containing image.
769  if (!intersections.empty()) {
770  image_cell_graph_data[intersections.front().facet.first]
771  .source_weight += alpha;
772  }
773 
774  // Accumulate edge weights from image to point.
775  for (const auto& intersection : intersections) {
776  image_cell_graph_data[intersection.facet.first]
777  .edge_weights[intersection.facet.second] +=
778  alpha * edge_weight_computer.ComputeDistanceProb(
779  intersection.target_distance_squared);
780  }
781 
782  // Accumulate edge weights from point to extended point
783  // and accumulate sink weight of the cell inside the surface.
784 
785  {
786  // Find the first facet that is intersected by the extended ray behind
787  // the observed point. Then accumulate the edge weight of that facet
788  // and accumulate the sink weight of the cell behind that facet.
789 
790  const Delaunay::Cell_handle behind_point_cell =
791  triangulation.locate(point_position + viewing_direction_epsilon);
792 
793  int behind_neighbor_idx = -1;
794  double behind_distance_squared = 0.0;
795  for (int neighbor_idx = 0; neighbor_idx < 4; ++neighbor_idx) {
796  const K::Triangle_3 triangle =
797  triangulation.triangle(behind_point_cell, neighbor_idx);
798 
799  K::Point_3 inter_point;
800  if (CGAL::assign(inter_point,
801  CGAL::intersection(viewing_ray, triangle))) {
802  const double distance_squared =
803  (inter_point - point_position).squared_length();
804  if (distance_squared > behind_distance_squared) {
805  behind_distance_squared = distance_squared;
806  behind_neighbor_idx = neighbor_idx;
807  }
808  }
809  }
810 
811  if (behind_neighbor_idx >= 0) {
812  image_cell_graph_data[behind_point_cell]
813  .edge_weights[behind_neighbor_idx] +=
814  alpha *
815  edge_weight_computer.ComputeDistanceProb(behind_distance_squared);
816 
817  const auto& inside_cell =
818  behind_point_cell->neighbor(behind_neighbor_idx);
819  image_cell_graph_data[inside_cell].sink_weight += alpha;
820  }
821  }
822  }
823 
824  CHECK(result_queue.Push(image_cell_graph_data));
825  };
826 
827  // Add first batch of images to the thread job queue.
828  size_t image_idx = 0;
829  const size_t init_num_tasks =
830  std::min(input_data.images.size(), 2 * thread_pool.NumThreads());
831  for (; image_idx < init_num_tasks; ++image_idx) {
832  thread_pool.AddTask(IntegreateImage, image_idx);
833  }
834 
835  // Pop the integrated images from the thread job queue and integrate their
836  // accumulated weights into the global graph.
837  for (size_t i = 0; i < input_data.images.size(); ++i) {
838  Timer timer;
839  timer.Start();
840 
841  std::cout << StringPrintf("Integrating image [%d/%d]", i + 1,
842  input_data.images.size())
843  << std::flush;
844 
845  // Push the next image to the queue.
846  if (image_idx < input_data.images.size()) {
847  thread_pool.AddTask(IntegreateImage, image_idx);
848  image_idx += 1;
849  }
850 
851  // Pop the next results from the queue.
852  const auto result = result_queue.Pop();
853  CHECK(result.IsValid());
854 
855  // Accumulate the weights of the image into the global graph.
856  const auto& image_cell_graph_data = result.Data();
857  for (const auto& image_cell_data : image_cell_graph_data) {
858  auto& cell_data = cell_graph_data.at(image_cell_data.first);
859  cell_data.sink_weight += image_cell_data.second.sink_weight;
860  cell_data.source_weight += image_cell_data.second.source_weight;
861  for (size_t j = 0; j < cell_data.edge_weights.size(); ++j) {
862  cell_data.edge_weights[j] += image_cell_data.second.edge_weights[j];
863  }
864  }
865 
866  std::cout << StringPrintf(" in %.3fs", timer.ElapsedSeconds()) << std::endl;
867  }
868 
869  // Setup the min-cut (max-flow) graph optimization.
870 
871  std::cout << "Setting up optimization..." << std::endl;
872 
873  // Each oriented facet in the Delaunay triangulation corresponds to a directed
874  // edge and each cell corresponds to a node in the graph.
875  MinSTGraphCut<size_t, float> graph_cut(cell_graph_data.size());
876 
877  // Iterate all cells in the triangulation.
878  for (auto& cell_data : cell_graph_data) {
879  graph_cut.AddNode(cell_data.second.index, cell_data.second.source_weight,
880  cell_data.second.sink_weight);
881 
882  // Iterate all facets of the current cell to accumulate edge weight.
883  for (int i = 0; i < 4; ++i) {
884  // Compose the current facet.
885  const Delaunay::Facet facet = std::make_pair(cell_data.first, i);
886 
887  // Extract the mirrored facet of the current cell (opposite orientation).
888  const Delaunay::Facet mirror_facet = triangulation.mirror_facet(facet);
889  const auto& mirror_cell_data = cell_graph_data.at(mirror_facet.first);
890 
891  // Avoid duplicate edges in graph.
892  if (cell_data.second.index < mirror_cell_data.index) {
893  continue;
894  }
895 
896  // Implementation of geometry visualized in Figure 9 in P. Labatut, J‐P.
897  // Pons, and R. Keriven. "Robust and efficient surface reconstruction from
898  // range data." Computer graphics forum, 2009.
899  const double edge_shape_weight =
900  options.quality_regularization *
901  (1.0 -
902  std::min(ComputeCosFacetCellAngle(triangulation, facet),
903  ComputeCosFacetCellAngle(triangulation, mirror_facet)));
904 
905  const float forward_edge_weight =
906  cell_data.second.edge_weights[facet.second] + edge_shape_weight;
907  const float backward_edge_weight =
908  mirror_cell_data.edge_weights[mirror_facet.second] +
909  edge_shape_weight;
910 
911  graph_cut.AddEdge(cell_data.second.index, mirror_cell_data.index,
912  forward_edge_weight, backward_edge_weight);
913  }
914  }
915 
916  // Extract the surface facets as the oriented min-cut of the graph.
917 
918  std::cout << "Running graph-cut optimization..." << std::endl;
919  graph_cut.Compute();
920 
921  std::cout << "Extracting surface as min-cut..." << std::endl;
922 
923  std::unordered_set<Delaunay::Vertex_handle> surface_vertices;
924  std::vector<Delaunay::Facet> surface_facets;
925  std::vector<float> surface_facet_side_lengths;
926 
927  for (auto it = triangulation.finite_facets_begin();
928  it != triangulation.finite_facets_end(); ++it) {
929  const auto& cell_data = cell_graph_data.at(it->first);
930  const auto& mirror_cell_data =
931  cell_graph_data.at(it->first->neighbor(it->second));
932 
933  // Obtain labeling after the graph-cut.
934  const bool cell_is_source = graph_cut.IsConnectedToSource(cell_data.index);
935  const bool mirror_cell_is_source =
936  graph_cut.IsConnectedToSource(mirror_cell_data.index);
937 
938  // The surface is equal to the location of the cut, which is at the
939  // transition between source and sink nodes.
940  if (cell_is_source == mirror_cell_is_source) {
941  continue;
942  }
943 
944  // Remember all unique vertices of the surface mesh.
945  for (int i = 0; i < 3; ++i) {
946  const auto& vertex =
947  it->first->vertex(triangulation.vertex_triple_index(it->second, i));
948  surface_vertices.insert(vertex);
949  }
950 
951  // Determine maximum side length of facet.
952  const K::Triangle_3 triangle = triangulation.triangle(*it);
953  const float max_squared_side_length =
954  std::max({(triangle[0] - triangle[1]).squared_length(),
955  (triangle[0] - triangle[2]).squared_length(),
956  (triangle[1] - triangle[2]).squared_length()});
957  surface_facet_side_lengths.push_back(std::sqrt(max_squared_side_length));
958 
959  // Remember surface mesh facet and make sure it is oriented correctly.
960  if (cell_is_source) {
961  surface_facets.push_back(*it);
962  } else {
963  surface_facets.push_back(triangulation.mirror_facet(*it));
964  }
965  }
966 
967  std::cout << "Creating surface mesh model..." << std::endl;
968 
969  PlyMesh mesh;
970 
971  std::unordered_map<const Delaunay::Vertex_handle, size_t>
972  surface_vertex_indices;
973  surface_vertex_indices.reserve(surface_vertices.size());
974  mesh.vertices.reserve(surface_vertices.size());
975  for (const auto& vertex : surface_vertices) {
976  mesh.vertices.emplace_back(vertex->point().x(), vertex->point().y(),
977  vertex->point().z());
978  surface_vertex_indices.emplace(vertex, surface_vertex_indices.size());
979  }
980 
981  const float max_facet_side_length =
982  options.max_side_length_factor *
983  Percentile(surface_facet_side_lengths,
984  options.max_side_length_percentile);
985 
986  mesh.faces.reserve(surface_facets.size());
987 
988  for (size_t i = 0; i < surface_facets.size(); ++i) {
989  // Note that skipping some of the facets here means that there will be
990  // some unused vertices in the final mesh.
991  if (surface_facet_side_lengths[i] > max_facet_side_length) {
992  continue;
993  }
994 
995  const auto& facet = surface_facets[i];
996  mesh.faces.emplace_back(
997  surface_vertex_indices.at(facet.first->vertex(
998  triangulation.vertex_triple_index(facet.second, 0))),
999  surface_vertex_indices.at(facet.first->vertex(
1000  triangulation.vertex_triple_index(facet.second, 1))),
1001  surface_vertex_indices.at(facet.first->vertex(
1002  triangulation.vertex_triple_index(facet.second, 2))));
1003  }
1004 
1005  return mesh;
1006 }
1007 
1008 void SparseDelaunayMeshing(const DelaunayMeshingOptions& options,
1009  const std::string& input_path,
1010  const std::string& output_path) {
1011  Timer timer;
1012  timer.Start();
1013 
1014  DelaunayMeshingInput input_data;
1015  input_data.ReadSparseReconstruction(input_path);
1016 
1017  const auto mesh = DelaunayMeshing(options, input_data);
1018 
1019  std::cout << "Writing surface mesh to " << output_path << std::endl;
1020  WriteBinaryPlyMesh(output_path, mesh);
1021 
1022  timer.PrintSeconds();
1023 }
1024 
1025 void DenseDelaunayMeshing(const DelaunayMeshingOptions& options,
1026  const std::string& input_path,
1027  const std::string& output_path) {
1028  Timer timer;
1029  timer.Start();
1030 
1031  DelaunayMeshingInput input_data;
1032  input_data.ReadDenseReconstruction(input_path);
1033 
1034  const auto mesh = DelaunayMeshing(options, input_data);
1035 
1036  std::cout << "Writing surface mesh to " << output_path << std::endl;
1037  WriteBinaryPlyMesh(output_path, mesh);
1038 
1039  timer.PrintSeconds();
1040 }
1041 
1042 #endif // CGAL_ENABLED
1043 
1044 } // namespace mvs
1045 } // namespace colmap
std::shared_ptr< core::Tensor > image
int points
math::float3 position
core::Tensor result
Definition: VtkUtils.cpp:76
#define CHECK_OPTION_LE(val1, val2)
Definition: logging.h:31
#define CHECK_OPTION_GT(val1, val2)
Definition: logging.h:34
#define CHECK_OPTION_NE(val1, val2)
Definition: logging.h:30
#define CHECK_OPTION_GE(val1, val2)
Definition: logging.h:33
Matrix< float, 3, 4 > Matrix3x4f
Definition: types.h:38
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
CLOUDVIEWER_HOST_DEVICE int Intersection(const Point &p1, const Point &p0, const Point &q1, const Point &q0, Point &ans)
Definition: IoUImpl.h:78
static const std::string path
Definition: PointCloud.cpp:59
bool PoissonMeshing(const PoissonMeshingOptions &options, const std::string &input_path, const std::string &output_path)
Definition: meshing.cc:123
const camera_t kInvalidCameraId
Definition: types.h:75
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
uint32_t camera_t
Definition: types.h:58
void WriteBinaryPlyMesh(const std::string &path, const PlyMesh &mesh)
Definition: ply.cc:448
std::vector< PlyPoint > ReadPly(const std::string &path)
Definition: ply.cc:43
void Shuffle(const uint32_t num_to_shuffle, std::vector< T > *elems)
Definition: random.h:96
int GetEffectiveNumThreads(const int num_threads)
Definition: threading.cc:269
T Percentile(const std::vector< T > &elems, const double p)
Definition: math.h:209
std::string to_string(const T &n)
Definition: Common.h:20
Definition: Eigen.h:85