Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
3dsc.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2010-2011, Willow Garage, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of Willow Garage, Inc. nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * $Id: 3dsc.hpp 4961 2012-03-07 23:44:07Z rusu $
37  *
38  */
39 
40 #ifndef PCL_FEATURES_IMPL_3DSC_HPP_
41 #define PCL_FEATURES_IMPL_3DSC_HPP_
42 
43 #include <cmath>
44 #include <pcl/features/3dsc.h>
45 #include <pcl/common/utils.h>
46 #include <pcl/common/geometry.h>
47 #include <pcl/common/angles.h>
48 
50 template <typename PointInT, typename PointNT, typename PointOutT> bool
52 {
53  if (!FeatureFromNormals<PointInT, PointNT, PointOutT>::initCompute ())
54  {
55  PCL_ERROR ("[pcl::%s::initCompute] Init failed.\n", getClassName ().c_str ());
56  return (false);
57  }
58 
59  if (search_radius_< min_radius_)
60  {
61  PCL_ERROR ("[pcl::%s::initCompute] search_radius_ must be GREATER than min_radius_.\n", getClassName ().c_str ());
62  return (false);
63  }
64 
65  // Update descriptor length
66  descriptor_length_ = elevation_bins_ * azimuth_bins_ * radius_bins_;
67 
68  // Compute radial, elevation and azimuth divisions
69  float azimuth_interval = 360.0f / static_cast<float> (azimuth_bins_);
70  float elevation_interval = 180.0f / static_cast<float> (elevation_bins_);
71 
72  // Reallocate divisions and volume lut
73  radii_interval_.clear ();
74  phi_divisions_.clear ();
75  theta_divisions_.clear ();
76  volume_lut_.clear ();
77 
78  // Fills radii interval based on formula (1) in section 2.1 of Frome's paper
79  radii_interval_.resize (radius_bins_ + 1);
80  for (size_t j = 0; j < radius_bins_ + 1; j++)
81  radii_interval_[j] = static_cast<float> (exp (log (min_radius_) + ((static_cast<float> (j) / static_cast<float> (radius_bins_)) * log (search_radius_ / min_radius_))));
82 
83  // Fill theta divisions of elevation
84  theta_divisions_.resize (elevation_bins_ + 1);
85  for (size_t k = 0; k < elevation_bins_ + 1; k++)
86  theta_divisions_[k] = static_cast<float> (k) * elevation_interval;
87 
88  // Fill phi didvisions of elevation
89  phi_divisions_.resize (azimuth_bins_ + 1);
90  for (size_t l = 0; l < azimuth_bins_ + 1; l++)
91  phi_divisions_[l] = static_cast<float> (l) * azimuth_interval;
92 
93  // LookUp Table that contains the volume of all the bins
94  // "phi" term of the volume integral
95  // "integr_phi" has always the same value so we compute it only one time
96  float integr_phi = pcl::deg2rad (phi_divisions_[1]) - pcl::deg2rad (phi_divisions_[0]);
97  // exponential to compute the cube root using pow
98  float e = 1.0f / 3.0f;
99  // Resize volume look up table
100  volume_lut_.resize (radius_bins_ * elevation_bins_ * azimuth_bins_);
101  // Fill volumes look up table
102  for (size_t j = 0; j < radius_bins_; j++)
103  {
104  // "r" term of the volume integral
105  float integr_r = (radii_interval_[j+1] * radii_interval_[j+1] * radii_interval_[j+1] / 3.0f) - (radii_interval_[j] * radii_interval_[j] * radii_interval_[j] / 3.0f);
106 
107  for (size_t k = 0; k < elevation_bins_; k++)
108  {
109  // "theta" term of the volume integral
110  float integr_theta = cosf (pcl::deg2rad (theta_divisions_[k])) - cosf (pcl::deg2rad (theta_divisions_[k+1]));
111  // Volume
112  float V = integr_phi * integr_theta * integr_r;
113  // Compute cube root of the computed volume commented for performance but left
114  // here for clarity
115  // float cbrt = pow(V, e);
116  // cbrt = 1 / cbrt;
117 
118  for (size_t l = 0; l < azimuth_bins_; l++)
119  {
120  // Store in lut 1/cbrt
121  //volume_lut_[ (l*elevation_bins_*radius_bins_) + k*radius_bins_ + j ] = cbrt;
122  volume_lut_[(l*elevation_bins_*radius_bins_) + k*radius_bins_ + j] = 1.0f / powf (V, e);
123  }
124  }
125  }
126  return (true);
127 }
128 
130 template <typename PointInT, typename PointNT, typename PointOutT> bool
132  size_t index, const pcl::PointCloud<PointNT> &normals, float rf[9], std::vector<float> &desc)
133 {
134  // The RF is formed as this x_axis | y_axis | normal
135  Eigen::Map<Eigen::Vector3f> x_axis (rf);
136  Eigen::Map<Eigen::Vector3f> y_axis (rf + 3);
137  Eigen::Map<Eigen::Vector3f> normal (rf + 6);
138 
139  // Find every point within specified search_radius_
140  std::vector<int> nn_indices;
141  std::vector<float> nn_dists;
142  const size_t neighb_cnt = searchForNeighbors ((*indices_)[index], search_radius_, nn_indices, nn_dists);
143  if (neighb_cnt == 0)
144  {
145  for (size_t i = 0; i < desc.size (); ++i)
146  desc[i] = std::numeric_limits<float>::quiet_NaN ();
147 
148  memset (rf, 0, sizeof (rf[0]) * 9);
149  return (false);
150  }
151 
152  float minDist = std::numeric_limits<float>::max ();
153  int minIndex = -1;
154  for (size_t i = 0; i < nn_indices.size (); i++)
155  {
156  if (nn_dists[i] < minDist)
157  {
158  minDist = nn_dists[i];
159  minIndex = nn_indices[i];
160  }
161  }
162 
163  // Get origin point
164  Vector3fMapConst origin = input_->points[(*indices_)[index]].getVector3fMap ();
165  // Get origin normal
166  // Use pre-computed normals
167  normal = normals[minIndex].getNormalVector3fMap ();
168 
169  // Compute and store the RF direction
170  x_axis[0] = static_cast<float> (rnd ());
171  x_axis[1] = static_cast<float> (rnd ());
172  x_axis[2] = static_cast<float> (rnd ());
173  if (!pcl::utils::equal (normal[2], 0.0f))
174  x_axis[2] = - (normal[0]*x_axis[0] + normal[1]*x_axis[1]) / normal[2];
175  else if (!pcl::utils::equal (normal[1], 0.0f))
176  x_axis[1] = - (normal[0]*x_axis[0] + normal[2]*x_axis[2]) / normal[1];
177  else if (!pcl::utils::equal (normal[0], 0.0f))
178  x_axis[0] = - (normal[1]*x_axis[1] + normal[2]*x_axis[2]) / normal[0];
179 
180  x_axis.normalize ();
181 
182  // Check if the computed x axis is orthogonal to the normal
183  assert (pcl::utils::equal (x_axis[0]*normal[0] + x_axis[1]*normal[1] + x_axis[2]*normal[2], 0.0f, 1E-6f));
184 
185  // Store the 3rd frame vector
186  y_axis = normal.cross (x_axis);
187 
188  // For each point within radius
189  for (size_t ne = 0; ne < neighb_cnt; ne++)
190  {
191  if (pcl::utils::equal (nn_dists[ne], 0.0f))
192  continue;
193  // Get neighbours coordinates
194  Eigen::Vector3f neighbour = surface_->points[nn_indices[ne]].getVector3fMap ();
195 
198  float r = sqrt (nn_dists[ne]);
199 
201  Eigen::Vector3f proj;
202  pcl::geometry::project (neighbour, origin, normal, proj);
203  proj -= origin;
204 
206  proj.normalize ();
207 
209  Eigen::Vector3f cross = x_axis.cross (proj);
210  float phi = pcl::rad2deg (std::atan2 (cross.norm (), x_axis.dot (proj)));
211  phi = cross.dot (normal) < 0.f ? (360.0f - phi) : phi;
213  Eigen::Vector3f no = neighbour - origin;
214  no.normalize ();
215  float theta = normal.dot (no);
216  theta = pcl::rad2deg (acosf (std::min (1.0f, std::max (-1.0f, theta))));
217 
218  // Bin (j, k, l)
219  size_t j = 0;
220  size_t k = 0;
221  size_t l = 0;
222 
223  // Compute the Bin(j, k, l) coordinates of current neighbour
224  for (size_t rad = 1; rad < radius_bins_+1; rad++)
225  {
226  if (r <= radii_interval_[rad])
227  {
228  j = rad-1;
229  break;
230  }
231  }
232 
233  for (size_t ang = 1; ang < elevation_bins_+1; ang++)
234  {
235  if (theta <= theta_divisions_[ang])
236  {
237  k = ang-1;
238  break;
239  }
240  }
241 
242  for (size_t ang = 1; ang < azimuth_bins_+1; ang++)
243  {
244  if (phi <= phi_divisions_[ang])
245  {
246  l = ang-1;
247  break;
248  }
249  }
250 
251  // Local point density = number of points in a sphere of radius "point_density_radius_" around the current neighbour
252  std::vector<int> neighbour_indices;
253  std::vector<float> neighbour_distances;
254  int point_density = searchForNeighbors (*surface_, nn_indices[ne], point_density_radius_, neighbour_indices, neighbour_distances);
255  // point_density is NOT always bigger than 0 (on error, searchForNeighbors returns 0), so we must check for that
256  if (point_density == 0)
257  continue;
258 
259  float w = (1.0f / static_cast<float> (point_density)) *
260  volume_lut_[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j];
261 
262  assert (w >= 0.0);
263  if (w == std::numeric_limits<float>::infinity ())
264  PCL_ERROR ("Shape Context Error INF!\n");
265  if (w != w)
266  PCL_ERROR ("Shape Context Error IND!\n");
268  desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] += w;
269 
270  assert (desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] >= 0);
271  } // end for each neighbour
272 
273  // 3DSC does not define a repeatable local RF, we set it to zero to signal it to the user
274  memset (rf, 0, sizeof (rf[0]) * 9);
275  return (true);
276 }
277 
279 template <typename PointInT, typename PointNT, typename PointOutT> void
281  size_t block_size, std::vector<float>& desc)
282 {
283  assert (desc.size () == descriptor_length_);
284  // L rotations for each descriptor
285  desc.resize (descriptor_length_ * azimuth_bins_);
286  // Create L azimuth rotated descriptors from reference descriptor
287  // The descriptor_length_ first ones are the same so start at 1
288  for (size_t l = 1; l < azimuth_bins_; l++)
289  for (size_t bin = 0; bin < descriptor_length_; bin++)
290  desc[(l * descriptor_length_) + bin] = desc[(l*block_size + bin) % descriptor_length_];
291 }
292 
294 template <typename PointInT, typename PointNT, typename PointOutT> void
296 {
297  output.is_dense = true;
298  // Iterate over all points and compute the descriptors
299  for (size_t point_index = 0; point_index < indices_->size (); point_index++)
300  {
301  output[point_index].descriptor.resize (descriptor_length_);
302 
303  // If the point is not finite, set the descriptor to NaN and continue
304  if (!isFinite ((*input_)[(*indices_)[point_index]]))
305  {
306  for (size_t i = 0; i < descriptor_length_; ++i)
307  output[point_index].descriptor[i] = std::numeric_limits<float>::quiet_NaN ();
308 
309  memset (output[point_index].rf, 0, sizeof (output[point_index].rf[0]) * 9);
310  output.is_dense = false;
311  continue;
312  }
313 
314  if (!computePoint (point_index, *normals_, output[point_index].rf, output[point_index].descriptor))
315  output.is_dense = false;
316  }
317 }
318 
320 template <typename PointInT, typename PointNT> void
323 {
324 
325  // Set up the output channels
326  output.channels["3dsc"].name = "3dsc";
327  output.channels["3dsc"].offset = 0;
328  output.channels["3dsc"].size = 4;
329  output.channels["3dsc"].count = static_cast<uint32_t> (descriptor_length_) + 9;
330  output.channels["3dsc"].datatype = sensor_msgs::PointField::FLOAT32;
331 
332  // Resize the output dataset
333  output.points.resize (indices_->size (), descriptor_length_ + 9);
334 
335  float rf[9];
336 
337  output.is_dense = true;
338  // Iterate over all points and compute the descriptors
339  for (size_t point_index = 0; point_index < indices_->size (); point_index++)
340  {
341  // If the point is not finite, set the descriptor to NaN and continue
342  if (!isFinite ((*input_)[(*indices_)[point_index]]))
343  {
344  output.points.row (point_index).setConstant (std::numeric_limits<float>::quiet_NaN ());
345  output.is_dense = false;
346  continue;
347  }
348 
349  std::vector<float> descriptor (descriptor_length_);
350  if (!this->computePoint (point_index, *normals_, rf, descriptor))
351  output.is_dense = false;
352  for (int j = 0; j < 9; ++j)
353  output.points (point_index, j) = rf[j];
354  for (size_t j = 0; j < descriptor_length_; ++j)
355  output.points (point_index, 9 + j) = descriptor[j];
356  }
357 }
358 
359 #define PCL_INSTANTIATE_ShapeContext3DEstimation(T,NT,OutT) template class PCL_EXPORTS pcl::ShapeContext3DEstimation<T,NT,OutT>;
360 
361 #endif