Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
flann_search.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: flann_search.hpp 5170 2012-03-18 04:21:56Z rusu $
37  *
38  */
39 
40 #ifndef PCL_SEARCH_IMPL_FLANN_SEARCH_H_
41 #define PCL_SEARCH_IMPL_FLANN_SEARCH_H_
42 
44 #include <pcl/kdtree/flann.h>
45 
47 template <typename PointT, typename FlannDistance>
48 typename pcl::search::FlannSearch<PointT, FlannDistance>::IndexPtr
50 {
51  return (IndexPtr (new flann::KDTreeSingleIndex<FlannDistance> (*data,flann::KDTreeSingleIndexParams (max_leaf_size_))));
52 }
53 
54 
56 template <typename PointT, typename FlannDistance>
57 pcl::search::FlannSearch<PointT, FlannDistance>::FlannSearch(bool sorted, FlannIndexCreator *creator) : pcl::search::Search<PointT> ("FlannSearch",sorted),
58  creator_ (creator), eps_ (0), input_copied_for_flann_ (false)
59 {
60  point_representation_.reset (new DefaultPointRepresentation<PointT>);
61  dim_ = point_representation_->getNumberOfDimensions ();
62 }
63 
65 template <typename PointT, typename FlannDistance>
67 {
68  delete creator_;
69  if (input_copied_for_flann_)
70  delete [] input_flann_->ptr();
71 }
72 
74 template <typename PointT, typename FlannDistance> void
75 pcl::search::FlannSearch<PointT, FlannDistance>::setInputCloud (const PointCloudConstPtr& cloud, const IndicesConstPtr& indices)
76 {
77  input_ = cloud;
78  indices_ = indices;
79  convertInputToFlannMatrix ();
80  index_ = creator_->createIndex (input_flann_);
81  index_->buildIndex ();
82 }
83 
85 template <typename PointT, typename FlannDistance> int
86 pcl::search::FlannSearch<PointT, FlannDistance>::nearestKSearch (const PointT &point, int k, std::vector<int> &indices, std::vector<float> &dists) const
87 {
88  assert (point_representation_->isValid (point) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!"); // remove this check as soon as FLANN does NaN checks internally
89  bool can_cast = point_representation_->isTrivial ();
90 
91  float* data = 0;
92  if (!can_cast)
93  {
94  data = new float [point_representation_->getNumberOfDimensions ()];
95  point_representation_->vectorize (point,data);
96  }
97 
98  float* cdata = can_cast ? const_cast<float*> (reinterpret_cast<const float*> (&point)): data;
99  const flann::Matrix<float> m (cdata ,1, point_representation_->getNumberOfDimensions ());
100 
101  flann::SearchParams p(-1);
102  p.eps = eps_;
103  p.sorted = sorted_results_;
104  if (indices.size() != static_cast<unsigned int> (k))
105  indices.resize (k,-1);
106  if (dists.size() != static_cast<unsigned int> (k))
107  dists.resize (k);
108  flann::Matrix<int> i (&indices[0],1,k);
109  flann::Matrix<float> d (&dists[0],1,k);
110  int result = index_->knnSearch (m,i,d,k, p);
111 
112  delete [] data;
113 
114  if (!identity_mapping_)
115  {
116  for (size_t i = 0; i < static_cast<unsigned int> (k); ++i)
117  {
118  int& neighbor_index = indices[i];
119  neighbor_index = index_mapping_[neighbor_index];
120  }
121  }
122  return result;
123 }
124 
126 template <typename PointT, typename FlannDistance> void
128  const PointCloud& cloud, const std::vector<int>& indices, int k, std::vector< std::vector<int> >& k_indices,
129  std::vector< std::vector<float> >& k_sqr_distances) const
130 {
131  if (indices.empty ())
132  {
133  k_indices.resize (cloud.size ());
134  k_sqr_distances.resize (cloud.size ());
135 
136  if (! cloud.is_dense) // remove this check as soon as FLANN does NaN checks internally
137  {
138  for (size_t i = 0; i < cloud.size(); i++)
139  {
140  assert (point_representation_->isValid (cloud[i]) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
141  }
142  }
143 
144  bool can_cast = point_representation_->isTrivial ();
145 
146  // full point cloud + trivial copy operation = no need to do any conversion/copying to the flann matrix!
147  float* data=0;
148  if (!can_cast)
149  {
150  data = new float[dim_*cloud.size ()];
151  for (size_t i = 0; i < cloud.size (); ++i)
152  {
153  float* out = data+i*dim_;
154  point_representation_->vectorize (cloud[i],out);
155  }
156  }
157 
158  // const cast is evil, but the matrix constructor won't change the data, and the
159  // search won't change the matrix
160  float* cdata = can_cast ? const_cast<float*> (reinterpret_cast<const float*> (&cloud[0])): data;
161  const flann::Matrix<float> m (cdata ,cloud.size (), dim_, can_cast ? sizeof (PointT) : dim_ * sizeof (float) );
162 
163  flann::SearchParams p;
164  p.sorted = sorted_results_;
165  p.eps = eps_;
166  index_->knnSearch (m,k_indices,k_sqr_distances,k, p);
167 
168  delete [] data;
169  }
170  else // if indices are present, the cloud has to be copied anyway. Only copy the relevant parts of the points here.
171  {
172  k_indices.resize (indices.size ());
173  k_sqr_distances.resize (indices.size ());
174 
175  if (! cloud.is_dense) // remove this check as soon as FLANN does NaN checks internally
176  {
177  for (size_t i = 0; i < indices.size(); i++)
178  {
179  assert (point_representation_->isValid (cloud [indices[i]]) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
180  }
181  }
182 
183  float* data=new float [dim_*indices.size ()];
184  for (size_t i = 0; i < indices.size (); ++i)
185  {
186  float* out = data+i*dim_;
187  point_representation_->vectorize (cloud[indices[i]],out);
188  }
189  const flann::Matrix<float> m (data ,indices.size (), point_representation_->getNumberOfDimensions ());
190 
191  flann::SearchParams p;
192  p.sorted = sorted_results_;
193  p.eps = eps_;
194  index_->knnSearch (m,k_indices,k_sqr_distances,k, p);
195 
196  delete[] data;
197  }
198  if (!identity_mapping_)
199  {
200  for (size_t j = 0; j < k_indices.size (); ++j)
201  {
202  for (size_t i = 0; i < static_cast<unsigned int> (k); ++i)
203  {
204  int& neighbor_index = k_indices[j][i];
205  neighbor_index = index_mapping_[neighbor_index];
206  }
207  }
208  }
209 }
210 
212 template <typename PointT, typename FlannDistance> int
214  std::vector<int> &indices, std::vector<float> &distances,
215  unsigned int max_nn) const
216 {
217  assert (point_representation_->isValid (point) && "Invalid (NaN, Inf) point coordinates given to radiusSearch!"); // remove this check as soon as FLANN does NaN checks internally
218  bool can_cast = point_representation_->isTrivial ();
219 
220  float* data = 0;
221  if (!can_cast)
222  {
223  data = new float [point_representation_->getNumberOfDimensions ()];
224  point_representation_->vectorize (point,data);
225  }
226 
227  float* cdata = can_cast ? const_cast<float*> (reinterpret_cast<const float*> (&point)) : data;
228  const flann::Matrix<float> m (cdata ,1, point_representation_->getNumberOfDimensions ());
229 
230  flann::SearchParams p;
231  p.sorted = sorted_results_;
232  p.eps = eps_;
233  p.max_neighbors = max_nn > 0 ? max_nn : -1;
234  std::vector<std::vector<int> > i (1);
235  std::vector<std::vector<float> > d (1);
236  int result = index_->radiusSearch (m,i,d,static_cast<float> (radius * radius), p);
237 
238  delete [] data;
239  indices = i [0];
240  distances = d [0];
241 
242  if (!identity_mapping_)
243  {
244  for (size_t i = 0; i < indices.size (); ++i)
245  {
246  int& neighbor_index = indices [i];
247  neighbor_index = index_mapping_ [neighbor_index];
248  }
249  }
250  return result;
251 }
252 
254 template <typename PointT, typename FlannDistance> void
256  const PointCloud& cloud, const std::vector<int>& indices, double radius, std::vector< std::vector<int> >& k_indices,
257  std::vector< std::vector<float> >& k_sqr_distances, unsigned int max_nn) const
258 {
259  if (indices.empty ()) // full point cloud + trivial copy operation = no need to do any conversion/copying to the flann matrix!
260  {
261  k_indices.resize (cloud.size ());
262  k_sqr_distances.resize (cloud.size ());
263 
264  if (! cloud.is_dense) // remove this check as soon as FLANN does NaN checks internally
265  {
266  for (size_t i = 0; i < cloud.size(); i++)
267  {
268  assert (point_representation_->isValid (cloud[i]) && "Invalid (NaN, Inf) point coordinates given to radiusSearch!");
269  }
270  }
271 
272  bool can_cast = point_representation_->isTrivial ();
273 
274  float* data = 0;
275  if (!can_cast)
276  {
277  data = new float[dim_*cloud.size ()];
278  for (size_t i = 0; i < cloud.size (); ++i)
279  {
280  float* out = data+i*dim_;
281  point_representation_->vectorize (cloud[i],out);
282  }
283  }
284 
285  float* cdata = can_cast ? const_cast<float*> (reinterpret_cast<const float*> (&cloud[0])) : data;
286  const flann::Matrix<float> m (cdata ,cloud.size (), dim_, can_cast ? sizeof (PointT) : dim_ * sizeof (float));
287 
288  flann::SearchParams p;
289  p.sorted = sorted_results_;
290  p.eps = eps_;
291  // here: max_nn==0: take all neighbors. flann: max_nn==0: return no neighbors, only count them. max_nn==-1: return all neighbors
292  p.max_neighbors = max_nn > 0 ? max_nn : -1;
293  index_->radiusSearch (m,k_indices,k_sqr_distances,static_cast<float> (radius * radius), p);
294 
295  delete [] data;
296  }
297  else // if indices are present, the cloud has to be copied anyway. Only copy the relevant parts of the points here.
298  {
299  k_indices.resize (indices.size ());
300  k_sqr_distances.resize (indices.size ());
301 
302  if (! cloud.is_dense) // remove this check as soon as FLANN does NaN checks internally
303  {
304  for (size_t i = 0; i < indices.size(); i++)
305  {
306  assert (point_representation_->isValid (cloud [indices[i]]) && "Invalid (NaN, Inf) point coordinates given to radiusSearch!");
307  }
308  }
309 
310  float* data = new float [dim_ * indices.size ()];
311  for (size_t i = 0; i < indices.size (); ++i)
312  {
313  float* out = data+i*dim_;
314  point_representation_->vectorize (cloud[indices[i]], out);
315  }
316  const flann::Matrix<float> m (data, cloud.size (), point_representation_->getNumberOfDimensions ());
317 
318  flann::SearchParams p;
319  p.sorted = sorted_results_;
320  p.eps = eps_;
321  // here: max_nn==0: take all neighbors. flann: max_nn==0: return no neighbors, only count them. max_nn==-1: return all neighbors
322  p.max_neighbors = max_nn > 0 ? max_nn : -1;
323  index_->radiusSearch (m, k_indices, k_sqr_distances, static_cast<float> (radius * radius), p);
324 
325  delete[] data;
326  }
327  if (!identity_mapping_)
328  {
329  for (size_t j = 0; j < k_indices.size (); ++j )
330  {
331  for (size_t i = 0; i < k_indices[j].size (); ++i)
332  {
333  int& neighbor_index = k_indices[j][i];
334  neighbor_index = index_mapping_[neighbor_index];
335  }
336  }
337  }
338 }
339 
341 template <typename PointT, typename FlannDistance> void
343 {
344  size_t original_no_of_points = indices_ && !indices_->empty () ? indices_->size () : input_->size ();
345 
346  if (input_copied_for_flann_)
347  delete input_flann_->ptr();
348  input_copied_for_flann_ = true;
349  index_mapping_.clear();
350  identity_mapping_ = true;
351 
352  //cloud_ = (float*)malloc (original_no_of_points * dim_ * sizeof (float));
353  //index_mapping_.reserve(original_no_of_points);
354  //identity_mapping_ = true;
355 
356  if (!indices_ || indices_->empty ())
357  {
358  // best case: all points can be passed to flann without any conversions
359  if (input_->is_dense && point_representation_->isTrivial ())
360  {
361  // const cast is evil, but flann won't change the data
362  input_flann_ = MatrixPtr (new flann::Matrix<float> (const_cast<float*>(reinterpret_cast<const float*>(&(*input_) [0])), original_no_of_points, point_representation_->getNumberOfDimensions (),sizeof (PointT)));
363  input_copied_for_flann_ = false;
364  }
365  else
366  {
367  input_flann_ = MatrixPtr (new flann::Matrix<float> (new float[original_no_of_points*point_representation_->getNumberOfDimensions ()], original_no_of_points, point_representation_->getNumberOfDimensions ()));
368  float* cloud_ptr = input_flann_->ptr();
369  for (size_t i = 0; i < original_no_of_points; ++i)
370  {
371  const PointT& point = (*input_)[i];
372  // Check if the point is invalid
373  if (!point_representation_->isValid (point))
374  {
375  identity_mapping_ = false;
376  continue;
377  }
378 
379  index_mapping_.push_back (static_cast<int> (i)); // If the returned index should be for the indices vector
380 
381  point_representation_->vectorize (point, cloud_ptr);
382  cloud_ptr += dim_;
383  }
384  }
385 
386  }
387  else
388  {
389  input_flann_ = MatrixPtr (new flann::Matrix<float> (new float[original_no_of_points*point_representation_->getNumberOfDimensions ()], original_no_of_points, point_representation_->getNumberOfDimensions ()));
390  float* cloud_ptr = input_flann_->ptr();
391  for (size_t indices_index = 0; indices_index < original_no_of_points; ++indices_index)
392  {
393  int cloud_index = (*indices_)[indices_index];
394  const PointT& point = (*input_)[cloud_index];
395  // Check if the point is invalid
396  if (!point_representation_->isValid (point))
397  {
398  identity_mapping_ = false;
399  continue;
400  }
401 
402  index_mapping_.push_back (static_cast<int> (indices_index)); // If the returned index should be for the indices vector
403 
404  point_representation_->vectorize (point, cloud_ptr);
405  cloud_ptr += dim_;
406  }
407  }
408  if (input_copied_for_flann_)
409  input_flann_->rows = index_mapping_.size ();
410 }
411 
412 #define PCL_INSTANTIATE_FlannSearch(T) template class PCL_EXPORTS pcl::search::FlannSearch<T>;
413 
414 #endif