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 #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
00062 clearData ();
00063 }
00064
00066 void
00067 NarfKeypoint::clearData ()
00068 {
00069
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
00106
00107 if (range_image_border_extractor_ == NULL || !range_image_border_extractor_->hasRangeImage () ||
00108 !border_extractor_scale_space_.empty ())
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 {
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
00147
00148
00149
00150
00151
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
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 }
00249
00250 void
00251 NarfKeypoint::calculateInterestImage ()
00252 {
00253
00254
00255 if (interest_image_!=NULL)
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
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
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
00295 }
00296
00297
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
00318
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)
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
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)
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)
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
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
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
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
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)
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)
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
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
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
00631
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
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
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
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
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&¶meters_.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;
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;
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
00956 detectKeypoints (output);
00957 }
00958
00959 }