cgal icon indicating copy to clipboard operation
cgal copied to clipboard

CGAL::Assertion_exception at memory location 0x004FEA74 when running Efficient RANSAC->Detecting 3D Planes

Open arielc-brillianetor opened this issue 2 years ago • 3 comments

Please use the following template to help us solving your issue.

Issue Details

I loaded my own point cloud file (.xyz) which contains 373812 points, many more then the example provided. The code compiles fine, but when I run it after a few minutes I get an error at: "assertions_impl.h" line 172, which is the end of the following function:

// failure functions
// -----------------
CGAL_INLINE_FUNCTION
void
assertion_fail( const char* expr,
                const char* file,
                int         line,
                const char* msg)
{
    get_static_error_handler()("assertion", expr, file, line, msg);
    switch (get_static_error_behaviour()) {
    case ABORT:
        std::abort();
    case EXIT:
        std::exit(1);  // EXIT_FAILURE
    case EXIT_WITH_SUCCESS:
        std::exit(0);  // EXIT_SUCCESS
    case CONTINUE: // The CONTINUE case should not be used anymore.
    case THROW_EXCEPTION:
    default:
        throw Assertion_exception("CGAL", expr, file, line, msg);
    }
}

The error that I get is: Unhandled exception at 0x76DCCA42 in CGAL_test.exe: Microsoft C++ exception: CGAL::Assertion_exception at memory location 0x004FEA74.

The input variables to the assertion_fail function are:

expr = 0x01074730 "parray_ != nullptr"
file = 0x010746b8 "C:\\Projects\\vcpkg\\installed\\x86-windows\\include\\CGAL\\Surface_mesh\\Properties.h"
line = 586
msg = 0x0107337e ""

Source Code -- This is a copy and paste from: https://doc.cgal.org/latest/Shape_detection/index.html (Detecting 3D Planes):

// STL includes.
#include <string>
#include <vector>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <iterator>
// Boost includes.
#include <boost/iterator/function_output_iterator.hpp>
// CGAL includes.
#include <CGAL/Timer.h>
#include <CGAL/Random.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing_on_point_set.h>
// Type declarations.
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using FT = typename Kernel::FT;
using Point_3 = typename Kernel::Point_3;
using Vector_3 = typename Kernel::Vector_3;
using Input_range = CGAL::Point_set_3<Point_3>;
using Point_map = typename Input_range::Point_map;
using Normal_map = typename Input_range::Vector_map;
using Neighbor_query = CGAL::Shape_detection::Point_set::K_neighbor_query<Kernel, Input_range, Point_map>;
using Region_type = CGAL::Shape_detection::Point_set::Least_squares_plane_fit_region<Kernel, Input_range, Point_map, Normal_map>;
using Region_growing = CGAL::Shape_detection::Region_growing<Input_range, Neighbor_query, Region_type>;
using Indices = std::vector<std::size_t>;
using Output_range = CGAL::Point_set_3<Point_3>;
using Points_3 = std::vector<Point_3>;
// Define an insert iterator.
struct Insert_point_colored_by_region_index {
    using argument_type = Indices;
    using result_type = void;
    using Color_map =
        typename Output_range:: template Property_map<unsigned char>;
    const Input_range& m_input_range;
    const   Point_map  m_point_map;
    Output_range& m_output_range;
    std::size_t& m_number_of_regions;
    Color_map m_red, m_green, m_blue;
    Insert_point_colored_by_region_index(
        const Input_range& input_range,
        const   Point_map  point_map,
        Output_range& output_range,
        std::size_t& number_of_regions) :
        m_input_range(input_range),
        m_point_map(point_map),
        m_output_range(output_range),
        m_number_of_regions(number_of_regions) {
        m_red =
            m_output_range.template add_property_map<unsigned char>("red", 0).first;
        m_green =
            m_output_range.template add_property_map<unsigned char>("green", 0).first;
        m_blue =
            m_output_range.template add_property_map<unsigned char>("blue", 0).first;
    }
    result_type operator()(const argument_type& region) {
        CGAL::Random rand(static_cast<unsigned int>(m_number_of_regions));
        const unsigned char r =
            static_cast<unsigned char>(64 + rand.get_int(0, 192));
        const unsigned char g =
            static_cast<unsigned char>(64 + rand.get_int(0, 192));
        const unsigned char b =
            static_cast<unsigned char>(64 + rand.get_int(0, 192));
        for (const std::size_t index : region) {
            const auto& key = *(m_input_range.begin() + index);
            const Point_3& point = get(m_point_map, key);
            const auto it = m_output_range.insert(point);
            m_red[*it] = r;
            m_green[*it] = g;
            m_blue[*it] = b;
        }
        ++m_number_of_regions;
    }
}; // Insert_point_colored_by_region_index


int main(int argc, char* argv[]) {
    std::cout << std::endl <<
        "region_growing_on_point_set_3 example started"
        << std::endl << std::endl;
    std::cout <<
        "Note: if 0 points are loaded, please specify the path to the file data/point_set_3.xyz by hand!"
        << std::endl << std::endl;
    // Load xyz data either from a local folder or a user-provided file.    
    //std::ifstream in(argc > 1 ? argv[1] : CGAL::data_file_path("points_3/point_set_3.xyz"));
    const std::string sInFileName = "downsampled_Meruba_1.xyz";
    const std::string sOutFileName = "out_Meruba_1";
    std::ifstream in(sInFileName);
    
    
    CGAL::IO::set_ascii_mode(in);
    if (!in) {
        std::cout <<
            "Error: cannot read the file point_set_3.xyz!" << std::endl;
        std::cout <<
            "You can either create a symlink to the data folder or provide this file by hand."
            << std::endl << std::endl;
        return EXIT_FAILURE;
    }
    const bool with_normal_map = true;
    Input_range input_range(with_normal_map);
    in >> input_range;
    in.close();
    std::cout <<
        "* loaded "
        << input_range.size() <<
        " points with normals"
        << std::endl;
    // Default parameter values for the data file point_set_3.xyz.
    const std::size_t k = 12;
    const FT          max_distance_to_plane = FT(2);
    const FT          max_accepted_angle = FT(20);
    const std::size_t min_region_size = 50;
    // Create instances of the classes Neighbor_query and Region_type.
    Neighbor_query neighbor_query(
        input_range,
        k,
        input_range.point_map());
    Region_type region_type(
        input_range,
        max_distance_to_plane, max_accepted_angle, min_region_size,
        input_range.point_map(), input_range.normal_map());
    // Create an instance of the region growing class.
    Region_growing region_growing(
        input_range, neighbor_query, region_type);
    // Run the algorithm.
    Output_range output_range;
    std::size_t number_of_regions = 0;
    Insert_point_colored_by_region_index inserter(
        input_range, input_range.point_map(),
        output_range, number_of_regions);
    CGAL::Timer timer;
    timer.start();
    region_growing.detect(
        boost::make_function_output_iterator(inserter));
    timer.stop();
    // Print the number of found regions.
    std::cout << "* " << number_of_regions <<
        " regions have been found in " << timer.time() << " seconds"
        << std::endl;
    // Save the result to a file in the user-provided path if any.
    //if (argc > 2) 
    {
        //const std::string path = argv[2];
        const std::string fullpath = sOutFileName + "regions_point_set_3.ply";
        std::ofstream out(fullpath);
        out << output_range;
        std::cout << "* found regions are saved in " << fullpath << std::endl;
        out.close();
    }
    // Get all unassigned items.
    Indices unassigned_items;
    region_growing.unassigned_items(std::back_inserter(unassigned_items));
    // Print the number of unassigned items.
    std::cout << "* " << unassigned_items.size() <<
        " points do not belong to any region"
        << std::endl;
    // Store all unassigned points.
    Points_3 unassigned_points;
    unassigned_points.reserve(unassigned_items.size());
    for (const auto index : unassigned_items) {
        const auto& key = *(input_range.begin() + index);
        const Point_3& point = get(input_range.point_map(), key);
        unassigned_points.push_back(point);
    }
    std::cout << "* " << unassigned_points.size() <<
        " unassigned points are stored"
        << std::endl;
    std::cout << std::endl <<
        "region_growing_on_point_set_3 example finished"
        << std::endl << std::endl;
    return EXIT_SUCCESS;
}

Environment

  • Operating system: Windows 64 bits):

  • Compiler: Visual Studio 2019

  • Release or debug mode: Running in Debug Mode

  • CGAL version:

  • Boost version: 1.78.0

downsampled_Meruba_1.zip

Thanks a lot for your help

arielc-brillianetor avatar Aug 24 '22 06:08 arielc-brillianetor

Both shape detection methods, RANSAC and region growing, require points with normal vectors. Your point set does not have normals.

You can estimate them on the fly by inserting the following code snippet after loading the point cloud:

std::cout <<
  "* loaded "
  << input_range.size();

if (!input_range.has_normal_map()) {
    input_range.add_normal_map();
    std::cout << " points without normals" << std::endl;
    CGAL::pca_estimate_normals<CGAL::Parallel_if_available_tag>
      (input_range, 12);
    std::cout << "normals estimated" << std::endl;
}
else std::cout << " points with normals" << std::endl;

You also need to add an include: #include <CGAL/pca_estimate_normals.h>

soesau avatar Aug 29 '22 09:08 soesau

Thanks, It worked. Can I ask another question regarding the same issue? The output of the code is a .ply file that contains the X-Y-Z coordinates as well as the R-G-B values. How do I get the normal of each plane, or surface that is found? Thanks a lot

arielc-brillianetor avatar Sep 05 '22 18:09 arielc-brillianetor

This version of the region growing actually does not provide the fitted primitives just the points of each primitive. This will change with the next revision of the region growing.

You can take a look into linear_least_squares_fitting_3 to fit a plane to a range of points.

soesau avatar Sep 06 '22 09:09 soesau

Both shape detection methods, RANSAC and region growing, require points with normal vectors. Your point set does not have normals.

@soesau wouldn't it make sense to add a precondition that checks for normals?

afabri avatar Dec 12 '22 10:12 afabri

Yes, certainly. I'll look into it.

soesau avatar Dec 12 '22 10:12 soesau