/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2011, Alexandru-Eugen Ichim * Willow Garage, Inc * Copyright (c) 2012-, Open Perception, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the copyright holder(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $Id$ * */ #ifndef PCL_REGISTRATION_IMPL_PYRAMID_FEATURE_MATCHING_H_ #define PCL_REGISTRATION_IMPL_PYRAMID_FEATURE_MATCHING_H_ #include // for pcl::isFinite #include #include namespace pcl { template float PyramidFeatureHistogram::comparePyramidFeatureHistograms( const PyramidFeatureHistogramPtr& pyramid_a, const PyramidFeatureHistogramPtr& pyramid_b) { // do a few consistency checks before and during the computation if (pyramid_a->nr_dimensions != pyramid_b->nr_dimensions) { PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two " "given pyramids have different numbers of dimensions: %u vs %u\n", pyramid_a->nr_dimensions, pyramid_b->nr_dimensions); return -1; } if (pyramid_a->nr_levels != pyramid_b->nr_levels) { PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two " "given pyramids have different numbers of levels: %u vs %u\n", pyramid_a->nr_levels, pyramid_b->nr_levels); return -1; } // calculate for level 0 first if (pyramid_a->hist_levels[0].hist.size() != pyramid_b->hist_levels[0].hist.size()) { PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two " "given pyramids have different numbers of bins on level 0: %u vs %u\n", pyramid_a->hist_levels[0].hist.size(), pyramid_b->hist_levels[0].hist.size()); return -1; } float match_count_level = 0.0f; for (std::size_t bin_i = 0; bin_i < pyramid_a->hist_levels[0].hist.size(); ++bin_i) { if (pyramid_a->hist_levels[0].hist[bin_i] < pyramid_b->hist_levels[0].hist[bin_i]) match_count_level += static_cast(pyramid_a->hist_levels[0].hist[bin_i]); else match_count_level += static_cast(pyramid_b->hist_levels[0].hist[bin_i]); } float match_count = match_count_level; for (std::size_t level_i = 1; level_i < pyramid_a->nr_levels; ++level_i) { if (pyramid_a->hist_levels[level_i].hist.size() != pyramid_b->hist_levels[level_i].hist.size()) { PCL_ERROR( "[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two " "given pyramids have different numbers of bins on level %u: %u vs %u\n", level_i, pyramid_a->hist_levels[level_i].hist.size(), pyramid_b->hist_levels[level_i].hist.size()); return -1; } float match_count_prev_level = match_count_level; match_count_level = 0.0f; for (std::size_t bin_i = 0; bin_i < pyramid_a->hist_levels[level_i].hist.size(); ++bin_i) { if (pyramid_a->hist_levels[level_i].hist[bin_i] < pyramid_b->hist_levels[level_i].hist[bin_i]) match_count_level += static_cast(pyramid_a->hist_levels[level_i].hist[bin_i]); else match_count_level += static_cast(pyramid_b->hist_levels[level_i].hist[bin_i]); } float level_normalization_factor = powf(2.0f, static_cast(level_i)); match_count += (match_count_level - match_count_prev_level) / level_normalization_factor; } // include self-similarity factors float self_similarity_a = static_cast(pyramid_a->nr_features), self_similarity_b = static_cast(pyramid_b->nr_features); PCL_DEBUG("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] Self " "similarity measures: %f, %f\n", self_similarity_a, self_similarity_b); match_count /= std::sqrt(self_similarity_a * self_similarity_b); return match_count; } template PyramidFeatureHistogram::PyramidFeatureHistogram() : nr_dimensions(0) , nr_levels(0) , nr_features(0) , feature_representation_(new DefaultPointRepresentation) , is_computed_(false) , hist_levels() {} template void PyramidFeatureHistogram< PointFeature>::PyramidFeatureHistogramLevel::initializeHistogramLevel() { std::size_t total_vector_size = 1; for (std::vector::iterator dim_it = bins_per_dimension.begin(); dim_it != bins_per_dimension.end(); ++dim_it) total_vector_size *= *dim_it; hist.resize(total_vector_size, 0); } template bool PyramidFeatureHistogram::initializeHistogram() { // a few consistency checks before starting the computations if (!PCLBase::initCompute()) { PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] PCLBase initCompute " "failed\n"); return false; } if (dimension_range_input_.empty()) { PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Input dimension " "range was not set\n"); return false; } if (dimension_range_target_.empty()) { PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Target dimension " "range was not set\n"); return false; } if (dimension_range_input_.size() != dimension_range_target_.size()) { PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Input and target " "dimension ranges do not agree in size: %u vs %u\n", dimension_range_input_.size(), dimension_range_target_.size()); return false; } nr_dimensions = dimension_range_target_.size(); nr_features = input_->size(); float D = 0.0f; for (std::vector>::iterator range_it = dimension_range_target_.begin(); range_it != dimension_range_target_.end(); ++range_it) { float aux = range_it->first - range_it->second; D += aux * aux; } D = std::sqrt(D); nr_levels = static_cast(std::ceil(std::log2(D))); PCL_DEBUG("[pcl::PyramidFeatureHistogram::initializeHistogram] Pyramid will have %u " "levels with a hyper-parallelepiped diagonal size of %f\n", nr_levels, D); hist_levels.resize(nr_levels); for (std::size_t level_i = 0; level_i < nr_levels; ++level_i) { std::vector bins_per_dimension(nr_dimensions); std::vector bin_step(nr_dimensions); for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i) { bins_per_dimension[dim_i] = static_cast( std::ceil((dimension_range_target_[dim_i].second - dimension_range_target_[dim_i].first) / (powf(2.0f, static_cast(level_i)) * std::sqrt(static_cast(nr_dimensions))))); bin_step[dim_i] = powf(2.0f, static_cast(level_i)) * std::sqrt(static_cast(nr_dimensions)); } hist_levels[level_i] = PyramidFeatureHistogramLevel(bins_per_dimension, bin_step); PCL_DEBUG("[pcl::PyramidFeatureHistogram::initializeHistogram] Created vector of " "size %u at level %u\nwith #bins per dimension:", hist_levels.back().hist.size(), level_i); for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i) PCL_DEBUG("%u ", bins_per_dimension[dim_i]); PCL_DEBUG("\n"); } return true; } template unsigned int& PyramidFeatureHistogram::at(std::vector& access, std::size_t& level) { if (access.size() != nr_dimensions) { PCL_ERROR( "[pcl::PyramidFeatureHistogram::at] Cannot access histogram position because " "the access point does not have the right number of dimensions\n"); return hist_levels.front().hist.front(); } if (level >= hist_levels.size()) { PCL_ERROR( "[pcl::PyramidFeatureHistogram::at] Trying to access a too large level\n"); return hist_levels.front().hist.front(); } std::size_t vector_position = 0; std::size_t dim_accumulator = 1; for (int i = static_cast(access.size()) - 1; i >= 0; --i) { vector_position += access[i] * dim_accumulator; dim_accumulator *= hist_levels[level].bins_per_dimension[i]; } return hist_levels[level].hist[vector_position]; } template unsigned int& PyramidFeatureHistogram::at(std::vector& feature, std::size_t& level) { if (feature.size() != nr_dimensions) { PCL_ERROR("[pcl::PyramidFeatureHistogram::at] The given feature vector does not " "match the feature dimensions of the pyramid histogram: %u vs %u\n", feature.size(), nr_dimensions); return hist_levels.front().hist.front(); } if (level >= hist_levels.size()) { PCL_ERROR( "[pcl::PyramidFeatureHistogram::at] Trying to access a too large level\n"); return hist_levels.front().hist.front(); } std::vector access; for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i) access.push_back(static_cast( std::floor((feature[dim_i] - dimension_range_target_[dim_i].first) / hist_levels[level].bin_step[dim_i]))); return at(access, level); } template void PyramidFeatureHistogram::convertFeatureToVector( const PointFeature& feature, std::vector& feature_vector) { // convert feature to vector representation feature_vector.resize(feature_representation_->getNumberOfDimensions()); feature_representation_->vectorize(feature, feature_vector); // adapt the values from the input range to the target range for (std::size_t i = 0; i < feature_vector.size(); ++i) feature_vector[i] = (feature_vector[i] - dimension_range_input_[i].first) / (dimension_range_input_[i].second - dimension_range_input_[i].first) * (dimension_range_target_[i].second - dimension_range_target_[i].first) + dimension_range_target_[i].first; } template void PyramidFeatureHistogram::compute() { if (!initializeHistogram()) return; for (const auto& point : *input_) { std::vector feature_vector; // NaN is converted to very high number that gives out of bound exception. if (!pcl::isFinite(point)) continue; convertFeatureToVector(point, feature_vector); addFeature(feature_vector); } is_computed_ = true; } template void PyramidFeatureHistogram::addFeature(std::vector& feature) { for (std::size_t level_i = 0; level_i < nr_levels; ++level_i) at(feature, level_i)++; } } // namespace pcl #define PCL_INSTANTIATE_PyramidFeatureHistogram(PointFeature) \ template class PCL_EXPORTS pcl::PyramidFeatureHistogram; #endif /* PCL_REGISTRATION_IMPL_PYRAMID_FEATURE_MATCHING_H_ */