00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifndef _KDTREE_H_
00020 #define _KDTREE_H_
00021
00022 #include "Object.h"
00023 #include <stdio.h>
00024 #include <math.h>
00025 #include "KDNode.h"
00026 #include "KDPoint.h"
00027 #include "KDPQueue.h"
00028
00029 namespace RobotFlow {
00030
00031
00032
00033 template <class KDData>
00034 class KDTree : public FD::Object
00035 {
00036 public:
00037
00038
00039
00040
00041
00042 KDTree()
00043 : m_bboxLow(), m_bboxHigh()
00044 {
00045
00046 }
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062 KDTree(KDData **i_dataPts, int i_numPts, int i_dimSize, int i_bucketSize)
00063 : m_numPts(i_numPts), m_dimSize(i_dimSize), m_bucketSize(i_bucketSize),
00064 m_bboxLow(m_dimSize, (KDData)0), m_bboxHigh(m_dimSize, (KDData)0)
00065 {
00066 m_root = NULL;
00067 int *ptsIdx = new int[m_numPts];
00068
00069 if(m_numPts == 0) {
00070 return;
00071 }
00072
00073
00074 for(int i=0; i<m_numPts; ++i) {
00075 ptsIdx[i] = i;
00076 }
00077
00078 SetBBoxes(i_dataPts, ptsIdx);
00079
00080 m_root = RecurBuildTree(i_dataPts, ptsIdx, m_numPts);
00081
00082 delete [] ptsIdx;
00083 }
00084
00085
00086
00087
00088
00089
00090 ~KDTree()
00091 {
00092 delete m_root;
00093 }
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 void printOn(std::ostream &out) const
00104 {
00105 out << "<KDTree " << std::endl;
00106 out << "<NumPts " << m_numPts << " >" << std::endl;
00107 out << "<DimSize " << m_dimSize << " >" << std::endl;
00108 out << "<BucketSize " << m_bucketSize << " >" << std::endl;
00109
00110 out << "<BBoxLow " << std::endl;
00111 m_bboxLow.printOn(out);
00112 out << " >" << std::endl;
00113
00114 out << "<BBoxHigh " << std::endl;
00115 m_bboxHigh.printOn(out);
00116 out << " >" << std::endl;
00117
00118 if (m_root != NULL) {
00119 out << "<KDRoot " << std::endl;
00120 m_root->printOn(out);
00121 out << " >" << std::endl;
00122 }
00123
00124 out << " >" << std::endl;
00125 }
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135 void readFrom(std::istream &in)
00136 {
00137 std::string tag;
00138
00139 while (1) {
00140 char ch;
00141 in >> ch;
00142
00143 if (ch == '>') {
00144 break;
00145 }
00146 else if (ch != '<') {
00147 throw new FD::GeneralException ("KDTree::readFrom : Parse error: '<' expected",__FILE__,__LINE__);
00148 }
00149
00150 in >> tag;
00151
00152 if (tag == "KDTree") {
00153 continue;
00154 }
00155 else if (tag == "NumPts") {
00156 in >> m_numPts;
00157 }
00158 else if (tag == "DimSize") {
00159 in >> m_dimSize;
00160 }
00161 else if (tag == "BucketSize") {
00162 in >> m_bucketSize;
00163 }
00164 else if (tag == "BBoxLow") {
00165 try {
00166 m_bboxLow.readFrom(in);
00167 }
00168 catch (FD::GeneralException *e) {
00169 throw e->add(new FD::GeneralException("In KDTree::readFrom : ",__FILE__,__LINE__));
00170 }
00171 }
00172 else if (tag == "BBoxHigh") {
00173 try {
00174 m_bboxHigh.readFrom(in);
00175 }
00176 catch (FD::GeneralException *e) {
00177 throw e->add(new FD::GeneralException("In KDTree::readFrom : ",__FILE__,__LINE__));
00178 }
00179 }
00180 else if (tag == "KDRoot") {
00181
00182 try {
00183 m_root = new KDNode<KDData>();
00184 m_root->readFrom(in);
00185 }
00186 catch (FD::GeneralException *e) {
00187 throw e->add(new FD::GeneralException("In KDTree::readFrom : ",__FILE__,__LINE__));
00188 }
00189 }
00190 else {
00191 throw new FD::GeneralException ("KDTree::readFrom : Unknown argument: " + tag,__FILE__,__LINE__);
00192 }
00193
00194 if (!in) {
00195 throw new FD::GeneralException ("KDTree::readFrom : Parse error trying to build " + tag,__FILE__,__LINE__);
00196 }
00197
00198 in >> tag;
00199 if (tag != ">") {
00200 throw new FD::GeneralException ("KDTree::readFrom : Parse error: '>' expected ",__FILE__,__LINE__);
00201 }
00202 }
00203 }
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228 void NNSearch(KDPoint<KDData> *i_qp, int i_maxNumNodes, double i_errBnd,
00229 int i_numNN, bool i_squareDist, double *o_nnDist, KDPoint<KDData> *o_nn)
00230 {
00231 int numVisited = 0;
00232 double maxErr = (1.0 + i_errBnd)*(1.0 + i_errBnd);
00233 double dist = BBoxDist2Point(i_qp);
00234 KDPQueue<double, KDPoint<KDData> > nnQueue(i_numNN);
00235
00236
00237 if (m_root == NULL) {
00238 throw new FD::GeneralException ("KDTree::NNSearch : nearest neighbor search requires the KDTree to contain reference points. ",__FILE__,__LINE__);
00239 }
00240
00241 if (i_qp == NULL) {
00242 throw new FD::GeneralException ("KDTree::NNSearch : NULL query point. ",__FILE__,__LINE__);
00243 }
00244
00245 if (o_nnDist == NULL) {
00246 throw new FD::GeneralException ("KDTree::NNSearch : NULL nearest neighbor distances array. ",__FILE__,__LINE__);
00247 }
00248
00249 if (o_nn == NULL) {
00250 throw new FD::GeneralException ("KDTree::NNSearch : NULL nearest neighbor points array. ",__FILE__,__LINE__);
00251 }
00252
00253 if (i_qp->GetDimSize() != m_dimSize) {
00254 throw new FD::GeneralException ("KDTree::NNSearch : query point dimension size differs from KDTree reference points. ",__FILE__,__LINE__);
00255 }
00256
00257 if (i_numNN == 0) {
00258 return;
00259 }
00260
00261 if (i_numNN > m_numPts) {
00262 throw new FD::GeneralException ("KDTree::NNSearch : cannot search for more nearest neighbors than the number of reference points in KDTree. ",__FILE__,__LINE__);
00263 }
00264
00265
00266 m_root->NNSearch(i_qp, i_maxNumNodes, numVisited, maxErr, dist, &nnQueue);
00267
00268
00269 if (i_squareDist) {
00270 for (int i=0; i<i_numNN; ++i) {
00271 o_nnDist[i] = nnQueue.GetIthKey(i);
00272 o_nn[i] = *(nnQueue.GetIthElement(i));
00273 }
00274 }
00275 else {
00276 for (int i=0; i<i_numNN; ++i) {
00277 o_nnDist[i] = sqrt(nnQueue.GetIthKey(i));
00278 o_nn[i] = *(nnQueue.GetIthElement(i));
00279 }
00280 }
00281 }
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 void NNSearch(KDPoint<KDData> *i_qp, int i_maxNumNodes, double i_errBnd,
00308 int i_numNN, bool i_squareDist, double *o_nnDist, int *o_nnIdx)
00309 {
00310 int numVisited = 0;
00311 double maxErr = (1.0 + i_errBnd)*(1.0 + i_errBnd);
00312 double dist = BBoxDist2Point(i_qp);
00313 KDPQueue<double, int> nnQueue(i_numNN);
00314
00315
00316 if (m_root == NULL) {
00317 throw new FD::GeneralException ("KDTree::NNSearch : nearest neighbor search requires the KDTree to contain reference points. ",__FILE__,__LINE__);
00318 }
00319
00320 if (i_qp == NULL) {
00321 throw new FD::GeneralException ("KDTree::NNSearch : NULL query point. ",__FILE__,__LINE__);
00322 }
00323
00324 if (o_nnDist == NULL) {
00325 throw new FD::GeneralException ("KDTree::NNSearch : NULL nearest neighbor distances array. ",__FILE__,__LINE__);
00326
00327 }
00328
00329 if (o_nnIdx == NULL) {
00330 throw new FD::GeneralException ("KDTree::NNSearch : NULL nearest neighbor indexes array. ",__FILE__,__LINE__);
00331 }
00332
00333 if (i_qp->GetDimSize() != m_dimSize) {
00334 throw new FD::GeneralException ("KDTree::NNSearch : query point dimension size differs from KDTree reference points. ",__FILE__,__LINE__);
00335 }
00336
00337 if (i_numNN == 0) {
00338 return;
00339 }
00340
00341 if (i_numNN > m_numPts) {
00342 throw new FD::GeneralException ("KDTree::NNSearch : cannot search for more nearest neighbors than the number of reference points in KDTree. ",__FILE__,__LINE__);
00343 }
00344
00345
00346 m_root->NNSearch(i_qp, i_maxNumNodes, numVisited, maxErr, dist, &nnQueue);
00347
00348
00349 if (i_squareDist) {
00350 for (int i=0; i<i_numNN; ++i) {
00351 o_nnDist[i] = nnQueue.GetIthKey(i);
00352 o_nnIdx[i] = *(nnQueue.GetIthElement(i));
00353 }
00354 }
00355 else {
00356 for (int i=0; i<i_numNN; ++i) {
00357 o_nnDist[i] = sqrt(nnQueue.GetIthKey(i));
00358 o_nnIdx[i] = *(nnQueue.GetIthElement(i));
00359 }
00360 }
00361 }
00362
00363 private:
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376 double BBoxDist2Point(KDPoint<KDData> *i_qp)
00377 {
00378
00379 double dist = 0.0;
00380 double tmp;
00381
00382 for (int d = 0; d < m_dimSize; ++d) {
00383 if (i_qp->GetCoord(d) < m_bboxLow.GetCoord(d)) {
00384
00385 tmp = m_bboxLow.GetCoord(d) - i_qp->GetCoord(d);
00386 dist += tmp*tmp;
00387 }
00388 else if (i_qp->GetCoord(d) > m_bboxHigh.GetCoord(d)) {
00389
00390 tmp = i_qp->GetCoord(d) - m_bboxHigh.GetCoord(d);
00391 dist += tmp*tmp;
00392 }
00393 }
00394
00395 return dist;
00396 }
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 void SetBBoxes(KDData **i_points, int *i_ptsIdx)
00409 {
00410
00411 for (int d=0; d<m_dimSize; ++d) {
00412
00413 KDData low = i_points[i_ptsIdx[0]][d];
00414
00415 KDData high = i_points[i_ptsIdx[0]][d];
00416
00417 for (int i=0; i<m_numPts; ++i) {
00418 KDData cur = i_points[i_ptsIdx[i]][d];
00419 if (cur < low) {
00420 low = cur;
00421 }
00422 else if (cur > high) {
00423 high = cur;
00424 }
00425 }
00426
00427 m_bboxLow.SetCoord(d, low);
00428 m_bboxHigh.SetCoord(d, high);
00429 }
00430 }
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 KDNode<KDData> *RecurBuildTree(KDData **i_points, int *i_ptsIdx, int i_nPts)
00449 {
00450
00451 if (i_nPts <= m_bucketSize) {
00452 if (i_nPts == 0) {
00453
00454 return NULL;
00455 }
00456 else {
00457
00458 return new KDNode<KDData>(i_points, i_ptsIdx, i_nPts , m_dimSize);
00459 }
00460 }
00461
00462
00463
00464 int cd;
00465
00466 KDData cv;
00467
00468 int lowNum;
00469
00470 KDNode<KDData> *lowNode, *highNode;
00471
00472
00473 StdSplit(i_points, i_ptsIdx, i_nPts, cd, cv, lowNum);
00474
00475
00476 KDData lv = m_bboxLow.GetCoord(cd);
00477 KDData hv = m_bboxHigh.GetCoord(cd);
00478
00479
00480 m_bboxHigh.SetCoord(cd, cv);
00481 lowNode = RecurBuildTree(i_points, i_ptsIdx, lowNum);
00482
00483
00484 m_bboxHigh.SetCoord(cd, hv);
00485
00486
00487 m_bboxLow.SetCoord(cd, cv);
00488 highNode = RecurBuildTree(i_points, i_ptsIdx + lowNum, i_nPts-lowNum);
00489
00490 m_bboxLow.SetCoord(cd, lv);
00491
00492
00493 return new KDNode<KDData>(cd, cv, lv, hv, lowNode, highNode);
00494 }
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511 void StdSplit(KDData **i_points, int *i_ptsIdx, int i_nPts, int &o_cd,
00512 KDData &o_cv, int &o_lowNum)
00513 {
00514
00515 o_cd = MaxSpread(i_points, i_ptsIdx, i_nPts);
00516
00517
00518 o_lowNum = i_nPts >> 1;
00519
00520 MedianSplit(i_points, i_ptsIdx, i_nPts, o_cd, o_cv, o_lowNum);
00521 }
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537 int MaxSpread(KDData **i_points, int *i_ptsIdx, int i_nPts)
00538 {
00539 int maxDim = 0;
00540 KDData maxSprd = (KDData)0;
00541
00542 if (i_nPts == 0) {
00543 return maxDim;
00544 }
00545
00546 for (int d=0; d<m_dimSize; ++d) {
00547 KDData sprd = DimSpread(i_points, i_ptsIdx, i_nPts, d);
00548
00549 if (sprd > maxSprd) {
00550 maxSprd = sprd;
00551 maxDim = d;
00552 }
00553 }
00554
00555 return maxDim;
00556 }
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573 KDData DimSpread(KDData **i_points, int *i_ptsIdx, int i_nPts, int i_dim)
00574 {
00575 KDData min = i_points[i_ptsIdx[0]][i_dim];
00576 KDData max = i_points[i_ptsIdx[0]][i_dim];
00577
00578 for (int i=0; i<i_nPts; ++i) {
00579 KDData cur = i_points[i_ptsIdx[i]][i_dim];
00580
00581 if (cur < min) {
00582 min = cur;
00583 }
00584 else if (cur > max) {
00585 max = cur;
00586 }
00587 }
00588
00589
00590 return (max - min);
00591 }
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608 void MedianSplit(KDData **i_points, int *i_ptsIdx, int i_nPts, int i_dim,
00609 KDData &o_cv, int i_lowIdx)
00610 {
00611
00612 int l = 0;
00613
00614 int r = i_nPts-1;
00615
00616 while (l < r) {
00617
00618 int i = (r+l)/2;
00619 int k = 0;
00620
00621
00622 if (i_points[i_ptsIdx[i]][i_dim] > i_points[i_ptsIdx[r]][i_dim]) {
00623 SwapPtsIdx(i_ptsIdx,i,r);
00624 }
00625
00626
00627 SwapPtsIdx(i_ptsIdx,l,i);
00628
00629 KDData pivot = i_points[i_ptsIdx[l]][i_dim];
00630 i = l;
00631 k = r;
00632
00633
00634 while (1) {
00635 while (i_points[i_ptsIdx[++i]][i_dim] < pivot);
00636 while (i_points[i_ptsIdx[--k]][i_dim] > pivot);
00637 if (i < k) {
00638 SwapPtsIdx(i_ptsIdx,i,k);
00639 }
00640 else {
00641 break;
00642 }
00643 }
00644
00645
00646 SwapPtsIdx(i_ptsIdx,l,k);
00647
00648
00649 if (k > i_lowIdx) {
00650 r = k-1;
00651 }
00652 else if (k < i_lowIdx) {
00653 l = k+1;
00654 }
00655 else {
00656
00657 break;
00658 }
00659 }
00660
00661
00662 if (i_lowIdx > 0) {
00663 KDData c = i_points[i_ptsIdx[0]][i_dim];
00664 int k = 0;
00665
00666 for (int i = 1; i < i_lowIdx; i++) {
00667 if (i_points[i_ptsIdx[i]][i_dim] > c) {
00668 c = i_points[i_ptsIdx[i]][i_dim];
00669 k = i;
00670 }
00671 }
00672
00673 SwapPtsIdx(i_ptsIdx, i_lowIdx-1, k);
00674 }
00675
00676
00677 o_cv = (i_points[i_ptsIdx[i_lowIdx-1]][i_dim] + i_points[i_ptsIdx[i_lowIdx]][i_dim])/(KDData)2;
00678 }
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690 void SwapPtsIdx(int *i_ptsIdx, int i_i, int i_j)
00691 {
00692 int tmp = i_ptsIdx[i_i];
00693 i_ptsIdx[i_i] = i_ptsIdx[i_j];
00694 i_ptsIdx[i_j] = tmp;
00695 }
00696
00697 private:
00698
00699 int m_numPts;
00700
00701 int m_dimSize;
00702
00703 int m_bucketSize;
00704
00705
00706 KDPoint<KDData> m_bboxLow;
00707
00708
00709 KDPoint<KDData> m_bboxHigh;
00710
00711 KDNode<KDData> *m_root;
00712
00713
00714
00715 int m_numVisited;
00716
00717 double m_maxErr;
00718 };
00719
00720 }
00721
00722 #endif