/* * Software License Agreement (BSD License) * * Copyright (c) 2011, Alexandru-Eugen Ichim * Willow Garage, 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 Willow Garage, Inc. 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_SURFACE_IMPL_SURFEL_SMOOTHING_H_ #define PCL_SURFACE_IMPL_SURFEL_SMOOTHING_H_ #include // for OrganizedNeighbor #include // for KdTree #include #include #include // for PCL_ERROR, PCL_DEBUG ////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::SurfelSmoothing::initCompute () { if (!PCLBase::initCompute ()) return false; if (!normals_) { PCL_ERROR ("SurfelSmoothing: normal cloud not set\n"); return false; } if (input_->size () != normals_->size ()) { PCL_ERROR ("SurfelSmoothing: number of input points different from the number of given normals\n"); return false; } // Initialize the spatial locator if (!tree_) { if (input_->isOrganized ()) tree_.reset (new pcl::search::OrganizedNeighbor ()); else tree_.reset (new pcl::search::KdTree (false)); } // create internal copies of the input - these will be modified interm_cloud_ = PointCloudInPtr (new PointCloudIn (*input_)); interm_normals_ = NormalCloudPtr (new NormalCloud (*normals_)); return (true); } ////////////////////////////////////////////////////////////////////////////////////////////// template float pcl::SurfelSmoothing::smoothCloudIteration (PointCloudInPtr &output_positions, NormalCloudPtr &output_normals) { // PCL_INFO ("SurfelSmoothing: cloud smoothing iteration starting ...\n"); output_positions = PointCloudInPtr (new PointCloudIn); output_positions->points.resize (interm_cloud_->size ()); output_normals = NormalCloudPtr (new NormalCloud); output_normals->points.resize (interm_cloud_->size ()); pcl::Indices nn_indices; std::vector nn_distances; std::vector diffs (interm_cloud_->size ()); float total_residual = 0.0f; for (std::size_t i = 0; i < interm_cloud_->size (); ++i) { Eigen::Vector4f smoothed_point = Eigen::Vector4f::Zero (); Eigen::Vector4f smoothed_normal = Eigen::Vector4f::Zero (); // get neighbors // @todo using 5x the scale for searching instead of all the points to avoid O(N^2) tree_->radiusSearch ((*interm_cloud_)[i], 5*scale_, nn_indices, nn_distances); float theta_normalization_factor = 0.0; std::vector theta (nn_indices.size ()); for (std::size_t nn_index_i = 0; nn_index_i < nn_indices.size (); ++nn_index_i) { float dist = pcl::squaredEuclideanDistance ((*interm_cloud_)[i], (*input_)[nn_indices[nn_index_i]]);//(*interm_cloud_)[nn_indices[nn_index_i]]); float theta_i = std::exp ( (-1) * dist / scale_squared_); theta_normalization_factor += theta_i; smoothed_normal += theta_i * (*interm_normals_)[nn_indices[nn_index_i]].getNormalVector4fMap (); theta[nn_index_i] = theta_i; } smoothed_normal /= theta_normalization_factor; smoothed_normal(3) = 0.0f; smoothed_normal.normalize (); // find minimum along the normal float e_residual; smoothed_point = (*interm_cloud_)[i].getVector4fMap (); while (true) { e_residual = 0.0f; smoothed_point(3) = 0.0f; for (std::size_t nn_index_i = 0; nn_index_i < nn_indices.size (); ++nn_index_i) { Eigen::Vector4f neighbor = (*input_)[nn_indices[nn_index_i]].getVector4fMap ();//(*interm_cloud_)[nn_indices[nn_index_i]].getVector4fMap (); neighbor(3) = 0.0f; float dot_product = smoothed_normal.dot (neighbor - smoothed_point); e_residual += theta[nn_index_i] * dot_product;// * dot_product; } e_residual /= theta_normalization_factor; if (e_residual < 1e-5) break; smoothed_point += e_residual * smoothed_normal; } total_residual += e_residual; (*output_positions)[i].getVector4fMap () = smoothed_point; (*output_normals)[i].getNormalVector4fMap () = (*normals_)[i].getNormalVector4fMap ();//smoothed_normal; } // std::cerr << "Total residual after iteration: " << total_residual << std::endl; // PCL_INFO("SurfelSmoothing done iteration\n"); return total_residual; } template void pcl::SurfelSmoothing::smoothPoint (std::size_t &point_index, PointT &output_point, PointNT &output_normal) { Eigen::Vector4f average_normal = Eigen::Vector4f::Zero (); Eigen::Vector4f result_point = (*input_)[point_index].getVector4fMap (); result_point(3) = 0.0f; // @todo parameter float error_residual_threshold_ = 1e-3f; float error_residual = error_residual_threshold_ + 1; float last_error_residual = error_residual + 100.0f; pcl::Indices nn_indices; std::vector nn_distances; int big_iterations = 0; int max_big_iterations = 500; while (std::fabs (error_residual) < std::fabs (last_error_residual) -error_residual_threshold_ && big_iterations < max_big_iterations) { average_normal = Eigen::Vector4f::Zero (); big_iterations ++; PointT aux_point; aux_point.x = result_point(0); aux_point.y = result_point(1); aux_point.z = result_point(2); tree_->radiusSearch (aux_point, 5*scale_, nn_indices, nn_distances); float theta_normalization_factor = 0.0; std::vector theta (nn_indices.size ()); for (std::size_t nn_index_i = 0; nn_index_i < nn_indices.size (); ++nn_index_i) { float dist = nn_distances[nn_index_i]; float theta_i = std::exp ( (-1) * dist / scale_squared_); theta_normalization_factor += theta_i; average_normal += theta_i * (*normals_)[nn_indices[nn_index_i]].getNormalVector4fMap (); theta[nn_index_i] = theta_i; } average_normal /= theta_normalization_factor; average_normal(3) = 0.0f; average_normal.normalize (); // find minimum along the normal float e_residual_along_normal = 2, last_e_residual_along_normal = 3; int small_iterations = 0; int max_small_iterations = 10; while ( std::fabs (e_residual_along_normal) < std::fabs (last_e_residual_along_normal) && small_iterations < max_small_iterations) { small_iterations ++; e_residual_along_normal = 0.0f; for (std::size_t nn_index_i = 0; nn_index_i < nn_indices.size (); ++nn_index_i) { Eigen::Vector4f neighbor = (*input_)[nn_indices[nn_index_i]].getVector4fMap (); neighbor(3) = 0.0f; float dot_product = average_normal.dot (neighbor - result_point); e_residual_along_normal += theta[nn_index_i] * dot_product; } e_residual_along_normal /= theta_normalization_factor; if (e_residual_along_normal < 1e-3) break; result_point += e_residual_along_normal * average_normal; } // if (small_iterations == max_small_iterations) // PCL_INFO ("passed the number of small iterations %d\n", small_iterations); last_error_residual = error_residual; error_residual = e_residual_along_normal; // PCL_INFO ("last %f current %f\n", last_error_residual, error_residual); } output_point.x = result_point(0); output_point.y = result_point(1); output_point.z = result_point(2); output_normal = (*normals_)[point_index]; if (big_iterations == max_big_iterations) PCL_DEBUG ("[pcl::SurfelSmoothing::smoothPoint] Passed the number of BIG iterations: %d\n", big_iterations); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SurfelSmoothing::computeSmoothedCloud (PointCloudInPtr &output_positions, NormalCloudPtr &output_normals) { if (!initCompute ()) { PCL_ERROR ("[pcl::SurfelSmoothing::computeSmoothedCloud]: SurfelSmoothing not initialized properly, skipping computeSmoothedCloud ().\n"); return; } tree_->setInputCloud (input_); output_positions->header = input_->header; output_positions->height = input_->height; output_positions->width = input_->width; output_normals->header = input_->header; output_normals->height = input_->height; output_normals->width = input_->width; output_positions->points.resize (input_->size ()); output_normals->points.resize (input_->size ()); for (std::size_t i = 0; i < input_->size (); ++i) { smoothPoint (i, (*output_positions)[i], (*output_normals)[i]); } } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SurfelSmoothing::extractSalientFeaturesBetweenScales (PointCloudInPtr &cloud2, NormalCloudPtr &cloud2_normals, pcl::IndicesPtr &output_features) { if (interm_cloud_->size () != cloud2->size () || cloud2->size () != cloud2_normals->size ()) { PCL_ERROR ("[pcl::SurfelSmoothing::extractSalientFeaturesBetweenScales]: Number of points in the clouds does not match.\n"); return; } std::vector diffs (cloud2->size ()); for (std::size_t i = 0; i < cloud2->size (); ++i) diffs[i] = (*cloud2_normals)[i].getNormalVector4fMap ().dot ((*cloud2)[i].getVector4fMap () - (*interm_cloud_)[i].getVector4fMap ()); pcl::Indices nn_indices; std::vector nn_distances; output_features->resize (cloud2->size ()); for (int point_i = 0; point_i < static_cast (cloud2->size ()); ++point_i) { // Get neighbors tree_->radiusSearch (point_i, scale_, nn_indices, nn_distances); bool largest = true; bool smallest = true; for (const auto &nn_index : nn_indices) { if (diffs[point_i] < diffs[nn_index]) largest = false; else smallest = false; } if (largest || smallest) (*output_features)[point_i] = point_i; } } #define PCL_INSTANTIATE_SurfelSmoothing(PointT,PointNT) template class PCL_EXPORTS pcl::SurfelSmoothing; #endif /* PCL_SURFACE_IMPL_SURFEL_SMOOTHING_H_ */