Skip to Content.
Sympa Menu

cgal-discuss - Re: [cgal-discuss] Assertion failure doing collect_garbage() after removing isolated vertices from a surface mesh

Subject: CGAL users discussion list

List archive

Re: [cgal-discuss] Assertion failure doing collect_garbage() after removing isolated vertices from a surface mesh


Chronological Thread 
  • From: Sebastien Loriot <>
  • To:
  • Subject: Re: [cgal-discuss] Assertion failure doing collect_garbage() after removing isolated vertices from a surface mesh
  • Date: Thu, 2 Dec 2021 17:50:55 +0100
  • Authentication-results: mail2-smtp-roc.national.inria.fr; spf=None ; spf=Pass ; spf=None
  • Ironport-data: A9a23:eG4SwqmHILfaTd7qH7OdHzDo5gzsJ0RdPkR7XQ2eYbTBsI5bpzcOzGUXXTqPPfbZN2Tzc991O4/n8h8F68LQmN5hHFRt3Hw8FHgiRejtVY3IdB+oV8+xBpSeFxw/t512huEtnanYd1eEzvuWGuWn/SYUOZ2gHOKmUbeeYnopH2eIdQ944f5ds75h6mJXqYPha++9kYuaT/z3YDdJ6RYsWo4nw/7rRCdUgRjHkGhwUmrSyhx8lAS2e3E9VPrzLEwqRpfyatE88uWSH44vwFwll1418SvBCvv9+lr6WkgDQ7qXIg3Xz3QKCu6thR9NoiF02aE+XBYeQR0P2nPZwpYokoUL6c3YpQQBZsUgnMwGVx5CEiZie6hC0LDCKHm798eUyiUqdlOzkqU0VRlsVWEf0r8vXTsmGeYjADsCZxTGi+Oty6+gUcF3l8E7JY/qOpkeszdu11nk4VwOVciWGeOV8YYNhHFokpobRbCENptAfWE6NFKdd0IaE0kzI5casOeMp3DZTyd8llOwsfNvtjiXkRgZPKPFNdPUfpmVQJwQkBrJ4G3B+Gv9D1cRM9n39NZMyVr07senoM8xcNl6+HyEGv9WbJm7w2USDFgJUAL+r6XozEG5XN1bJgof/S9GQW0anKC0ZoGVYvF6iCfsUt0gtx54HOgz6QXLwa3Ri+pcLnZRVSZPMbTKq+dvLQHHFTa1cxfBCjlmsbnTQnWYnltRhVteJgBNRVI/ieQ4ocfpLjUtTEzfTv4Cczq7LJOIsw==
  • Ironport-hdrordr: A9a23:He/qUaA4QsOvOmHlHemP55DYdb4zR+YMi2TDpHoBNCC9Ffbo6/xG/c5rryMc7Qx6ZJhOo6HnBEDtewK5yXcx2/hrAV7AZnifhILLFvAE0WKK+VSJcE3DH6xmpMJdmsBFaeEYZmIK7voSjjPIcerIjOP3iZxARt2z856ud2xXgm1bgDuRwzz0LnFL
  • Ironport-phdr: A9a23:ouMpVhevtfIMNyUkjaUROtZmlGM+WdnLVj580XLHo4xHfqnrxZn+JkuXvawr0AWQG9uGoKMew8Pt8InYEVQa5piAtH1QOLdtbDQizfssogo7HcSeAlf6JvO5JwYzHcBFSUM3tyrjaRsdF8nxfUDdrWOv5jAOBBr/KRB1JuPoEYLOksi7ze+/94PObwlShTewYbx+IRGooQ7MqsQYnIxuJ7orxBDUuHVIYeNWxW1pJVKXgRnx49q78YBg/SpNpf8v7tZMXqrmcas2S7xYFykmPHsu5ML3rxnDTBCA6WUaX24LjxdHGQnF7BX9Xpfsriv3s/d21SeGMcHqS70/RDOt4bp2SB/zkCcIKSI28H3ZhMx3iaJUuhOhpxpiyILQb4yYMP9yc6XAdt0YWGVBRN5cWTFfDIOyb4UBDOQPMuhXoIb/u1QAogCzBRWvCe711jNEmnH70K883u88EQ/GxgsgH9cWvXrJstr6L70dUfupzKnJ0zrDae5d1zH66IjScxAhpu2MVq93fMrKzUkvEBnFjlSXqYz5JT+V0+ANvnOU7+plT+2vimonpxttrTiow8chk4/EjZ8axV7Y7yt22po1JcGmR05hZ96pCIdduz2VOYZ3TM0vX29mtigmxrAFtpO1czUGxpskyhPQa/KKd4uF7xH/WOuQLjl1hXNodb2jihuw/kWuxezxWtey3V1XoCRFldzMuWoM1xzV8sWIVvR98V2l2TqV0ADT8O5ELVg7laraN54hwqMwmYEJvUvfGS/2nV36jKCRdkUj9eio7/robq/6qZ+bMo94kgD+MqI0msy+G+s0KAYOX3Kd9O+h17Pj5VX0TKtWgvAyiKXUs5DXKd4GqqKnAAJZyIku5hKnAzql09kUh2cLIE9BdR6dkYTlJU/CLOrlAfq8nVihlipgyercMb37GJrNK2DOkLf/crZ57E5R0A8zwspe55JQE70ALvfzVlLouNzWARI0Nxa4w+ngCNV62YMeXXyADrWFP6PVtF+E/uMvI++Sa48JoDvxNeQp6vr0gXI6mVIRZ7Sl0YUUZXyiEflrJ12VYX/2jdcAFWcKsBA+TOvviFCaSj5TY3GyX7g95jE8FIKqF4LDRoS2jbyO2Se0BJxWZmRcBl+QFnfocp2IW+0QZyKKPs9hjjsEWKC9RI8uzx6usBb2xKdmLurP5iIYqInj1MNu6u3IlRAy8CR0AN6H32GMSWF0hGIISCUs0KBxu0wugmqFyrVy1vxECcRItbQOSRY/LZeazupgCtm0VBiGZcaMUF/hQ9OoBnY6Qds1htMPeE1gAM7xsxbYwiCWDq8JwryXGIQvoOWbxGn0P887ynDc1aBngUNhWdpKLWThh6hx8E/YCIfN1kmYjK23br9P4CmY/2iKyS+CvVpTTRVreaTDR3EWIEXM/vrj4UaXdLKkAK87MwZHgeqFMKpNdpW9llFBXvbkJJLbZ0q+nm6xAVCDwbbaP9miQHkUwCiIUBtMqAsU53vTaVlW7saJpmvfCHlxEAuqbR+9t+Z5r3y/Qwk/yATYNyWJMpK6/xcUgbqXTPZBhtrsVw8urjx1GBC22NeEUrK9

Several remarks:

You are using float as NT. If the mesh was created with double, you are doing a potentially harmful rounding.

You are using self-intersections with a Kernel with inexact predicates so the output is not guaranteed to be valid. Using CGAL::Exact_predicates_inexact_constructions_kernel is recommended.

You are getting an assertion because faces are indeed removed but
the mesh connectivity is not updated. It basically simply marks the faces as removed. You need to use Euler operations for that:
https://doc.cgal.org/latest/BGL/group__PkgBGLEulerOperations.html#gacfae7ff8e782da55b941e4487e86c738

if (!mesh.is_removed(inter_pair.first))
CGAL::Euler::remove_face( halfedge(inter_pair.first,mesh), mesh );
if (!mesh.is_removed(inter_pair.second))
CGAL::Euler::remove_face( halfedge(inter_pair.second, mesh), mesh );

Then you will see that you'll get the following assertion:
terminate called after throwing an instance of 'CGAL::Assertion_exception'
what(): CGAL ERROR: assertion violation!
Expr: abad1 != NULL_VECTOR
File: /home/sloriot/CGAL/git/master/Kernel_23/include/CGAL/Kernel/function_objects.h
Line: 251
Explanation: ab1 and ad1 are collinear


That is because of the inexact kernel used.

About face removal, I advice you to use consider the set of faces to remove as a selection and use that function:
https://doc.cgal.org/latest/BGL/group__PkgBGLSelectionFct.html#ga6f8d338df28c24bbf93677f24f7bcbbe
to avoid creating new non-manifold vertices.

A function like this one
https://doc.cgal.org/latest/Polygon_mesh_processing/group__keep__connected__components__grp.html#ga17cb532198888791f2ceb00666f4c1d4

could then be used to remove all selected faces (considering them as one connected component).

HTH

Sebastien.


On 12/2/21 6:07 AM, Yaoyu Hu ( via cgal-discuss Mailing List) wrote:
Hi,

I am trying to fix and clear some issues from a surface mesh. I do the following three steps in sequence:
(1) Duplicate non-manifold vertices.
(2) Remove self-intersections. And,
(3) Remove isolated vertices.
(4) Remove small connected components.

I always get an assertion error in (4). In my code, (1), (3), and (4) are directly calling CGAL functions to do the job. For (2) I have a simple procedure where I first identify all the self-intersection pairs and then remove all the faces that have self-intersection. I have set up a sample code and a sample mesh. The code is also available as a GitHub Gist
https://gist.github.com/huyaoyu/c10ff67a2fe67c141af77142e475a83d <https://gist.github.com/huyaoyu/c10ff67a2fe67c141af77142e475a83d>

The sample mesh is saved in my personal Google Drive.
https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing <https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing>

I am using CGAL 5.3 on Ubuntu 20.04.

=== Code begins. ===

#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>

#include <CGAL/Surface_mesh/IO/PLY.h>

#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>

namespace PMP = CGAL::Polygon_mesh_processing;

typedef CGAL::Simple_cartesian<float> Kernel_t;
typedef Kernel_t::Point_3  Point_t;

typedef CGAL::Surface_mesh<Point_t> Mesh_t;

typedef Kernel_t::Compare_dihedral_angle_3 Compare_dihedral_angle_3;

template < typename MeshT >
void read_mesh(const std::string& fn, MeshT& mesh) {
    std::ifstream ifs(fn, std::ios::binary);

    if ( !ifs ) {
        std::stringstream ss;
        ss << "Cannot open " << fn;
        throw std::runtime_error(ss.str());
    }

    std::string comments;
    CGAL::IO::read_PLY(ifs, mesh, comments);
}

template < typename MeshT >
void duplicate_non_manifold_vertices(MeshT& mesh) {
    PMP::duplicate_non_manifold_vertices(mesh);
}

template < typename MeshT >
int remove_self_intersections( MeshT& mesh ) {
    typedef typename boost::graph_traits<MeshT>::face_descriptor Face_t;

    std::vector< std::pair< Face_t, Face_t > > intersections;
    PMP::self_intersections<CGAL::Parallel_if_available_tag>(
        faces(mesh),
        mesh,
        std::back_inserter(intersections)
    );

    const int n_intersections = intersections.size();
    if ( n_intersections > 0 ) {
        for ( const auto& inter_pair : intersections ) {
            mesh.remove_face( inter_pair.first );
            mesh.remove_face( inter_pair.second );
        }

        mesh.collect_garbage();
    }

    return n_intersections;
}

template < typename MeshT >
int remove_isolated_vertices( MeshT& mesh ) {
    const int N = PMP::remove_isolated_vertices(mesh);
    return N;
}

// For computing the connected components.
template < typename MeshT >
struct ConnectionConstraint : public boost::put_get_helper< bool, ConnectionConstraint<MeshT> > {
    typedef typename boost::graph_traits<MeshT>::edge_descriptor EdgeT;

    typedef boost::readable_property_map_tag category;
    typedef bool                             value_type;
    typedef bool                             reference;
    typedef EdgeT                            key_type;

    ConnectionConstraint()
    : m_mesh(nullptr) {}

    ConnectionConstraint( MeshT& mesh, double bound )
    : m_mesh(&mesh), m_bound(bound) {}

    bool operator[] ( EdgeT edge ) const {
        const MeshT& mesh = *m_mesh;
        return m_compare(
            mesh.point( source( edge, mesh ) ),
            mesh.point( target( edge, mesh ) ),
            mesh.point( target( next( halfedge( edge, mesh ), mesh ), mesh ) ),
            mesh.point( target( next( opposite( halfedge( edge, mesh ), mesh), mesh ) ,mesh ) ),
            m_bound ) == CGAL::SMALLER;
    }

    const MeshT* m_mesh;
    Compare_dihedral_angle_3 m_compare;
    double m_bound;
};

template < typename MeshT >
void remove_small_connected_components(
    MeshT& mesh,
    int limit_num=10,
    double limit_di_angle_rad=2.36 /* 3/4 pi */ ) {

    const double bound = std::cos( limit_di_angle_rad );

    PMP::keep_large_connected_components(
        mesh,
        limit_num,
        PMP::parameters::edge_is_constrained_map(
            ConnectionConstraint<MeshT>(mesh, bound) )
    );
}

int main(int argc, char** argv) {
    const std::string in_mesh_fn =
        "please download from https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing <https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing>";

    // Read the mesh.
    Mesh_t mesh;
    read_mesh( in_mesh_fn, mesh );

    // Duplicate non-manifold_vertices.
    duplicate_non_manifold_vertices(mesh);

    // Remove self-intersection faces.
    remove_self_intersections(mesh);

    // Remove isolated vertices.
    remove_isolated_vertices(mesh);

    // Remove small connected components.
    remove_small_connected_components(mesh);

    return 0;
}

=== Code ends. ===

Step (2) is the remove_self_intersections() function. The assertion error shows in my terminal is

===  Terminal output begins. ===

terminate called after throwing an instance of 'CGAL::Assertion_exception'
  what():  CGAL ERROR: assertion violation!
Expr: _idx < data_.size()
File: /usr/local/include/CGAL/Surface_mesh/Properties.h
Line: 193

=== Terminal output ends. ===

I have also saved the backtrace from GDB in the Google Drive folder (a txt and a screenshot). please download from https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing <https://drive.google.com/drive/folders/1AmQONiss-q26cgs4wNBp3fMSlVmo09Ej?usp=sharing>

From the backtrace, it looks like when iterating through the faces of the mesh for computing the connected components, a face descriptor is out of the valid range.

Another thing that I found during the debugging is that, if you check the output of mesh.has_garbage() after calling PMP::remove_isolated_vertices() in my remove_isolated_vertices() function, mesh.has_garbage() is true. I suppose PMP::remove_isolated_vertices() should not leave the mesh in a state where it has uncollected garbage.

Any help is appreciated!

Thank you!

Yaoyu


--
You are currently subscribed to cgal-discuss.
To unsubscribe or access the archives, go to
https://sympa.inria.fr/sympa/info/cgal-discuss




Archive powered by MHonArc 2.6.19+.

Top of Page