I was experimenting with the stock correspondance_grouping.cpp code and noticed an issue with the visualizer. I modified the code by changing the pointtype to XYZ and removing the uniform filtering so that the model_keypoints and scene_keypoints clouds were identical to model and scene respectively. I was running tests to identify a backpack in a sparse pointcloud that I created, and was using a model of the backpack that I made by cropping out the rest of the scene. I was able to get the code to run by using the geometric consistency grouping method, and the software was able to succesfully identify 814 correspondances.
However, when I'm visualizing the scene and model clouds using the stock visualization code that came with the tutorial, I'm seeing the following display:
It's clear that the identified model cloud, and even the off-model cloud (which should only be shifted by 1m in the x direction), are being displayed incorrectly, even though the green lines which display the correspondences between the model cloud and scene cloud are in the correct position.
I think that there's an issue with how the pointclouds are being added to the visualizer. To verify this, I took the pointclouds for the scene, the rotated model, and the off-scene model and published this data as ROS pointcloud2 messages. When I visualize this data in RVIZ, the model (shown in purple) and off-scene model (shown in yellow) are in the correct positions:
Your Environment
I am currently running PCL 1.7.2-14build1 on an amd64 machine, which I originally installed via apt (apt-get install libpcl-dev). My ROS version is Lunar, and for the record, I built and compiled the correspondance_grouping code within a ROS package using catkin. I am running Ubuntu 16.04.
Context
Expected Behavior
Current Behavior
Code to Reproduce
Here's my raw code:
`
include <pcl/io/pcd_io.h>
include <pcl/point_cloud.h>
include <pcl/correspondence.h>
include <pcl/features/normal_3d_omp.h>
include <pcl/features/shot_omp.h>
include <pcl/features/board.h>
//#include <pcl/filters/uniform_sampling.h>
include <pcl/keypoints/uniform_sampling.h>
include <pcl/recognition/cg/hough_3d.h>
include <pcl/recognition/cg/geometric_consistency.h>
double
computeCloudResolution (const pcl::PointCloud::ConstPtr &cloud)
{
double res = 0.0;
int n_points = 0;
int nres;
std::vector indices (2);
std::vector sqr_distances (2);
pcl::search::KdTree tree;
tree.setInputCloud (cloud);
for (size_t i = 0; i < cloud->size (); ++i)
{
if (! pcl_isfinite ((*cloud)[i].x))
{
continue;
}
//Considering the second neighbor since the first is the point itself.
nres = tree.nearestKSearch (i, 2, indices, sqr_distances);
if (nres == 2)
{
res += sqrt (sqr_distances[1]);
++n_points;
}
}
if (n_points != 0)
{
res /= n_points;
}
return res;
}
int
main (int argc, char *argv[])
{
parseCommandLine (argc, argv);
pcl::PointCloud::Ptr model (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_keypoints (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_keypoints (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_normals (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_normals (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_descriptors (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_descriptors (new pcl::PointCloud ());
// For each scene keypoint descriptor, find nearest neighbor into the model keypoints descriptor cloud and add it to the correspondences vector.
for (size_t i = 0; i < scene_descriptors->size (); ++i)
{
std::vector neigh_indices (1);
std::vector neigh_sqr_dists (1);
if (!pcl_isfinite (scene_descriptors->at (i).descriptor[0])) //skipping NaNs
{
continue;
}
int found_neighs = match_search.nearestKSearch (scene_descriptors->at (i), 1, neigh_indices, neigh_sqr_dists);
if(found_neighs == 1 && neigh_sqr_dists[0] < 0.25f) // add match only if the squared descriptor distance is less than 0.25 (SHOT descriptor distances are between 0 and 1 by design)
{
pcl::Correspondence corr (neigh_indices[0], static_cast (i), neigh_sqr_dists[0]);
model_scene_corrs->push_back (corr);
}
}
std::cout << "Correspondences found: " << model_scene_corrs->size () << std::endl;
// Using Hough3D
if (usehough)
{
//
// Compute (Keypoints) Reference Frames only for Hough
//
pcl::PointCloud::Ptr model_rf (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_rf (new pcl::PointCloud ());
pcl::PointCloud::Ptr off_scene_model (new pcl::PointCloud ());
pcl::PointCloud::Ptr off_scene_model_keypoints (new pcl::PointCloud ());
if (showcorrespondences || showkeypoints)
{
// We are translating the model so that it doesn't end in the middle of the scene representation
pcl::transformPointCloud (model, off_scene_model, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0));
pcl::transformPointCloud (model_keypoints, off_scene_model_keypoints, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0));
}
pcl::PointCloud::Ptr rotated_model_permanent (new pcl::PointCloud ());
for (size_t i = 0; i < rototranslations.size (); ++i)
{
pcl::PointCloud::Ptr rotated_model (new pcl::PointCloud ());
pcl::transformPointCloud (model, rotated_model, rototranslations[i]);
pcl::transformPointCloud (model, rotated_model_permanent, rototranslations[0]);
std::stringstream ss_cloud;
ss_cloud << "instance" << i;
pcl::visualization::PointCloudColorHandlerCustom<PointType> rotated_model_color_handler (rotated_model, 255, 0, 0);
viewer.addPointCloud (rotated_model, rotated_model_color_handler, ss_cloud.str ());
if (show_correspondences_)
{
for (size_t j = 0; j < clustered_corrs[i].size (); ++j)
{
std::stringstream ss_line;
ss_line << "correspondence_line" << i << "_" << j;
PointType& model_point = off_scene_model_keypoints->at (clustered_corrs[i][j].index_query);
PointType& scene_point = scene_keypoints->at (clustered_corrs[i][j].index_match);
// We are drawing a line for each pair of clustered correspondences found between the model and the scene
viewer.addLine<PointType, PointType> (model_point, scene_point, 0, 255, 0, ss_line.str ());
}
}
//change point cloud, make it into a point cloud 2 message.
pcl::toROSMsg(scene, sceneCloud);
pcl::toROSMsg(rotated_model_permanent, modelCloud);
pcl::toROSMsg(*off_scene_model, offSceneModelCloud);
//set frame id of published message
sceneCloud.header.frame_id = "map";
modelCloud.header.frame_id = "map";
offSceneModelCloud.header.frame_id = "map";
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_definitions(-DCOMPILEDWITHC11)
message(STATUS "Using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
add_definitions(-DCOMPILEDWITHC0X)
message(STATUS "Using flag -std=c++0x.")
else()
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
I was experimenting with the stock correspondance_grouping.cpp code and noticed an issue with the visualizer. I modified the code by changing the pointtype to XYZ and removing the uniform filtering so that the model_keypoints and scene_keypoints clouds were identical to model and scene respectively. I was running tests to identify a backpack in a sparse pointcloud that I created, and was using a model of the backpack that I made by cropping out the rest of the scene. I was able to get the code to run by using the geometric consistency grouping method, and the software was able to succesfully identify 814 correspondances.
However, when I'm visualizing the scene and model clouds using the stock visualization code that came with the tutorial, I'm seeing the following display:
It's clear that the identified model cloud, and even the off-model cloud (which should only be shifted by 1m in the x direction), are being displayed incorrectly, even though the green lines which display the correspondences between the model cloud and scene cloud are in the correct position.
I think that there's an issue with how the pointclouds are being added to the visualizer. To verify this, I took the pointclouds for the scene, the rotated model, and the off-scene model and published this data as ROS pointcloud2 messages. When I visualize this data in RVIZ, the model (shown in purple) and off-scene model (shown in yellow) are in the correct positions:
Your Environment
I am currently running PCL 1.7.2-14build1 on an amd64 machine, which I originally installed via apt (apt-get install libpcl-dev). My ROS version is Lunar, and for the record, I built and compiled the correspondance_grouping code within a ROS package using catkin. I am running Ubuntu 16.04.
Context
Expected Behavior
Current Behavior
Code to Reproduce
Here's my raw code: `
include <pcl/io/pcd_io.h>
include <pcl/point_cloud.h>
include <pcl/correspondence.h>
include <pcl/features/normal_3d_omp.h>
include <pcl/features/shot_omp.h>
include <pcl/features/board.h>
//#include <pcl/filters/uniform_sampling.h>
include <pcl/keypoints/uniform_sampling.h>
include <pcl/recognition/cg/hough_3d.h>
include <pcl/recognition/cg/geometric_consistency.h>
include <pcl/visualization/pcl_visualizer.h>
include <pcl/kdtree/kdtree_flann.h>
include <pcl/kdtree/impl/kdtree_flann.hpp>
include <pcl/common/transforms.h>
include <pcl/console/parse.h>
include <ros/ros.h>
include <pcl_ros/point_cloud.h>
include <sensor_msgs/PointCloud2.h>
include <pcl_conversions/pcl_conversions.h>
//typedef pcl::PointXYZRGBA PointType; typedef pcl::PointXYZ PointType; typedef pcl::Normal NormalType; typedef pcl::ReferenceFrame RFType; typedef pcl::SHOT352 DescriptorType;
std::string modelfilename; std::string scenefilename;
//Algorithm params bool showkeypoints (false); bool showcorrespondences (false); bool use_cloudresolution (false); bool usehough (true); float modelss (0.01f); float sceness (0.03f); float rfrad (0.015f); float descrrad (0.02f); float cgsize (0.01f); float cgthresh (5.0f);
void showHelp (char *filename) { std::cout << std::endl; std::cout << "" << std::endl; std::cout << " " << std::endl; std::cout << " Correspondence Grouping Tutorial - Usage Guide " << std::endl; std::cout << " " << std::endl; std::cout << "" << std::endl << std::endl; std::cout << "Usage: " << filename << " model_filename.pcd scene_filename.pcd [Options]" << std::endl << std::endl; std::cout << "Options:" << std::endl; std::cout << " -h: Show this help." << std::endl; std::cout << " -k: Show used keypoints." << std::endl; std::cout << " -c: Show used correspondences." << std::endl; std::cout << " -r: Compute the model cloud resolution and multiply" << std::endl; std::cout << " each radius given by that value." << std::endl; std::cout << " --algorithm (Hough|GC): Clustering algorithm used (default Hough)." << std::endl; std::cout << " --model_ss val: Model uniform sampling radius (default 0.01)" << std::endl; std::cout << " --scene_ss val: Scene uniform sampling radius (default 0.03)" << std::endl; std::cout << " --rf_rad val: Reference frame radius (default 0.015)" << std::endl; std::cout << " --descr_rad val: Descriptor radius (default 0.02)" << std::endl; std::cout << " --cg_size val: Cluster size (default 0.01)" << std::endl; std::cout << " --cg_thresh val: Clustering threshold (default 5)" << std::endl << std::endl; }
void parseCommandLine (int argc, char *argv[]) { //Show help if (pcl::console::find_switch (argc, argv, "-h")) { showHelp (argv[0]); exit (0); }
//Model & scene filenames std::vector filenames;
filenames = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");
if (filenames.size () != 2)
{
std::cout << "Filenames missing.\n";
showHelp (argv[0]);
exit (-1);
}
modelfilename = argv[filenames[0]]; scenefilename = argv[filenames[1]];
//Program behavior if (pcl::console::find_switch (argc, argv, "-k")) { showkeypoints = true; } if (pcl::console::find_switch (argc, argv, "-c")) { showcorrespondences = true; } if (pcl::console::find_switch (argc, argv, "-r")) { use_cloudresolution = true; }
std::string used_algorithm; if (pcl::console::parse_argument (argc, argv, "--algorithm", used_algorithm) != -1) { if (used_algorithm.compare ("Hough") == 0) { usehough = true; }else if (used_algorithm.compare ("GC") == 0) { usehough = false; } else { std::cout << "Wrong algorithm name.\n"; showHelp (argv[0]); exit (-1); } }
//General parameters pcl::console::parse_argument (argc, argv, "--model_ss", modelss); pcl::console::parse_argument (argc, argv, "--scene_ss", sceness); pcl::console::parse_argument (argc, argv, "--rf_rad", rfrad); pcl::console::parse_argument (argc, argv, "--descr_rad", descrrad); pcl::console::parse_argument (argc, argv, "--cg_size", cgsize); pcl::console::parse_argument (argc, argv, "--cg_thresh", cgthresh); }
double computeCloudResolution (const pcl::PointCloud::ConstPtr &cloud)
{
double res = 0.0;
int n_points = 0;
int nres;
std::vector indices (2);
std::vector sqr_distances (2);
pcl::search::KdTree tree;
tree.setInputCloud (cloud);
for (size_t i = 0; i < cloud->size (); ++i) { if (! pcl_isfinite ((*cloud)[i].x)) { continue; } //Considering the second neighbor since the first is the point itself. nres = tree.nearestKSearch (i, 2, indices, sqr_distances); if (nres == 2) { res += sqrt (sqr_distances[1]); ++n_points; } } if (n_points != 0) { res /= n_points; } return res; }
int main (int argc, char *argv[]) { parseCommandLine (argc, argv);
pcl::PointCloud::Ptr model (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_keypoints (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_keypoints (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_normals (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_normals (new pcl::PointCloud ());
pcl::PointCloud::Ptr model_descriptors (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_descriptors (new pcl::PointCloud ());
// // Load clouds // if (pcl::io::loadPCDFile (modelfilename, model) < 0) { std::cout << "Error loading model cloud." << std::endl; showHelp (argv[0]); return (-1); } if (pcl::io::loadPCDFile (scenefilename, scene) < 0) { std::cout << "Error loading scene cloud." << std::endl; showHelp (argv[0]); return (-1); }
// // Set up resolution invariance // if (use_cloudresolution) { float resolution = static_cast (computeCloudResolution (model));
if (resolution != 0.0f)
{
modelss = resolution;
sceness = resolution;
rfrad = resolution;
descrrad = resolution;
cgsize *= resolution;
}
}
// // Compute Normals // pcl::NormalEstimationOMP<PointType, NormalType> norm_est; norm_est.setKSearch (10); norm_est.setInputCloud (model); norm_est.compute (*model_normals);
norm_est.setInputCloud (scene); norm_est.compute (*scene_normals);
// // Downsample Clouds to Extract keypoints //
/ pcl::UniformSampling uniform_sampling;
uniform_sampling.setInputCloud (model);
uniform_sampling.setRadiusSearch (modelss);
//uniform_sampling.filter ( model_keypoints);
pcl::PointCloud keypointIndices1;
uniform_sampling.compute(keypointIndices1);
pcl::copyPointCloud(model, keypointIndices1.points, model_keypoints);
std::cout << "Model total points: " << model->size () << "; Selected Keypoints: " << model_keypoints->size () << std::endl;
uniform_sampling.setInputCloud (scene); uniform_sampling.setRadiusSearch (sceness); //uniform_sampling.filter (scene_keypoints); pcl::PointCloud keypointIndices2;
uniform_sampling.compute(keypointIndices2);
pcl::copyPointCloud( scene, keypointIndices2.points, scene_keypoints);
std::cout << "Scene total points: " << scene->size () << "; Selected Keypoints: " << scene_keypoints->size () << std::endl;
/
model_keypoints = model; scene_keypoints = scene; // // Compute Descriptor for keypoints // pcl::SHOTEstimationOMP<PointType, NormalType, DescriptorType> descr_est; descr_est.setRadiusSearch (descrrad);
descr_est.setInputCloud (model_keypoints); descr_est.setInputNormals (model_normals); descr_est.setSearchSurface (model); descr_est.compute (*model_descriptors);
descr_est.setInputCloud (scene_keypoints); descr_est.setInputNormals (scene_normals); descr_est.setSearchSurface (scene); descr_est.compute (*scene_descriptors);
// // Find Model-Scene Correspondences with KdTree // pcl::CorrespondencesPtr model_scene_corrs (new pcl::Correspondences ());
pcl::KdTreeFLANN match_search;
match_search.setInputCloud (model_descriptors);
// For each scene keypoint descriptor, find nearest neighbor into the model keypoints descriptor cloud and add it to the correspondences vector. for (size_t i = 0; i < scene_descriptors->size (); ++i) { std::vector neigh_indices (1);
std::vector neigh_sqr_dists (1);
if (!pcl_isfinite (scene_descriptors->at (i).descriptor[0])) //skipping NaNs
{
continue;
}
int found_neighs = match_search.nearestKSearch (scene_descriptors->at (i), 1, neigh_indices, neigh_sqr_dists);
if(found_neighs == 1 && neigh_sqr_dists[0] < 0.25f) // add match only if the squared descriptor distance is less than 0.25 (SHOT descriptor distances are between 0 and 1 by design)
{
pcl::Correspondence corr (neigh_indices[0], static_cast (i), neigh_sqr_dists[0]);
model_scene_corrs->push_back (corr);
}
}
std::cout << "Correspondences found: " << model_scene_corrs->size () << std::endl;
// // Actual Clustering // std::vector<Eigen::Matrix4f, Eigen::aligned_allocator > rototranslations;
std::vector clustered_corrs;
// Using Hough3D if (usehough) { // // Compute (Keypoints) Reference Frames only for Hough // pcl::PointCloud::Ptr model_rf (new pcl::PointCloud ());
pcl::PointCloud::Ptr scene_rf (new pcl::PointCloud ());
} else // Using GeometricConsistency { pcl::GeometricConsistencyGrouping<PointType, PointType> gc_clusterer; gc_clusterer.setGCSize (cgsize); gc_clusterer.setGCThreshold (cgthresh);
}
// // Output results // std::cout << "Model instances found: " << rototranslations.size () << std::endl; for (size_t i = 0; i < rototranslations.size (); ++i) { std::cout << "\n Instance " << i + 1 << ":" << std::endl; std::cout << " Correspondences belonging to this instance: " << clustered_corrs[i].size () << std::endl;
}
// // Visualization // pcl::visualization::PCLVisualizer viewer ("Correspondence Grouping"); viewer.addPointCloud (scene, "scene_cloud");
usleep(500000); viewer.addPointCloud (scene, "scene_cloud");
pcl::PointCloud::Ptr off_scene_model (new pcl::PointCloud ());
pcl::PointCloud::Ptr off_scene_model_keypoints (new pcl::PointCloud ());
if (showcorrespondences || showkeypoints) { // We are translating the model so that it doesn't end in the middle of the scene representation pcl::transformPointCloud (model, off_scene_model, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0)); pcl::transformPointCloud (model_keypoints, off_scene_model_keypoints, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0));
}
if (showkeypoints) { pcl::visualization::PointCloudColorHandlerCustom scene_keypoints_color_handler (scene_keypoints, 0, 0, 255);
viewer.addPointCloud (scene_keypoints, scene_keypoints_color_handler, "scene_keypoints");
viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "scene_keypoints");
} pcl::PointCloud::Ptr rotated_model_permanent (new pcl::PointCloud ());
for (size_t i = 0; i < rototranslations.size (); ++i)
{
pcl::PointCloud::Ptr rotated_model (new pcl::PointCloud ());
pcl::transformPointCloud (model, rotated_model, rototranslations[i]);
pcl::transformPointCloud (model, rotated_model_permanent, rototranslations[0]);
std::stringstream ss_cloud;
ss_cloud << "instance" << i;
}
//initialize ros... lol
ros::init(argc, argv, "correspondance_grouping"); ros::start();
ros::NodeHandle nh("~");
//create ros messages. sensor_msgs::PointCloud2 sceneCloud, modelCloud, offSceneModelCloud;
//change point cloud, make it into a point cloud 2 message. pcl::toROSMsg(scene, sceneCloud); pcl::toROSMsg(rotated_model_permanent, modelCloud); pcl::toROSMsg(*off_scene_model, offSceneModelCloud); //set frame id of published message sceneCloud.header.frame_id = "map"; modelCloud.header.frame_id = "map"; offSceneModelCloud.header.frame_id = "map";
//set up pubs. ros::Publisher scenePub = nh.advertise("scene", 1);
ros::Publisher modelPub = nh.advertise("model", 1);
ros::Publisher offSceneModelPub = nh.advertise("offSceneModel", 1);
while (!viewer.wasStopped ()) { viewer.spinOnce (); //update time sceneCloud.header.stamp = ros::Time::now(); modelCloud.header.stamp = ros::Time::now(); offSceneModelCloud.header.stamp = ros::Time::now();
}
ros::shutdown();
return (0); } `
Here's my project's CMakeLists file: `cmake_minimum_required(VERSION 2.8.3) project(monocular_slam_nav)
IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Debug) ENDIF()
MESSAGE("Build type: " ${CMAKE_BUILD_TYPE})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -march=native ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -march=native")
Check C++11 or C++0x support
include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") add_definitions(-DCOMPILEDWITHC11) message(STATUS "Using flag -std=c++11.") elseif(COMPILER_SUPPORTS_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") add_definitions(-DCOMPILEDWITHC0X) message(STATUS "Using flag -std=c++0x.") else() message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif()
LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
catkin dependencies
find_package(catkin REQUIRED COMPONENTS roscpp pcl_ros sensor_msgs )
set(Boost_USE_STATIC_LIBS ON)
add_definitions(-DBOOST_TEST_DYN_LINK)
System dependencies are found with CMake's conventions
find_package(Boost REQUIRED COMPONENTS system )
find_package(metslib REQUIRED)
catkin_package( INCLUDE_DIRS include LIBRARIES CATKIN_DEPENDS roscpp pcl_ros sensor_msgs DEPENDS metslib )
include_directories( ${PROJECT_SOURCE_DIR}/include ${catkin_INCLUDE_DIRS} ${metslib_INCLUDE_DIRS} )
set(LIBS ${catkin_LIBRARIES} )
######################################
correspondance grouping example
add_executable(correspondance_grouping src/correspondance_grouping.cpp )
target_link_libraries(correspondance_grouping ${LIBS} )
global hypothesis verification example
add_executable(global_hypothesis_verification src/global_hypothesis_verification.cpp )
target_link_libraries(global_hypothesis_verification ${LIBS} )
add_executable(correspondance_grouping_original src/correspondance_grouping_original.cpp )
target_link_libraries(correspondance_grouping_original ${LIBS} ) `
Here's my package.xml in case you want to recreate within a catkinized workspace: `<?xml version="1.0"?>
`
Possible Solution