00001
00028 #include "SurfSet.h"
00029 #include <iostream>
00030 #include <fstream>
00031 #include <string>
00032 #include <opencv/cv.h>
00033 #include <opencv/highgui.h>
00034 #include <opencv2/nonfree/features2d.hpp>
00035 #include <vector>
00036
00037 using namespace std;
00038 using namespace DVision;
00039
00040
00041
00042 void SurfSet::SaveCustom(const std::string &filename) const
00043 {
00044 fstream f(filename.c_str(), ios::out);
00045 if(!f.is_open()) throw string("SurfSet: cannot open ") + filename;
00046
00047 int L = 0;
00048 if(!keys.empty()) L = descriptors.size() / keys.size();
00049
00050 f << keys.size() << " " << L << endl;
00051
00052 vector<cv::KeyPoint>::const_iterator kit;
00053 vector<float>::const_iterator dit = descriptors.begin();
00054 for(kit = keys.begin(); kit != keys.end(); ++kit){
00055 f << kit->pt.x << " "
00056 << kit->pt.y << " "
00057 << kit->angle << " "
00058 << kit->size << " "
00059 << kit->response << " "
00060 << kit->octave << " "
00061 << laplacians[kit - keys.begin()] << " ";
00062
00063 for(int i = 0; i < L; ++i, ++dit){
00064 f << *dit << " ";
00065 }
00066 f << endl;
00067 }
00068
00069 f.close();
00070 }
00071
00072
00073
00074 void SurfSet::LoadCustom(const std::string &filename)
00075 {
00076 keys.resize(0);
00077 descriptors.resize(0);
00078 laplacians.resize(0);
00079 delete m_index; m_index = NULL;
00080
00081 fstream f(filename.c_str(), ios::in);
00082 if(!f.is_open()) throw string("SurfSet: cannot open ") + filename;
00083
00084 int N, L;
00085 f >> N >> L;
00086
00087 if (f.is_open() && !f.fail() ){
00088 keys.resize(N);
00089 descriptors.resize(N * L);
00090 laplacians.resize(N);
00091
00092 vector<float>::iterator dit = descriptors.begin();
00093 for(int i = 0; i < N; ++i){
00094 cv::KeyPoint& k = keys[i];
00095
00096 f >> k.pt.x >> k.pt.y >> k.angle >> k.size >> k.response
00097 >> k.octave >> laplacians[i];
00098
00099 for(int j = 0; j < L; ++j, ++dit){
00100 f >> *dit;
00101 }
00102 }
00103 }else{
00104 throw string("SurfSet: cannot read file ") + filename;
00105 }
00106
00107 f.close();
00108 }
00109
00110
00111
00112 void SurfSet::Extract(const cv::Mat &image, double hessianTh, bool extended)
00113 {
00114
00115
00116 cv::SURF surf;
00117 surf.set("hessianThreshold", hessianTh);
00118 surf.set("extended", extended);
00119
00120 cv::Mat descs;
00121 surf(image, cv::Mat() , this->keys, descs);
00122
00123 if(this->keys.empty())
00124 {
00125 this->descriptors.clear();
00126 this->laplacians.clear();
00127 }
00128 else
00129 {
00130 this->descriptors.resize(this->keys.size() * descs.cols);
00131 this->laplacians.resize(this->keys.size());
00132
00133 vector<cv::KeyPoint>::const_iterator kit;
00134 vector<int>::iterator lit = this->laplacians.begin();
00135 for(kit = this->keys.begin(); kit != this->keys.end(); ++kit, ++lit)
00136 {
00137 *lit = kit->class_id;
00138 }
00139
00140 assert(descs.type() == CV_32F && descs.isContinuous());
00141
00142 std::copy(descs.ptr<float>(), descs.ptr<float>() + (descs.rows * descs.cols),
00143 this->descriptors.begin());
00144 }
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 }
00189
00190
00191
00192 void SurfSet::Compute(const cv::Mat &image,
00193 const std::vector<cv::KeyPoint> &keypoints, bool extended)
00194 {
00195
00196 if(keypoints.empty())
00197 {
00198 this->keys.clear();
00199 this->descriptors.clear();
00200 this->laplacians.clear();
00201 }
00202 else
00203 {
00204 this->keys = keypoints;
00205
00206 cv::SURF surf;
00207 surf.set("extended", extended);
00208
00209 cv::Mat descs;
00210 surf(image, cv::Mat() , this->keys, descs, true);
00211
00212 this->descriptors.resize(this->keys.size() * descs.cols);
00213 this->laplacians.resize(this->keys.size());
00214
00215 vector<cv::KeyPoint>::const_iterator kit;
00216 vector<int>::iterator lit = this->laplacians.begin();
00217 for(kit = this->keys.begin(); kit != this->keys.end(); ++kit, ++lit)
00218 {
00219 *lit = kit->class_id;
00220 }
00221
00222 assert(descs.type() == CV_32F && descs.isContinuous());
00223
00224 std::copy(descs.ptr<float>(), descs.ptr<float>() + (descs.rows * descs.cols),
00225 this->descriptors.begin());
00226 }
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279 }
00280
00281
00282
00283 void SurfSet::CalculateCorrespondences(const SurfSet &B,
00284 vector<int> &corr_A, vector<int> &corr_B,
00285 vector<double> *distances, bool remove_duplicates,
00286 double max_ratio) const
00287 {
00288 calculateCorrespondencesNaive(B, corr_A, corr_B, distances,
00289 remove_duplicates, max_ratio);
00290 }
00291
00292
00293
00294 void SurfSet::CalculateFastCorrespondences(const SurfSet &B,
00295 vector<int> &corr_A, vector<int> &corr_B,
00296 vector<double> *distances, bool remove_duplicates,
00297 double max_ratio)
00298 {
00299 calculateCorrespondencesApproximate(B, corr_A, corr_B, distances,
00300 remove_duplicates, max_ratio);
00301 }
00302
00303
00304
00305 void SurfSet::calculateCorrespondencesApproximate(const SurfSet &B,
00306 std::vector<int> &A_corr, std::vector<int> &B_corr,
00307 std::vector<double> *distances,
00308 bool remove_duplicates, double max_ratio)
00309 {
00310 const int L = GetDescriptorLength();
00311
00312
00313
00314 const cv::Mat B_features( B.keys.size(), L, CV_32F,
00315 const_cast<float*>(&B.descriptors[0]) );
00316
00317
00318 const cv::Mat *queries;
00319 std::vector<int> *f_corr, *q_corr;
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 queries = &B_features;
00337 f_corr = &A_corr;
00338 q_corr = &B_corr;
00339
00340 cv::Mat indices(queries->rows, 2, CV_32S);
00341 cv::Mat dists(queries->rows, 2, CV_32F);
00342
00343 if(!m_index) RecalculateApproximationTree();
00344 m_index->knnSearch(*queries, indices, dists, 2, cv::flann::SearchParams(64));
00345
00346 A_corr.resize(0);
00347 B_corr.resize(0);
00348 if(distances) distances->resize(0);
00349
00350 int* indices_ptr = indices.ptr<int>(0);
00351 float* dists_ptr = dists.ptr<float>(0);
00352
00353 if(!remove_duplicates)
00354 {
00355 if(distances)
00356 {
00357 for (int i=0; i < indices.rows; ++i) {
00358 if (dists_ptr[2*i] < max_ratio * dists_ptr[2*i+1])
00359 {
00360 q_corr->push_back(i);
00361 f_corr->push_back(indices_ptr[2*i]);
00362 distances->push_back(dists_ptr[2*i]);
00363 }
00364 }
00365 }
00366 else
00367 {
00368 for (int i=0; i < indices.rows; ++i) {
00369 if (dists_ptr[2*i] < max_ratio * dists_ptr[2*i+1])
00370 {
00371 q_corr->push_back(i);
00372 f_corr->push_back(indices_ptr[2*i]);
00373 }
00374 }
00375 }
00376 }
00377 else
00378 {
00379 vector<int>::const_iterator it;
00380 if(distances)
00381 {
00382 for (int i=0; i < indices.rows; ++i) {
00383 if (dists_ptr[2*i] < max_ratio * dists_ptr[2*i+1])
00384 {
00385 int i_f = indices_ptr[2*i];
00386 float d_now = dists_ptr[2*i];
00387
00388 it = find(f_corr->begin(), f_corr->end(), i_f);
00389 if(it != f_corr->end())
00390 {
00391 int i_before = it - f_corr->begin();
00392 float d_before = dists_ptr[2*i_before];
00393
00394 if(d_now < d_before)
00395 {
00396
00397 (*q_corr)[i_before] = i;
00398 (*f_corr)[i_before] = i_f;
00399 (*distances)[i_before] = d_now;
00400 }
00401 }
00402 else
00403 {
00404 q_corr->push_back(i);
00405 f_corr->push_back(indices_ptr[2*i]);
00406 distances->push_back(dists_ptr[2*i]);
00407 }
00408 }
00409 }
00410 }
00411 else
00412 {
00413 for (int i=0; i < indices.rows; ++i) {
00414 if (dists_ptr[2*i] < max_ratio * dists_ptr[2*i+1])
00415 {
00416 int i_f = indices_ptr[2*i];
00417 float d_now = dists_ptr[2*i];
00418
00419 it = find(f_corr->begin(), f_corr->end(), i_f);
00420 if(it != f_corr->end())
00421 {
00422 int i_before = it - f_corr->begin();
00423 float d_before = dists_ptr[2*i_before];
00424
00425 if(d_now < d_before)
00426 {
00427
00428 (*q_corr)[i_before] = i;
00429 (*f_corr)[i_before] = i_f;
00430 }
00431 }
00432 else
00433 {
00434 q_corr->push_back(i);
00435 f_corr->push_back(indices_ptr[2*i]);
00436 }
00437 }
00438 }
00439 }
00440 }
00441
00442 }
00443
00444
00445
00446 void SurfSet::calculateCorrespondencesNaive(const SurfSet &B,
00447 vector<int> &corr_A, vector<int> &corr_B,
00448 vector<double> *distances, bool remove_duplicates,
00449 double max_ratio) const
00450 {
00451
00452 const SurfSet& A = *this;
00453
00454 corr_A.clear();
00455 corr_B.clear();
00456
00457 bool deallocate;
00458 if(distances)
00459 {
00460 deallocate = false;
00461 }else{
00462 distances = new vector<double>;
00463 deallocate = true;
00464 }
00465
00466 int L = 0;
00467 if(!A.keys.empty()) L = A.descriptors.size() / A.keys.size();
00468
00469 vector<float>::const_iterator da;
00470 vector<float>::const_iterator db;
00471
00472 da = A.descriptors.begin();
00473 for(unsigned int a = 0; a < A.keys.size(); ++a, da += L)
00474 {
00475 double best_1 = 1e9;
00476 double best_2 = 1e9;
00477 int best_b = 0;
00478
00479 db = B.descriptors.begin();
00480 for(unsigned int b = 0; b < B.keys.size(); ++b, db += L)
00481 {
00482 if(A.laplacians[a] == B.laplacians[b]){
00483 double d = calculateSqDistance(da, db, L);
00484
00485 if(d < best_1){
00486 best_2 = best_1;
00487 best_1 = d;
00488 best_b = b;
00489 }else if(d < best_2){
00490 best_2 = d;
00491 }
00492 }
00493 }
00494
00495
00496 if(best_1 / best_2 < max_ratio*max_ratio)
00497 {
00498
00499 if(remove_duplicates)
00500 {
00501
00502
00503
00504 vector<int>::const_iterator it =
00505 find(corr_B.begin(), corr_B.end(), best_b);
00506
00507 if(it == corr_B.end()){
00508
00509 corr_A.push_back(a);
00510 corr_B.push_back(best_b);
00511 distances->push_back(best_1);
00512 }else{
00513 int corr_idx = it - corr_B.begin();
00514 if(best_1 < (*distances)[ corr_idx ]){
00515
00516 corr_A[corr_idx] = a;
00517 (*distances)[corr_idx] = best_1;
00518 }
00519 }
00520 }
00521 else
00522 {
00523
00524 corr_A.push_back(a);
00525 corr_B.push_back(best_b);
00526 distances->push_back(best_1);
00527 }
00528 }
00529
00530 }
00531
00532 if(deallocate)
00533 delete distances;
00534 else
00535 {
00536
00537 vector<double>::iterator dit;
00538 for(dit = distances->begin(); dit != distances->end(); ++dit)
00539 {
00540 *dit = sqrt(*dit);
00541 }
00542 }
00543
00544 }
00545
00546
00547
00548
00549 double SurfSet::calculateSqDistance(
00550 vector<float>::const_iterator ita, vector<float>::const_iterator itb,
00551 const int L) const
00552 {
00553 double d = 0.0;
00554 for(int i = 0; i < L; i += 4, ita += 4, itb += 4)
00555 {
00556 d += (*ita - *itb)*(*ita - *itb);
00557 d += (*(ita+1) - *(itb+1))*(*(ita+1) - *(itb+1));
00558 d += (*(ita+2) - *(itb+2))*(*(ita+2) - *(itb+2));
00559 d += (*(ita+3) - *(itb+3))*(*(ita+3) - *(itb+3));
00560 }
00561 return d;
00562 }
00563
00564
00565
00566 void SurfSet::RecalculateApproximationTree()
00567 {
00568 if(m_index) delete m_index;
00569
00570 const int L = GetDescriptorLength();
00571 const cv::Mat features( this->keys.size(), L, CV_32F,
00572 const_cast<float*>(&this->descriptors[0]) );
00573
00574
00575 m_index = new cv::flann::Index(features, cv::flann::AutotunedIndexParams());
00576 }
00577
00578
00579
00580 void SurfSet::Save(const std::string &filename) const
00581 {
00582 cv::FileStorage fs(filename.c_str(), cv::FileStorage::WRITE);
00583 save(fs, 0);
00584 }
00585
00586
00587
00588 void SurfSet::save(cv::FileStorage &fs, int idx) const
00589 {
00590 stringstream ss;
00591 ss << "set" << idx;
00592
00593 fs << ss.str() << "{";
00594
00595 fs << "descriptor_length" << GetDescriptorLength();
00596 cv::write(fs, "keypoints", keys);
00597
00598 cv::write(fs, "descriptors", descriptors);
00599 cv::write(fs, "laplacians", laplacians);
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613 fs << "}";
00614 }
00615
00616
00617
00618 void SurfSet::Load(const std::string &filename)
00619 {
00620 cv::FileStorage fs(filename.c_str(), cv::FileStorage::READ);
00621 load(fs, 0);
00622 }
00623
00624
00625 int SurfSet::getPointOctave(const CvSURFPoint& kpt, const CvSURFParams& params)
00626 {
00627 const int HAAR_SIZE0 = 9;
00628 const int HAAR_SIZE_INC = 6;
00629 int octave = 0, layer = 0, best_octave = 0;
00630 float min_diff = FLT_MAX;
00631 for( octave = 1; octave < params.nOctaves; octave++ )
00632 for( layer = 0; layer < params.nOctaveLayers; layer++ )
00633 {
00634 float diff = std::abs(kpt.size - (float)((HAAR_SIZE0 + HAAR_SIZE_INC*layer) << octave));
00635 if( min_diff > diff )
00636 {
00637 min_diff = diff;
00638 best_octave = octave;
00639 if( min_diff == 0 )
00640 return best_octave;
00641 }
00642 }
00643
00644 return best_octave;
00645 }
00646
00647
00648
00649 void SurfSet::load(cv::FileStorage &fs, int idx)
00650 {
00651 keys.resize(0);
00652 descriptors.resize(0);
00653 laplacians.resize(0);
00654 delete m_index; m_index = NULL;
00655
00656 stringstream ss;
00657 ss << "set" << idx;
00658
00659 cv::FileNode fn = fs[ss.str()];
00660
00661 int L = (int)fn["descriptor_length"];
00662
00663 cv::read(fn["keypoints"], keys);
00664
00665 descriptors.resize(keys.size() * L);
00666 laplacians.resize(keys.size());
00667
00668 cv::read(fn["descriptors"], descriptors);
00669 cv::read(fn["laplacians"], laplacians);
00670 }
00671
00672
00673
00674 int SurfSet::getPointOctave(const CvSURFPoint& kpt, const CvSURFParams& params)
00675 const
00676 {
00677
00678 const int HAAR_SIZE0 = 9;
00679 const int HAAR_SIZE_INC = 6;
00680
00681 int octave = 0, layer = 0, best_octave = 0;
00682 float min_diff = FLT_MAX;
00683 for( octave = 1; octave < params.nOctaves; octave++ )
00684 for( layer = 0; layer < params.nOctaveLayers; layer++ )
00685 {
00686 float diff = std::abs(kpt.size -
00687 (float)((HAAR_SIZE0 + HAAR_SIZE_INC*layer) << octave));
00688 if( min_diff > diff )
00689 {
00690 min_diff = diff;
00691 best_octave = octave;
00692 if( min_diff == 0 )
00693 return best_octave;
00694 }
00695 }
00696 return best_octave;
00697 }
00698