40 #ifndef PCL_FEATURES_IMPL_BOARD_H_
41 #define PCL_FEATURES_IMPL_BOARD_H_
43 #include <pcl/features/board.h>
47 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
49 Eigen::Vector3f
const &axis,
50 Eigen::Vector3f
const &axis_origin,
51 Eigen::Vector3f
const &point,
52 Eigen::Vector3f &directed_ortho_axis)
54 Eigen::Vector3f projection;
55 projectPointOnPlane (point, axis_origin, axis, projection);
56 directed_ortho_axis = projection - axis_origin;
58 directed_ortho_axis.normalize ();
65 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
67 Eigen::Vector3f
const &point,
68 Eigen::Vector3f
const &origin_point,
69 Eigen::Vector3f
const &plane_normal,
70 Eigen::Vector3f &projected_point)
75 xo = point - origin_point;
76 t = plane_normal.dot (xo);
78 projected_point = point - (t * plane_normal);
82 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
float
84 Eigen::Vector3f
const &v1,
85 Eigen::Vector3f
const &v2,
86 Eigen::Vector3f
const &axis)
88 Eigen::Vector3f angle_orientation;
89 angle_orientation = v1.cross (v2);
90 float angle_radians = std::acos (std::max (-1.0f, std::min (1.0f, v1.dot (v2))));
92 angle_radians = angle_orientation.dot (axis) < 0.f ? (2 *
static_cast<float> (
M_PI) - angle_radians) : angle_radians;
94 return (angle_radians);
98 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
100 Eigen::Vector3f
const &axis,
101 Eigen::Vector3f &rand_ortho_axis)
103 if (!areEquals (axis.z (), 0.0f))
105 rand_ortho_axis.x () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
106 rand_ortho_axis.y () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
107 rand_ortho_axis.z () = -(axis.x () * rand_ortho_axis.x () + axis.y () * rand_ortho_axis.y ()) / axis.z ();
109 else if (!areEquals (axis.y (), 0.0f))
111 rand_ortho_axis.x () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
112 rand_ortho_axis.z () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
113 rand_ortho_axis.y () = -(axis.x () * rand_ortho_axis.x () + axis.z () * rand_ortho_axis.z ()) / axis.y ();
115 else if (!areEquals (axis.x (), 0.0f))
117 rand_ortho_axis.y () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
118 rand_ortho_axis.z () = (
static_cast<float> (rand ()) /
static_cast<float> (RAND_MAX)) * 2.0f - 1.0f;
119 rand_ortho_axis.x () = -(axis.y () * rand_ortho_axis.y () + axis.z () * rand_ortho_axis.z ()) / axis.x ();
122 rand_ortho_axis.normalize ();
129 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
132 Eigen::Dynamic, 3>
const &points,
133 Eigen::Vector3f ¢er,
134 Eigen::Vector3f &norm)
140 const auto n_points = points.rows ();
147 center = points.colwise().mean().transpose();
150 const Eigen::Matrix<float, Eigen::Dynamic, 3> A = points.rowwise() - center.transpose();
152 Eigen::JacobiSVD<Eigen::MatrixXf> svd (A, Eigen::ComputeFullV);
153 norm = svd.matrixV ().col (2);
157 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
161 Eigen::Vector3f &normal)
163 Eigen::Vector3f normal_mean;
164 normal_mean.setZero ();
166 for (
const auto &normal_index : normal_indices)
168 const PointNT& curPt = normal_cloud[normal_index];
170 normal_mean += curPt.getNormalVector3fMap ();
173 normal_mean.normalize ();
175 if (normal.dot (normal_mean) < 0)
182 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
float
184 Eigen::Matrix3f &lrf)
190 std::vector<float> neighbours_distances;
191 std::size_t n_neighbours = this->searchForNeighbors (index, search_parameter_, neighbours_indices, neighbours_distances);
194 if (n_neighbours < 6)
201 lrf.setConstant (std::numeric_limits<float>::quiet_NaN ());
203 return (std::numeric_limits<float>::max ());
207 Eigen::Matrix<float, Eigen::Dynamic, 3> neigh_points_mat (n_neighbours, 3);
208 for (std::size_t i = 0; i < n_neighbours; ++i)
210 neigh_points_mat.row (i) = (*surface_)[neighbours_indices[i]].getVector3fMap ();
213 Eigen::Vector3f x_axis, y_axis;
215 Eigen::Vector3f fitted_normal;
216 Eigen::Vector3f centroid;
217 planeFitting (neigh_points_mat, centroid, fitted_normal);
220 normalDisambiguation (*normals_, neighbours_indices, fitted_normal);
223 lrf.row (2).matrix () = fitted_normal;
229 if (tangent_radius_ != 0.0f && search_parameter_ != tangent_radius_)
231 n_neighbours = this->searchForNeighbors (index, tangent_radius_, neighbours_indices, neighbours_distances);
236 float min_normal_cos = std::numeric_limits<float>::max ();
237 int min_normal_index = -1;
239 bool margin_point_found =
false;
240 Eigen::Vector3f best_margin_point;
241 bool best_point_found_on_margins =
false;
243 const float radius2 = tangent_radius_ * tangent_radius_;
244 const float margin_distance2 = margin_thresh_ * margin_thresh_ * radius2;
246 float max_boundary_angle = 0;
252 lrf.row (0).matrix () = x_axis;
254 check_margin_array_.assign(check_margin_array_size_,
false);
255 margin_array_min_angle_.assign(check_margin_array_size_, std::numeric_limits<float>::max ());
256 margin_array_max_angle_.assign(check_margin_array_size_, -std::numeric_limits<float>::max ());
257 margin_array_min_angle_normal_.assign(check_margin_array_size_, -1.0);
258 margin_array_max_angle_normal_.assign(check_margin_array_size_, -1.0);
260 max_boundary_angle = (2 *
static_cast<float> (
M_PI)) /
static_cast<float> (check_margin_array_size_);
263 for (std::size_t curr_neigh = 0; curr_neigh < n_neighbours; ++curr_neigh)
265 const int& curr_neigh_idx = neighbours_indices[curr_neigh];
266 const float& neigh_distance_sqr = neighbours_distances[curr_neigh];
267 if (neigh_distance_sqr <= margin_distance2)
273 margin_point_found =
true;
275 Eigen::Vector3f normal_mean = normals_->at (curr_neigh_idx).getNormalVector3fMap ();
277 float normal_cos = fitted_normal.dot (normal_mean);
278 if (normal_cos < min_normal_cos)
280 min_normal_index = curr_neigh_idx;
281 min_normal_cos = normal_cos;
282 best_point_found_on_margins =
false;
288 Eigen::Vector3f indicating_normal_vect;
289 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (),
290 surface_->at (curr_neigh_idx).getVector3fMap (), indicating_normal_vect);
291 float angle = getAngleBetweenUnitVectors (x_axis, indicating_normal_vect, fitted_normal);
293 int check_margin_array_idx = std::min (
static_cast<int> (std::floor (angle / max_boundary_angle)), check_margin_array_size_ - 1);
294 check_margin_array_[check_margin_array_idx] =
true;
296 if (angle < margin_array_min_angle_[check_margin_array_idx])
298 margin_array_min_angle_[check_margin_array_idx] = angle;
299 margin_array_min_angle_normal_[check_margin_array_idx] = normal_cos;
301 if (angle > margin_array_max_angle_[check_margin_array_idx])
303 margin_array_max_angle_[check_margin_array_idx] = angle;
304 margin_array_max_angle_normal_[check_margin_array_idx] = normal_cos;
310 if (!margin_point_found)
313 for (std::size_t curr_neigh = 0; curr_neigh < n_neighbours; curr_neigh++)
315 const int& curr_neigh_idx = neighbours_indices[curr_neigh];
316 const float& neigh_distance_sqr = neighbours_distances[curr_neigh];
318 if (neigh_distance_sqr > margin_distance2)
321 Eigen::Vector3f normal_mean = normals_->at (curr_neigh_idx).getNormalVector3fMap ();
323 float normal_cos = fitted_normal.dot (normal_mean);
325 if (normal_cos < min_normal_cos)
327 min_normal_index = curr_neigh_idx;
328 min_normal_cos = normal_cos;
333 if (min_normal_index == -1)
335 lrf.setConstant (std::numeric_limits<float>::quiet_NaN ());
336 return (std::numeric_limits<float>::max ());
339 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (),
340 surface_->at (min_normal_index).getVector3fMap (), x_axis);
341 y_axis = fitted_normal.cross (x_axis);
343 lrf.row (0).matrix () = x_axis;
344 lrf.row (1).matrix () = y_axis;
348 return (min_normal_cos);
353 if (best_point_found_on_margins)
356 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (), best_margin_point, x_axis);
357 y_axis = fitted_normal.cross (x_axis);
359 lrf.row (0).matrix () = x_axis;
360 lrf.row (1).matrix () = y_axis;
363 return (min_normal_cos);
367 if (min_normal_index == -1)
369 lrf.setConstant (std::numeric_limits<float>::quiet_NaN ());
370 return (std::numeric_limits<float>::max ());
373 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (),
374 surface_->at (min_normal_index).getVector3fMap (), x_axis);
375 y_axis = fitted_normal.cross (x_axis);
377 lrf.row (0).matrix () = x_axis;
378 lrf.row (1).matrix () = y_axis;
381 return (min_normal_cos);
385 bool is_hole_present =
false;
386 for (
const auto check_margin: check_margin_array_)
390 is_hole_present =
true;
395 if (!is_hole_present)
397 if (best_point_found_on_margins)
400 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (), best_margin_point, x_axis);
401 y_axis = fitted_normal.cross (x_axis);
403 lrf.row (0).matrix () = x_axis;
404 lrf.row (1).matrix () = y_axis;
407 return (min_normal_cos);
411 if (min_normal_index == -1)
413 lrf.setConstant (std::numeric_limits<float>::quiet_NaN ());
414 return (std::numeric_limits<float>::max ());
418 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (),
419 surface_->at (min_normal_index).getVector3fMap (), x_axis);
420 y_axis = fitted_normal.cross (x_axis);
422 lrf.row (0).matrix () = x_axis;
423 lrf.row (1).matrix () = y_axis;
426 return (min_normal_cos);
435 const auto find_first_no_border_pie = [](
const auto& array) -> std::size_t {
440 const auto result = std::find_if(array.cbegin (), array.cend (),
441 [](
const auto& x) ->
bool { return x;});
444 const auto first_no_border = find_first_no_border_pie(check_margin_array_);
447 float max_hole_prob = -std::numeric_limits<float>::max ();
450 for (
auto ch = first_no_border; ch < static_cast<std::size_t>(check_margin_array_size_); ch++)
452 if (!check_margin_array_[ch])
456 hole_end = hole_first + 1;
457 while (!check_margin_array_[hole_end % check_margin_array_size_])
463 if ((hole_end - hole_first) > 0)
466 int previous_hole = (((hole_first - 1) < 0) ? (hole_first - 1) + check_margin_array_size_ : (hole_first - 1))
467 % check_margin_array_size_;
468 int following_hole = (hole_end) % check_margin_array_size_;
469 float normal_begin = margin_array_max_angle_normal_[previous_hole];
470 float normal_end = margin_array_min_angle_normal_[following_hole];
471 normal_begin -= min_normal_cos;
472 normal_end -= min_normal_cos;
473 normal_begin = normal_begin / (1.0f - min_normal_cos);
474 normal_end = normal_end / (1.0f - min_normal_cos);
475 normal_begin = 1.0f - normal_begin;
476 normal_end = 1.0f - normal_end;
479 float hole_width = 0.0f;
480 if (following_hole < previous_hole)
482 hole_width = margin_array_min_angle_[following_hole] + 2 *
static_cast<float> (
M_PI)
483 - margin_array_max_angle_[previous_hole];
487 hole_width = margin_array_min_angle_[following_hole] - margin_array_max_angle_[previous_hole];
489 float hole_prob = hole_width / (2 *
static_cast<float> (
M_PI));
492 float steep_prob = (normal_end + normal_begin) / 2.0f;
496 if (hole_prob > hole_size_prob_thresh_)
498 if (steep_prob > steep_thresh_)
500 if (hole_prob > max_hole_prob)
502 max_hole_prob = hole_prob;
504 float angle_weight = ((normal_end - normal_begin) + 1.0f) / 2.0f;
505 if (following_hole < previous_hole)
507 angle = margin_array_max_angle_[previous_hole] + (margin_array_min_angle_[following_hole] + 2
508 *
static_cast<float> (
M_PI) - margin_array_max_angle_[previous_hole]) * angle_weight;
512 angle = margin_array_max_angle_[previous_hole] + (margin_array_min_angle_[following_hole]
513 - margin_array_max_angle_[previous_hole]) * angle_weight;
520 if (hole_end >= check_margin_array_size_)
528 if (max_hole_prob > -std::numeric_limits<float>::max ())
531 Eigen::AngleAxisf rotation = Eigen::AngleAxisf (angle, fitted_normal);
532 x_axis = rotation * x_axis;
534 min_normal_cos -= 10.0f;
538 if (best_point_found_on_margins)
541 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (), best_margin_point, x_axis);
546 if (min_normal_index == -1)
548 lrf.setConstant (std::numeric_limits<float>::quiet_NaN ());
549 return (std::numeric_limits<float>::max ());
553 directedOrthogonalAxis (fitted_normal, input_->at (index).getVector3fMap (),
554 surface_->at (min_normal_index).getVector3fMap (), x_axis);
558 y_axis = fitted_normal.cross (x_axis);
560 lrf.row (0).matrix () = x_axis;
561 lrf.row (1).matrix () = y_axis;
564 return (min_normal_cos);
568 template<
typename Po
intInT,
typename Po
intNT,
typename Po
intOutT>
void
572 if (this->getKSearch () != 0)
575 "[pcl::%s::computeFeature] Error! Search method set to k-neighborhood. Call setKSearch(0) and setRadiusSearch( radius ) to use this class.\n",
576 getClassName().c_str());
581 for (std::size_t point_idx = 0; point_idx < indices_->size (); ++point_idx)
583 Eigen::Matrix3f currentLrf;
584 PointOutT &rf = output[point_idx];
588 if (computePointLRF ((*indices_)[point_idx], currentLrf) == std::numeric_limits<float>::max ())
590 output.is_dense =
false;
593 for (
int d = 0; d < 3; ++d)
595 rf.x_axis[d] = currentLrf (0, d);
596 rf.y_axis[d] = currentLrf (1, d);
597 rf.z_axis[d] = currentLrf (2, d);
602 #define PCL_INSTANTIATE_BOARDLocalReferenceFrameEstimation(T,NT,OutT) template class PCL_EXPORTS pcl::BOARDLocalReferenceFrameEstimation<T,NT,OutT>;
void projectPointOnPlane(Eigen::Vector3f const &point, Eigen::Vector3f const &origin_point, Eigen::Vector3f const &plane_normal, Eigen::Vector3f &projected_point)
Given a plane (origin and normal) and a point, return the projection of x on plane.
void directedOrthogonalAxis(Eigen::Vector3f const &axis, Eigen::Vector3f const &axis_origin, Eigen::Vector3f const &point, Eigen::Vector3f &directed_ortho_axis)
Given an axis (with origin axis_origin), return the orthogonal axis directed to point.
float computePointLRF(const int &index, Eigen::Matrix3f &lrf)
Estimate the LRF descriptor for a given point based on its spatial neighborhood of 3D points with nor...
void planeFitting(Eigen::Matrix< float, Eigen::Dynamic, 3 > const &points, Eigen::Vector3f ¢er, Eigen::Vector3f &norm)
Compute Least Square Plane Fitting in a set of 3D points.
typename Feature< PointInT, PointOutT >::PointCloudOut PointCloudOut
float getAngleBetweenUnitVectors(Eigen::Vector3f const &v1, Eigen::Vector3f const &v2, Eigen::Vector3f const &axis)
return the angle (in radians) that rotate v1 to v2 with respect to axis .
void normalDisambiguation(pcl::PointCloud< PointNT > const &normals_cloud, pcl::Indices const &normal_indices, Eigen::Vector3f &normal)
Disambiguates a normal direction using adjacent normals.
void computeFeature(PointCloudOut &output) override
Abstract feature estimation method.
void randomOrthogonalAxis(Eigen::Vector3f const &axis, Eigen::Vector3f &rand_ortho_axis)
Given an axis, return a random orthogonal axis.
Eigen::Vector3f randomOrthogonalAxis(Eigen::Vector3f const &axis)
Define a random unit vector orthogonal to axis.
float distance(const PointT &p1, const PointT &p2)
IndicesAllocator<> Indices
Type used for indices in PCL.