Point Cloud Library (PCL)  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
sift_keypoint.hpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Point Cloud Library (PCL) - www.pointclouds.org
00005  *  Copyright (c) 2010-2011, Willow Garage, Inc.
00006  *
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of Willow Garage, Inc. nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  */
00037 
00038 #ifndef PCL_SIFT_KEYPOINT_IMPL_H_
00039 #define PCL_SIFT_KEYPOINT_IMPL_H_
00040 
00041 #include "pcl/keypoints/sift_keypoint.h"
00042 #include <pcl/common/io.h>
00043 #include <pcl/filters/voxel_grid.h>
00044 #include <pcl/filters/approximate_voxel_grid.h>
00045 
00047 template <typename PointInT, typename PointOutT> void 
00048 pcl::SIFTKeypoint<PointInT, PointOutT>::setScales (float min_scale, int nr_octaves, int nr_scales_per_octave)
00049 {
00050   if (min_scale <= 0)
00051   {
00052     PCL_ERROR ("[pcl::%s::setScales] : min_scale (%f) must be positive!\n", 
00053                name_.c_str (), min_scale);
00054     return;
00055   }
00056   if (nr_octaves <= 0)
00057   {
00058     PCL_ERROR ("[pcl::%s::setScales] : Number of octaves (%d) must be at least 1!\n", 
00059                name_.c_str (), nr_octaves);
00060     return;
00061   }
00062   if (nr_scales_per_octave < 1)
00063   {
00064     PCL_ERROR ("[pcl::%s::setScales] : Number of scales per octave (%d) must be at least 1!\n", 
00065                name_.c_str (), nr_scales_per_octave);
00066     return;
00067   }
00068   min_scale_ = min_scale;
00069   nr_octaves_ = nr_octaves;
00070   nr_scales_per_octave_ = nr_scales_per_octave;
00071   
00072   this->setKSearch (1);
00073 }
00074 
00075 
00077 template <typename PointInT, typename PointOutT> void 
00078 pcl::SIFTKeypoint<PointInT, PointOutT>::setMinimumContrast (float min_contrast)
00079 {
00080   if (min_contrast < 0)
00081   {
00082     PCL_ERROR ("[pcl::%s::setMinimumContrast] : min_contrast (%f) must be non-negative!\n", 
00083                name_.c_str (), min_contrast);
00084     return;
00085   } 
00086   min_contrast_ = min_contrast;
00087 }
00088 
00090 template <typename PointInT, typename PointOutT> void 
00091 pcl::SIFTKeypoint<PointInT, PointOutT>::detectKeypoints (PointCloudOut &output)
00092 {
00093   tree_.reset (new pcl::search::KdTree<PointInT> (true));
00094 
00095   // Check for valid inputs
00096   if (min_scale_ == 0 || nr_octaves_ == 0 || nr_scales_per_octave_ == 0)
00097   {
00098     PCL_ERROR ("[pcl::%s::detectKeypoints] : ", name_.c_str ());
00099     PCL_ERROR ("A valid range of scales must be specified by setScales before detecting keypoints!\n");
00100     return;
00101   }
00102   if (surface_ != input_)
00103   {
00104     PCL_WARN ("[pcl::%s::detectKeypoints] : ", name_.c_str ());
00105     PCL_WARN ("A search surface has been set by setSearchSurface, but this SIFT keypoint detection algorithm does ");
00106     PCL_WARN ("not support search surfaces other than the input cloud.  ");
00107     PCL_WARN ("The cloud provided in setInputCloud is being used instead.\n");
00108   }
00109 
00110   // Check if the output has a "scale" field
00111   scale_idx_ = pcl::getFieldIndex<PointOutT> (output, "scale", out_fields_);
00112 
00113   // Make sure the output cloud is empty
00114   output.points.clear ();
00115 
00116   // Create a local copy of the input cloud that will be resized for each octave
00117   boost::shared_ptr<pcl::PointCloud<PointInT> > cloud (new pcl::PointCloud<PointInT> (*input_));
00118 
00119   VoxelGrid<PointInT> voxel_grid;
00120   // Search for keypoints at each octave
00121   float scale = min_scale_;
00122   for (int i_octave = 0; i_octave < nr_octaves_; ++i_octave)
00123   {
00124     // Downsample the point cloud
00125     float s = 1.0 * scale; // note: this can be adjusted
00126     voxel_grid.setLeafSize (s, s, s);
00127     voxel_grid.setInputCloud (cloud);
00128     boost::shared_ptr<pcl::PointCloud<PointInT> > temp (new pcl::PointCloud<PointInT>);    
00129     voxel_grid.filter (*temp);
00130     cloud = temp;
00131 
00132     // Make sure the downsampled cloud still has enough points
00133     const size_t min_nr_points = 25;
00134     if (cloud->points.size () < min_nr_points)
00135       break;
00136 
00137     // Update the KdTree with the downsampled points
00138     tree_->setInputCloud (cloud);
00139 
00140     // Detect keypoints for the current scale
00141     detectKeypointsForOctave (*cloud, *tree_, scale, nr_scales_per_octave_, output);
00142 
00143     // Increase the scale by another octave
00144     scale *= 2;
00145   }
00146 
00147   output.height = 1;
00148   output.width = output.points.size ();
00149 }
00150 
00151 
00153 template <typename PointInT, typename PointOutT> void 
00154 pcl::SIFTKeypoint<PointInT, PointOutT>::detectKeypointsForOctave (
00155     const PointCloudIn &input, KdTree &tree, float base_scale, int nr_scales_per_octave, 
00156     PointCloudOut &output)
00157 {
00158   // Compute the difference of Gaussians (DoG) scale space
00159   std::vector<float> scales (nr_scales_per_octave + 3);
00160   for (int i_scale = 0; i_scale <= nr_scales_per_octave + 2; ++i_scale)
00161   {
00162     scales[i_scale] = base_scale * pow (2.0, (1.0 * i_scale - 1) / nr_scales_per_octave);
00163   }
00164   Eigen::MatrixXf diff_of_gauss;
00165   computeScaleSpace (input, tree, scales, diff_of_gauss);
00166 
00167   // Find extrema in the DoG scale space
00168   std::vector<int> extrema_indices, extrema_scales;
00169   findScaleSpaceExtrema (input, tree, diff_of_gauss, extrema_indices, extrema_scales);
00170 
00171   output.points.reserve (output.points.size () + extrema_indices.size ());
00172   // Save scale?
00173   if (scale_idx_ != -1)
00174   {
00175     // Add keypoints to output
00176     for (size_t i_keypoint = 0; i_keypoint < extrema_indices.size (); ++i_keypoint)
00177     {
00178       PointOutT keypoint;
00179       const int &keypoint_index = extrema_indices[i_keypoint];
00180    
00181       keypoint.x = input.points[keypoint_index].x;
00182       keypoint.y = input.points[keypoint_index].y;
00183       keypoint.z = input.points[keypoint_index].z;
00184       memcpy (((char*)&keypoint) + out_fields_[scale_idx_].offset, &scales[extrema_scales[i_keypoint]], sizeof (float));
00185       output.points.push_back (keypoint); 
00186     }
00187   }
00188   else
00189   {
00190     // Add keypoints to output
00191     for (size_t i_keypoint = 0; i_keypoint < extrema_indices.size (); ++i_keypoint)
00192     {
00193       PointOutT keypoint;
00194       const int &keypoint_index = extrema_indices[i_keypoint];
00195    
00196       keypoint.x = input.points[keypoint_index].x;
00197       keypoint.y = input.points[keypoint_index].y;
00198       keypoint.z = input.points[keypoint_index].z;
00199 
00200       output.points.push_back (keypoint); 
00201     }
00202   }
00203 }
00204 
00205 
00207 template <typename PointInT, typename PointOutT> 
00208 void pcl::SIFTKeypoint<PointInT, PointOutT>::computeScaleSpace (
00209     const PointCloudIn &input, KdTree &tree, const std::vector<float> &scales, 
00210     Eigen::MatrixXf &diff_of_gauss)
00211 {
00212   std::vector<int> nn_indices;
00213   std::vector<float> nn_dist;
00214   diff_of_gauss.resize (input.size (), scales.size () - 1);
00215 
00216   // For efficiency, we will only filter over points within 3 standard deviations 
00217   const float max_radius = 3.0 * scales.back ();
00218 
00219   for (size_t i_point = 0; i_point < input.size (); ++i_point)
00220   {
00221     tree.radiusSearch (i_point, max_radius, nn_indices, nn_dist); // *
00222     // * note: at this stage of the algorithm, we must find all points within a radius defined by the maximum scale, 
00223     //   regardless of the configurable search method specified by the user, so we directly employ tree.radiusSearch 
00224     //   here instead of using searchForNeighbors.
00225 
00226     // For each scale, compute the Gaussian "filter response" at the current point
00227     float filter_response = 0;
00228     float previous_filter_response;
00229     for (size_t i_scale = 0; i_scale < scales.size (); ++i_scale)
00230     {
00231       float sigma_sqr = pow (scales[i_scale], 2);
00232 
00233       float numerator = 0.0;
00234       float denominator = 0.0;
00235       for (size_t i_neighbor = 0; i_neighbor < nn_indices.size (); ++i_neighbor)
00236       {
00237         const float &value = getFieldValue_ (input.points[nn_indices[i_neighbor]]);
00238         const float &dist_sqr = nn_dist[i_neighbor];
00239         if (dist_sqr <= 9*sigma_sqr)
00240         {
00241           float w = exp (-0.5 * dist_sqr / sigma_sqr);
00242           numerator += value * w;
00243           denominator += w;
00244         }
00245         else break; // i.e. if dist > 3 standard deviations, then terminate early
00246       }
00247       previous_filter_response = filter_response;
00248       filter_response = numerator / denominator;
00249 
00250       // Compute the difference between adjacent scales
00251       if (i_scale > 0)
00252         diff_of_gauss (i_point, i_scale - 1) = filter_response - previous_filter_response;
00253     }
00254   }
00255 }
00256 
00258 template <typename PointInT, typename PointOutT> void 
00259 pcl::SIFTKeypoint<PointInT, PointOutT>::findScaleSpaceExtrema (
00260     const PointCloudIn &input, KdTree &tree, const Eigen::MatrixXf &diff_of_gauss, 
00261     std::vector<int> &extrema_indices, std::vector<int> &extrema_scales)
00262 {
00263   const int k = 25;
00264   std::vector<int> nn_indices (k);
00265   std::vector<float> nn_dist (k);
00266 
00267   float nr_scales = diff_of_gauss.cols ();
00268   std::vector<float> min_val (nr_scales), max_val (nr_scales);
00269 
00270   for (size_t i_point = 0; i_point < input.size (); ++i_point)
00271   {
00272     // Define the local neighborhood around the current point
00273     tree.nearestKSearch (i_point, k, nn_indices, nn_dist); //*
00274     // * note: the neighborhood for finding local extrema is best defined as a small fixed-k neighborhood, regardless of
00275     //   the configurable search method specified by the user, so we directly employ tree.nearestKSearch here instead 
00276     //   of using searchForNeighbors
00277 
00278     // At each scale, find the extreme values of the DoG within the current neighborhood
00279     for (int i_scale = 0; i_scale < nr_scales; ++i_scale)
00280     {
00281       min_val[i_scale] = std::numeric_limits<float>::max ();
00282       max_val[i_scale] = -std::numeric_limits<float>::max ();
00283 
00284       for (size_t i_neighbor = 0; i_neighbor < nn_indices.size (); ++i_neighbor)
00285       {
00286         const float &d = diff_of_gauss (nn_indices[i_neighbor], i_scale);
00287 
00288         min_val[i_scale] = (std::min) (min_val[i_scale], d);
00289         max_val[i_scale] = (std::max) (max_val[i_scale], d);
00290       }
00291     }
00292 
00293     // If the current point is an extreme value with high enough contrast, add it as a keypoint 
00294     for (int i_scale = 1; i_scale < nr_scales - 1; ++i_scale)
00295     {
00296       const float &val = diff_of_gauss (i_point, i_scale);
00297 
00298       // Does the point have sufficient contrast?
00299       if (fabs (val) >= min_contrast_)
00300       {
00301         // Is it a local minimum?
00302         if ((val == min_val[i_scale]) && 
00303             (val <  min_val[i_scale - 1]) && 
00304             (val <  min_val[i_scale + 1]))
00305         {
00306           extrema_indices.push_back (i_point);
00307           extrema_scales.push_back (i_scale);
00308         }
00309         // Is it a local maximum?
00310         else if ((val == max_val[i_scale]) && 
00311                  (val >  max_val[i_scale - 1]) && 
00312                  (val >  max_val[i_scale + 1]))
00313         {
00314           extrema_indices.push_back (i_point);
00315           extrema_scales.push_back (i_scale);
00316         }
00317       }
00318     }
00319   }
00320 }
00321 
00322 #define PCL_INSTANTIATE_SIFTKeypoint(T,U) template class PCL_EXPORTS pcl::SIFTKeypoint<T,U>;
00323 
00324 #endif // #ifndef PCL_SIFT_KEYPOINT_IMPL_H_
00325 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines