Point Cloud Library (PCL)  1.5.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
organized.hpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Point Cloud Library (PCL) - www.pointclouds.org
00005  *  Copyright (c) 2010-2011, Willow Garage, Inc.
00006  *
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of Willow Garage, Inc. nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  * $Id: organized.hpp 4702 2012-02-23 09:39:33Z gedikli $
00037  *
00038  */
00039 
00040 #ifndef PCL_SEARCH_IMPL_ORGANIZED_NEIGHBOR_SEARCH_H_
00041 #define PCL_SEARCH_IMPL_ORGANIZED_NEIGHBOR_SEARCH_H_
00042 
00043 #include "pcl/search/organized.h"
00044 #include <pcl/common/eigen.h>
00045 #include "pcl/common/time.h"
00046 #include <Eigen/Eigenvalues>
00047 
00049 template<typename PointT> int
00050 pcl::search::OrganizedNeighbor<PointT>::radiusSearch (const               PointT &query,
00051                                                       const double        radius,
00052                                                       std::vector<int>    &k_indices,
00053                                                       std::vector<float>  &k_sqr_distances,
00054                                                       unsigned int        max_nn) const
00055 {
00056   // NAN test
00057   assert (isFinite (query) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
00058 
00059   // search window
00060   unsigned left, right, top, bottom;
00061   //unsigned x, y, idx;
00062   float squared_distance, squared_radius;
00063 
00064   k_indices.clear ();
00065   k_sqr_distances.clear ();
00066 
00067   squared_radius = radius * radius;
00068 
00069   this->getProjectedRadiusSearchBox (query, squared_radius, left, right, top, bottom);
00070 
00071   // iterate over search box
00072   if (max_nn == 0 || max_nn >= (unsigned int)input_->points.size ())
00073     max_nn = input_->points.size ();
00074 
00075   k_indices.reserve (max_nn);
00076   k_sqr_distances.reserve (max_nn);
00077 
00078   unsigned yEnd  = (bottom + 1) * input_->width + right + 1;
00079   register unsigned idx  = top * input_->width + left;
00080   unsigned skip = input_->width - right + left - 1;
00081   unsigned xEnd = idx - left + right + 1;
00082 
00083   for (; xEnd != yEnd; idx += skip, xEnd += input_->width)
00084   {
00085     for (; idx < xEnd; ++idx)
00086     {
00087       if (!mask_[idx])
00088         continue;
00089 
00090       squared_distance = (input_->points[idx].getVector3fMap () - query.getVector3fMap ()).squaredNorm ();
00091       if (squared_distance <= squared_radius)
00092       {
00093         k_indices.push_back (idx);
00094         k_sqr_distances.push_back (squared_distance);
00095         // already done ?
00096         if (k_indices.size () == max_nn)
00097         {
00098           if (sorted_results_)
00099             this->sortResults (k_indices, k_sqr_distances);
00100           return max_nn;
00101         }
00102       }
00103     }
00104   }
00105   if (sorted_results_)
00106     this->sortResults (k_indices, k_sqr_distances);  
00107   return (k_indices.size ());
00108 }
00109 
00111 template<typename PointT> int
00112 pcl::search::OrganizedNeighbor<PointT>::nearestKSearch (const PointT &query,
00113                                                         int k,
00114                                                         std::vector<int> &k_indices,
00115                                                         std::vector<float> &k_sqr_distances) const
00116 {
00117   assert (isFinite (query) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
00118   if (k < 1)
00119   {
00120     k_indices.clear ();
00121     k_sqr_distances.clear ();
00122     return 0;
00123   }
00124 
00125   // project query point on the image plane
00126   Eigen::Vector3f q = KR_ * query.getVector3fMap () + projection_matrix_.block <3, 1> (0, 3);
00127   int xBegin = int(q [0] / q [2] + 0.5f);
00128   int yBegin = int(q [1] / q [2] + 0.5f);
00129   int xEnd   = xBegin + 1; // end is the pixel that is not used anymore, like in iterators
00130   int yEnd   = yBegin + 1;
00131 
00132   // the search window. This is supposed to shrink within the iterations
00133   unsigned left = 0;
00134   unsigned right = input_->width - 1;
00135   unsigned top = 0;
00136   unsigned bottom = input_->height - 1;
00137 
00138   std::priority_queue <Entry> results;
00139   //std::vector<Entry> k_results;
00140   //k_results.reserve (k);
00141   // add point laying on the projection of the query point.
00142   if (xBegin >= 0 && xBegin < (int)input_->width && yBegin >= 0 && yBegin < (int)input_->height )
00143     testPoint (query, k, results, yBegin * input_->width + xBegin);
00144   else // point lys
00145   {
00146     // find the box that touches the image border -> dont waste time evaluating boxes that are completely outside the image!
00147     int dist = std::numeric_limits<int>::max ();
00148 
00149     if (xBegin < 0)
00150       dist = -xBegin;
00151     else if (xBegin >= (int)input_->width)
00152       dist = xBegin - (int)input_->width;
00153 
00154     if (yBegin < 0)
00155       dist = std::min (dist, -yBegin);
00156     else if (yBegin >= (int)input_->height)
00157       dist = std::min (dist, yBegin - (int)input_->height);
00158 
00159     xBegin -= dist;
00160     xEnd   += dist;
00161 
00162     yBegin -= dist;
00163     yEnd   += dist;
00164   }
00165 
00166   
00167   // stop used as isChanged as well as stop.
00168   bool stop = false;
00169   do
00170   {
00171     // increment box size
00172     --xBegin;
00173     ++xEnd;
00174     --yBegin;
00175     ++yEnd;
00176 
00177     // the range in x-direction which intersects with the image width
00178     int xFrom = xBegin;
00179     int xTo   = xEnd;
00180     clipRange (xFrom, xTo, 0, input_->width);
00181     
00182     // if x-extend is not 0
00183     if (xTo > xFrom)
00184     {
00185       // if upper line of the rectangle is visible and x-extend is not 0
00186       if (yBegin >= 0 && yBegin < (int)input_->height)
00187       {
00188         int idx   = yBegin * input_->width + xFrom;
00189         int idxTo = idx + xTo - xFrom;
00190         for (; idx < idxTo; ++idx)
00191           stop = testPoint (query, k, results, idx) || stop;
00192       }
00193       
00194 
00195       // the row yEnd does NOT belong to the box -> last row = yEnd - 1
00196       // if lower line of the rectangle is visible
00197       if (yEnd > 0 && yEnd <= (int)input_->height)
00198       {
00199         int idx   = (yEnd - 1) * input_->width + xFrom;
00200         int idxTo = idx + xTo - xFrom;
00201 
00202         for (; idx < idxTo; ++idx)
00203           stop = testPoint (query, k, results, idx) || stop;
00204       }
00205       
00206       // skip first row and last row (already handled above)
00207       int yFrom = yBegin + 1;
00208       int yTo   = yEnd - 1;
00209       clipRange (yFrom, yTo, 0, input_->height);
00210       
00211       // if we have lines in between that are also visible
00212       if (yFrom < yTo)
00213       {
00214         if (xBegin >= 0 && xBegin < (int)input_->width)
00215         {
00216           int idx   = yFrom * input_->width + xBegin;
00217           int idxTo = yTo * input_->width + xBegin;
00218 
00219           for (; idx < idxTo; idx += input_->width)
00220             stop = testPoint (query, k, results, idx) || stop;
00221         }
00222         
00223         if (xEnd > 0 && xEnd <= (int)input_->width)
00224         {
00225           int idx   = yFrom * input_->width + xEnd - 1;
00226           int idxTo = yTo * input_->width + xEnd - 1;
00227 
00228           for (; idx < idxTo; idx += input_->width)
00229             stop = testPoint (query, k, results, idx) || stop;
00230         }
00231         
00232       }
00233       // stop here means that the k-nearest neighbor changed -> recalculate bounding box of ellipse.
00234       if (stop)
00235         getProjectedRadiusSearchBox (query, results.top ().distance, left, right, top, bottom);
00236       
00237     }
00238     // now we use it as stop flag -> if bounding box is completely within the already examined search box were done!
00239     stop = ((int)left >= xBegin && (int)left < xEnd && (int)right  >= xBegin && (int)right  < xEnd &&
00240             (int)top  >= yBegin && (int)top  < yEnd && (int)bottom >= yBegin && (int)bottom < yEnd);
00241     
00242   } while (!stop);
00243 
00244   
00245   k_indices.resize (results.size ());
00246   k_sqr_distances.resize (results.size ());
00247   unsigned idx = results.size () - 1;
00248   while (!results.empty ())
00249   {
00250     k_indices [idx] = results.top ().index;
00251     k_sqr_distances [idx] = results.top ().distance;
00252     results.pop ();
00253     --idx;
00254   }
00255   
00256   return k_indices.size ();
00257 }
00258 
00260 template<typename PointT> void
00261 pcl::search::OrganizedNeighbor<PointT>::getProjectedRadiusSearchBox (const PointT& point,
00262                                                                       float squared_radius,
00263                                                                       unsigned &minX,
00264                                                                       unsigned &maxX,
00265                                                                       unsigned &minY,
00266                                                                       unsigned &maxY) const
00267 {
00268   Eigen::Vector3f q = KR_ * point.getVector3fMap () + projection_matrix_.block <3, 1> (0, 3);
00269 
00270   float a = squared_radius * KR_KRT_.coeff (8) - q [2] * q [2];
00271   float b = squared_radius * KR_KRT_.coeff (7) - q [1] * q [2];
00272   float c = squared_radius * KR_KRT_.coeff (4) - q [1] * q [1];
00273   int min, max;
00274   // a and c are multiplied by two already => - 4ac -> - ac
00275   float det = b * b - a * c;
00276   if (det < 0)
00277   {
00278     minY = 0;
00279     maxY = input_->height - 1;
00280   }
00281   else
00282   {
00283     float y1 = (b - sqrt (det)) / a;
00284     float y2 = (b + sqrt (det)) / a;
00285 
00286 //    min = (int)std::floor (std::min (y1, y2));
00287 //    max = (int)std::ceil  (std::max (y1, y2));
00288 //
00289 //    minY = (unsigned) std::min ((int) input_->height - 1, std::max (0, min));
00290 //    maxY = (unsigned) std::max (std::min ((int) input_->height - 1, max), 0);
00291 
00292     min = std::min ((int) floor (y1), (int) floor (y2));
00293     max = std::max ((int) ceil (y1), (int) ceil (y2));
00294     minY = (unsigned) std::min ((int) input_->height - 1, std::max (0, min));
00295     maxY = (unsigned) std::max (std::min ((int) input_->height - 1, max), 0);
00296   }
00297 
00298   b = squared_radius * KR_KRT_.coeff (6) - q [0] * q [2];
00299   c = squared_radius * KR_KRT_.coeff (0) - q [0] * q [0];
00300 
00301   det = b * b - a * c;
00302   if (det < 0)
00303   {
00304     minX = 0;
00305     maxX = input_->width - 1;
00306   }
00307   else
00308   {
00309     float x1 = (b - sqrt (det)) / a;
00310     float x2 = (b + sqrt (det)) / a;
00311 //
00312 //    min = (int)std::floor (std::min (x1, x2));
00313 //    max = (int)std::ceil  (std::max (x1, x2));
00314 //
00315 //    minX = (unsigned) std::min ((int) input_->width - 1, std::max (0, min));
00316 //    maxX = (unsigned) std::max (std::min ((int) input_->width - 1, max), 0);
00317 
00318     min = std::min ((int) floor (x1), (int) floor (x2));
00319     max = std::max ((int) ceil (x1), (int) ceil (x2));
00320     minX = (unsigned) std::min ((int)input_->width - 1, std::max (0, min));
00321     maxX = (unsigned) std::max (std::min ((int)input_->width - 1, max), 0);
00322   }
00323 }
00324 
00326 template<typename PointT> template <typename MatrixType> void
00327 pcl::search::OrganizedNeighbor<PointT>::makeSymmetric (MatrixType& matrix, bool use_upper_triangular) const
00328 {
00329   if (use_upper_triangular && (MatrixType::Flags & Eigen::RowMajorBit) )
00330   {
00331     matrix.coeffRef (4) = matrix.coeff (1);
00332     matrix.coeffRef (8) = matrix.coeff (2);
00333     matrix.coeffRef (9) = matrix.coeff (6);
00334     matrix.coeffRef (12) = matrix.coeff (3);
00335     matrix.coeffRef (13) = matrix.coeff (7);
00336     matrix.coeffRef (14) = matrix.coeff (11);
00337   }
00338   else
00339   {
00340     matrix.coeffRef (1) = matrix.coeff (4);
00341     matrix.coeffRef (2) = matrix.coeff (8);
00342     matrix.coeffRef (6) = matrix.coeff (9);
00343     matrix.coeffRef (3) = matrix.coeff (12);
00344     matrix.coeffRef (7) = matrix.coeff (13);
00345     matrix.coeffRef (11) = matrix.coeff (14);
00346   }
00347 }
00348 
00350 template<typename PointT> void
00351 pcl::search::OrganizedNeighbor<PointT>::computeCameraMatrix (Eigen::Matrix3f& camera_matrix) const
00352 {
00353   Eigen::Matrix3f cam = KR_KRT_ / KR_KRT_.coeff (8);
00354 
00355   memset (&(camera_matrix.coeffRef (0)), 0, sizeof (Eigen::Matrix3f::Scalar) * 9);
00356   camera_matrix.coeffRef (8) = 1.0;
00357   
00358   if (camera_matrix.Flags & Eigen::RowMajorBit)
00359   {
00360     camera_matrix.coeffRef (2) = cam.coeff (2);
00361     camera_matrix.coeffRef (5) = cam.coeff (5);
00362     camera_matrix.coeffRef (4) = sqrt (cam.coeff (4) - cam.coeff (5) * cam.coeff (5));
00363     camera_matrix.coeffRef (1) = (cam.coeff (1) - cam.coeff (2) * cam.coeff (5)) / camera_matrix.coeff (4);
00364     camera_matrix.coeffRef (0) = sqrt (cam.coeff (0) - camera_matrix.coeff (1) * camera_matrix.coeff (1) - cam.coeff (2) * cam.coeff (2));
00365   }
00366   else
00367   {
00368     camera_matrix.coeffRef (6) = cam.coeff (2);
00369     camera_matrix.coeffRef (7) = cam.coeff (5);
00370     camera_matrix.coeffRef (4) = sqrt (cam.coeff (4) - cam.coeff (5) * cam.coeff (5));
00371     camera_matrix.coeffRef (3) = (cam.coeff (1) - cam.coeff (2) * cam.coeff (5)) / camera_matrix.coeff (4);
00372     camera_matrix.coeffRef (0) = sqrt (cam.coeff (0) - camera_matrix.coeff (3) * camera_matrix.coeff (3) - cam.coeff (2) * cam.coeff (2));
00373   }
00374 }
00375 
00377 template<typename PointT> void
00378 pcl::search::OrganizedNeighbor<PointT>::estimateProjectionMatrix ()
00379 {
00380   // internally we calculate with double but store the result into float matrices.
00381   typedef double Scalar;
00382   projection_matrix_.setZero ();
00383   if (input_->height == 1 || input_->width == 1)
00384   {
00385     PCL_ERROR ("[pcl::%s::estimateProjectionMatrix] Input dataset is not organized!\n", this->getName ().c_str ());
00386     return;
00387   }
00388   
00389   // we just want to use every 16th column and row -> skip = 2^4
00390   const unsigned int skip = input_->width >> 3;
00391   Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor> A = Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor>::Zero ();
00392   Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor> B = Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor>::Zero ();
00393   Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor> C = Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor>::Zero ();
00394   Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor> D = Eigen::Matrix<Scalar, 4, 4, Eigen::RowMajor>::Zero ();
00395 
00396   for (unsigned yIdx = 0, idx = 0; yIdx < input_->height; yIdx += skip, idx += input_->width * (skip-1))
00397   {
00398     for (unsigned xIdx = 0; xIdx < input_->width; xIdx += skip, idx += skip)
00399     {
00400       if (!mask_ [idx])
00401         continue;
00402 
00403       const PointT& point = input_->points[idx];
00404       if (pcl_isfinite (point.x))
00405       {
00406         Scalar xx = point.x * point.x;
00407         Scalar xy = point.x * point.y;
00408         Scalar xz = point.x * point.z;
00409         Scalar yy = point.y * point.y;
00410         Scalar yz = point.y * point.z;
00411         Scalar zz = point.z * point.z;
00412         Scalar xx_yy = xIdx * xIdx + yIdx * yIdx;
00413 
00414         A.coeffRef (0) += xx;
00415         A.coeffRef (1) += xy;
00416         A.coeffRef (2) += xz;
00417         A.coeffRef (3) += point.x;
00418 
00419         A.coeffRef (5) += yy;
00420         A.coeffRef (6) += yz;
00421         A.coeffRef (7) += point.y;
00422 
00423         A.coeffRef (10) += zz;
00424         A.coeffRef (11) += point.z;
00425         A.coeffRef (15) += 1.0;
00426 
00427         B.coeffRef (0) -= xx * xIdx;
00428         B.coeffRef (1) -= xy * xIdx;
00429         B.coeffRef (2) -= xz * xIdx;
00430         B.coeffRef (3) -= point.x * xIdx;
00431 
00432         B.coeffRef (5) -= yy * xIdx;
00433         B.coeffRef (6) -= yz * xIdx;
00434         B.coeffRef (7) -= point.y * xIdx;
00435 
00436         B.coeffRef (10) -= zz * xIdx;
00437         B.coeffRef (11) -= point.z * xIdx;
00438 
00439         B.coeffRef (15) -= xIdx;
00440 
00441         C.coeffRef (0) -= xx * yIdx;
00442         C.coeffRef (1) -= xy * yIdx;
00443         C.coeffRef (2) -= xz * yIdx;
00444         C.coeffRef (3) -= point.x * yIdx;
00445 
00446         C.coeffRef (5) -= yy * yIdx;
00447         C.coeffRef (6) -= yz * yIdx;
00448         C.coeffRef (7) -= point.y * yIdx;
00449 
00450         C.coeffRef (10) -= zz * yIdx;
00451         C.coeffRef (11) -= point.z * yIdx;
00452 
00453         C.coeffRef (15) -= yIdx;
00454 
00455         D.coeffRef (0) += xx * xx_yy;
00456         D.coeffRef (1) += xy * xx_yy;
00457         D.coeffRef (2) += xz * xx_yy;
00458         D.coeffRef (3) += point.x * xx_yy;
00459 
00460         D.coeffRef (5) += yy * xx_yy;
00461         D.coeffRef (6) += yz * xx_yy;
00462         D.coeffRef (7) += point.y * xx_yy;
00463 
00464         D.coeffRef (10) += zz * xx_yy;
00465         D.coeffRef (11) += point.z * xx_yy;
00466 
00467         D.coeffRef (15) += xx_yy;
00468       }
00469     }
00470   }
00471 
00472   makeSymmetric(A);
00473   makeSymmetric(B);
00474   makeSymmetric(C);
00475   makeSymmetric(D);
00476 
00477   Eigen::Matrix<Scalar, 12, 12, Eigen::RowMajor> X = Eigen::Matrix<Scalar, 12, 12, Eigen::RowMajor>::Zero ();
00478   X.topLeftCorner<4,4> () = A;
00479   X.block<4,4> (0, 8) = B;
00480   X.block<4,4> (8, 0) = B;
00481   X.block<4,4> (4, 4) = A;
00482   X.block<4,4> (4, 8) = C;
00483   X.block<4,4> (8, 4) = C;
00484   X.block<4,4> (8, 8) = D;
00485 
00486   Eigen::SelfAdjointEigenSolver<Eigen::Matrix<Scalar, 12, 12, Eigen::RowMajor> > ei_symm(X);
00487   Eigen::Matrix<Scalar, 12, 12, Eigen::RowMajor> eigen_vectors = ei_symm.eigenvectors();
00488 
00489   // check whether the residual MSE is low. If its high, the cloud was not captured from a projective device.
00490   Eigen::Matrix<Scalar, 1, 1> residual_sqr = eigen_vectors.col (0).transpose () * X *  eigen_vectors.col (0);
00491   if ( fabs (residual_sqr.coeff (0)) > eps_ * A.coeff (15))
00492   {
00493     PCL_ERROR ("[pcl::%s::radiusSearch] Input dataset is not from a projective device!\nResidual (MSE) %f, using %d valid points\n", this->getName ().c_str (), residual_sqr.coeff (0) / A.coeff (15), (int)A.coeff (15));
00494     return;
00495   }
00496 
00497   projection_matrix_.coeffRef (0) = eigen_vectors.coeff (0);
00498   projection_matrix_.coeffRef (1) = eigen_vectors.coeff (12);
00499   projection_matrix_.coeffRef (2) = eigen_vectors.coeff (24);
00500   projection_matrix_.coeffRef (3) = eigen_vectors.coeff (36);
00501   projection_matrix_.coeffRef (4) = eigen_vectors.coeff (48);
00502   projection_matrix_.coeffRef (5) = eigen_vectors.coeff (60);
00503   projection_matrix_.coeffRef (6) = eigen_vectors.coeff (72);
00504   projection_matrix_.coeffRef (7) = eigen_vectors.coeff (84);
00505   projection_matrix_.coeffRef (8) = eigen_vectors.coeff (96);
00506   projection_matrix_.coeffRef (9) = eigen_vectors.coeff (108);
00507   projection_matrix_.coeffRef (10) = eigen_vectors.coeff (120);
00508   projection_matrix_.coeffRef (11) = eigen_vectors.coeff (132);
00509 
00510   if (projection_matrix_.coeff (0) < 0)
00511     projection_matrix_ *= -1.0;
00512 
00513   // get left 3x3 sub matrix, which contains K * R, with K = camera matrix = [[fx s cx] [0 fy cy] [0 0 1]]
00514   // and R being the rotation matrix
00515   KR_ = projection_matrix_.topLeftCorner <3, 3> ();
00516 
00517   // precalculate KR * KR^T needed by calculations during nn-search
00518   KR_KRT_ = KR_ * KR_.transpose ();  
00519 }
00520 
00521 #define PCL_INSTANTIATE_OrganizedNeighbor(T) template class PCL_EXPORTS pcl::search::OrganizedNeighbor<T>;
00522 
00523 #endif