@@ -187,7 +187,7 @@ bool Groom::image_pipeline(std::shared_ptr<Subject> subject, size_t domain) {
187187
188188 if (params.get_convert_to_mesh ()) {
189189 Mesh mesh = image.toMesh (0.0 );
190- run_mesh_pipeline (mesh, params);
190+ run_mesh_pipeline (mesh, params, original );
191191 groomed_name = get_output_filename (original, DomainType::Mesh);
192192 // save the groomed mesh
193193 MeshUtils::threadSafeWriteMesh (groomed_name, mesh);
@@ -323,7 +323,7 @@ bool Groom::mesh_pipeline(std::shared_ptr<Subject> subject, size_t domain) {
323323 auto transform = vtkSmartPointer<vtkTransform>::New ();
324324
325325 if (!params.get_skip_grooming ()) {
326- run_mesh_pipeline (mesh, params);
326+ run_mesh_pipeline (mesh, params, original );
327327
328328 // reflection
329329 if (params.get_reflect ()) {
@@ -367,28 +367,17 @@ bool Groom::mesh_pipeline(std::shared_ptr<Subject> subject, size_t domain) {
367367}
368368
369369// ---------------------------------------------------------------------------
370- bool Groom::run_mesh_pipeline (Mesh& mesh, GroomParameters params) {
371- // Extract largest component (should be first)
372- if (params.get_mesh_largest_component ()) {
373- mesh.extractLargestComponent ();
374- increment_progress ();
375- }
370+ bool Groom::run_mesh_pipeline (Mesh& mesh, GroomParameters params, const std::string& filename) {
371+ // Repair mesh: triangulate, extract largest component, clean, fix non-manifold, remove zero-area triangles
372+ mesh = Mesh (MeshUtils::repair_mesh (mesh.getVTKMesh ()));
376373
377374 if (params.get_fill_mesh_holes_tool ()) {
378375 mesh.fillHoles ();
376+ check_and_fix_mesh (mesh, " fill_holes" , filename);
379377 increment_progress ();
380378 }
381379
382380 if (params.get_remesh ()) {
383- auto poly_data = mesh.getVTKMesh ();
384- if (poly_data->GetNumberOfCells () == 0 || poly_data->GetCell (0 )->GetNumberOfPoints () == 2 ) {
385- SW_DEBUG (" Number of cells: {}" , poly_data->GetNumberOfCells ());
386- SW_DEBUG (" Number of points in first cell: {}" , poly_data->GetCell (0 )->GetNumberOfPoints ());
387- // write out to /tmp
388- std::string tmpname = " /tmp/bad_mesh.vtk" ;
389- MeshUtils::threadSafeWriteMesh (tmpname, mesh);
390- throw std::runtime_error (" malformed mesh, mesh should be triangular" );
391- }
392381 int total_vertices = mesh.getVTKMesh ()->GetNumberOfPoints ();
393382 int num_vertices = params.get_remesh_num_vertices ();
394383 if (params.get_remesh_percent_mode ()) {
@@ -397,6 +386,7 @@ bool Groom::run_mesh_pipeline(Mesh& mesh, GroomParameters params) {
397386 num_vertices = std::max<int >(num_vertices, 25 );
398387 double gradation = clamp<double >(params.get_remesh_gradation (), 0.0 , 2.0 );
399388 mesh.remesh (num_vertices, gradation);
389+ check_and_fix_mesh (mesh, " remesh" , filename);
400390 increment_progress ();
401391 }
402392
@@ -406,6 +396,7 @@ bool Groom::run_mesh_pipeline(Mesh& mesh, GroomParameters params) {
406396 } else if (params.get_mesh_smoothing_method () == GroomParameters::GROOM_SMOOTH_VTK_WINDOWED_SINC_C ) {
407397 mesh.smoothSinc (params.get_mesh_vtk_windowed_sinc_iterations (), params.get_mesh_vtk_windowed_sinc_passband ());
408398 }
399+ check_and_fix_mesh (mesh, " smooth" , filename);
409400 increment_progress ();
410401 }
411402 return true ;
@@ -511,7 +502,6 @@ int Groom::get_total_ops() {
511502 (project_->get_original_domain_types ()[i] == DomainType::Image && params.get_convert_to_mesh ());
512503
513504 if (run_mesh) {
514- num_tools += params.get_mesh_largest_component () ? 1 : 0 ;
515505 num_tools += params.get_fill_mesh_holes_tool () ? 1 : 0 ;
516506 num_tools += params.get_mesh_smooth () ? 1 : 0 ;
517507 num_tools += params.get_remesh () ? 1 : 0 ;
@@ -1064,6 +1054,45 @@ void Groom::assign_transforms(std::vector<std::vector<double>> transforms, int d
10641054 }
10651055}
10661056
1057+ // ---------------------------------------------------------------------------
1058+ Mesh Groom::check_and_fix_mesh (Mesh& mesh, const std::string& step, const std::string& filename) {
1059+ auto issues = mesh.checkIntegrity ();
1060+ if (!issues.empty ()) {
1061+ SW_WARN (" Mesh issues detected after step '{}', file '{}', issues: {}" , step, filename, issues);
1062+ // try cleaning the mesh
1063+ SW_LOG (" Cleaning mesh..." );
1064+ mesh.clean ();
1065+ issues = mesh.checkIntegrity ();
1066+ if (!issues.empty ()) {
1067+ SW_ERROR (" Mesh issues remain after cleaning after step '{}', file '{}', issues: {}" , step, filename, issues);
1068+ throw std::runtime_error (fmt::format (" Error in mesh, step: {}, file: {}, issues: {}" , step, filename, issues));
1069+ }
1070+ }
1071+ auto poly_data = mesh.getVTKMesh ();
1072+
1073+ if (poly_data->GetNumberOfCells () == 0 ) {
1074+ throw std::runtime_error (fmt::format (" Error in mesh, step: {}, file: {}, mesh has zero cells" , step, filename));
1075+ }
1076+ if (poly_data->GetCell (0 )->GetNumberOfPoints () == 2 ) {
1077+ // try cleaning the mesh
1078+ Mesh m (poly_data);
1079+ m.clean ();
1080+ poly_data = m.getVTKMesh ();
1081+
1082+ if (poly_data->GetNumberOfCells () == 0 || poly_data->GetCell (0 )->GetNumberOfPoints () == 2 ) {
1083+ // still failed
1084+ SW_DEBUG (" Number of cells: {}" , poly_data->GetNumberOfCells ());
1085+ SW_DEBUG (" Number of points in first cell: {}" , poly_data->GetCell (0 )->GetNumberOfPoints ());
1086+ // write out to /tmp
1087+ // std::string tmpname = "/tmp/bad_mesh.vtk";
1088+ // MeshUtils::threadSafeWriteMesh(tmpname, mesh);
1089+ throw std::runtime_error (
1090+ fmt::format (" Error in mesh, step: {}, file: {}, mesh has invalid cells" , step, filename));
1091+ }
1092+ }
1093+ return Mesh (poly_data);
1094+ }
1095+
10671096// ---------------------------------------------------------------------------
10681097vtkSmartPointer<vtkMatrix4x4> Groom::compute_landmark_transform (vtkSmartPointer<vtkPoints> source,
10691098 vtkSmartPointer<vtkPoints> target) {
0 commit comments