narf_keypoint.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Willow Garage, Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  */
00035 
00036 #include <iostream>
00037 #include <vector>
00038 #include <pcl/keypoints/narf_keypoint.h>
00039 #include <pcl/features/range_image_border_extractor.h>
00040 #include <pcl/pcl_macros.h>
00041 #include <pcl/common/polynomial_calculations.h>
00042 #include <pcl/range_image/range_image.h>
00043 
00044 namespace pcl 
00045 {
00046 
00048 NarfKeypoint::NarfKeypoint (RangeImageBorderExtractor* range_image_border_extractor, float support_size) :
00049     BaseClass (), interest_image_ (NULL), interest_points_ (NULL)
00050 {
00051   name_ = "NarfKeypoint";
00052   clearData ();
00053   setRangeImageBorderExtractor (range_image_border_extractor);
00054   if (support_size > 0.0f)
00055     parameters_.support_size = support_size;
00056 }
00057 
00059 NarfKeypoint::~NarfKeypoint ()
00060 {
00061   //std::cerr << __PRETTY_FUNCTION__<<" called.\n";
00062   clearData ();
00063 }
00064 
00066 void
00067   NarfKeypoint::clearData ()
00068 {
00069   //std::cerr << __PRETTY_FUNCTION__<<" called.\n";
00070   
00071   for (size_t scale_space_idx = 1; scale_space_idx<border_extractor_scale_space_.size (); ++scale_space_idx)
00072     delete border_extractor_scale_space_[scale_space_idx];
00073   border_extractor_scale_space_.clear ();
00074   for (size_t scale_space_idx = 1; scale_space_idx<range_image_scale_space_.size (); ++scale_space_idx)
00075     delete range_image_scale_space_[scale_space_idx];
00076   range_image_scale_space_.clear ();
00077   for (size_t scale_space_idx = 1; scale_space_idx<interest_image_scale_space_.size (); ++scale_space_idx)
00078     delete[] interest_image_scale_space_[scale_space_idx];
00079   interest_image_scale_space_.clear ();
00080   is_interest_point_image_.clear ();
00081   delete[] interest_image_; interest_image_=NULL;
00082   delete interest_points_;  interest_points_=NULL;
00083 }
00084 
00086 void
00087 NarfKeypoint::setRangeImageBorderExtractor (RangeImageBorderExtractor* range_image_border_extractor)
00088 {
00089   clearData ();
00090   range_image_border_extractor_ = range_image_border_extractor;
00091 }
00092 
00094 void
00095 NarfKeypoint::setRangeImage (const RangeImage* range_image)
00096 {
00097   clearData ();
00098   range_image_border_extractor_->setRangeImage (range_image);
00099 }
00100 
00102 void
00103 NarfKeypoint::calculateScaleSpace ()
00104 {
00105   //MEASURE_FUNCTION_TIME;
00106   
00107   if (range_image_border_extractor_ == NULL || !range_image_border_extractor_->hasRangeImage () ||
00108       !border_extractor_scale_space_.empty ())  // Nothing to compute or already done
00109     return;
00110   border_extractor_scale_space_.push_back (range_image_border_extractor_);
00111   range_image_scale_space_.push_back (const_cast<RangeImage*> (reinterpret_cast<const RangeImage*> (&range_image_border_extractor_->getRangeImage ())));
00112   
00113   if (!parameters_.use_recursive_scale_reduction)
00114     return;
00115   
00116   while (0.5f*range_image_scale_space_.back ()->getAngularResolution () < deg2rad (2.0f))
00117   {
00118     range_image_scale_space_.push_back (getRangeImage ().getNew ());
00119     range_image_scale_space_[range_image_scale_space_.size ()-2]->getHalfImage (*range_image_scale_space_.back ());
00120     border_extractor_scale_space_.push_back (new RangeImageBorderExtractor);
00121     border_extractor_scale_space_.back ()->getParameters () = range_image_border_extractor_->getParameters ();
00122     border_extractor_scale_space_.back ()->setRangeImage (range_image_scale_space_.back ());
00123   }
00124 }
00125 
00126 #define USE_BEAM_AVERAGE 1
00127 
00128 namespace 
00129 {  // Some helper functions in an anonymous namespace - only available in this file
00130   inline void 
00131   nkdGetScores (float distance_factor, float surface_change_score, float pixelDistance,
00132                 float optimal_distance, float& negative_score, float& positive_score)
00133   {
00134     negative_score = 1.0f - 0.5f * surface_change_score * (std::max) (1.0f - distance_factor/optimal_distance, 0.0f);
00135     negative_score = powf (negative_score, 2);
00136     
00137     if (pixelDistance < 2.0)
00138       positive_score = surface_change_score;
00139     else
00140       positive_score = surface_change_score * (1.0f-distance_factor);
00141   }
00142 
00143   void 
00144   translateDirection180To360 (Eigen::Vector2f& direction_vector)
00145   {
00146     // The following code does the equivalent to this:
00147     // Get the angle of the 2D direction (with positive x) alpha, and return the direction of 2*alpha
00148     // We do this to create a normal angular wrap-around at -180,180 instead of the one at -90,90, 
00149     // enabling us to calculate the average angle as the direction of the sum of all unit vectors.
00150     // We use sin (2a)=2*sin (a)*cos (a) and cos (2a)=2cos^2 (a)-1 so that we needn't actually compute the angles,
00151     // which would be expensive
00152     float cos_a = direction_vector[0],
00153           cos_2a = 2*cos_a*cos_a - 1.0f,
00154           sin_a = direction_vector[1],
00155           sin_2a = 2.0f*sin_a*cos_a;
00156     direction_vector[0] = cos_2a;
00157     direction_vector[1] = sin_2a;
00158   }
00159 
00160   void 
00161   translateDirection360To180 (Eigen::Vector2f& direction_vector)
00162   {
00163     // Inverse of the above
00164     float cos_2a = direction_vector[0],
00165           cos_a = sqrtf (0.5f* (cos_2a+1.0f)),
00166           sin_2a = direction_vector[1],
00167           sin_a = sin_2a / (2.0f*cos_a);
00168     direction_vector[0] = cos_a;
00169     direction_vector[1] = sin_a;
00170   }
00171   
00172   inline Eigen::Vector2f 
00173   nkdGetDirectionVector (const Eigen::Vector3f& direction, const Eigen::Affine3f& rotation)
00174   {
00175     Eigen::Vector3f rotated_direction = rotation*direction;
00176     Eigen::Vector2f direction_vector (rotated_direction[0], rotated_direction[1]);
00177     direction_vector.normalize ();
00178     if (direction_vector[0]<0.0f)
00179       direction_vector *= -1.0f;
00180     
00181 
00182 #   if USE_BEAM_AVERAGE
00183       translateDirection180To360 (direction_vector);
00184 #   endif
00185     
00186     return direction_vector;
00187   }
00188   
00189   inline float 
00190   nkdGetDirectionAngle (const Eigen::Vector3f& direction, const Eigen::Affine3f& rotation)
00191   {
00192     Eigen::Vector3f rotated_direction = rotation*direction;
00193     Eigen::Vector2f direction_vector (rotated_direction[0], rotated_direction[1]);
00194     direction_vector.normalize ();
00195     float angle = 0.5f*normAngle (2.0f*acosf (direction_vector[0]));
00196     return (angle);
00197   }
00198   
00199   inline void 
00200   propagateInvalidBeams (int new_radius, std::vector<bool>& old_beams, std::vector<bool>& new_beams)
00201   {
00202     new_beams.clear ();
00203     new_beams.resize (std::max (8*new_radius,1), false);
00204     if (new_radius >= 2)
00205     {
00206       float mapping_factor = 1.0f + (1.0f / static_cast<float> (new_radius-1));
00207       for (size_t old_idx=0; old_idx<old_beams.size (); ++old_idx)
00208       {
00209         if (old_beams[old_idx])
00210         {
00211           int middle_idx = static_cast<int> (pcl_lrint (mapping_factor * static_cast<float> (old_idx)));
00212           for (int idx_offset=-1; idx_offset<=1; ++idx_offset)
00213           {
00214             if (idx_offset != 0)
00215             {
00216               int old_neighbor_idx = static_cast<int> (old_idx) + idx_offset;
00217               if (old_neighbor_idx<0)
00218                 old_neighbor_idx += static_cast<int> (old_beams.size ());
00219               if (old_neighbor_idx>= static_cast<int> (old_beams.size ()))
00220                 old_neighbor_idx -= static_cast<int> (old_beams.size ());
00221               if (!old_beams[old_neighbor_idx])
00222                 continue;
00223             }
00224             int new_idx = middle_idx+idx_offset;
00225             if (new_idx<0)
00226               new_idx += static_cast<int> (new_beams.size ());
00227             if (new_idx>= static_cast<int> (new_beams.size ()))
00228               new_idx -= static_cast<int> (new_beams.size ());
00229             new_beams[new_idx] = true;
00230           }
00231         }
00232       }
00233     }
00234   }
00235   
00236   inline bool
00237   isBetterInterestPoint (const InterestPoint& p1, const InterestPoint& p2)
00238   {
00239     return (p1.strength > p2.strength);
00240   }
00241   
00242   inline bool
00243   secondPairElementIsGreater (const std::pair<int, float>& p1, const std::pair<int, float>& p2)
00244   {
00245     return (p1.second > p2.second);
00246   }
00247 
00248 }  // end anonymous namespace
00249 
00250 void 
00251 NarfKeypoint::calculateInterestImage () 
00252 {
00253   //std::cout << __PRETTY_FUNCTION__ << " called.\n";
00254   
00255   if (interest_image_!=NULL)  // Already done
00256     return;
00257   
00258   if (parameters_.calculate_sparse_interest_image)
00259     calculateSparseInterestImage ();
00260   else
00261     calculateCompleteInterestImage ();
00262 }
00263 
00264 void 
00265 NarfKeypoint::calculateCompleteInterestImage ()
00266 {
00267   //std::cout << __PRETTY_FUNCTION__ << " called.\n";
00268   
00269   if (parameters_.support_size <= 0.0f)
00270   {
00271     std::cerr << __PRETTY_FUNCTION__<<": parameters_.support_size is not set!\n";
00272     return;
00273   }
00274   if (range_image_border_extractor_==NULL)
00275   {
00276     std::cerr << __PRETTY_FUNCTION__<<": range_image_border_extractor_ is not set!\n";
00277     return;
00278   }
00279   
00280   float search_radius = 0.5f * parameters_.support_size,
00281         radius_squared = search_radius*search_radius,
00282         radius_reciprocal = 1.0f / search_radius;
00283   
00284   calculateScaleSpace ();
00285   //std::cout << PVARN(range_image_scale_space_.size ());
00286   
00287   std::vector<float> start_usage_ranges;
00288   start_usage_ranges.resize (range_image_scale_space_.size ());
00289   start_usage_ranges[int (range_image_scale_space_.size ())-1] = 0.0f;
00290   for (int scale_idx = int (range_image_scale_space_.size ())-2;  scale_idx >= 0; --scale_idx)
00291   {
00292     start_usage_ranges[scale_idx] = parameters_.support_size / 
00293       tanf (static_cast<float> (parameters_.optimal_range_image_patch_size) * range_image_scale_space_[scale_idx+1]->getAngularResolution ());
00294     //std::cout << PVARN(start_usage_ranges[scale_idx]);
00295   }
00296   
00297   //double interest_value_calculation_start_time = getTime ();
00298   interest_image_scale_space_.clear ();
00299   interest_image_scale_space_.resize (range_image_scale_space_.size (), NULL);
00300   for (int scale_idx = int (range_image_scale_space_.size ())-1;  scale_idx >= 0; --scale_idx)
00301   {
00302     const RangeImage& range_image = *range_image_scale_space_[scale_idx];
00303     RangeImageBorderExtractor& border_extractor = *border_extractor_scale_space_[scale_idx];
00304     int original_max_no_of_threads = border_extractor.getParameters ().max_no_of_threads;
00305     border_extractor.getParameters ().max_no_of_threads = parameters_.max_no_of_threads;
00306     const ::pcl::PointCloud<BorderDescription>& border_descriptions = border_extractor.getBorderDescriptions ();
00307     const float* surface_change_scores = border_extractor.getSurfaceChangeScores ();
00308     const Eigen::Vector3f* surface_change_directions = border_extractor.getSurfaceChangeDirections ();
00309     float start_usage_range = start_usage_ranges[scale_idx];
00310     
00311     int width  = range_image.width,
00312         height = range_image.height,
00313         array_size = width*height;
00314 
00315     float* interest_image = new float[array_size];
00316     interest_image_scale_space_[scale_idx] = interest_image;
00317     //for (int i=0; i<array_size; ++i)
00318       //interest_image[i] = -1.0f;
00319     
00320     const int angle_histogram_size = 18;
00321     std::vector<float> angle_histogram;
00322     angle_histogram.resize(angle_histogram_size);
00323     
00324     std::vector<bool> was_touched;
00325     was_touched.resize (array_size, false);
00326     std::vector<int> neighbors_to_check;
00327     
00328 #   pragma omp parallel for num_threads (parameters_.max_no_of_threads) default (shared) \
00329                             firstprivate (was_touched, neighbors_to_check, angle_histogram) schedule (dynamic, 10)
00330     for (int index=0; index<array_size; ++index)
00331     {
00332       float& interest_value = interest_image[index];
00333       interest_value = 0.0f;
00334       if (!range_image.isValid (index))
00335         continue;
00336       int y = index/range_image.width,
00337           x = index - y*range_image.width;
00338       
00339       const BorderTraits& border_traits = border_descriptions.points[index].traits;
00340       if (border_traits[BORDER_TRAIT__SHADOW_BORDER] || border_traits[BORDER_TRAIT__VEIL_POINT])
00341         continue;
00342       
00343       const PointWithRange& point = range_image.getPoint (index);
00344       
00345       if (point.range < start_usage_range)  // Point is close enough that we can use the value calculated at a lower resolution
00346       {
00347         const RangeImage& half_range_image = *range_image_scale_space_[scale_idx+1];
00348         float* half_interest_image = interest_image_scale_space_[scale_idx+1];
00349         int half_x = std::min (x/2, int (half_range_image.width)-1),
00350             half_y = std::min (y/2, int (half_range_image.height)-1);
00351         interest_value = half_interest_image[half_y*half_range_image.width + half_x];
00352         continue;
00353       }
00354       
00355       Eigen::Affine3f rotation_to_viewer_coordinate_system;
00356       range_image.getRotationToViewerCoordinateFrame (point.getVector3fMap (),
00357                                                       rotation_to_viewer_coordinate_system);
00358       float negative_score = 1.0f;
00359       
00360       // -----Start region growing to cover all connected points within the support size-----
00361       neighbors_to_check.clear ();
00362       neighbors_to_check.push_back (index);
00363       was_touched[index] = true;
00364       
00365       angle_histogram.clear ();
00366       angle_histogram.resize(angle_histogram_size, 0);
00367       for (size_t neighbors_to_check_idx=0; neighbors_to_check_idx<neighbors_to_check.size (); ++neighbors_to_check_idx)
00368       {
00369         int index2 = neighbors_to_check[neighbors_to_check_idx];
00370         if (!range_image.isValid (index2))
00371           continue;
00372         const BorderTraits& border_traits2 = border_descriptions.points[index2].traits;
00373         if (border_traits2[BORDER_TRAIT__SHADOW_BORDER] || border_traits2[BORDER_TRAIT__VEIL_POINT])
00374           continue;
00375         int y2 = index2/range_image.width,
00376             x2 = index2 - y2*range_image.width;
00377         const PointWithRange& point2 = range_image.getPoint (index2);
00378         
00379         float pixelDistance = static_cast<float> (std::max (abs (x2-x), abs (y2-y)));
00380         float distance_squared = squaredEuclideanDistance (point, point2);
00381         if (pixelDistance > 2.0f)  // Always consider immediate neighbors, even if to far away
00382         {
00383           if (distance_squared>radius_squared)
00384             continue;
00385         }
00386         
00387         for (int y3=std::max (0,y2-1); y3<=std::min (y2+1,int (range_image.height)-1); ++y3)
00388         {
00389           for (int x3=std::max (0,x2-1); x3<=std::min (x2+1,int (range_image.width)-1); ++x3)
00390           {
00391             int index3 = y3*range_image.width + x3;
00392             if (!was_touched[index3])
00393             {
00394               neighbors_to_check.push_back (index3);
00395               was_touched[index3] = true;
00396             }
00397           }
00398         }
00399         
00400         float surface_change_score = surface_change_scores[index2];
00401         if (surface_change_score < parameters_.min_surface_change_score)  // Pixel not 'interesting' enough to consider?
00402           continue;
00403         const Eigen::Vector3f& surface_change_direction = surface_change_directions[index2];
00404         
00405         float distance = sqrtf (distance_squared);
00406         float distance_factor = radius_reciprocal*distance;
00407         float positive_score, current_negative_score;
00408         nkdGetScores (distance_factor, surface_change_score, pixelDistance,
00409                      parameters_.optimal_distance_to_high_surface_change,
00410                      current_negative_score, positive_score);
00411         float angle = nkdGetDirectionAngle (surface_change_direction, rotation_to_viewer_coordinate_system);
00412         int histogram_cell = (std::min) (angle_histogram_size-1,
00413                           static_cast<int> (pcl_lrint (floorf ( (angle+deg2rad (90.0f))/deg2rad (180.0f) * angle_histogram_size))));
00414         float& histogram_value = angle_histogram[histogram_cell];
00415         
00416         histogram_value = (std::max) (histogram_value, positive_score);
00417         negative_score  = (std::min) (negative_score, current_negative_score);
00418       }
00419       
00420       // Reset was_touched to false
00421       for (size_t neighbors_to_check_idx=0; neighbors_to_check_idx<neighbors_to_check.size (); ++neighbors_to_check_idx)
00422         was_touched[neighbors_to_check[neighbors_to_check_idx]] = false;
00423       
00424       float angle_change_value = 0.0f;
00425       for (int histogram_cell1=0; histogram_cell1<angle_histogram_size-1; ++histogram_cell1)
00426       {
00427         if (angle_histogram[histogram_cell1]==0.0f)
00428           continue;
00429         for (int histogram_cell2=histogram_cell1+1; histogram_cell2<angle_histogram_size; ++histogram_cell2)
00430         {
00431           if (angle_histogram[histogram_cell2]==0.0f)
00432             continue;
00433           // TODO: lookup table for the following:
00434           float normalized_angle_diff = 2.0f*float (histogram_cell2-histogram_cell1)/float (angle_histogram_size);
00435           normalized_angle_diff = (normalized_angle_diff <= 1.0f ? normalized_angle_diff : 2.0f-normalized_angle_diff);
00436           
00437           angle_change_value = std::max (angle_histogram[histogram_cell1] * angle_histogram[histogram_cell2] *
00438                                          normalized_angle_diff,   angle_change_value);
00439         }
00440       }
00441       angle_change_value = sqrtf (angle_change_value);
00442       interest_value = negative_score * angle_change_value;
00443       
00444       if (parameters_.add_points_on_straight_edges)
00445       {
00446         float max_histogram_cell_value = 0.0f;
00447         for (int histogram_cell=0; histogram_cell<angle_histogram_size; ++histogram_cell)
00448           max_histogram_cell_value = (std::max) (max_histogram_cell_value, angle_histogram[histogram_cell]);
00449         interest_value = (std::min) (interest_value+max_histogram_cell_value, 1.0f);
00450       }
00451     }
00452     
00453     border_extractor.getParameters ().max_no_of_threads = original_max_no_of_threads;
00454   }
00455   
00456   if (interest_image_scale_space_.empty ())
00457     interest_image_ = NULL;
00458   else
00459     interest_image_ = interest_image_scale_space_[0];
00460 }
00461 
00462 void 
00463 NarfKeypoint::calculateSparseInterestImage ()
00464 {
00465   if (parameters_.support_size <= 0.0f)
00466   {
00467     std::cerr << __PRETTY_FUNCTION__<<": parameters_.support_size is not set!\n";
00468     return;
00469   }
00470   if (range_image_border_extractor_==NULL)
00471   {
00472     std::cerr << __PRETTY_FUNCTION__<<": range_image_border_extractor_ is not set!\n";
00473     return;
00474   }
00475 
00476   float search_radius = 0.5f * parameters_.support_size,
00477         radius_reciprocal = 1.0f / search_radius,
00478         increased_radius = 1.5f * search_radius,
00479         increased_radius_squared = increased_radius*increased_radius,
00480         radius_overhead = increased_radius-search_radius,
00481         radius_overhead_squared = radius_overhead*radius_overhead;
00482   
00483   const RangeImage& range_image = range_image_border_extractor_->getRangeImage ();
00484   RangeImageBorderExtractor& border_extractor = *range_image_border_extractor_;
00485   int original_max_no_of_threads = border_extractor.getParameters ().max_no_of_threads;
00486   border_extractor.getParameters ().max_no_of_threads = parameters_.max_no_of_threads;
00487   const ::pcl::PointCloud<BorderDescription>& border_descriptions = border_extractor.getBorderDescriptions ();
00488   const float* surface_change_scores = border_extractor.getSurfaceChangeScores ();
00489   const Eigen::Vector3f* surface_change_directions = border_extractor.getSurfaceChangeDirections ();
00490   
00491   int width  = range_image.width,
00492       height = range_image.height,
00493       array_size = width*height;
00494   
00495   interest_image_ = new float[array_size];
00496   
00497   for (int index=0; index<array_size; ++index)
00498   {
00499     interest_image_[index] = 0.0f;
00500     if (!range_image.isValid (index))
00501       continue;
00502     const BorderTraits& border_traits = border_descriptions.points[index].traits;
00503     if (border_traits[BORDER_TRAIT__SHADOW_BORDER] || border_traits[BORDER_TRAIT__VEIL_POINT])
00504       continue;
00505     interest_image_[index] = 2.0f;
00506   }
00507   
00508   const int angle_histogram_size = 18;
00509   std::vector<float> angle_histogram;
00510   angle_histogram.resize(angle_histogram_size);
00511   std::vector<std::vector<std::pair<int, float> > > angle_elements (angle_histogram_size);
00512   std::vector<bool> relevant_point_still_valid;
00513   
00514   std::vector<bool> was_touched;
00515   was_touched.resize (array_size, false);
00516   std::vector<int> neighbors_to_check,
00517                    neighbors_within_radius_overhead;
00518   
00519   //double interest_value_calculation_start_time = getTime ();
00520 # pragma omp parallel for default (shared) num_threads (parameters_.max_no_of_threads) schedule (guided, 10) \
00521                           firstprivate (was_touched, neighbors_to_check, angle_histogram, neighbors_within_radius_overhead, \
00522                                         angle_elements, relevant_point_still_valid) 
00523   for (int index=0; index<array_size; ++index)
00524   {
00525     if (interest_image_[index] <= 1.0f)
00526       continue;
00527     int y = index/range_image.width,
00528         x = index - y*range_image.width;
00529     
00530     const PointWithRange& point = range_image.getPoint (index);
00531     
00532     Eigen::Affine3f rotation_to_viewer_coordinate_system;
00533     range_image.getRotationToViewerCoordinateFrame (point.getVector3fMap (),
00534                                                     rotation_to_viewer_coordinate_system);
00535     
00536     // -----Start region growing to cover all connected points within the support size-----
00537     neighbors_to_check.clear ();
00538     neighbors_to_check.push_back (index);
00539     neighbors_within_radius_overhead.clear ();
00540     was_touched[index] = true;
00541     
00542     for (int angle_histogram_idx=0; angle_histogram_idx<angle_histogram_size; ++angle_histogram_idx)
00543     {
00544       angle_histogram[angle_histogram_idx] = 0;
00545       angle_elements[angle_histogram_idx].clear ();
00546     }
00547     
00548     for (size_t neighbors_to_check_idx=0; neighbors_to_check_idx<neighbors_to_check.size (); ++neighbors_to_check_idx)
00549     {
00550       int index2 = neighbors_to_check[neighbors_to_check_idx];
00551       if (!range_image.isValid (index2))
00552         continue;
00553       const BorderTraits& border_traits2 = border_descriptions.points[index2].traits;
00554       if (border_traits2[BORDER_TRAIT__SHADOW_BORDER] || border_traits2[BORDER_TRAIT__VEIL_POINT])
00555         continue;
00556       int y2 = index2/range_image.width,
00557           x2 = index2 - y2*range_image.width;
00558       const PointWithRange& point2 = range_image.getPoint (index2);
00559       
00560       float pixelDistance = static_cast<float> (std::max (abs (x2-x), abs (y2-y)));
00561 
00562       float distance_squared = squaredEuclideanDistance (point, point2);
00563       if (distance_squared <= radius_overhead_squared) 
00564         neighbors_within_radius_overhead.push_back (index2);
00565 
00566       if (pixelDistance > 2.0f)  // Always consider immediate neighbors, even if to far away
00567       {
00568         if (distance_squared>increased_radius_squared)
00569           continue;
00570       }
00571       
00572       for (int y3=std::max (0,y2-1); y3<=std::min (y2+1,int (range_image.height)-1); ++y3)
00573       {
00574         for (int x3=std::max (0,x2-1); x3<=std::min (x2+1,int (range_image.width)-1); ++x3)
00575         {
00576           int index3 = y3*range_image.width + x3;
00577           if (!was_touched[index3])
00578           {
00579             neighbors_to_check.push_back (index3);
00580             was_touched[index3] = true;
00581           }
00582         }
00583       }
00584       
00585       float surface_change_score = surface_change_scores[index2];
00586       if (surface_change_score < parameters_.min_surface_change_score)  // Pixel not 'interesting' enough to consider?
00587         continue;
00588       const Eigen::Vector3f& surface_change_direction = surface_change_directions[index2];
00589       
00590       float angle = nkdGetDirectionAngle (surface_change_direction, rotation_to_viewer_coordinate_system);
00591       int histogram_cell = (std::min) (angle_histogram_size-1,
00592                                        static_cast<int> (pcl_lrint (floorf ( (angle+deg2rad (90.0f))/deg2rad (180.0f) * angle_histogram_size))));
00593       float& histogram_value = angle_histogram[histogram_cell];
00594       histogram_value = (std::max) (histogram_value, surface_change_score);
00595       angle_elements[histogram_cell].push_back (std::make_pair(index2, surface_change_score));
00596     }
00597     
00598     // Reset was_touched to false
00599     for (size_t neighbors_to_check_idx=0; neighbors_to_check_idx<neighbors_to_check.size (); ++neighbors_to_check_idx)
00600       was_touched[neighbors_to_check[neighbors_to_check_idx]] = false;
00601     
00602     float angle_change_value = 0.0f;
00603     for (int histogram_cell1=0; histogram_cell1<angle_histogram_size-1; ++histogram_cell1)
00604     {
00605       if (angle_histogram[histogram_cell1]==0.0f)
00606         continue;
00607       for (int histogram_cell2=histogram_cell1+1; histogram_cell2<angle_histogram_size; ++histogram_cell2)
00608       {
00609         if (angle_histogram[histogram_cell2]==0.0f)
00610           continue;
00611         // TODO: lookup table for the following:
00612         float normalized_angle_diff = 2.0f*float (histogram_cell2-histogram_cell1)/float (angle_histogram_size);
00613         normalized_angle_diff = (normalized_angle_diff <= 1.0f ? normalized_angle_diff : 2.0f-normalized_angle_diff);
00614         
00615         angle_change_value = std::max (angle_histogram[histogram_cell1] * angle_histogram[histogram_cell2] *
00616                                        normalized_angle_diff,   angle_change_value);
00617       }
00618     }
00619     angle_change_value = sqrtf (angle_change_value);
00620     float maximum_interest_value = angle_change_value;
00621     
00622     if (parameters_.add_points_on_straight_edges)
00623     {
00624       float max_histogram_cell_value = 0.0f;
00625       for (int histogram_cell=0; histogram_cell<angle_histogram_size; ++histogram_cell)
00626         max_histogram_cell_value = (std::max) (max_histogram_cell_value, angle_histogram[histogram_cell]);
00627       maximum_interest_value = (std::min) (maximum_interest_value+max_histogram_cell_value, 1.0f);
00628     }
00629     
00630     // Every point in distance search_radius cannot have a higher value
00631     // Therefore: if too low, set all to zero. Else calculate properly
00632     if (maximum_interest_value < parameters_.min_interest_value)
00633       for (size_t neighbors_idx=0; neighbors_idx<neighbors_within_radius_overhead.size (); ++neighbors_idx)
00634         interest_image_[neighbors_within_radius_overhead[neighbors_idx]] = 0.0f;
00635     else
00636     {
00637       // Reduce number of neighbors to go through by filtering close by points with the same angle
00638       bool do_neighbor_size_reduction = true;
00639       if (do_neighbor_size_reduction)
00640       {
00641         float min_distance_between_relevant_points = 0.25f * search_radius,
00642               min_distance_between_relevant_points_squared = powf(min_distance_between_relevant_points, 2);
00643         for (int angle_histogram_idx=0; angle_histogram_idx<angle_histogram_size; ++angle_histogram_idx)
00644         {
00645           std::vector<std::pair<int,float> >& relevent_point_indices = angle_elements[angle_histogram_idx];
00646           std::sort(relevent_point_indices.begin(), relevent_point_indices.end(), secondPairElementIsGreater);
00647           relevant_point_still_valid.clear();
00648           relevant_point_still_valid.resize(relevent_point_indices.size(), true);
00649           for (int rpi_idx1=0; rpi_idx1<int(relevent_point_indices.size ())-1; ++rpi_idx1)
00650           {
00651             if (!relevant_point_still_valid[rpi_idx1])
00652               continue;
00653             const PointWithRange& relevant_point1 = range_image.getPoint (relevent_point_indices[rpi_idx1].first);
00654             for (int rpi_idx2=rpi_idx1+1; rpi_idx2<int(relevent_point_indices.size ()); ++rpi_idx2)
00655             {
00656               if (!relevant_point_still_valid[rpi_idx2])
00657                 continue;
00658               const PointWithRange& relevant_point2 = range_image.getPoint (relevent_point_indices[rpi_idx2].first);
00659               float distance_squared = (relevant_point1.getVector3fMap ()-relevant_point2.getVector3fMap ()).norm ();
00660               if (distance_squared > min_distance_between_relevant_points_squared)
00661                 continue;
00662               relevant_point_still_valid[rpi_idx2] = false;
00663             }
00664           }
00665           int newPointIdx=0;
00666           for (int oldPointIdx=0; oldPointIdx<int(relevant_point_still_valid.size()); ++oldPointIdx) {
00667             if (relevant_point_still_valid[oldPointIdx])
00668               relevent_point_indices[newPointIdx++] = relevent_point_indices[oldPointIdx];
00669           }
00670           relevent_point_indices.resize(newPointIdx);
00671         }
00672       }
00673       
00674       // Caclulate interest values for neighbors
00675       for (size_t neighbors_idx=0; neighbors_idx<neighbors_within_radius_overhead.size (); ++neighbors_idx)
00676       {
00677         int index2 = neighbors_within_radius_overhead[neighbors_idx];
00678         int y2 = index2/range_image.width,
00679             x2 = index2 - y2*range_image.width;
00680         const PointWithRange& point2 = range_image.getPoint (index2);
00681         float& interest_value = interest_image_[index2];
00682         if (interest_value <= 1.0f)
00683           continue;
00684         float negative_score = 1.0;
00685 
00686         for (int angle_histogram_idx=0; angle_histogram_idx<angle_histogram_size; ++angle_histogram_idx)
00687         {
00688           float& histogram_value = angle_histogram[angle_histogram_idx];
00689           histogram_value = 0;
00690           const std::vector<std::pair<int,float> >& relevent_point_indices = angle_elements[angle_histogram_idx];
00691           for (size_t rpi_idx=0; rpi_idx<relevent_point_indices.size (); ++rpi_idx)
00692           {
00693             int index3 = relevent_point_indices[rpi_idx].first;
00694             int y3 = index3/range_image.width,
00695                 x3 = index3 - y3*range_image.width;
00696             const PointWithRange& point3 = range_image.getPoint (index3);
00697             float surface_change_score = relevent_point_indices[rpi_idx].second;
00698             
00699             float pixelDistance = static_cast<float> (std::max (abs (x3-x2), abs (y3-y2)));
00700             float distance = (point3.getVector3fMap ()-point2.getVector3fMap ()).norm ();
00701             float distance_factor = radius_reciprocal*distance;
00702             float positive_score, current_negative_score;
00703             nkdGetScores (distance_factor, surface_change_score, pixelDistance,
00704                          parameters_.optimal_distance_to_high_surface_change,
00705                          current_negative_score, positive_score);
00706             histogram_value = (std::max) (histogram_value, positive_score);
00707             negative_score  = (std::min) (negative_score, current_negative_score);
00708           }
00709         }
00710         float angle_change_value = 0.0f;
00711         for (int histogram_cell1=0; histogram_cell1<angle_histogram_size-1; ++histogram_cell1)
00712         {
00713           if (angle_histogram[histogram_cell1]==0.0f)
00714             continue;
00715           for (int histogram_cell2=histogram_cell1+1; histogram_cell2<angle_histogram_size; ++histogram_cell2)
00716           {
00717             if (angle_histogram[histogram_cell2]==0.0f)
00718               continue;
00719             // TODO: lookup table for the following:
00720             float normalized_angle_diff = 2.0f*float (histogram_cell2-histogram_cell1)/float (angle_histogram_size);
00721             normalized_angle_diff = (normalized_angle_diff <= 1.0f ? normalized_angle_diff : 2.0f-normalized_angle_diff);
00722             angle_change_value = std::max (angle_change_value, angle_histogram[histogram_cell1] *
00723                                                                angle_histogram[histogram_cell2] *
00724                                                                normalized_angle_diff);
00725           }
00726         }
00727         angle_change_value = sqrtf (angle_change_value);
00728         interest_value = negative_score * angle_change_value;
00729       }
00730     }
00731   }
00732   
00733   border_extractor.getParameters ().max_no_of_threads = original_max_no_of_threads;
00734 }
00735 
00736 void 
00737 NarfKeypoint::calculateInterestPoints ()
00738 {
00739   //std::cout << __PRETTY_FUNCTION__ << " called.\n";
00740 
00741   if (interest_points_ != NULL)
00742     return;
00743 
00744   calculateInterestImage ();
00745   
00746   interest_points_ = new ::pcl::PointCloud<InterestPoint>;
00747   
00748   float max_distance_squared = powf (0.3f*parameters_.support_size, 2);
00749   
00750   const RangeImage& range_image = range_image_border_extractor_->getRangeImage ();
00751   const ::pcl::PointCloud<BorderDescription>& border_descriptions =
00752       range_image_border_extractor_->getBorderDescriptions ();
00753   int width  = range_image.width,
00754       height = range_image.height,
00755       size = width*height;
00756   is_interest_point_image_.clear ();
00757   is_interest_point_image_.resize (size, false);
00758   
00759   typedef double RealForPolynomial;
00760   PolynomialCalculationsT<RealForPolynomial> polynomial_calculations;
00761   BivariatePolynomialT<RealForPolynomial> polynomial (2);
00762   std::vector<Eigen::Matrix<RealForPolynomial, 3, 1> > sample_points;
00763   std::vector<RealForPolynomial> x_values, y_values;
00764   std::vector<int> types;
00765   std::vector<bool> invalid_beams, old_invalid_beams;
00766   
00767   pcl::PointCloud<InterestPoint>::VectorType tmp_interest_points;
00768   for (int y=0; y<height; ++y)
00769   {
00770     for (int x=0; x<width; ++x)
00771     {
00772       int index = y*width + x;
00773       float interest_value = interest_image_[index];
00774       if (interest_value < parameters_.min_interest_value)
00775         continue;
00776       const PointWithRange& point = range_image.getPoint (index);
00777       bool is_maximum = true;
00778       for (int y2=y-1; y2<=y+1&&is_maximum&&parameters_.do_non_maximum_suppression; ++y2)
00779       {
00780         for (int x2=x-1; x2<=x+1; ++x2)
00781         {
00782           if (!range_image.isInImage (x2,y2))
00783             continue;
00784           int index2 = y2*width + x2;
00785           float interest_value2 = interest_image_[index2];
00786           if (interest_value2 <= interest_value)
00787             continue;
00788           is_maximum = false;
00789           break;
00790         }
00791       }
00792       if (!is_maximum)
00793         continue;
00794       
00795       PointWithRange keypoint_3d = point;
00796       int keypoint_x_int=x, keypoint_y_int=y;
00797       
00798       int no_of_polynomial_approximations_per_point = parameters_.no_of_polynomial_approximations_per_point;
00799       if (!parameters_.do_non_maximum_suppression)
00800         no_of_polynomial_approximations_per_point = 0;
00801       
00802       for (int poly_step=0; poly_step<no_of_polynomial_approximations_per_point; ++poly_step)
00803       {
00804         sample_points.clear ();
00805         invalid_beams.clear ();
00806         old_invalid_beams.clear ();
00807         for (int radius=0, stop=false;  !stop;  ++radius) 
00808         {
00809           std::swap (invalid_beams, old_invalid_beams);
00810           propagateInvalidBeams (radius, old_invalid_beams, invalid_beams);
00811           int x2=keypoint_x_int-radius-1, y2=keypoint_y_int-radius;  // Top left - 1
00812           stop = true;
00813           for (int i=0; (radius==0&&i==0) || i<8*radius; ++i)
00814           {
00815             if (i<=2*radius) ++x2; else if (i<=4*radius) ++y2; else if (i<=6*radius) --x2; else --y2;
00816             if (invalid_beams[i] || !range_image.isValid (x2, y2))
00817               continue;
00818             int index2 = y2*width + x2;
00819             const BorderTraits& neighbor_border_traits = border_descriptions.points[index2].traits;
00820             if (neighbor_border_traits[BORDER_TRAIT__SHADOW_BORDER] || neighbor_border_traits[BORDER_TRAIT__VEIL_POINT])
00821             {
00822               invalid_beams[i] = true;
00823               continue;
00824             }
00825             const PointWithRange& neighbor = range_image.getPoint (index2);
00826             float distance_squared = squaredEuclideanDistance (point, neighbor);
00827             if (distance_squared>max_distance_squared)
00828             {
00829               invalid_beams[i] = true;
00830               continue;
00831             }
00832             stop = false; // There is a point in range -> Have to check further distances
00833             
00834             float interest_value2 = interest_image_[index2];
00835             sample_points.push_back (Eigen::Vector3d (x2-keypoint_x_int, y2-keypoint_y_int, interest_value2));
00836           }
00837         }
00838         if (!polynomial_calculations.bivariatePolynomialApproximation (sample_points, 2, polynomial))
00839           continue;
00840 
00841         polynomial.findCriticalPoints (x_values, y_values, types);
00842         
00843         if (!types.empty () && types[0]==0)
00844         {
00845           float keypoint_x = static_cast<float> (x_values[0]+keypoint_x_int),
00846                 keypoint_y = static_cast<float> (y_values[0]+keypoint_y_int);
00847           
00848           keypoint_x_int = static_cast<int> (pcl_lrint (keypoint_x));
00849           keypoint_y_int = static_cast<int> (pcl_lrint (keypoint_y));
00850           
00851           range_image.calculate3DPoint (keypoint_x, keypoint_y, keypoint_3d);
00852           if (!pcl_isfinite (keypoint_3d.range))
00853           {
00854             keypoint_3d = point;
00855             break;
00856           }
00857         }
00858         else
00859         {
00860           break;
00861         }
00862       }
00863       
00864       InterestPoint interest_point;
00865       interest_point.getVector3fMap () = keypoint_3d.getVector3fMap ();
00866       interest_point.strength = interest_value;
00867       interest_point.strength = interest_value;
00868       tmp_interest_points.push_back (interest_point);
00869     }
00870   }
00871   
00872   std::sort (tmp_interest_points.begin (), tmp_interest_points.end (), isBetterInterestPoint);
00873   
00874   float min_distance_squared = powf (parameters_.min_distance_between_interest_points*parameters_.support_size, 2);
00875   for (size_t int_point_idx=0; int_point_idx<tmp_interest_points.size (); ++int_point_idx)
00876   {
00877     if (parameters_.max_no_of_interest_points > 0  &&  int (interest_points_->size ()) >= parameters_.max_no_of_interest_points)
00878       break;
00879     const InterestPoint& interest_point = tmp_interest_points[int_point_idx];
00880     
00881     bool better_point_too_close = false;
00882     for (size_t int_point_idx2=0; int_point_idx2<interest_points_->points.size (); ++int_point_idx2)
00883     {
00884       const InterestPoint& interest_point2 = interest_points_->points[int_point_idx2];
00885       float distance_squared = (interest_point.getVector3fMap ()-interest_point2.getVector3fMap ()).squaredNorm ();
00886       if (distance_squared < min_distance_squared)
00887       {
00888         better_point_too_close = true;
00889         break;
00890       }
00891     }
00892     if (better_point_too_close)
00893       continue;
00894     interest_points_->points.push_back (interest_point);
00895     int image_x, image_y;
00896     range_image.getImagePoint (interest_point.getVector3fMap (), image_x, image_y);
00897     if (range_image.isValid (image_x, image_y))
00898       is_interest_point_image_[image_y*width + image_x] = true;
00899   }
00900   interest_points_->width = static_cast<uint32_t> (interest_points_->points.size ());
00901   interest_points_->height = 1;
00902   interest_points_->is_dense = true;
00903 }
00904 
00905 const RangeImage& 
00906 NarfKeypoint::getRangeImage ()
00907 {
00908   return (range_image_border_extractor_->getRangeImage ());
00909 }
00910 
00911 void 
00912 NarfKeypoint::detectKeypoints (NarfKeypoint::PointCloudOut& output)
00913 {
00914   output.points.clear ();
00915   
00916   if (indices_)
00917   {
00918     std::cerr << __PRETTY_FUNCTION__
00919               << ": Sorry, usage of indices for the extraction is not supported for NARF interest points (yet).\n\n";
00920     return;
00921   }
00922   
00923   if (range_image_border_extractor_ == NULL)
00924   {
00925     std::cerr << __PRETTY_FUNCTION__
00926               << ": RangeImageBorderExtractor member is not set. "
00927               <<   "Sorry, this is needed for the NARF keypoint extraction.\n\n";
00928     return;
00929   }
00930   
00931   if (!range_image_border_extractor_->hasRangeImage ())
00932   {
00933     std::cerr << __PRETTY_FUNCTION__
00934               << ": RangeImage is not set. Sorry, the NARF keypoint extraction works on range images, "
00935                    "not on normal point clouds.\n\n"
00936               << " Use setRangeImage (...).\n\n";
00937     return;
00938   }
00939   
00940   calculateInterestPoints ();
00941   
00942   int size = getRangeImage ().width * getRangeImage ().height;
00943   
00944   for (int index=0; index<size; ++index)
00945   {
00946     if (!is_interest_point_image_[index])
00947       continue;
00948     output.points.push_back (index);
00949   }
00950 }
00951 
00952 void 
00953 NarfKeypoint::compute (NarfKeypoint::PointCloudOut& output)
00954 {
00955   //std::cout << __PRETTY_FUNCTION__ << " called.\n";
00956   detectKeypoints (output);
00957 }
00958 
00959 }  // end namespace pcl


pcl
Author(s): Open Perception
autogenerated on Mon Oct 6 2014 03:15:46