597{
598 auto report = [&](int pct, const char* msg) {
599 if (progress_callback)
600 progress_callback({pct, msg});
601 };
602 auto cancelled = [&]() -> bool {
603 if (cancel_callback && cancel_callback()) {
604 BOOST_LOG_TRIVIAL(debug) << log_prefix << " cancelled";
605 return true;
606 }
607 return false;
608 };
609
610 report(0, "Repairing mesh");
611 if (cancelled()) return false;
612
613
614 auto resample_face_colors = [&](
TriMesh&& repaired_mesh) ->
bool {
615 TriVertices old_vertices = std::move(mesh.vertices);
616 TriFaces old_indices = std::move(mesh.indices);
618 mesh = std::move(repaired_mesh);
619
621 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": repaired mesh is closed.";
622 } else {
623 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": repaired mesh is open.";
624 }
625
627 tbb::parallel_for(tbb::blocked_range<std::size_t>(0, mesh.
facets_count()), [&](
const tbb::blocked_range<size_t>&
range) {
628 for (std::size_t fid = range.begin(); fid < range.end(); ++fid) {
629 const auto& face = mesh.indices[fid];
630 Vec3f center = (mesh.vertices[face[0]] + mesh.vertices[face[1]] + mesh.vertices[face[2]]) / 3.0f;
631 size_t hit_idx = 0;
632 Vec3f closest;
633 AABBTreeIndirect::squared_distance_to_indexed_triangle_set(
634 old_vertices, old_indices, aabb_tree, center, hit_idx, closest);
635 new_face_colors[fid] = face_colors[hit_idx];
636 }
637 });
638 face_colors = std::move(new_face_colors);
639 return true;
640 };
641
642 auto repair_and_resample = [&]() -> bool {
643 std::shared_ptr<TriMesh> repaired_mesh;
645 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": RepairMesh failed.";
646 return false;
647 }
648 if (cancelled()) return false;
649 return resample_face_colors(std::move(*repaired_mesh));
650 };
651
652 {
653 TriangleMesh stats_mesh(static_cast<const indexed_triangle_set&>(mesh));
654 const auto& stats = stats_mesh.stats();
655
656
657 if (!stats.manifold()) {
658 BOOST_LOG_TRIVIAL(info) << log_prefix << ": mesh has non-manifold geometry or open boundaries, open_edges="
659 << stats.open_edges;
663 return false;
664 }
666 indexed_triangle_set repaired_its;
667 std::string repair_error;
669 static_cast<const indexed_triangle_set&>(mesh), repaired_its,
670 [&](const char* message, unsigned ) {
671 report(5, message ? message : "Repairing mesh");
672 },
673 [&]() { return cancelled(); }, &repair_error);
674 if (repaired) {
675 if (cancelled()) return false;
676 BOOST_LOG_TRIVIAL(info) << log_prefix << ": mesh repair finished.";
677 if (!resample_face_colors(
TriMesh(std::move(repaired_its))))
678 return false;
679 } else {
680 BOOST_LOG_TRIVIAL(warning) << log_prefix << ": mesh repair failed: " << repair_error;
681 }
682 } else {
683 BOOST_LOG_TRIVIAL(info) << log_prefix << ": importing mesh without repair.";
684 }
685 }
686 }
687
689 BOOST_LOG_TRIVIAL(info) << log_prefix << ": mesh not halfedge-compatible, attempting RepairMesh.";
690 if (!repair_and_resample())
691 return false;
692 }
693
694#ifdef OUTPUT_TEST_RESULT
695 SaveToOFF(std::string(log_prefix) +
"_1_repair.off", mesh, face_colors);
696#endif
697
698 report(20, "Color clustering");
699 if (cancelled()) return false;
700
701
702 std::vector<RGB> cluster_centers;
703 out_clustered_face_colors = face_colors;
704 std::vector<std::size_t> clustered_face_labels(face_colors.size());
706
707 if (adaptive_cluster) {
708 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": use cluster adaptive method.";
712 para.cancel_callback = cancel_callback ? [&]() { return cancel_callback(); } : std::function<bool()>{};
714 if (cancelled()) return false;
715 } else {
716 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": use cluster k-means method.";
719 para.cancel_callback = cancel_callback ? [&]() { return cancel_callback(); } : std::function<bool()>{};
721 if (cancelled()) return false;
722 }
723
724 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": k = " << cluster_centers.size() << ".";
725 if (cluster_centers.empty()) {
726 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": no cluster center generated.";
727 return false;
728 }
729
730 report(40, "Assigning cluster labels");
731 if (cancelled()) return false;
732
733
734 {
735 std::atomic<size_t> done{0};
736 std::atomic<bool> cancel_requested{false};
737 const size_t total = mesh.indices.size();
738 const size_t interval = std::max<size_t>(total / 20, 1);
739 tbb::parallel_for(tbb::blocked_range<std::size_t>(0, total), [&](
const tbb::blocked_range<size_t>&
range) {
740 for (std::size_t fid =
range.begin(); fid <
range.end(); ++fid) {
741 if (cancel_requested.load(std::memory_order_relaxed)) return;
742 std::size_t nearest_id = 0;
744 clustered_face_labels[fid] = nearest_id;
745 out_clustered_face_colors[fid] = cluster_centers[nearest_id];
746 size_t cnt = done.fetch_add(1, std::memory_order_relaxed) + 1;
747 if (cnt % interval == 0) {
748 if (cancelled()) { cancel_requested.store(true, std::memory_order_relaxed); return; }
749 }
750 }
751 });
752 if (cancel_requested.load() || cancelled()) return false;
753 }
754 if (adaptive_cluster) {
756 return false;
757 } else {
759 }
760
761#ifdef OUTPUT_TEST_RESULT
762 {
763 std::vector<RGB> tmp = out_clustered_face_colors;
764 for (std::size_t i = 0; i < tmp.size(); ++i)
765 tmp[i] = cluster_centers[clustered_face_labels[i]];
766 SaveToOFF(std::string(log_prefix) +
"_3_cluster.off", mesh, tmp);
767 }
768#endif
769
770 report(65, "Smoothing colors");
771 if (cancelled()) return false;
772
775 if (!
smooth_region(mesh, clustered_face_labels, smooth_parameters)) {
776 BOOST_LOG_TRIVIAL(debug) << log_prefix << ": smooth region failed.";
777 return false;
778 }
779 if (adaptive_cluster) {
781 return false;
782 } else {
784 }
785
786 report(90, "Updating face colors");
787 if (cancelled()) return false;
788
789 for (std::size_t i = 0; i < out_clustered_face_colors.size(); ++i)
790 out_clustered_face_colors[i] = cluster_centers[clustered_face_labels[i]];
791
792#ifdef OUTPUT_TEST_RESULT
793 SaveToOFF(std::string(log_prefix) +
"_4_smooth.off", mesh, out_clustered_face_colors);
794#endif
795
796 report(100, "Completed");
797 return true;
798}
Tree< 3, typename VertexType::Scalar > build_aabb_tree_over_indexed_triangle_set(const std::vector< VertexType > &vertices, const std::vector< IndexedFaceType > &faces, const typename VertexType::Scalar eps=0)
Definition AABBTreeIndirect.hpp:677
bool is_mesh_halfedge_compatible(const TriMesh &mesh)
Definition CgalUtils.hpp:54
bool is_closed(const TriMesh &bbs_mesh)
Check whether the mesh is closed (watertight).
Definition ColorUtils.cpp:855
bool smooth_region(TriMesh &tri_mesh, std::vector< std::array< std::size_t, 3 > > &face_colors, const SmoothParameters &smooth_parameters)
Smooth region boundaries (RGB color labels).
Definition ColorUtils.cpp:941
double smooth_weight
Definition ColorUtils.hpp:41
double max_color_distance
Definition ColorUtils.hpp:29
std::size_t cluster_k
Definition ColorUtils.hpp:31
Definition ColorUtils.hpp:26
Definition ColorUtils.hpp:40
double smooth_weight
Definition TextureToColor.hpp:33
static bool ensure_all_cluster_centers_used(const std::vector< RGB > &source_face_colors, const std::vector< RGB > &cluster_centers, std::vector< std::size_t > &face_labels, const char *stage_name)
Definition TextureToColor.cpp:128
bool * mesh_repair_decision_required
Definition TextureToColor.hpp:46
bool RepairMesh(const TriMesh &mesh, std::shared_ptr< TriMesh > &out_mesh, AlgoProgressCallback progress_callback=nullptr, AlgoCancelCallback cancel_callback=nullptr, const RepairSetting &setting=RepairSetting{})
Definition Repair.hpp:109
static bool discard_unused_cluster_centers(std::vector< RGB > &cluster_centers, std::vector< std::size_t > &face_labels, const char *stage_name)
Definition TextureToColor.cpp:83
MeshRepairCallback mesh_repair_callback
Definition TextureToColor.hpp:48
MeshRepairDecision mesh_repair_decision
Definition TextureToColor.hpp:43
static void SaveToOFF(const std::string &path, const TriMesh &mesh, const std::vector< RGB > &face_colors)
Definition TextureToColor.cpp:40