/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010-2011, Willow Garage, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the copyright holder(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $Id$ * */ #pragma once #include #include // for pcl::isFinite #include // for getCameraMatrixFromProjectionMatrix, ... #include ////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::search::OrganizedNeighbor::radiusSearch (const PointT &query, const double radius, Indices &k_indices, std::vector &k_sqr_distances, unsigned int max_nn) const { // NAN test assert (isFinite (query) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!"); // search window unsigned left, right, top, bottom; //unsigned x, y, idx; float squared_distance; const float squared_radius = radius * radius; k_indices.clear (); k_sqr_distances.clear (); this->getProjectedRadiusSearchBox (query, squared_radius, left, right, top, bottom); // iterate over search box if (max_nn == 0 || max_nn >= static_cast (input_->size ())) max_nn = static_cast (input_->size ()); k_indices.reserve (max_nn); k_sqr_distances.reserve (max_nn); unsigned yEnd = (bottom + 1) * input_->width + right + 1; unsigned idx = top * input_->width + left; unsigned skip = input_->width - right + left - 1; unsigned xEnd = idx - left + right + 1; for (; xEnd != yEnd; idx += skip, xEnd += input_->width) { for (; idx < xEnd; ++idx) { if (!mask_[idx] || !isFinite ((*input_)[idx])) continue; float dist_x = (*input_)[idx].x - query.x; float dist_y = (*input_)[idx].y - query.y; float dist_z = (*input_)[idx].z - query.z; squared_distance = dist_x * dist_x + dist_y * dist_y + dist_z * dist_z; //squared_distance = ((*input_)[idx].getVector3fMap () - query.getVector3fMap ()).squaredNorm (); if (squared_distance <= squared_radius) { k_indices.push_back (idx); k_sqr_distances.push_back (squared_distance); // already done ? if (k_indices.size () == max_nn) { if (sorted_results_) this->sortResults (k_indices, k_sqr_distances); return (max_nn); } } } } if (sorted_results_) this->sortResults (k_indices, k_sqr_distances); return (static_cast (k_indices.size ())); } ////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::search::OrganizedNeighbor::nearestKSearch (const PointT &query, int k, Indices &k_indices, std::vector &k_sqr_distances) const { assert (isFinite (query) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!"); if (k < 1) { k_indices.clear (); k_sqr_distances.clear (); return (0); } Eigen::Vector3f queryvec (query.x, query.y, query.z); // project query point on the image plane //Eigen::Vector3f q = KR_ * query.getVector3fMap () + projection_matrix_.block <3, 1> (0, 3); Eigen::Vector3f q (KR_ * queryvec + projection_matrix_.block <3, 1> (0, 3)); int xBegin = int(q [0] / q [2] + 0.5f); int yBegin = int(q [1] / q [2] + 0.5f); int xEnd = xBegin + 1; // end is the pixel that is not used anymore, like in iterators int yEnd = yBegin + 1; // the search window. This is supposed to shrink within the iterations unsigned left = 0; unsigned right = input_->width - 1; unsigned top = 0; unsigned bottom = input_->height - 1; std::vector results; // sorted from smallest to largest distance results.reserve (k); // add point laying on the projection of the query point. if (xBegin >= 0 && xBegin < static_cast (input_->width) && yBegin >= 0 && yBegin < static_cast (input_->height)) testPoint (query, k, results, yBegin * input_->width + xBegin); else // point lys { // find the box that touches the image border -> don't waste time evaluating boxes that are completely outside the image! int dist = std::numeric_limits::max (); if (xBegin < 0) dist = -xBegin; else if (xBegin >= static_cast (input_->width)) dist = xBegin - static_cast (input_->width); if (yBegin < 0) dist = std::min (dist, -yBegin); else if (yBegin >= static_cast (input_->height)) dist = std::min (dist, yBegin - static_cast (input_->height)); xBegin -= dist; xEnd += dist; yBegin -= dist; yEnd += dist; } // stop used as isChanged as well as stop. bool stop = false; do { // increment box size --xBegin; ++xEnd; --yBegin; ++yEnd; // the range in x-direction which intersects with the image width int xFrom = xBegin; int xTo = xEnd; clipRange (xFrom, xTo, 0, input_->width); // if x-extend is not 0 if (xTo > xFrom) { // if upper line of the rectangle is visible and x-extend is not 0 if (yBegin >= 0 && yBegin < static_cast (input_->height)) { index_t idx = yBegin * input_->width + xFrom; index_t idxTo = idx + xTo - xFrom; for (; idx < idxTo; ++idx) stop = testPoint (query, k, results, idx) || stop; } // the row yEnd does NOT belong to the box -> last row = yEnd - 1 // if lower line of the rectangle is visible if (yEnd > 0 && yEnd <= static_cast (input_->height)) { index_t idx = (yEnd - 1) * input_->width + xFrom; index_t idxTo = idx + xTo - xFrom; for (; idx < idxTo; ++idx) stop = testPoint (query, k, results, idx) || stop; } // skip first row and last row (already handled above) int yFrom = yBegin + 1; int yTo = yEnd - 1; clipRange (yFrom, yTo, 0, input_->height); // if we have lines in between that are also visible if (yFrom < yTo) { if (xBegin >= 0 && xBegin < static_cast (input_->width)) { index_t idx = yFrom * input_->width + xBegin; index_t idxTo = yTo * input_->width + xBegin; for (; idx < idxTo; idx += input_->width) stop = testPoint (query, k, results, idx) || stop; } if (xEnd > 0 && xEnd <= static_cast (input_->width)) { index_t idx = yFrom * input_->width + xEnd - 1; index_t idxTo = yTo * input_->width + xEnd - 1; for (; idx < idxTo; idx += input_->width) stop = testPoint (query, k, results, idx) || stop; } } // stop here means that the k-nearest neighbor changed -> recalculate bounding box of ellipse. if (stop) getProjectedRadiusSearchBox (query, results.back ().distance, left, right, top, bottom); } // now we use it as stop flag -> if bounding box is completely within the already examined search box were done! stop = (static_cast (left) >= xBegin && static_cast (left) < xEnd && static_cast (right) >= xBegin && static_cast (right) < xEnd && static_cast (top) >= yBegin && static_cast (top) < yEnd && static_cast (bottom) >= yBegin && static_cast (bottom) < yEnd); } while (!stop); const auto results_size = results.size (); k_indices.resize (results_size); k_sqr_distances.resize (results_size); std::size_t idx = 0; for(const auto& result : results) { k_indices [idx] = result.index; k_sqr_distances [idx] = result.distance; ++idx; } return (static_cast (results_size)); } //////////////////////////////////////////////////////////////////////////////////////////// template void pcl::search::OrganizedNeighbor::getProjectedRadiusSearchBox (const PointT& point, float squared_radius, unsigned &minX, unsigned &maxX, unsigned &minY, unsigned &maxY) const { Eigen::Vector3f queryvec (point.x, point.y, point.z); //Eigen::Vector3f q = KR_ * point.getVector3fMap () + projection_matrix_.block <3, 1> (0, 3); Eigen::Vector3f q (KR_ * queryvec + projection_matrix_.block <3, 1> (0, 3)); float a = squared_radius * KR_KRT_.coeff (8) - q [2] * q [2]; float b = squared_radius * KR_KRT_.coeff (7) - q [1] * q [2]; float c = squared_radius * KR_KRT_.coeff (4) - q [1] * q [1]; int min, max; // a and c are multiplied by two already => - 4ac -> - ac float det = b * b - a * c; if (det < 0) { minY = 0; maxY = input_->height - 1; } else { float y1 = static_cast ((b - std::sqrt (det)) / a); float y2 = static_cast ((b + std::sqrt (det)) / a); min = std::min (static_cast (std::floor (y1)), static_cast (std::floor (y2))); max = std::max (static_cast (std::ceil (y1)), static_cast (std::ceil (y2))); minY = static_cast (std::min (static_cast (input_->height) - 1, std::max (0, min))); maxY = static_cast (std::max (std::min (static_cast (input_->height) - 1, max), 0)); } b = squared_radius * KR_KRT_.coeff (6) - q [0] * q [2]; c = squared_radius * KR_KRT_.coeff (0) - q [0] * q [0]; det = b * b - a * c; if (det < 0) { minX = 0; maxX = input_->width - 1; } else { float x1 = static_cast ((b - std::sqrt (det)) / a); float x2 = static_cast ((b + std::sqrt (det)) / a); min = std::min (static_cast (std::floor (x1)), static_cast (std::floor (x2))); max = std::max (static_cast (std::ceil (x1)), static_cast (std::ceil (x2))); minX = static_cast (std::min (static_cast (input_->width)- 1, std::max (0, min))); maxX = static_cast (std::max (std::min (static_cast (input_->width) - 1, max), 0)); } } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::search::OrganizedNeighbor::computeCameraMatrix (Eigen::Matrix3f& camera_matrix) const { pcl::getCameraMatrixFromProjectionMatrix (projection_matrix_, camera_matrix); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::search::OrganizedNeighbor::estimateProjectionMatrix () { // internally we calculate with double but store the result into float matrices. projection_matrix_.setZero (); if (input_->height == 1 || input_->width == 1) { PCL_ERROR ("[pcl::%s::estimateProjectionMatrix] Input dataset is not organized!\n", this->getName ().c_str ()); return; } const unsigned ySkip = (std::max) (input_->height >> pyramid_level_, unsigned (1)); const unsigned xSkip = (std::max) (input_->width >> pyramid_level_, unsigned (1)); Indices indices; indices.reserve (input_->size () >> (pyramid_level_ << 1)); for (unsigned yIdx = 0, idx = 0; yIdx < input_->height; yIdx += ySkip, idx += input_->width * ySkip) { for (unsigned xIdx = 0, idx2 = idx; xIdx < input_->width; xIdx += xSkip, idx2 += xSkip) { if (!mask_ [idx2]) continue; indices.push_back (idx2); } } double residual_sqr = pcl::estimateProjectionMatrix (input_, projection_matrix_, indices); if (std::abs (residual_sqr) > eps_ * float (indices.size ())) { PCL_ERROR ("[pcl::%s::estimateProjectionMatrix] Input dataset is not from a projective device!\nResidual (MSE) %f, using %d valid points\n", this->getName ().c_str (), residual_sqr / double (indices.size()), indices.size ()); return; } // get left 3x3 sub matrix, which contains K * R, with K = camera matrix = [[fx s cx] [0 fy cy] [0 0 1]] // and R being the rotation matrix KR_ = projection_matrix_.topLeftCorner <3, 3> (); // precalculate KR * KR^T needed by calculations during nn-search KR_KRT_ = KR_ * KR_.transpose (); } ////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::search::OrganizedNeighbor::projectPoint (const PointT& point, pcl::PointXY& q) const { Eigen::Vector3f projected = KR_ * point.getVector3fMap () + projection_matrix_.block <3, 1> (0, 3); q.x = projected [0] / projected [2]; q.y = projected [1] / projected [2]; return (projected[2] != 0); } #define PCL_INSTANTIATE_OrganizedNeighbor(T) template class PCL_EXPORTS pcl::search::OrganizedNeighbor;