35 #include <unordered_map>
39 #include <CGAL/Delaunay_triangulation_3.h>
40 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
43 #include "PoissonRecon/PoissonRecon.h"
44 #include "PoissonRecon/SurfaceTrimmer.h"
58 typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
59 typedef CGAL::Delaunay_triangulation_3<K, CGAL::Fast_location> Delaunay;
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);
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);
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);
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);
124 const std::string& input_path,
125 const std::string& output_path) {
126 CHECK(options.
Check());
128 std::vector<std::string> args;
130 args.push_back(
"./binary");
132 args.push_back(
"--in");
133 args.push_back(input_path);
135 args.push_back(
"--out");
136 args.push_back(output_path);
138 args.push_back(
"--pointWeight");
141 args.push_back(
"--depth");
144 if (options.
color > 0) {
145 args.push_back(
"--color");
149 #ifdef OPENMP_ENABLED
151 args.push_back(
"--threads");
156 if (options.
trim > 0) {
157 args.push_back(
"--density");
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());
166 if (PoissonRecon(args_cstr.size(),
const_cast<char**
>(args_cstr.data())) !=
171 if (options.
trim == 0) {
178 args.push_back(
"./binary");
180 args.push_back(
"--in");
181 args.push_back(output_path);
183 args.push_back(
"--out");
184 args.push_back(output_path);
186 args.push_back(
"--trim");
189 args_cstr.reserve(args.size());
190 for (
const auto& arg : args) {
191 args_cstr.push_back(arg.c_str());
194 return SurfaceTrimmer(args_cstr.size(),
195 const_cast<char**
>(args_cstr.data())) == EXIT_SUCCESS;
200 K::Point_3 EigenToCGAL(
const Eigen::Vector3f& point) {
201 return K::Point_3(point.x(), point.y(), point.z());
204 Eigen::Vector3f CGALToEigen(
const K::Point_3& point) {
205 return Eigen::Vector3f(point.x(), point.y(), point.z());
208 class DelaunayMeshingInput {
213 Eigen::Vector3f proj_center = Eigen::Vector3f::Zero();
214 std::vector<size_t> point_idxs;
218 Eigen::Vector3f
position = Eigen::Vector3f::Zero();
219 uint32_t num_visible_images = 0;
222 std::unordered_map<camera_t, Camera> cameras;
223 std::vector<Image> images;
224 std::vector<Point>
points;
226 void ReadSparseReconstruction(
const std::string&
path) {
227 Reconstruction reconstruction;
228 reconstruction.Read(
path);
229 CopyFromSparseReconstruction(reconstruction);
232 void CopyFromSparseReconstruction(
const Reconstruction& reconstruction) {
233 images.reserve(reconstruction.NumRegImages());
234 points.reserve(reconstruction.NumPoints3D());
236 cameras = reconstruction.Cameras();
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);
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()));
261 images.push_back(input_image);
265 void ReadDenseReconstruction(
const std::string&
path) {
267 Reconstruction reconstruction;
270 cameras = reconstruction.Cameras();
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);
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;
289 const size_t vis_num_points = ReadBinaryLittleEndian<uint64_t>(&vis_file);
290 CHECK_EQ(vis_num_points, ply_points.size());
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);
304 points.push_back(input_point);
308 Delaunay CreateDelaunayTriangulation()
const {
309 std::vector<Delaunay::Point> delaunay_points(
points.size());
310 for (
size_t i = 0; i <
points.size(); ++i) {
315 return Delaunay(delaunay_points.begin(), delaunay_points.end());
318 Delaunay CreateSubSampledDelaunayTriangulation(
319 const float max_proj_dist,
const float max_depth_dist)
const {
320 CHECK_GE(max_proj_dist, 0);
322 if (max_proj_dist == 0) {
323 return CreateDelaunayTriangulation();
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);
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);
337 Delaunay triangulation;
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;
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];
347 const K::Point_3 point_position = EigenToCGAL(point.position);
350 if (triangulation.number_of_vertices() < 4) {
351 triangulation.insert(point_position);
355 const Delaunay::Cell_handle cell = triangulation.locate(point_position);
358 if (triangulation.is_infinite(cell)) {
359 triangulation.insert(point_position);
366 bool insert_point =
false;
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);
372 for (
int i = 0; i < 4; ++i) {
373 const Eigen::Vector3f cell_point =
374 CGALToEigen(cell->vertex(i)->point());
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();
382 if (point_local.z() <= 0 || cell_point_local.z() <= 0) {
388 const float depth_ratio = point_local.z() / cell_point_local.z();
389 if (depth_ratio < min_depth_ratio || depth_ratio > max_depth_ratio) {
395 const Eigen::Vector2f point_proj =
396 camera.WorldToImage(point_local.hnormalized().cast<
double>())
398 const Eigen::Vector2f cell_point_proj =
399 camera.WorldToImage(cell_point_local.hnormalized().cast<
double>())
401 const float squared_proj_dist =
402 (point_proj - cell_point_proj).squaredNorm();
403 if (squared_proj_dist > max_squared_proj_dist) {
415 triangulation.insert(point_position);
419 std::cout <<
StringPrintf(
"Triangulation has %d using %d points.",
420 triangulation.number_of_vertices(),
points.size())
423 return triangulation;
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());
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())
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_);
450 double DistanceSigma()
const {
return distance_sigma_; }
452 double ComputeVisibilityProb(
const double visibility_squared)
const {
453 if (visibility_squared < visibility_threshold_) {
455 0.0, 1.0 - std::exp(visibility_squared * visibility_normalization_));
461 double ComputeDistanceProb(
const double distance_squared)
const {
462 if (distance_squared < distance_threshold_) {
464 0.0, 1.0 - std::exp(distance_squared * distance_normalization_));
471 double visibility_threshold_;
472 double visibility_normalization_;
473 double distance_sigma_;
474 double distance_threshold_;
475 double distance_normalization_;
484 struct DelaunayTriangulationRayCaster {
486 Delaunay::Facet facet;
487 double target_distance_squared = 0.0;
490 DelaunayTriangulationRayCaster(
const Delaunay& triangulation)
491 : triangulation_(triangulation) {
495 void CastRaySegment(
const K::Segment_3& ray_segment,
496 std::vector<Intersection>* intersections)
const {
497 intersections->clear();
499 Delaunay::Cell_handle next_cell =
500 triangulation_.locate(ray_segment.start());
502 bool next_cell_found =
true;
503 while (next_cell_found) {
504 next_cell_found =
false;
506 if (triangulation_.is_infinite(next_cell)) {
509 for (
const auto& hull_facet : hull_facets_) {
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) {
519 K::Point_3 intersection_point;
520 if (!CGAL::assign(intersection_point,
521 CGAL::intersection(ray_segment, triangle))) {
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) {
536 Delaunay::Facet(hull_facet.first, hull_facet.second);
537 intersection.target_distance_squared = target_distance_squared;
538 intersections->push_back(intersection);
540 next_cell = hull_facet.first->neighbor(hull_facet.second);
541 next_cell_found =
true;
548 for (
int i = 0; i < 4; ++i) {
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) {
558 K::Point_3 intersection_point;
559 if (!CGAL::assign(intersection_point,
560 CGAL::intersection(ray_segment, triangle))) {
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) {
574 intersection.facet = Delaunay::Facet(next_cell, i);
575 intersection.target_distance_squared = target_distance_squared;
576 intersections->push_back(intersection);
578 next_cell = next_cell->neighbor(i);
579 next_cell_found =
true;
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);
602 const Delaunay& triangulation_;
603 std::vector<Delaunay::Facet> hull_facets_;
609 double ComputeCosFacetCellAngle(
const Delaunay& triangulation,
610 const Delaunay::Facet& facet) {
611 if (triangulation.is_infinite(facet.first)) {
615 const K::Triangle_3 triangle = triangulation.triangle(facet);
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) {
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) {
630 return (facet_normal * co_tangent) /
631 std::sqrt(facet_normal_length_squared * co_tangent_length_squared);
634 void WriteDelaunayTriangulationPly(
const std::string&
path,
635 const Delaunay& triangulation) {
636 std::fstream file(
path, std::ios::out);
637 CHECK(file.is_open());
641 file <<
"element vertex " << triangulation.number_of_vertices() <<
std::endl;
645 file <<
"element edge " << triangulation.number_of_finite_edges()
647 file <<
"property int vertex1" <<
std::endl;
648 file <<
"property int vertex2" <<
std::endl;
649 file <<
"element face " << triangulation.number_of_finite_facets()
651 file <<
"property list uchar int vertex_index" <<
std::endl;
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()
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;
669 for (
auto it = triangulation.finite_facets_begin();
670 it != triangulation.finite_facets_end(); ++it) {
672 << vertex_indices.at(it->first->vertex(
673 triangulation.vertex_triple_index(it->second, 0)))
675 << vertex_indices.at(it->first->vertex(
676 triangulation.vertex_triple_index(it->second, 1)))
678 << vertex_indices.at(it->first->vertex(
679 triangulation.vertex_triple_index(it->second, 2)))
684 struct DelaunayCellData {
685 DelaunayCellData() : DelaunayCellData(-1) {}
686 DelaunayCellData(
const int index)
690 edge_weights({{0, 0, 0, 0}}) {}
694 std::array<float, 4> edge_weights;
697 PlyMesh DelaunayMeshing(
const DelaunayMeshingOptions& options,
698 const DelaunayMeshingInput& input_data) {
699 CHECK(options.Check());
702 std::cout <<
"Triangulating points..." <<
std::endl;
703 const auto triangulation = input_data.CreateSubSampledDelaunayTriangulation(
704 options.max_proj_dist, options.max_depth_dist);
707 std::cout <<
"Initializing ray tracer..." <<
std::endl;
708 const DelaunayTriangulationRayCaster ray_caster(triangulation);
711 const DelaunayMeshingEdgeWeightComputer edge_weight_computer(
712 triangulation, options.visibility_sigma, options.distance_sigma_factor);
716 std::cout <<
"Initializing graph optimization..." <<
std::endl;
718 typedef std::unordered_map<const Delaunay::Cell_handle, DelaunayCellData>
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()));
730 ThreadPool thread_pool(num_threads);
731 JobQueue<CellGraphData> result_queue(num_threads);
734 auto IntegreateImage = [&](
const size_t image_idx) {
736 CellGraphData image_cell_graph_data;
739 const auto&
image = input_data.images[image_idx];
740 const K::Point_3 image_position = EigenToCGAL(
image.proj_center);
743 std::vector<DelaunayTriangulationRayCaster::Intersection> intersections;
746 for (
const auto& point_idx :
image.point_idxs) {
747 const auto& point = input_data.points[point_idx];
750 const double alpha = edge_weight_computer.ComputeVisibilityProb(
751 point.num_visible_images * point.num_visible_images);
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;
763 ray_caster.CastRaySegment(
764 K::Segment_3(image_position,
765 point_position - viewing_direction_epsilon),
769 if (!intersections.empty()) {
770 image_cell_graph_data[intersections.front().facet.first]
771 .source_weight += alpha;
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);
790 const Delaunay::Cell_handle behind_point_cell =
791 triangulation.locate(point_position + viewing_direction_epsilon);
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);
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;
811 if (behind_neighbor_idx >= 0) {
812 image_cell_graph_data[behind_point_cell]
813 .edge_weights[behind_neighbor_idx] +=
815 edge_weight_computer.ComputeDistanceProb(behind_distance_squared);
817 const auto& inside_cell =
818 behind_point_cell->neighbor(behind_neighbor_idx);
819 image_cell_graph_data[inside_cell].sink_weight += alpha;
824 CHECK(result_queue.Push(image_cell_graph_data));
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);
837 for (
size_t i = 0; i < input_data.images.size(); ++i) {
841 std::cout <<
StringPrintf(
"Integrating image [%d/%d]", i + 1,
842 input_data.images.size())
846 if (image_idx < input_data.images.size()) {
847 thread_pool.AddTask(IntegreateImage, image_idx);
852 const auto result = result_queue.Pop();
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];
871 std::cout <<
"Setting up optimization..." <<
std::endl;
875 MinSTGraphCut<size_t, float> graph_cut(cell_graph_data.size());
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);
883 for (
int i = 0; i < 4; ++i) {
888 const Delaunay::Facet mirror_facet = triangulation.mirror_facet(facet);
889 const auto& mirror_cell_data = cell_graph_data.at(mirror_facet.first);
892 if (cell_data.second.index < mirror_cell_data.index) {
899 const double edge_shape_weight =
900 options.quality_regularization *
902 std::min(ComputeCosFacetCellAngle(triangulation, facet),
903 ComputeCosFacetCellAngle(triangulation, mirror_facet)));
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] +
911 graph_cut.AddEdge(cell_data.second.index, mirror_cell_data.index,
912 forward_edge_weight, backward_edge_weight);
918 std::cout <<
"Running graph-cut optimization..." <<
std::endl;
921 std::cout <<
"Extracting surface as min-cut..." <<
std::endl;
923 std::unordered_set<Delaunay::Vertex_handle> surface_vertices;
924 std::vector<Delaunay::Facet> surface_facets;
925 std::vector<float> surface_facet_side_lengths;
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));
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);
940 if (cell_is_source == mirror_cell_is_source) {
945 for (
int i = 0; i < 3; ++i) {
947 it->first->vertex(triangulation.vertex_triple_index(it->second, i));
948 surface_vertices.insert(vertex);
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));
960 if (cell_is_source) {
961 surface_facets.push_back(*it);
963 surface_facets.push_back(triangulation.mirror_facet(*it));
967 std::cout <<
"Creating surface mesh model..." <<
std::endl;
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());
981 const float max_facet_side_length =
982 options.max_side_length_factor *
984 options.max_side_length_percentile);
986 mesh.faces.reserve(surface_facets.size());
988 for (
size_t i = 0; i < surface_facets.size(); ++i) {
991 if (surface_facet_side_lengths[i] > max_facet_side_length) {
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))));
1008 void SparseDelaunayMeshing(
const DelaunayMeshingOptions& options,
1009 const std::string& input_path,
1010 const std::string& output_path) {
1014 DelaunayMeshingInput input_data;
1015 input_data.ReadSparseReconstruction(input_path);
1017 const auto mesh = DelaunayMeshing(options, input_data);
1019 std::cout <<
"Writing surface mesh to " << output_path <<
std::endl;
1022 timer.PrintSeconds();
1025 void DenseDelaunayMeshing(
const DelaunayMeshingOptions& options,
1026 const std::string& input_path,
1027 const std::string& output_path) {
1031 DelaunayMeshingInput input_data;
1032 input_data.ReadDenseReconstruction(input_path);
1034 const auto mesh = DelaunayMeshing(options, input_data);
1036 std::cout <<
"Writing surface mesh to " << output_path <<
std::endl;
1039 timer.PrintSeconds();
std::shared_ptr< core::Tensor > image
#define CHECK_OPTION_LE(val1, val2)
#define CHECK_OPTION_GT(val1, val2)
#define CHECK_OPTION_NE(val1, val2)
#define CHECK_OPTION_GE(val1, val2)
Matrix< float, 3, 4 > Matrix3x4f
QTextStream & endl(QTextStream &stream)
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
CLOUDVIEWER_HOST_DEVICE int Intersection(const Point &p1, const Point &p0, const Point &q1, const Point &q0, Point &ans)
static const std::string path
bool PoissonMeshing(const PoissonMeshingOptions &options, const std::string &input_path, const std::string &output_path)
const camera_t kInvalidCameraId
std::string JoinPaths(T const &... paths)
std::string StringPrintf(const char *format,...)
void WriteBinaryPlyMesh(const std::string &path, const PlyMesh &mesh)
std::vector< PlyPoint > ReadPly(const std::string &path)
void Shuffle(const uint32_t num_to_shuffle, std::vector< T > *elems)
int GetEffectiveNumThreads(const int num_threads)
T Percentile(const std::vector< T > &elems, const double p)
std::string to_string(const T &n)
double max_side_length_factor
double max_side_length_percentile
double distance_sigma_factor
double quality_regularization