|
Point Cloud Library (PCL)
1.5.1
|
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
1.8.0