-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix segfault, added tests for extractLabeledEuclideanClusters #4084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -39,45 +39,99 @@ | |||||||||||||||||||
|
||||||||||||||||||||
#include <pcl/segmentation/extract_labeled_clusters.h> | ||||||||||||||||||||
|
||||||||||||||||||||
namespace impl | ||||||||||||||||||||
{ | ||||||||||||||||||||
// convert labeled_cluster_map_t | ||||||||||||||||||||
void | ||||||||||||||||||||
labeled_cluster_map_to_labeled_cluster_vector( const pcl::PCLHeader& header, pcl::labeled_cluster_map_t labeled_map, std::vector<std::vector<pcl::PointIndices>>& vec ) | ||||||||||||||||||||
kunaltyagi marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
{ | ||||||||||||||||||||
if ( labeled_map.empty() ) | ||||||||||||||||||||
return; | ||||||||||||||||||||
|
||||||||||||||||||||
// get max label | ||||||||||||||||||||
const auto extracted_max_label = std::max_element( labeled_map.begin(), labeled_map.end(), | ||||||||||||||||||||
[]( const pcl::labeled_cluster_map_t::value_type& lhs, const pcl::labeled_cluster_map_t::value_type& rhs ) { return lhs.first < rhs.first; } | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any C++11 concerns? Or are we >= C++14 now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PCL requires C++14 |
||||||||||||||||||||
) | ||||||||||||||||||||
->first; | ||||||||||||||||||||
|
||||||||||||||||||||
// if the user didn't provide a preallocated vector, create it for them based on max label | ||||||||||||||||||||
if ( vec.empty() ) | ||||||||||||||||||||
vec.resize(extracted_max_label+1); | ||||||||||||||||||||
else if ( vec.size() <= extracted_max_label ) | ||||||||||||||||||||
{ | ||||||||||||||||||||
PCL_ERROR ("[pcl::extractLabeledEuclideanClusters] labeled_clusters size (%lu) less than extracted max_label (%lu)!\n", vec.size (), (unsigned int)extracted_max_label ); | ||||||||||||||||||||
return; | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// move clusters to output vector | ||||||||||||||||||||
// convert pcl::Indices to pcl:PointIndices | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are planning on deprecating the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you envision this class having 3 extraction methods? I had assumed 2: the new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you're touching this, I thought it might be easy to add it now, rather than later when someone is going through the interface updating the API. That means 3 interfaces:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If Actually, in searching for existing usage within PCL, I stumbled across There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrt deprecation and the alternate of rewriting, I'm not a user of these classes. Hopefully others can chime in |
||||||||||||||||||||
for ( auto& label_group : labeled_map ) | ||||||||||||||||||||
{ | ||||||||||||||||||||
for ( auto& cluster : label_group.second ) // cluster is pcl::Indices | ||||||||||||||||||||
{ | ||||||||||||||||||||
pcl::PointIndices pi = {}; | ||||||||||||||||||||
pi.header = header; | ||||||||||||||||||||
pi.indices = std::move(cluster); | ||||||||||||||||||||
vec[label_group.first].emplace_back(std::move(pi)); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
////////////////////////////////////////////////////////////////////////////////////////////// | ||||||||||||||||||||
template <typename PointT> void | ||||||||||||||||||||
pcl::extractLabeledEuclideanClusters (const PointCloud<PointT> &cloud, | ||||||||||||||||||||
const typename search::Search<PointT>::Ptr &tree, | ||||||||||||||||||||
float tolerance, | ||||||||||||||||||||
std::vector<std::vector<PointIndices> > &labeled_clusters, | ||||||||||||||||||||
labeled_cluster_map_t &labeled_clusters, | ||||||||||||||||||||
unsigned int min_pts_per_cluster, | ||||||||||||||||||||
unsigned int max_pts_per_cluster, | ||||||||||||||||||||
unsigned int) | ||||||||||||||||||||
unsigned int max_labels ) | ||||||||||||||||||||
{ | ||||||||||||||||||||
if (tree->getInputCloud ()->points.size () != cloud.points.size ()) | ||||||||||||||||||||
assert(tree); // ensure we have a tree | ||||||||||||||||||||
|
||||||||||||||||||||
if (tree->getInputCloud ()->size () != cloud.size ()) | ||||||||||||||||||||
{ | ||||||||||||||||||||
PCL_ERROR ("[pcl::extractLabeledEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", tree->getInputCloud ()->points.size (), cloud.points.size ()); | ||||||||||||||||||||
PCL_ERROR ("[pcl::extractLabeledEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", tree->getInputCloud ()->size (), cloud.size ()); | ||||||||||||||||||||
return; | ||||||||||||||||||||
} | ||||||||||||||||||||
// Create a bool vector of processed point indices, and initialize it to false | ||||||||||||||||||||
std::vector<bool> processed (cloud.points.size (), false); | ||||||||||||||||||||
|
||||||||||||||||||||
std::vector<int> nn_indices; | ||||||||||||||||||||
std::vector<float> nn_distances; | ||||||||||||||||||||
// Create a bool vector of processed point indices, and initialize it to false | ||||||||||||||||||||
std::vector<bool> processed (cloud.size (), false); | ||||||||||||||||||||
|
||||||||||||||||||||
// Process all points in the indices vector | ||||||||||||||||||||
for (int i = 0; i < static_cast<int> (cloud.points.size ()); ++i) | ||||||||||||||||||||
// Process all points in the cloud | ||||||||||||||||||||
for ( index_t i = 0; i < static_cast<index_t>(cloud.size()); ++i) | ||||||||||||||||||||
{ | ||||||||||||||||||||
if (processed[i]) | ||||||||||||||||||||
continue; | ||||||||||||||||||||
|
||||||||||||||||||||
std::vector<int> seed_queue; | ||||||||||||||||||||
int sq_idx = 0; | ||||||||||||||||||||
seed_queue.push_back (i); | ||||||||||||||||||||
|
||||||||||||||||||||
processed[i] = true; | ||||||||||||||||||||
|
||||||||||||||||||||
while (sq_idx < static_cast<int> (seed_queue.size ())) | ||||||||||||||||||||
// label for the current point | ||||||||||||||||||||
const auto current_label = cloud[i].label; | ||||||||||||||||||||
|
||||||||||||||||||||
// iterator in labeled_clusters for the current label | ||||||||||||||||||||
const auto current_label_cluster_it = labeled_clusters.find( current_label ); | ||||||||||||||||||||
|
||||||||||||||||||||
// max labels check | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want to break the interface by removing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the new interface: remove it; in the old interface: keep it. I'll create a PR for deprecating it while adding an overload without the extra parameter. |
||||||||||||||||||||
// if we would have to add a new label and we're already at max, no need to proceed | ||||||||||||||||||||
if ( current_label_cluster_it == labeled_clusters.end() && labeled_clusters.size() >= max_labels ) | ||||||||||||||||||||
continue; | ||||||||||||||||||||
|
||||||||||||||||||||
pcl::Indices seed_queue = {i}; | ||||||||||||||||||||
|
||||||||||||||||||||
index_t sq_idx = 0; | ||||||||||||||||||||
while (sq_idx < static_cast<index_t> (seed_queue.size ())) | ||||||||||||||||||||
{ | ||||||||||||||||||||
// Search for sq_idx | ||||||||||||||||||||
int ret = tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances, std::numeric_limits<int>::max()); | ||||||||||||||||||||
pcl::Indices nn_indices = {}; | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pulling out the vectors from the loop reduces the total dynamic allocations required There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I had assumed the compiler would optimize this but a brief googling tells me I am probably wrong. I will allocate outside and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
C++20 brings that (compiler optimize matching pair of new and delete). In any case, it'd not apply here because the allocations/deallocations aren't happening in pairs. If only there was a way to share a buffer in a loop from one iteration to another (without needing to use a PITA allocator) |
||||||||||||||||||||
std::vector<float> nn_distances = {}; | ||||||||||||||||||||
|
||||||||||||||||||||
const int ret = tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances, max_pts_per_cluster ); | ||||||||||||||||||||
if(ret == -1) | ||||||||||||||||||||
PCL_ERROR("radiusSearch on tree came back with error -1"); | ||||||||||||||||||||
|
||||||||||||||||||||
if (!ret) | ||||||||||||||||||||
{ | ||||||||||||||||||||
sq_idx++; | ||||||||||||||||||||
|
@@ -86,12 +140,10 @@ pcl::extractLabeledEuclideanClusters (const PointCloud<PointT> &cloud, | |||||||||||||||||||
|
||||||||||||||||||||
for (std::size_t j = 1; j < nn_indices.size (); ++j) // nn_indices[0] should be sq_idx | ||||||||||||||||||||
{ | ||||||||||||||||||||
if (processed[nn_indices[j]]) // Has this point been processed before ? | ||||||||||||||||||||
continue; | ||||||||||||||||||||
if (cloud.points[i].label == cloud.points[nn_indices[j]].label) | ||||||||||||||||||||
// labels match, and nn point not processed before | ||||||||||||||||||||
if ( !processed[nn_indices[j]] && current_label == cloud[nn_indices[j]].label ) | ||||||||||||||||||||
{ | ||||||||||||||||||||
// Perform a simple Euclidean clustering | ||||||||||||||||||||
seed_queue.push_back (nn_indices[j]); | ||||||||||||||||||||
seed_queue.push_back ( nn_indices[j] ) ; | ||||||||||||||||||||
processed[nn_indices[j]] = true; | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
@@ -101,26 +153,54 @@ pcl::extractLabeledEuclideanClusters (const PointCloud<PointT> &cloud, | |||||||||||||||||||
|
||||||||||||||||||||
// If this queue is satisfactory, add to the clusters | ||||||||||||||||||||
if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster) | ||||||||||||||||||||
{ | ||||||||||||||||||||
pcl::PointIndices r; | ||||||||||||||||||||
r.indices.resize (seed_queue.size ()); | ||||||||||||||||||||
for (std::size_t j = 0; j < seed_queue.size (); ++j) | ||||||||||||||||||||
r.indices[j] = seed_queue[j]; | ||||||||||||||||||||
|
||||||||||||||||||||
std::sort (r.indices.begin (), r.indices.end ()); | ||||||||||||||||||||
r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ()); | ||||||||||||||||||||
{ | ||||||||||||||||||||
if ( current_label_cluster_it == labeled_clusters.end() ) // label not found in map, add | ||||||||||||||||||||
{ | ||||||||||||||||||||
std::vector<pcl::Indices> v = {}; | ||||||||||||||||||||
v.emplace_back(std::move(seed_queue)); | ||||||||||||||||||||
labeled_clusters.emplace(current_label, std::move(v)); | ||||||||||||||||||||
} | ||||||||||||||||||||
else // label already exists in map; append seed_queue to label vector | ||||||||||||||||||||
current_label_cluster_it->second.emplace_back(std::move(seed_queue)); | ||||||||||||||||||||
Comment on lines
+157
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how I missed that one -- I guess it's been a while since I used |
||||||||||||||||||||
|
||||||||||||||||||||
r.header = cloud.header; | ||||||||||||||||||||
labeled_clusters[cloud.points[i].label].push_back (r); // We could avoid a copy by working directly in the vector | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
////////////////////////////////////////////////////////////////////////////////////////////// | ||||||||||||||||||||
template <typename PointT> void | ||||||||||||||||||||
pcl::extractLabeledEuclideanClusters (const PointCloud<PointT> &cloud, | ||||||||||||||||||||
const typename search::Search<PointT>::Ptr &tree, | ||||||||||||||||||||
float tolerance, | ||||||||||||||||||||
std::vector<std::vector<PointIndices> > &labeled_clusters, | ||||||||||||||||||||
unsigned int min_pts_per_cluster, | ||||||||||||||||||||
unsigned int max_pts_per_cluster, | ||||||||||||||||||||
unsigned int max_label ) | ||||||||||||||||||||
{ | ||||||||||||||||||||
labeled_cluster_map_t label_map = {}; | ||||||||||||||||||||
extractLabeledEuclideanClusters( cloud, tree, tolerance, label_map, min_pts_per_cluster, max_pts_per_cluster, max_label ); | ||||||||||||||||||||
|
||||||||||||||||||||
impl::labeled_cluster_map_to_labeled_cluster_vector( cloud.header, std::move(label_map), labeled_clusters ); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
////////////////////////////////////////////////////////////////////////////////////////////// | ||||||||||||||||||||
////////////////////////////////////////////////////////////////////////////////////////////// | ||||||||||||||||||||
////////////////////////////////////////////////////////////////////////////////////////////// | ||||||||||||||||||||
|
||||||||||||||||||||
template <typename PointT> void | ||||||||||||||||||||
pcl::LabeledEuclideanClusterExtraction<PointT>::extract (std::vector<std::vector<PointIndices> > &labeled_clusters) | ||||||||||||||||||||
{ | ||||||||||||||||||||
labeled_cluster_map_t label_map = {}; | ||||||||||||||||||||
extract( label_map ); | ||||||||||||||||||||
impl::labeled_cluster_map_to_labeled_cluster_vector( input_->header, std::move(label_map), labeled_clusters ); | ||||||||||||||||||||
|
||||||||||||||||||||
// Sort the clusters based on their size (largest one first) | ||||||||||||||||||||
for (auto &labeled_cluster : labeled_clusters) | ||||||||||||||||||||
std::sort (labeled_cluster.rbegin (), labeled_cluster.rend (), comparePointClusters); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
template <typename PointT> void | ||||||||||||||||||||
pcl::LabeledEuclideanClusterExtraction<PointT>::extract ( pcl::labeled_cluster_map_t &labeled_clusters) | ||||||||||||||||||||
{ | ||||||||||||||||||||
if (!initCompute () || | ||||||||||||||||||||
(input_ && input_->points.empty ()) || | ||||||||||||||||||||
|
@@ -143,14 +223,11 @@ pcl::LabeledEuclideanClusterExtraction<PointT>::extract (std::vector<std::vector | |||||||||||||||||||
tree_->setInputCloud (input_); | ||||||||||||||||||||
extractLabeledEuclideanClusters (*input_, tree_, static_cast<float> (cluster_tolerance_), labeled_clusters, min_pts_per_cluster_, max_pts_per_cluster_, max_label_); | ||||||||||||||||||||
|
||||||||||||||||||||
// Sort the clusters based on their size (largest one first) | ||||||||||||||||||||
for (auto &labeled_cluster : labeled_clusters) | ||||||||||||||||||||
std::sort (labeled_cluster.rbegin (), labeled_cluster.rend (), comparePointClusters); | ||||||||||||||||||||
|
||||||||||||||||||||
deinitCompute (); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
#define PCL_INSTANTIATE_LabeledEuclideanClusterExtraction(T) template class PCL_EXPORTS pcl::LabeledEuclideanClusterExtraction<T>; | ||||||||||||||||||||
#define PCL_INSTANTIATE_extractLabeledEuclideanClusters(T) template void PCL_EXPORTS pcl::extractLabeledEuclideanClusters<T>(const pcl::PointCloud<T> &, const typename pcl::search::Search<T>::Ptr &, float , std::vector<std::vector<pcl::PointIndices> > &, unsigned int, unsigned int, unsigned int); | ||||||||||||||||||||
#define PCL_INSTANTIATE_extractLabeledEuclideanClustersMap(T) template void PCL_EXPORTS pcl::extractLabeledEuclideanClusters<T>(const pcl::PointCloud<T> &, const typename pcl::search::Search<T>::Ptr &, float , pcl::labeled_cluster_map_t &, unsigned int, unsigned int, unsigned int); | ||||||||||||||||||||
|
||||||||||||||||||||
#endif // PCL_EXTRACT_CLUSTERS_IMPL_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the keys are
uint32
, it might make sense to either let the user choose the map by templating the function, or use a faster map thanstd::map
.Pinging @Morwenn for the assist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any plans to typedef the
label_type
? Ideally, alabel_t
or equivalent would go right here.Also, I'm torn between
unordered_map
andmap
, any suggestions? I'm assuming a smalln
, wheren
==number of labels, and amap
may be slightly better. But I could be wrong.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disclaimer: Personal opinion: None, though it makes sense.