00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 #include <iostream>
00039 using std::cout;
00040 using std::cerr;
00041 #include <map>
00042 #include <set>
00043 #include <cmath>
00044 #include <pcl/pcl_macros.h>
00045 #include <pcl/common/common_headers.h>
00046 #include <pcl/range_image/range_image.h>
00047 #include <pcl/point_cloud.h>
00048 #include <pcl/common/vector_average.h>
00049 #include <pcl/features/eigen.h>
00050 #include <pcl/features/range_image_border_extractor.h>
00051 
00052 namespace pcl 
00053 {
00054 
00056 RangeImageBorderExtractor::RangeImageBorderExtractor(const RangeImage* range_image) : BaseClass(),
00057   parameters_ (), range_image_(range_image), range_image_size_during_extraction_(0),
00058   border_scores_left_(NULL), border_scores_right_(NULL), border_scores_top_(NULL), border_scores_bottom_(NULL),
00059   surface_structure_(NULL), border_descriptions_(NULL), shadow_border_informations_(NULL), border_directions_(NULL),
00060   surface_change_scores_(NULL), surface_change_directions_(NULL)
00061 {
00062 }
00063 
00065 RangeImageBorderExtractor::~RangeImageBorderExtractor()
00066 {
00067   clearData ();
00068 }
00069 
00071 void 
00072 RangeImageBorderExtractor::setRangeImage (const RangeImage* range_image)
00073 {
00074   clearData ();
00075   range_image_ = range_image;
00076 }
00077 
00079 void 
00080 RangeImageBorderExtractor::clearData ()
00081 {
00082   delete[] border_scores_left_;    border_scores_left_   = NULL;
00083   delete[] border_scores_right_;   border_scores_right_  = NULL;
00084   delete[] border_scores_top_;     border_scores_top_    = NULL;
00085   delete[] border_scores_bottom_;  border_scores_bottom_ = NULL;
00086   
00087   for (int i=0; i<range_image_size_during_extraction_; ++i)
00088   {
00089     if (surface_structure_!=NULL)
00090       delete surface_structure_[i];
00091     if (shadow_border_informations_!=NULL)
00092       delete shadow_border_informations_[i];
00093     if (border_directions_!=NULL)
00094       delete border_directions_[i];
00095   }
00096   delete[] surface_structure_; surface_structure_ = NULL;
00097   delete border_descriptions_; border_descriptions_ = NULL;
00098   delete[] shadow_border_informations_; shadow_border_informations_ = NULL;
00099   delete[] border_directions_; border_directions_ = NULL;
00100   
00101   delete[] surface_change_scores_;  surface_change_scores_ = NULL;
00102   delete[] surface_change_directions_;  surface_change_directions_ = NULL;
00103 }
00104 
00106 void 
00107 RangeImageBorderExtractor::extractLocalSurfaceStructure ()
00108 {
00109   if (surface_structure_ != NULL)
00110     return;
00111   
00112   
00113   
00114   int width  = range_image_->width,
00115       height = range_image_->height;
00116   range_image_size_during_extraction_ = width*height;
00117   int array_size = range_image_size_during_extraction_;
00118   surface_structure_ = new LocalSurface*[array_size];
00119   int step_size = (std::max)(1, parameters_.pixel_radius_plane_extraction/2);
00120   
00121   int no_of_nearest_neighbors = static_cast<int> (pow (static_cast<double> (parameters_.pixel_radius_plane_extraction/step_size + 1), 2.0));
00122   
00123 # pragma omp parallel for num_threads(parameters_.max_no_of_threads) default(shared) schedule(dynamic, 10)
00124   for (int y=0; y<height; ++y)
00125   {
00126     for (int x=0; x<width; ++x)
00127     {
00128       int index = y*width + x;
00129       LocalSurface*& local_surface = surface_structure_[index];
00130       local_surface = NULL;
00131       if (!range_image_->isValid(index))
00132         continue;
00133       local_surface = new LocalSurface;
00134       Eigen::Vector3f point;
00135       range_image_->getPoint(x, y, point);
00136       
00137       if (!range_image_->getSurfaceInformation(x, y, parameters_.pixel_radius_plane_extraction, point,
00138                                   no_of_nearest_neighbors, step_size, local_surface->max_neighbor_distance_squared,
00139                                   local_surface->normal_no_jumps, local_surface->neighborhood_mean_no_jumps,
00140                                   local_surface->eigen_values_no_jumps,  &local_surface->normal,
00141                                   &local_surface->neighborhood_mean, &local_surface->eigen_values))
00142       {
00143         delete local_surface;
00144         local_surface = NULL;
00145       }
00146       
00147       
00148     }
00149   }
00150 }
00151 
00153 void 
00154 RangeImageBorderExtractor::extractBorderScoreImages ()
00155 {
00156   if (border_scores_left_ != NULL)
00157     return;
00158   
00159   extractLocalSurfaceStructure();
00160   
00161   
00162   
00163   int width  = range_image_->width,
00164       height = range_image_->height,
00165       size   = width*height;
00166   border_scores_left_   = new float[size];
00167   border_scores_right_  = new float[size];
00168   border_scores_top_    = new float[size];
00169   border_scores_bottom_ = new float[size];
00170   
00171 # pragma omp parallel for num_threads(parameters_.max_no_of_threads) default(shared) schedule(dynamic, 10)
00172   for (int y=0; y<height; ++y) {
00173     for (int x=0; x<width; ++x) {
00174       int index = y*width + x;
00175       float& left=border_scores_left_[index]; float& right=border_scores_right_[index];
00176       float& top=border_scores_top_[index]; float& bottom=border_scores_bottom_[index]; 
00177       LocalSurface* local_surface_ptr = surface_structure_[index];
00178       if (local_surface_ptr==NULL)
00179       {
00180         left=right=top=bottom = 0.0f;
00181         continue;
00182       }
00183       
00184       left   = getNeighborDistanceChangeScore(*local_surface_ptr, x, y, -1,  0, parameters_.pixel_radius_borders);
00185       right  = getNeighborDistanceChangeScore(*local_surface_ptr, x, y,  1,  0, parameters_.pixel_radius_borders);
00186       top    = getNeighborDistanceChangeScore(*local_surface_ptr, x, y,  0, -1, parameters_.pixel_radius_borders);
00187       bottom = getNeighborDistanceChangeScore(*local_surface_ptr, x, y,  0,  1, parameters_.pixel_radius_borders);
00188     }
00189   }
00190 }
00191 
00193 float* 
00194 RangeImageBorderExtractor::updatedScoresAccordingToNeighborValues (const float* border_scores) const
00195 {
00196   float* new_scores = new float[range_image_->width*range_image_->height];
00197   float* new_scores_ptr = new_scores;
00198   for (int y=0; y < static_cast<int> (range_image_->height); ++y) 
00199     for (int x=0; x < static_cast<int> (range_image_->width); ++x) 
00200       *(new_scores_ptr++) = updatedScoreAccordingToNeighborValues(x, y, border_scores);
00201   return (new_scores);
00202 }
00203 
00205 void 
00206 RangeImageBorderExtractor::updateScoresAccordingToNeighborValues ()
00207 {
00208   extractBorderScoreImages();
00209   
00210   
00211   
00212   float* left_with_propagated_neighbors = updatedScoresAccordingToNeighborValues(border_scores_left_);
00213   delete[] border_scores_left_;
00214   border_scores_left_ = left_with_propagated_neighbors;
00215   float* right_with_propagated_neighbors = updatedScoresAccordingToNeighborValues(border_scores_right_);
00216   delete[] border_scores_right_;
00217   border_scores_right_ = right_with_propagated_neighbors;
00218   float* top_with_propagated_neighbors = updatedScoresAccordingToNeighborValues(border_scores_top_);
00219   delete[] border_scores_top_;
00220   border_scores_top_ = top_with_propagated_neighbors;
00221   float* bottom_with_propagated_neighbors = updatedScoresAccordingToNeighborValues(border_scores_bottom_);
00222   delete[] border_scores_bottom_;
00223   border_scores_bottom_ = bottom_with_propagated_neighbors;
00224 }
00225  
00227 void 
00228 RangeImageBorderExtractor::findAndEvaluateShadowBorders ()
00229 {
00230   if (shadow_border_informations_ != NULL)
00231     return;
00232   
00233   if (border_scores_left_==NULL)
00234   {
00235     std::cerr << __PRETTY_FUNCTION__<<": border score images not available!\n";
00236   }
00237   
00238   
00239   
00240   int width  = range_image_->width,
00241       height = range_image_->height;
00242   shadow_border_informations_ = new ShadowBorderIndices*[width*height];
00243   for (int y = 0; y < static_cast<int> (height); ++y) 
00244   {
00245     for (int x = 0; x < static_cast<int> (width); ++x) 
00246     {
00247       int index = y*width+x;
00248       ShadowBorderIndices*& shadow_border_indices = shadow_border_informations_[index];
00249       shadow_border_indices = NULL;
00250       int shadow_border_idx;
00251       
00252       if (changeScoreAccordingToShadowBorderValue(x, y, -1, 0, border_scores_left_, border_scores_right_, shadow_border_idx))
00253       {
00254         shadow_border_indices = (shadow_border_indices==NULL ? new ShadowBorderIndices : shadow_border_indices);
00255         shadow_border_indices->left = shadow_border_idx;
00256       }
00257       if (changeScoreAccordingToShadowBorderValue(x, y, 1, 0, border_scores_right_, border_scores_left_, shadow_border_idx))
00258       {
00259         shadow_border_indices = (shadow_border_indices==NULL ? new ShadowBorderIndices : shadow_border_indices);
00260         shadow_border_indices->right = shadow_border_idx;
00261       }
00262       if (changeScoreAccordingToShadowBorderValue(x, y, 0, -1, border_scores_top_, border_scores_bottom_, shadow_border_idx))
00263       {
00264         shadow_border_indices = (shadow_border_indices==NULL ? new ShadowBorderIndices : shadow_border_indices);
00265         shadow_border_indices->top = shadow_border_idx;
00266       }
00267       if (changeScoreAccordingToShadowBorderValue(x, y, 0, 1, border_scores_bottom_, border_scores_top_, shadow_border_idx))
00268       {
00269         shadow_border_indices = (shadow_border_indices==NULL ? new ShadowBorderIndices : shadow_border_indices);
00270         shadow_border_indices->bottom = shadow_border_idx;
00271       }
00272     }
00273   }
00274 }
00275 
00277 float* 
00278 RangeImageBorderExtractor::getAnglesImageForBorderDirections ()
00279 {
00280   calculateBorderDirections();
00281   
00282   int width  = range_image_->width,
00283       height = range_image_->height,
00284       array_size = width*height;
00285   float* angles_image = new float[array_size];
00286   
00287   for (int y=0; y<height; ++y)
00288   {
00289     for (int x=0; x<width; ++x)
00290     {
00291       int index = y*width + x;
00292       float& angle = angles_image[index];
00293       angle = -std::numeric_limits<float>::infinity ();
00294       const Eigen::Vector3f* border_direction_ptr = border_directions_[index];
00295       if (border_direction_ptr == NULL)
00296         continue;
00297       const Eigen::Vector3f& border_direction = *border_direction_ptr;
00298       const PointWithRange& point = range_image_->getPoint(index);
00299       
00300       float border_direction_in_image_x, border_direction_in_image_y;
00301       float tmp_factor = point.range*range_image_->getAngularResolution();
00302       range_image_->getImagePoint(point.x+tmp_factor*border_direction[0], point.y+tmp_factor*border_direction[1], point.z+tmp_factor*border_direction[2],
00303                                 border_direction_in_image_x, border_direction_in_image_y);
00304       border_direction_in_image_x -= static_cast<float> (x);  border_direction_in_image_y -= static_cast<float> (y);
00305       angle = atan2f (border_direction_in_image_y, border_direction_in_image_x);
00306     }
00307   }
00308   return angles_image;
00309 }
00310 
00312 float* 
00313 RangeImageBorderExtractor::getAnglesImageForSurfaceChangeDirections ()
00314 {
00315   
00316   
00317   calculateSurfaceChanges();
00318   
00319   int width  = range_image_->width,
00320       height = range_image_->height,
00321       array_size = width*height;
00322   float* angles_image = new float[array_size];
00323   
00324   for (int y=0; y<height; ++y)
00325   {
00326     for (int x=0; x<width; ++x)
00327     {
00328       int index = y*width + x;
00329       float& angle = angles_image[index];
00330       angle = -std::numeric_limits<float>::infinity ();
00331       float surface_change_score = surface_change_scores_[index];
00332       if (surface_change_score <= 0.1f)
00333         continue;
00334       const Eigen::Vector3f& direction = surface_change_directions_[index];
00335       const PointWithRange& point = range_image_->getPoint(index);
00336       
00337       float border_direction_in_image_x, border_direction_in_image_y;
00338       float tmp_factor = point.range*range_image_->getAngularResolution();
00339       range_image_->getImagePoint(point.x+tmp_factor*direction[0], point.y+tmp_factor*direction[1], point.z+tmp_factor*direction[2],
00340                                 border_direction_in_image_x, border_direction_in_image_y);
00341       border_direction_in_image_x -= static_cast<float> (x);  border_direction_in_image_y -= static_cast<float> (y);
00342       angle = atan2f (border_direction_in_image_y, border_direction_in_image_x);
00343       if (angle <= deg2rad (-90.0f))
00344         angle += static_cast<float> (M_PI);
00345       else if (angle > deg2rad (90.0f))
00346         angle -= static_cast<float> (M_PI);
00347     }
00348   }
00349   return (angles_image);
00350 }
00351 
00352 
00354 void 
00355 RangeImageBorderExtractor::classifyBorders ()
00356 {
00357   if (border_descriptions_ != NULL)
00358     return;
00359   
00360   
00361   extractLocalSurfaceStructure();
00362   
00363   
00364   extractBorderScoreImages();
00365   
00366   
00367   updateScoresAccordingToNeighborValues();
00368   
00369   
00370   findAndEvaluateShadowBorders();
00371   
00372   int width  = range_image_->width,
00373       height = range_image_->height,
00374       size   = width*height;
00375   
00376   BorderDescription initial_border_description;
00377   initial_border_description.traits = 0;
00378   border_descriptions_ = new PointCloudOut;
00379   border_descriptions_->width = width;
00380   border_descriptions_->height = height;
00381   border_descriptions_->is_dense = true;
00382   border_descriptions_->points.resize(size, initial_border_description);
00383   
00384   for (int y = 0; y < static_cast<int> (height); ++y) 
00385   {
00386     for (int x = 0; x < static_cast<int> (width); ++x) 
00387     {
00388       int index = y*width+x;
00389       BorderDescription& border_description = border_descriptions_->points[index];
00390       border_description.x = x;
00391       border_description.y = y;
00392       BorderTraits& border_traits = border_description.traits;
00393       
00394       ShadowBorderIndices* shadow_border_indices = shadow_border_informations_[index];
00395       if (shadow_border_indices == NULL)
00396         continue;
00397       
00398       int shadow_border_index = shadow_border_indices->left;
00399       if (shadow_border_index >= 0 && checkIfMaximum(x, y, -1, 0, border_scores_left_, shadow_border_index))
00400       {
00401         BorderTraits& shadow_traits = border_descriptions_->points[shadow_border_index].traits;
00402         border_traits[BORDER_TRAIT__OBSTACLE_BORDER] = border_traits[BORDER_TRAIT__OBSTACLE_BORDER_LEFT] = true;
00403         shadow_traits[BORDER_TRAIT__SHADOW_BORDER] = shadow_traits[BORDER_TRAIT__SHADOW_BORDER_RIGHT] = true;
00404         for (int index3=index-1; index3>shadow_border_index; --index3)
00405         {
00406           BorderTraits& veil_point = border_descriptions_->points[index3].traits;
00407           veil_point[BORDER_TRAIT__VEIL_POINT] = veil_point[BORDER_TRAIT__VEIL_POINT_RIGHT] = true;
00408         }
00409       }
00410       
00411       shadow_border_index = shadow_border_indices->right;
00412       if (shadow_border_index >= 0 && checkIfMaximum(x, y, 1, 0, border_scores_right_, shadow_border_index))
00413       {
00414         BorderTraits& shadow_traits = border_descriptions_->points[shadow_border_index].traits;
00415         border_traits[BORDER_TRAIT__OBSTACLE_BORDER] = border_traits[BORDER_TRAIT__OBSTACLE_BORDER_RIGHT] = true;
00416         shadow_traits[BORDER_TRAIT__SHADOW_BORDER] = shadow_traits[BORDER_TRAIT__SHADOW_BORDER_LEFT] = true;
00417         for (int index3=index+1; index3<shadow_border_index; ++index3)
00418         {
00419           BorderTraits& veil_point = border_descriptions_->points[index3].traits;
00420           veil_point[BORDER_TRAIT__VEIL_POINT] = veil_point[BORDER_TRAIT__VEIL_POINT_LEFT] = true;
00421         }
00422       }
00423       
00424       shadow_border_index = shadow_border_indices->top;
00425       if (shadow_border_index >= 0 && checkIfMaximum(x, y, 0, -1, border_scores_top_, shadow_border_index))
00426       {
00427         BorderTraits& shadow_traits = border_descriptions_->points[shadow_border_index].traits;
00428         border_traits[BORDER_TRAIT__OBSTACLE_BORDER] = border_traits[BORDER_TRAIT__OBSTACLE_BORDER_TOP] = true;
00429         shadow_traits[BORDER_TRAIT__SHADOW_BORDER] = shadow_traits[BORDER_TRAIT__SHADOW_BORDER_BOTTOM] = true;
00430         for (int index3=index-width; index3>shadow_border_index; index3-=width)
00431         {
00432           
00433           BorderTraits& veil_point = border_descriptions_->points[index3].traits;
00434           veil_point[BORDER_TRAIT__VEIL_POINT] = veil_point[BORDER_TRAIT__VEIL_POINT_BOTTOM] = true;
00435         }
00436       }
00437       
00438       shadow_border_index = shadow_border_indices->bottom;
00439       if (shadow_border_index >= 0 && checkIfMaximum(x, y, 0, 1, border_scores_bottom_, shadow_border_index))
00440       {
00441         BorderTraits& shadow_traits = border_descriptions_->points[shadow_border_index].traits;
00442         border_traits[BORDER_TRAIT__OBSTACLE_BORDER] = border_traits[BORDER_TRAIT__OBSTACLE_BORDER_BOTTOM] = true;
00443         shadow_traits[BORDER_TRAIT__SHADOW_BORDER] = shadow_traits[BORDER_TRAIT__SHADOW_BORDER_TOP] = true;
00444         for (int index3=index+width; index3<shadow_border_index; index3+=width)
00445         {
00446           
00447           BorderTraits& veil_point = border_descriptions_->points[index3].traits;
00448           veil_point[BORDER_TRAIT__VEIL_POINT] = veil_point[BORDER_TRAIT__VEIL_POINT_TOP] = true;
00449         }
00450       }
00451       
00452       
00453       
00454         
00455       
00456     }
00457   }
00458 }
00459 
00461 void 
00462 RangeImageBorderExtractor::calculateBorderDirections ()
00463 {
00464   if (border_directions_!=NULL)
00465     return;
00466   classifyBorders();
00467   
00468   
00469   
00470   int width  = range_image_->width,
00471       height = range_image_->height,
00472       size   = width*height;
00473   border_directions_ = new Eigen::Vector3f*[size];
00474   for (int y=0; y<height; ++y)
00475   {
00476     for (int x=0; x<width; ++x)
00477     {
00478       calculateBorderDirection(x, y);
00479     }
00480   }
00481   
00482   Eigen::Vector3f** average_border_directions = new Eigen::Vector3f*[size];
00483   int radius = parameters_.pixel_radius_border_direction;
00484   int minimum_weight = radius+1;
00485   float min_cos_angle=cosf(deg2rad(120.0f));
00486   for (int y=0; y<height; ++y)
00487   {
00488     for (int x=0; x<width; ++x)
00489     {
00490       int index = y*width + x;
00491       Eigen::Vector3f*& average_border_direction = average_border_directions[index];
00492       average_border_direction = NULL;
00493       const Eigen::Vector3f* border_direction = border_directions_[index];
00494       if (border_direction==NULL)
00495         continue;
00496       average_border_direction = new Eigen::Vector3f(*border_direction);
00497       float weight_sum = 1.0f;
00498       for (int y2=(std::max)(0, y-radius); y2<=(std::min)(y+radius, height-1); ++y2)
00499       {
00500         for (int x2=(std::max)(0, x-radius); x2<=(std::min)(x+radius, width-1); ++x2)
00501         {
00502           int index2 = y2*width + x2;
00503           const Eigen::Vector3f* neighbor_border_direction = border_directions_[index2];
00504           if (neighbor_border_direction==NULL || index2==index)
00505             continue;
00506           
00507           
00508           float cos_angle = neighbor_border_direction->dot(*border_direction);
00509           if (cos_angle<min_cos_angle)
00510           {
00511             
00512             continue;
00513           }
00514           
00515             
00516           
00517           
00518           float border_between_points_score = getNeighborDistanceChangeScore(*surface_structure_[index], x, y, x2-x,  y2-y, 1);
00519           if (fabsf(border_between_points_score) >= 0.95f*parameters_.minimum_border_probability)
00520             continue;
00521           
00522           *average_border_direction += *neighbor_border_direction;
00523           weight_sum += 1.0f;
00524         }
00525       }
00526       if (pcl_lrint (weight_sum) < minimum_weight)
00527       {
00528         delete average_border_direction;
00529         average_border_direction=NULL;
00530       }
00531       else
00532         average_border_direction->normalize();
00533     }
00534   }
00535   
00536   for (int i=0; i<size; ++i)
00537     delete border_directions_[i];
00538   delete[] border_directions_;
00539   border_directions_ = average_border_directions;
00540 }
00541 
00543 void 
00544 RangeImageBorderExtractor::calculateSurfaceChanges ()
00545 {
00546   if (surface_change_scores_!=NULL)
00547     return;
00548   
00549   calculateBorderDirections();
00550   
00551   
00552   
00553   int width  = range_image_->width,
00554       height = range_image_->height,
00555       size   = width*height;
00556   surface_change_scores_ = new float[size];
00557   surface_change_directions_ = new Eigen::Vector3f[size];
00558 # pragma omp parallel for num_threads(parameters_.max_no_of_threads) default(shared) schedule(dynamic, 10)
00559   for (int y=0; y<height; ++y)
00560   {
00561     for (int x=0; x<width; ++x)
00562     {
00563       int index = y*width + x;
00564       float& surface_change_score = surface_change_scores_[index];
00565       surface_change_score = 0.0f;
00566       Eigen::Vector3f& surface_change_direction = surface_change_directions_[index];
00567       surface_change_direction.setZero();
00568       
00569       const BorderTraits& border_traits = border_descriptions_->points[index].traits;
00570       if (border_traits[BORDER_TRAIT__VEIL_POINT] || border_traits[BORDER_TRAIT__SHADOW_BORDER])
00571         continue;
00572       if (border_directions_[index]!=NULL)
00573       {
00574         surface_change_score = 1.0f;
00575         surface_change_direction = *border_directions_[index];
00576       }
00577       else
00578       {
00579         if (!calculateMainPrincipalCurvature(x, y, parameters_.pixel_radius_principal_curvature,
00580                                              surface_change_score, surface_change_direction))
00581         {
00582           surface_change_score = 0.0f;
00583           continue;
00584         }
00585       }
00586     }
00587   }
00588   
00589 }
00590 
00592 void 
00593 RangeImageBorderExtractor::blurSurfaceChanges ()
00594 {
00595   int blur_radius = 1;
00596   if (blur_radius==0)
00597     return;
00598   
00599   const RangeImage& range_image = *range_image_;
00600   
00601   Eigen::Vector3f* blurred_directions = new Eigen::Vector3f[range_image.width*range_image.height];
00602   float* blurred_scores = new float[range_image.width*range_image.height];
00603   for (int y=0; y<int(range_image.height); ++y)
00604   {
00605     for (int x=0; x<int(range_image.width); ++x)
00606     {
00607       int index = y*range_image.width + x;
00608       Eigen::Vector3f& new_point = blurred_directions[index];
00609       new_point.setZero();
00610       float& new_score = blurred_scores[index];
00611       new_score = 0.0f;
00612       if (!range_image.isValid(index))
00613         continue;
00614       const BorderTraits& border_traits = border_descriptions_->points[index].traits;
00615       if (border_traits[BORDER_TRAIT__VEIL_POINT] || border_traits[BORDER_TRAIT__SHADOW_BORDER])
00616         continue;
00617       const Eigen::Vector3f& point = surface_change_directions_[index];
00618       float counter = 0.0f;
00619       for (int y2=y-blur_radius; y2<y+blur_radius; ++y2)
00620       {
00621         for (int x2=x-blur_radius; x2<x+blur_radius; ++x2)
00622         {
00623           if (!range_image.isInImage(x2,y2))
00624             continue;
00625           int index2 = y2*range_image.width + x2;
00626           float score = surface_change_scores_[index2];
00627           
00628             
00629             
00630           if (score > 0.0f)
00631           {
00632             Eigen::Vector3f& neighbor = surface_change_directions_[index2];
00633             
00634               
00635             if (point.dot(neighbor)<0.0f)
00636               neighbor *= -1.0f;
00637             new_point += score*neighbor;
00638           }
00639           new_score += score;
00640           counter += 1.0f;
00641         }
00642       }
00643       new_point.normalize();
00644       if (counter > 0.0f)
00645         new_score /= counter;
00646     }
00647   }
00648   delete[] surface_change_directions_;
00649   surface_change_directions_ = blurred_directions;
00650   delete[] surface_change_scores_;
00651   surface_change_scores_ = blurred_scores;
00652 }
00653 
00655 void 
00656 RangeImageBorderExtractor::computeFeature (PointCloudOut& output)
00657 {
00658   output.points.clear();
00659   
00660   if (indices_)
00661   {
00662     std::cerr << __PRETTY_FUNCTION__
00663               << ": Sorry, usage of indices for the extraction is not supported for range image border extraction (yet).\n\n";
00664     output.width = output.height = 0;
00665     output.points.clear ();
00666     return;
00667   }
00668   
00669   if (range_image_==NULL)
00670   {
00671     std::cerr << __PRETTY_FUNCTION__
00672               << ": RangeImage is not set. Sorry, the border extraction works on range images, not on normal point clouds."
00673               << " Use setRangeImage(...).\n\n";
00674     output.width = output.height = 0;
00675     output.points.clear ();
00676     return;
00677   }
00678   output = getBorderDescriptions ();
00679 }
00680 
00682 void 
00683 RangeImageBorderExtractor::compute (PointCloudOut& output)
00684 {
00685   computeFeature (output);
00686 }
00687 
00688 }