kdtree_cpu.cpp
Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (c) 2010--2011, Stephane Magnenat, ASL, ETHZ, Switzerland
00004 You can contact the author at <stephane at magnenat dot net>
00005 
00006 All rights reserved.
00007 
00008 Redistribution and use in source and binary forms, with or without
00009 modification, are permitted provided that the following conditions are met:
00010     * Redistributions of source code must retain the above copyright
00011       notice, this list of conditions and the following disclaimer.
00012     * Redistributions in binary form must reproduce the above copyright
00013       notice, this list of conditions and the following disclaimer in the
00014       documentation and/or other materials provided with the distribution.
00015     * Neither the name of the <organization> nor the
00016       names of its contributors may be used to endorse or promote products
00017       derived from this software without specific prior written permission.
00018 
00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022 DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
00023 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 
00030 */
00031 
00032 #include "nabo_private.h"
00033 #include "index_heap.h"
00034 #include <iostream>
00035 #include <stdexcept>
00036 #include <limits>
00037 #include <queue>
00038 #include <algorithm>
00039 #include <utility>
00040 #ifdef HAVE_OPENMP
00041 #include <omp.h>
00042 #endif
00043 
00049 namespace Nabo
00050 {
00052 
00053         
00054         using namespace std;
00055         
00057 
00060         template<typename T>
00061         T getStorageBitCount(T v)
00062         {
00063                 for (T i = 0; i < 64; ++i)
00064                 {
00065                         if (v == 0)
00066                                 return i;
00067                         v >>= 1;
00068                 }
00069                 return 64;
00070         }
00071         
00073 
00076         template<typename T, typename CloudType>
00077         size_t argMax(const typename NearestNeighbourSearch<T, CloudType>::Vector& v)
00078         {
00079                 T maxVal(0);
00080                 size_t maxIdx(0);
00081                 for (int i = 0; i < v.size(); ++i)
00082                 {
00083                         if (v[i] > maxVal)
00084                         {
00085                                 maxVal = v[i];
00086                                 maxIdx = i;
00087                         }
00088                 }
00089                 return maxIdx;
00090         }
00091         
00092         // OPT
00093         template<typename T, typename Heap, typename CloudType>
00094         pair<T,T> KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::getBounds(const BuildPointsIt first, const BuildPointsIt last, const unsigned dim)
00095         {
00096                 T minVal(std::numeric_limits<T>::max());
00097                 T maxVal(std::numeric_limits<T>::lowest());
00098                 
00099                 for (BuildPointsCstIt it(first); it != last; ++it)
00100                 {
00101                         const T val(cloud.coeff(dim, *it));
00102                         minVal = min(val, minVal);
00103                         maxVal = max(val, maxVal);
00104                 }
00105                 
00106                 return make_pair(minVal, maxVal);
00107         }
00108         
00109         template<typename T, typename Heap, typename CloudType>
00110         unsigned KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::buildNodes(const BuildPointsIt first, const BuildPointsIt last, const Vector minValues, const Vector maxValues)
00111         {
00112                 const int count(last - first);
00113                 assert(count >= 1);
00114                 const unsigned pos(nodes.size());
00115                 
00116                 //cerr << count << endl;
00117                 if (count <= int(bucketSize))
00118                 {
00119                         const uint32_t initBucketsSize(buckets.size());
00120                         //cerr << "creating bucket with " << count << " values" << endl;
00121                         for (int i = 0; i < count; ++i)
00122                         {
00123                                 const Index index(*(first+i));
00124                                 assert(index < cloud.cols());
00125                                 buckets.push_back(BucketEntry(&cloud.coeff(0, index), index));
00126                                 //cerr << "  " << &cloud.coeff(0, index) << ", " << index << endl;
00127                         }
00128                         //cerr << "at address " << bucketStart << endl;
00129                         nodes.push_back(Node(createDimChildBucketSize(dim, count),initBucketsSize));
00130                         return pos;
00131                 }
00132                 
00133                 // find the largest dimension of the box
00134                 const unsigned cutDim = argMax<T, CloudType>(maxValues - minValues);
00135                 const T idealCutVal((maxValues(cutDim) + minValues(cutDim))/2);
00136                 
00137                 // get bounds from actual points
00138                 const pair<T,T> minMaxVals(getBounds(first, last, cutDim));
00139                 
00140                 // correct cut following bounds
00141                 T cutVal;
00142                 if (idealCutVal < minMaxVals.first)
00143                         cutVal = minMaxVals.first;
00144                 else if (idealCutVal > minMaxVals.second)
00145                         cutVal = minMaxVals.second;
00146                 else
00147                         cutVal = idealCutVal;
00148                 
00149                 int l(0);
00150                 int r(count-1);
00151                 // partition points around cutVal
00152                 while (1)
00153                 {
00154                         while (l < count && cloud.coeff(cutDim, *(first+l)) < cutVal)
00155                                 ++l;
00156                         while (r >= 0 && cloud.coeff(cutDim, *(first+r)) >= cutVal)
00157                                 --r;
00158                         if (l > r)
00159                                 break;
00160                         swap(*(first+l), *(first+r));
00161                         ++l; --r;
00162                 }
00163                 const int br1 = l;      // now: points[0..br1-1] < cutVal <= points[br1..count-1]
00164                 r = count-1;
00165                 // partition points[br1..count-1] around cutVal
00166                 while (1)
00167                 {
00168                         while (l < count && cloud.coeff(cutDim, *(first+l)) <= cutVal)
00169                                 ++l;
00170                         while (r >= br1 && cloud.coeff(cutDim, *(first+r)) > cutVal)
00171                                 --r;
00172                         if (l > r)
00173                                 break;
00174                         swap(*(first+l), *(first+r));
00175                         ++l; --r;
00176                 }
00177                 const int br2 = l; // now: points[br1..br2-1] == cutVal < points[br2..count-1]
00178                 
00179                 // find best split index
00180                 int leftCount;
00181                 if (idealCutVal < minMaxVals.first)
00182                         leftCount = 1;
00183                 else if (idealCutVal > minMaxVals.second)
00184                         leftCount = count-1;
00185                 else if (br1 > count / 2)
00186                         leftCount = br1;
00187                 else if (br2 < count / 2)
00188                         leftCount = br2;
00189                 else
00190                         leftCount = count / 2;
00191                 assert(leftCount > 0);
00192                 /*if (leftCount >= count)
00193                 {
00194                         cerr << "Error found in kdtree:" << endl;
00195                         cerr << "cloud size: " << cloud.cols() << endl;
00196                         cerr << "count:" << count << endl;
00197                         cerr << "leftCount: " << leftCount << endl;
00198                         cerr << "br1: " << br1 << endl;
00199                         cerr << "br2: " << br2 << endl;
00200                         cerr << "idealCutVal: " << idealCutVal << endl;
00201                         cerr << "cutVal: " << cutVal << endl;
00202                         cerr << "minMaxVals.first: " << minMaxVals.first << endl;
00203                         cerr << "minMaxVals.second: " << minMaxVals.second << endl;
00204                 }*/
00205                 assert(leftCount < count);
00206                 
00207                 // update bounds for left
00208                 Vector leftMaxValues(maxValues);
00209                 leftMaxValues[cutDim] = cutVal;
00210                 // update bounds for right
00211                 Vector rightMinValues(minValues);
00212                 rightMinValues[cutDim] = cutVal;
00213                 
00214                 // add this
00215                 nodes.push_back(Node(0, cutVal));
00216                 
00217                 // recurse
00218                 const unsigned _UNUSED leftChild = buildNodes(first, first + leftCount, minValues, leftMaxValues);
00219                 assert(leftChild == pos + 1);
00220                 const unsigned rightChild = buildNodes(first + leftCount, last, rightMinValues, maxValues);
00221                 
00222                 // write right child index and return
00223                 nodes[pos].dimChildBucketSize = createDimChildBucketSize(cutDim, rightChild);
00224                 return pos;
00225         }
00226 
00227         template<typename T, typename Heap, typename CloudType>
00228         KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt(const CloudType& cloud, const Index dim, const unsigned creationOptionFlags, const Parameters& additionalParameters):
00229                 NearestNeighbourSearch<T, CloudType>::NearestNeighbourSearch(cloud, dim, creationOptionFlags),
00230                 bucketSize(additionalParameters.get<unsigned>("bucketSize", 8)),
00231                 dimBitCount(getStorageBitCount<uint32_t>(this->dim)),
00232                 dimMask((1<<dimBitCount)-1)
00233         {
00234                 if (bucketSize < 2)
00235                         throw runtime_error("Requested bucket size " + std::to_string(bucketSize) + ", but must be larger than 2");
00236                 if (cloud.cols() <= bucketSize)
00237                 {
00238                         // make a single-bucket tree
00239                         for (int i = 0; i < cloud.cols(); ++i)
00240                                 buckets.push_back(BucketEntry(&cloud.coeff(0, i), i));
00241                         nodes.push_back(Node(createDimChildBucketSize(this->dim, cloud.cols()),uint32_t(0)));
00242                         return;
00243                 }
00244                 
00245                 const uint64_t maxNodeCount((0x1ULL << (32-dimBitCount)) - 1);
00246                 const uint64_t estimatedNodeCount(cloud.cols() / (bucketSize / 2));
00247                 if (estimatedNodeCount > maxNodeCount)
00248                 {
00249                         throw runtime_error("Cloud has a risk to have more nodes (" + std::to_string(estimatedNodeCount) + ") than the kd-tree allows (" + std::to_string(maxNodeCount) + "). "
00250                                         "The kd-tree has " + std::to_string(dimBitCount) + " bits for dimensions and " + std::to_string((32-dimBitCount)) + " bits for node indices");
00251                 }
00252                 
00253                 // build point vector and compute bounds
00254                 BuildPoints buildPoints;
00255                 buildPoints.reserve(cloud.cols());
00256                 for (int i = 0; i < cloud.cols(); ++i)
00257                 {
00258                         const Vector& v(cloud.block(0,i,this->dim,1));
00259                         buildPoints.push_back(i);
00260 #ifdef EIGEN3_API
00261                         const_cast<Vector&>(minBound) = minBound.array().min(v.array());
00262                         const_cast<Vector&>(maxBound) = maxBound.array().max(v.array());
00263 #else // EIGEN3_API
00264                         const_cast<Vector&>(minBound) = minBound.cwise().min(v);
00265                         const_cast<Vector&>(maxBound) = maxBound.cwise().max(v);
00266 #endif // EIGEN3_API
00267                 }
00268                 
00269                 // create nodes
00270                 buildNodes(buildPoints.begin(), buildPoints.end(), minBound, maxBound);
00271                 buildPoints.clear();
00272         }
00273         
00274         template<typename T, typename Heap, typename CloudType>
00275         unsigned long KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::knn(const Matrix& query, IndexMatrix& indices, Matrix& dists2, const Index k, const T epsilon, const unsigned optionFlags, const T maxRadius) const
00276         {
00277                 checkSizesKnn(query, indices, dists2, k, optionFlags);
00278                 
00279                 const bool allowSelfMatch(optionFlags & NearestNeighbourSearch<T>::ALLOW_SELF_MATCH);
00280                 const bool sortResults(optionFlags & NearestNeighbourSearch<T>::SORT_RESULTS);
00281                 const bool collectStatistics(creationOptionFlags & NearestNeighbourSearch<T>::TOUCH_STATISTICS);
00282                 const T maxRadius2(maxRadius * maxRadius);
00283                 const T maxError2((1+epsilon)*(1+epsilon));
00284                 const int colCount(query.cols());
00285                 
00286                 assert(nodes.size() > 0);
00287 
00288                 IndexMatrix result(k, query.cols());
00289                 unsigned long leafTouchedCount(0);
00290 
00291 #pragma omp parallel 
00292                 {               
00293 
00294                 Heap heap(k);
00295                 std::vector<T> off(dim, 0);
00296 
00297 #pragma omp for reduction(+:leafTouchedCount) schedule(guided,32)
00298                 for (int i = 0; i < colCount; ++i)
00299                 {
00300                         leafTouchedCount += onePointKnn(query, indices, dists2, i, heap, off, maxError2, maxRadius2, allowSelfMatch, collectStatistics, sortResults);
00301                 }
00302                 }
00303                 return leafTouchedCount;
00304         }
00305         
00306         template<typename T, typename Heap, typename CloudType>
00307         unsigned long KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::knn(const Matrix& query, IndexMatrix& indices, Matrix& dists2, const Vector& maxRadii, const Index k, const T epsilon, const unsigned optionFlags) const
00308         {
00309                 checkSizesKnn(query, indices, dists2, k, optionFlags, &maxRadii);
00310                 
00311                 const bool allowSelfMatch(optionFlags & NearestNeighbourSearch<T>::ALLOW_SELF_MATCH);
00312                 const bool sortResults(optionFlags & NearestNeighbourSearch<T>::SORT_RESULTS);
00313                 const bool collectStatistics(creationOptionFlags & NearestNeighbourSearch<T>::TOUCH_STATISTICS);
00314                 const T maxError2((1+epsilon)*(1+epsilon));
00315                 const int colCount(query.cols());
00316                 
00317                 assert(nodes.size() > 0);
00318                 IndexMatrix result(k, query.cols());
00319                 unsigned long leafTouchedCount(0);
00320 
00321 #pragma omp parallel 
00322                 {               
00323 
00324                 Heap heap(k);
00325                 std::vector<T> off(dim, 0);
00326                 
00327 #pragma omp for reduction(+:leafTouchedCount) schedule(guided,32)
00328                 for (int i = 0; i < colCount; ++i)
00329                 {
00330                         const T maxRadius(maxRadii[i]);
00331                         const T maxRadius2(maxRadius * maxRadius);
00332                         leafTouchedCount += onePointKnn(query, indices, dists2, i, heap, off, maxError2, maxRadius2, allowSelfMatch, collectStatistics, sortResults);
00333                 }
00334                 }
00335                 return leafTouchedCount;
00336         }
00337         
00338         template<typename T, typename Heap, typename CloudType>
00339         unsigned long KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::onePointKnn(const Matrix& query, IndexMatrix& indices, Matrix& dists2, int i, Heap& heap, std::vector<T>& off, const T maxError2, const T maxRadius2, const bool allowSelfMatch, const bool collectStatistics, const bool sortResults) const
00340         {
00341                 fill(off.begin(), off.end(), static_cast<T>(0));
00342                 heap.reset();
00343                 unsigned long leafTouchedCount(0);
00344                 
00345                 if (allowSelfMatch)
00346                 {
00347                         if (collectStatistics)
00348                                 leafTouchedCount += recurseKnn<true, true>(&query.coeff(0, i), 0, 0, heap, off, maxError2, maxRadius2);
00349                         else
00350                                 recurseKnn<true, false>(&query.coeff(0, i), 0, 0, heap, off, maxError2, maxRadius2);
00351                 }
00352                 else
00353                 {
00354                         if (collectStatistics)
00355                                 leafTouchedCount += recurseKnn<false, true>(&query.coeff(0, i), 0, 0, heap, off, maxError2, maxRadius2);
00356                         else
00357                                 recurseKnn<false, false>(&query.coeff(0, i), 0, 0, heap, off, maxError2, maxRadius2);
00358                 }
00359                 
00360                 if (sortResults)
00361                         heap.sort();
00362                 
00363                 heap.getData(indices.col(i), dists2.col(i));
00364                 return leafTouchedCount;
00365         }
00366         
00367         template<typename T, typename Heap, typename CloudType> template<bool allowSelfMatch, bool collectStatistics>
00368         unsigned long KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<T, Heap, CloudType>::recurseKnn(const T* query, const unsigned n, T rd, Heap& heap, std::vector<T>& off, const T maxError2, const T maxRadius2) const
00369         {
00370                 const Node& node(nodes[n]);
00371                 const uint32_t cd(getDim(node.dimChildBucketSize));
00372                 
00373                 if (cd == uint32_t(dim))
00374                 {
00375                         //cerr << "entering bucket " << node.bucket << endl;
00376                         const BucketEntry* bucket(&buckets[node.bucketIndex]);
00377                         const uint32_t bucketSize(getChildBucketSize(node.dimChildBucketSize));
00378                         for (uint32_t i = 0; i < bucketSize; ++i)
00379                         {
00380                                 //cerr << "  " << bucket-> pt << endl;
00381                                 //const T dist(dist2<T>(query, cloud.col(index)));
00382                                 //const T dist((query - cloud.col(index)).squaredNorm());
00383                                 T dist(0);
00384                                 const T* qPtr(query);
00385                                 const T* dPtr(bucket->pt);
00386                                 for (int i = 0; i < this->dim; ++i)
00387                                 {
00388                                         const T diff(*qPtr - *dPtr);
00389                                         dist += diff*diff;
00390                                         qPtr++; dPtr++;
00391                                 }
00392                                 if ((dist <= maxRadius2) &&
00393                                         (dist < heap.headValue()) &&
00394                                         (allowSelfMatch || (dist > numeric_limits<T>::epsilon()))
00395                                 )
00396                                         heap.replaceHead(bucket->index, dist);
00397                                 ++bucket;
00398                         }
00399                         return (unsigned long)(bucketSize);
00400                 }
00401                 else
00402                 {
00403                         const unsigned rightChild(getChildBucketSize(node.dimChildBucketSize));
00404                         unsigned long leafVisitedCount(0);
00405                         T& offcd(off[cd]);
00406                         //const T old_off(off.coeff(cd));
00407                         const T old_off(offcd);
00408                         const T new_off(query[cd] - node.cutVal);
00409                         if (new_off > 0)
00410                         {
00411                                 if (collectStatistics)
00412                                         leafVisitedCount += recurseKnn<allowSelfMatch, true>(query, rightChild, rd, heap, off, maxError2, maxRadius2);
00413                                 else
00414                                         recurseKnn<allowSelfMatch, false>(query, rightChild, rd, heap, off, maxError2, maxRadius2);
00415                                 rd += - old_off*old_off + new_off*new_off;
00416                                 if ((rd <= maxRadius2) &&
00417                                         (rd * maxError2 < heap.headValue()))
00418                                 {
00419                                         offcd = new_off;
00420                                         if (collectStatistics)
00421                                                 leafVisitedCount += recurseKnn<allowSelfMatch, true>(query, n + 1, rd, heap, off, maxError2, maxRadius2);
00422                                         else
00423                                                 recurseKnn<allowSelfMatch, false>(query, n + 1, rd, heap, off, maxError2, maxRadius2);
00424                                         offcd = old_off;
00425                                 }
00426                         }
00427                         else
00428                         {
00429                                 if (collectStatistics)
00430                                         leafVisitedCount += recurseKnn<allowSelfMatch, true>(query, n+1, rd, heap, off, maxError2, maxRadius2);
00431                                 else
00432                                         recurseKnn<allowSelfMatch, false>(query, n+1, rd, heap, off, maxError2, maxRadius2);
00433                                 rd += - old_off*old_off + new_off*new_off;
00434                                 if ((rd <= maxRadius2) &&
00435                                         (rd * maxError2 < heap.headValue()))
00436                                 {
00437                                         offcd = new_off;
00438                                         if (collectStatistics)
00439                                                 leafVisitedCount += recurseKnn<allowSelfMatch, true>(query, rightChild, rd, heap, off, maxError2, maxRadius2);
00440                                         else
00441                                                 recurseKnn<allowSelfMatch, false>(query, rightChild, rd, heap, off, maxError2, maxRadius2);
00442                                         offcd = old_off;
00443                                 }
00444                         }
00445                         return leafVisitedCount;
00446                 }
00447         }
00448         
00449         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapSTL<int,float> >;
00450         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapBruteForceVector<int,float> >;
00451         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapSTL<int,double> >;
00452         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapBruteForceVector<int,double> >;
00453         
00454         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapSTL<int,float>,Eigen::Matrix3Xf>;
00455         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapBruteForceVector<int,float>,Eigen::Matrix3Xf>;
00456         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapSTL<int,double>,Eigen::Matrix3Xd>;
00457         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapBruteForceVector<int,double>,Eigen::Matrix3Xd>;
00458 
00459         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapSTL<int,float>,Eigen::Map<const Eigen::Matrix3Xf, Eigen::Aligned> >;
00460         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<float,IndexHeapBruteForceVector<int,float>,Eigen::Map<const Eigen::Matrix3Xf, Eigen::Aligned> >;
00461         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapSTL<int,double>,Eigen::Map<const Eigen::Matrix3Xd, Eigen::Aligned> >;
00462         template struct KDTreeUnbalancedPtInLeavesImplicitBoundsStackOpt<double,IndexHeapBruteForceVector<int,double>,Eigen::Map<const Eigen::Matrix3Xd, Eigen::Aligned> >;
00463 
00465 }
00466 /* vim: set ts=8 sw=8 tw=0 noexpandtab cindent softtabstop=8 :*/


libnabo
Author(s): Stéphane Magnenat
autogenerated on Sun Feb 10 2019 03:52:20