00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "SIFT/VisualObjectMatch.H"
00039 #include "SIFT/VisualObject.H"
00040 #include "SIFT/KDTree.H"
00041 #include "SIFT/SIFThough.H"
00042 #include "Image/MatrixOps.H"
00043 #include "Image/CutPaste.H"
00044 #include "Image/DrawOps.H"
00045
00046
00047 VisualObjectMatch::VisualObjectMatch(const rutz::shared_ptr<VisualObject>& voref,
00048 const rutz::shared_ptr<VisualObject>& votest,
00049 const VisualObjectMatchAlgo algo,
00050 const uint thresh) :
00051 itsVoRef(voref), itsVoTest(votest), itsMatches(), itsKDTree(),
00052 itsHasAff(false), itsAff(),
00053 itsHasKpAvgDist(false), itsHasAfAvgDist(false)
00054 {
00055 uint nm = 0U;
00056
00057 switch (algo)
00058 {
00059 case VOMA_SIMPLE: nm = matchSimple(thresh); break;
00060 case VOMA_KDTREE: nm = matchKDTree(thresh, 0); break;
00061 case VOMA_KDTREEBBF: nm = matchKDTree(thresh, 40); break;
00062 }
00063 LDEBUG("Got %u KP matches (th=%d) btw %s and %s",
00064 nm, thresh, voref->getName().c_str(), votest->getName().c_str());
00065 }
00066
00067
00068 VisualObjectMatch::VisualObjectMatch(const rutz::shared_ptr<KDTree>& kdref,
00069 const rutz::shared_ptr<VisualObject>& votest,
00070 const VisualObjectMatchAlgo algo,
00071 const uint thresh) :
00072 itsVoRef(new VisualObject("KDref")), itsVoTest(votest), itsMatches(),
00073 itsKDTree(kdref), itsHasAff(false), itsAff(),
00074 itsHasKpAvgDist(false), itsHasAfAvgDist(false)
00075 {
00076 uint nm = 0U;
00077
00078 switch (algo)
00079 {
00080 case VOMA_SIMPLE:
00081 LFATAL("Can't use Simple match when constructing from a KDTree"); break;
00082 case VOMA_KDTREE: nm = matchKDTree(thresh, 0); break;
00083 case VOMA_KDTREEBBF: nm = matchKDTree(thresh, 40); break;
00084 }
00085 LDEBUG("Got %u KP matches (th=%d) btw %s and %s",
00086 nm, thresh, itsVoRef->getName().c_str(), votest->getName().c_str());
00087 }
00088
00089
00090 VisualObjectMatch::VisualObjectMatch(const rutz::shared_ptr<VisualObject>& voref,
00091 const rutz::shared_ptr<VisualObject>& votest,
00092 const std::vector<KeypointMatch>& kpm) :
00093 itsVoRef(voref), itsVoTest(votest), itsMatches(kpm),
00094 itsKDTree(), itsHasAff(false), itsAff(),
00095 itsHasKpAvgDist(false), itsHasAfAvgDist(false)
00096 {
00097 LDEBUG("Got %"ZU" KP matches btw %s and %s",
00098 itsMatches.size(), itsVoRef->getName().c_str(),
00099 itsVoTest->getName().c_str());
00100 }
00101
00102
00103 VisualObjectMatch::~VisualObjectMatch()
00104 { }
00105
00106
00107 uint VisualObjectMatch::prune(const uint maxn, const uint minn)
00108 {
00109
00110 if (itsMatches.size() <= minn) return 0U;
00111 uint ndel = 0U;
00112
00113
00114
00115
00116
00117 const uint targetn1 = maxn * 2U; uint distthresh = 9U;
00118 while (itsMatches.size() > targetn1 && distthresh > 5)
00119 ndel += pruneByDist(distthresh--, targetn1);
00120 LDEBUG("After pruning by distance: total %d outliers pruned.", ndel);
00121
00122
00123 const uint targetn2 = (maxn + minn) / 2; uint iter = 2U;
00124 while (itsMatches.size() > targetn2 && iter > 0)
00125 { ndel += pruneByHough(0.6F, targetn2); --iter; }
00126 LDEBUG("After pruning by Hough: total %d outliers pruned.", ndel);
00127
00128
00129
00130
00131 const uint targetn3 = minn; float dist = 5.0F; iter = 3U;
00132 while (itsMatches.size() > targetn3 && iter > 0)
00133 { ndel += pruneByAff(dist, targetn3); dist *= 0.75F; --iter; }
00134 LDEBUG("After pruning by affine: total %d outliers pruned.", ndel);
00135
00136 return ndel;
00137 }
00138
00139
00140 uint VisualObjectMatch::pruneByDist(const uint thresh, const uint minn)
00141 {
00142
00143 if (itsMatches.size() <= minn) return 0U;
00144 std::vector<KeypointMatch>::iterator itr = itsMatches.begin();
00145 const uint t2 = thresh * thresh; uint ndel = 0U;
00146
00147 while (itr < itsMatches.end())
00148 {
00149 if (100U * itr->distSq >= t2 * itr->distSq2)
00150 {
00151 itr = itsMatches.erase(itr); ++ ndel;
00152 if (itsMatches.size() <= minn) return ndel;
00153 }
00154 else ++ itr;
00155
00156 }
00157 return ndel;
00158 }
00159
00160
00161 uint VisualObjectMatch::pruneByHough(const float rangefac, const uint minn)
00162 {
00163
00164 if (itsMatches.size() <= minn) return 0U;
00165 std::vector<KeypointMatch>::iterator
00166 itr = itsMatches.begin(), stop = itsMatches.end();
00167
00168
00169 float dxmi = 1.0e30F, dxma = -1.0e30F;
00170 float dymi = 1.0e30F, dyma = -1.0e30F;
00171 float domi = 1.0e30F, doma = -1.0e30F;
00172 float dsmi = 1.0e30F, dsma = -1.0e30F;
00173 while (itr < stop)
00174 {
00175
00176
00177 float dx, dy, doo, ds;
00178 getKdiff(*itr, dx, dy, doo, ds);
00179
00180 if (dx < dxmi) dxmi = dx; else if (dx > dxma) dxma = dx;
00181 if (dy < dymi) dymi = dy; else if (dy > dyma) dyma = dy;
00182 if (ds < dsmi) dsmi = ds; else if (ds > dsma) dsma = ds;
00183 if (doo < domi) domi = doo; else if (doo > doma) doma = doo;
00184
00185 ++ itr;
00186 }
00187
00188
00189
00190
00191
00192
00193 if (dxma - dxmi < 1.0F) dxma = dxmi + 1.0F;
00194 if (dyma - dymi < 1.0F) dyma = dymi + 1.0F;
00195 if (doma - domi < 1.0e-3F) doma = domi + 1.0e-3F;
00196 if (dsma - dsmi < 1.0e-3F) dsma = dsmi + 1.0e-3F;
00197
00198
00199 const float facx = 8.0F / (dxma - dxmi);
00200 const float facy = 8.0F / (dyma - dymi);
00201 const float faco = 8.0F / (doma - domi);
00202 const float facs = 8.0F / (dsma - dsmi);
00203
00204
00205 SIFThough h;
00206 itr = itsMatches.begin();
00207 while (itr < stop)
00208 {
00209
00210 float dx, dy, doo, ds;
00211 getKdiff(*itr, dx, dy, doo, ds);
00212
00213
00214 h.addValue((dx - dxmi) * facx, (dy - dymi) * facy,
00215 (doo - domi) * faco, (ds - dsmi) * facs, 1.0F);
00216
00217 ++ itr;
00218 }
00219
00220
00221 float peakx, peaky, peako, peaks;
00222 h.getPeak(peakx, peaky, peako, peaks);
00223
00224
00225 peakx = peakx / facx + dxmi;
00226 peaky = peaky / facy + dymi;
00227 peako = peako / faco + domi;
00228 peaks = peaks / facs + dsmi;
00229
00230
00231
00232 const float rxmi = peakx - rangefac * (dxma - dxmi);
00233 const float rxma = peakx + rangefac * (dxma - dxmi);
00234 const float rymi = peaky - rangefac * (dyma - dymi);
00235 const float ryma = peaky + rangefac * (dyma - dymi);
00236 const float romi = peako - rangefac * (doma - domi);
00237 const float roma = peako + rangefac * (doma - domi);
00238 const float rsmi = peaks - rangefac * (dsma - dsmi);
00239 const float rsma = peaks + rangefac * (dsma - dsmi);
00240
00241
00242
00243 uint ndel = 0U; itr = itsMatches.begin();
00244 while (itr < itsMatches.end())
00245 {
00246
00247 float dx, dy, doo, ds;
00248 getKdiff(*itr, dx, dy, doo, ds);
00249
00250
00251 if (dx < rxmi || dx > rxma ||
00252 dy < rymi || dy > ryma ||
00253 doo < romi || doo > roma ||
00254 ds < rsmi || ds > rsma)
00255 {
00256 itr = itsMatches.erase(itr); ++ ndel;
00257 if (itsMatches.size() <= minn) return ndel;
00258 }
00259 else
00260 ++ itr;
00261 }
00262 return ndel;
00263 }
00264
00265
00266 uint VisualObjectMatch::pruneByAff(const float dist, const uint minn)
00267 {
00268
00269 if (itsMatches.size() <= minn) return 0U;
00270 uint ndel = 0U; const float dist2 = dist * dist;
00271
00272
00273 computeAffine();
00274
00275
00276 std::vector<KeypointMatch>::iterator itr = itsMatches.begin();
00277 while (itr < itsMatches.end())
00278 {
00279
00280
00281 const float d = itsAff.getResidualDistSq(*itr);
00282
00283 if (d > dist2)
00284 {
00285
00286 itr = itsMatches.erase(itr); ++ndel;
00287
00288
00289 if (itsMatches.size() <= minn) return ndel;
00290 }
00291 else
00292 ++ itr;
00293 }
00294 return ndel;
00295 }
00296
00297
00298 void VisualObjectMatch::computeAffine()
00299 {
00300 const uint nmatches = itsMatches.size();
00301
00302
00303 if (nmatches < 3)
00304 {
00305 LDEBUG("Too few matches (%u) -- RETURNING IDENTITY", nmatches);
00306 itsAff = SIFTaffine();
00307 return;
00308 }
00309
00310
00311 Image<float> A(3, nmatches, NO_INIT);
00312 Image<float> b(2, nmatches, NO_INIT);
00313
00314 for (uint i = 0; i < nmatches; i ++)
00315 {
00316 rutz::shared_ptr<Keypoint> refkp = itsMatches[i].refkp;
00317 rutz::shared_ptr<Keypoint> tstkp = itsMatches[i].tstkp;
00318
00319 A.setVal(0, i, refkp->getX());
00320 A.setVal(1, i, refkp->getY());
00321 A.setVal(2, i, 1.0f);
00322
00323 b.setVal(0, i, tstkp->getX());
00324 b.setVal(1, i, tstkp->getY());
00325 }
00326
00327 try
00328 {
00329
00330 Image<float> At = transpose(A);
00331
00332 Image<float> x =
00333 matrixMult(matrixMult(matrixInv(matrixMult(At, A)), At), b);
00334
00335
00336 itsAff.m1 = x.getVal(0, 0); itsAff.m3 = x.getVal(1, 0);
00337 itsAff.m2 = x.getVal(0, 1); itsAff.m4 = x.getVal(1, 1);
00338 itsAff.tx = x.getVal(0, 2); itsAff.ty = x.getVal(1, 2);
00339
00340
00341 itsHasAff = true;
00342 }
00343 catch (SingularMatrixException& e)
00344 {
00345 LDEBUG("Couldn't invert matrix -- RETURNING IDENTITY");
00346 itsAff = SIFTaffine();
00347 itsHasAff = false;
00348 }
00349 }
00350
00351
00352 bool VisualObjectMatch::checkSIFTaffine(const float maxrot,
00353 const float maxscale,
00354 const float maxshear)
00355 {
00356 if (itsHasAff == false) computeAffine();
00357 if (itsAff.isInversible() == false) return false;
00358
00359 float theta, sx, sy, str;
00360 itsAff.decompose(theta, sx, sy, str);
00361
00362 LDEBUG("theta=%fdeg sx=%f sy=%f shx=%f shy=%f",
00363 theta * 180.0F / M_PI, sx, sy, str/sx, str/sy);
00364
00365
00366 if (fabsf(theta) > maxrot) return false;
00367
00368
00369 if (fabsf(sx) > maxscale || fabsf(sx) < 1.0F / maxscale) return false;
00370 if (fabsf(sy) > maxscale || fabsf(sy) < 1.0F / maxscale) return false;
00371
00372
00373
00374 if (fabsf(str/sx) > maxshear) return false;
00375 if (fabsf(str/sy) > maxshear) return false;
00376
00377
00378 return true;
00379 }
00380
00381
00382 float VisualObjectMatch::getScore(const float kcoeff,
00383 const float acoeff)
00384 {
00385 if(!itsHasKpAvgDist) getKeypointAvgDist();
00386 if(!itsHasAfAvgDist) getAffineAvgDist();
00387
00388
00389
00390
00391 float kp = itsKpAvgDist; if(kp < .05) kp = .05;
00392
00393
00394
00395
00396
00397 float af = itsAfAvgDist; if(af < .05) af = .05;
00398
00399
00400
00401
00402 float nm = 0.05F * float(itsMatches.size()); if(nm > 1.0) nm = 1.0F;
00403
00404
00405
00406 float score = kcoeff/kp + acoeff/af + nm;
00407
00408 return score;
00409 }
00410
00411
00412 float VisualObjectMatch::getSalScore(const float wcoeff,
00413 const float hcoeff )
00414 {
00415
00416 float sscore = getSalDiff();
00417 if(sscore == -1.0F) return sscore;
00418 float sdist = getSalDist();
00419 if(sdist == -1.0F) return sscore;
00420
00421 float maxDist = 0.0;
00422 rutz::shared_ptr<VisualObject> obj1 = getVoRef();
00423 rutz::shared_ptr<VisualObject> obj2 = getVoTest();
00424 if(wcoeff == 0.0F && hcoeff == 0.0F)
00425 {
00426 uint w1 = obj1->getImage().getWidth();
00427 uint h1 = obj1->getImage().getHeight();
00428 float dist1 = sqrt(w1*w1 + h1*h1);
00429 uint w2 = obj2->getImage().getWidth();
00430 uint h2 = obj2->getImage().getHeight();
00431 float dist2 = sqrt(w2*w2 + h2*h2);
00432 maxDist = dist1 + dist2;
00433 }
00434 else
00435 {
00436 maxDist = sqrt(wcoeff * wcoeff + hcoeff * hcoeff);
00437 }
00438 float dscore = 1.0 - sdist/maxDist;
00439 LINFO("dist score : 1.0 - %f/%f = %f", sdist, maxDist, dscore);
00440
00441 float score = dscore * sscore;
00442 LINFO("dist * sim: %f * %f = %f", dscore, sscore, score);
00443
00444 return score;
00445 }
00446
00447
00448 float VisualObjectMatch::getSalDiff()
00449 {
00450
00451 rutz::shared_ptr<VisualObject> obj1 = getVoRef();
00452 rutz::shared_ptr<VisualObject> obj2 = getVoTest();
00453 const std::vector<float>& feat1 = obj1->getFeatures();
00454 const std::vector<float>& feat2 = obj2->getFeatures();
00455 bool compfeat = ((feat1.size() > 0) && (feat1.size() == feat2.size()));
00456 if (!compfeat) return -1.0F;
00457
00458
00459 float cval = 0.0;
00460 for(uint i = 0; i < feat1.size(); i++)
00461 {
00462 const float val = feat1[i] - feat2[i];
00463 cval += val * val;
00464 }
00465 cval = sqrtf(cval / feat1.size());
00466 float sscore = 1.0F - cval;
00467 LDEBUG("cval: %f: score: %f", cval, sscore);
00468 return sscore;
00469 }
00470
00471
00472 float VisualObjectMatch::getSalDist()
00473 {
00474
00475 rutz::shared_ptr<VisualObject> obj1 = getVoRef();
00476 rutz::shared_ptr<VisualObject> obj2 = getVoTest();
00477 Point2D<int> salpt1 = obj1->getSalPoint();
00478 Point2D<int> salpt2 = obj2->getSalPoint();
00479 bool compfeat = ((salpt1.i != -1) && (salpt2.i != -1));
00480 if(!compfeat) return -1.0F;
00481
00482
00483 SIFTaffine aff = getSIFTaffine();
00484 float u, v; aff.transform(salpt1.i, salpt1.j, u, v);
00485 float dist = salpt2.distance(Point2D<int>(int(u+0.5F),int(v+0.5F)));
00486 LDEBUG("pos1: (%d,%d) -> (%f,%f) & pos2: (%d,%d): dist: %f",
00487 salpt1.i, salpt1.j, u, v, salpt2.i, salpt2.j, dist);
00488 return dist;
00489 }
00490
00491
00492 float VisualObjectMatch::getKeypointAvgDist()
00493 {
00494 if(itsHasKpAvgDist) return itsKpAvgDist;
00495 if (itsMatches.size() == 0U) return 1.0e30F;
00496
00497 float d = 0.0F;
00498 std::vector<KeypointMatch>::const_iterator
00499 itr = itsMatches.begin(), stop = itsMatches.end();
00500 float fac = 1.0F; if (itr != stop) fac /= float(itr->refkp->getFVlength());
00501
00502 while(itr != stop)
00503 {
00504 d += sqrtf(float(itr->refkp->distSquared(itr->tstkp)) * fac); ++itr;
00505 }
00506
00507 itsKpAvgDist = 0.1F * d / float(itsMatches.size());
00508 itsHasKpAvgDist = true;
00509
00510 return itsKpAvgDist;
00511 }
00512
00513
00514 float VisualObjectMatch::getAffineAvgDist()
00515 {
00516 if(itsHasAfAvgDist) return itsAfAvgDist;
00517 if (itsMatches.size() < 3U) return 1.0e30F;
00518 if (itsHasAff == false) computeAffine();
00519
00520 float d = 0.0F;
00521 std::vector<KeypointMatch>::const_iterator
00522 itr = itsMatches.begin(), stop = itsMatches.end();
00523
00524 while(itr != stop)
00525 { d += sqrtf(itsAff.getResidualDistSq(*itr)); ++itr; }
00526
00527 itsAfAvgDist = d / float(itsMatches.size());
00528 itsHasAfAvgDist = true;
00529
00530 return itsAfAvgDist;
00531 }
00532
00533
00534 uint VisualObjectMatch::matchSimple(const uint thresh)
00535 {
00536 const uint refnkp = itsVoRef->numKeypoints();
00537 const uint tstnkp = itsVoTest->numKeypoints();
00538 if (refnkp == 0 || tstnkp == 0) return 0U;
00539
00540 const int maxdsq = itsVoRef->getKeypoint(0)->maxDistSquared();
00541 uint nmatches = 0; uint thresh2 = thresh * thresh;
00542
00543
00544 for (uint i = 0; i < tstnkp; i++)
00545 {
00546 int distsq1 = maxdsq, distsq2 = maxdsq;
00547 rutz::shared_ptr<Keypoint> tstkey = itsVoTest->getKeypoint(i);
00548 rutz::shared_ptr<Keypoint> refkey;
00549
00550
00551 for (uint j = 0; j < refnkp; j ++)
00552 {
00553 rutz::shared_ptr<Keypoint> rkey = itsVoRef->getKeypoint(j);
00554 const int distsq = rkey->distSquared(tstkey);
00555
00556
00557 if (distsq < distsq1)
00558 {
00559 distsq2 = distsq1;
00560 distsq1 = distsq;
00561 refkey = rkey;
00562 }
00563 else if (distsq < distsq2)
00564 distsq2 = distsq;
00565 }
00566
00567
00568 if (100U * distsq1 < thresh2 * distsq2)
00569 {
00570 KeypointMatch m;
00571 m.refkp = refkey; m.tstkp = tstkey;
00572 m.distSq = distsq1; m.distSq2 = distsq2;
00573 itsMatches.push_back(m);
00574 ++ nmatches;
00575 }
00576 }
00577
00578
00579 return nmatches;
00580 }
00581
00582
00583 uint VisualObjectMatch::matchKDTree(const uint thresh, const int bbf)
00584 {
00585 const uint refnkp = itsVoRef->numKeypoints();
00586 const uint tstnkp = itsVoTest->numKeypoints();
00587 if (refnkp == 0 || tstnkp == 0) return 0U;
00588 const int maxdsq = itsVoRef->getKeypoint(0)->maxDistSquared();
00589 uint nmatches = 0; uint thresh2 = thresh * thresh;
00590
00591
00592
00593 if (itsKDTree.is_invalid())
00594 {
00595 LINFO("Building KDTree for VisualObject '%s'...",
00596 itsVoRef->getName().c_str());
00597 itsKDTree.reset(new KDTree(itsVoRef->getKeypoints()));
00598 LINFO("KDTree for VisualObject '%s' complete.",
00599 itsVoRef->getName().c_str());
00600 }
00601
00602
00603 for (uint i = 0; i < tstnkp; i++)
00604 {
00605 int distsq1 = maxdsq, distsq2 = maxdsq;
00606 rutz::shared_ptr<Keypoint> tstkey = itsVoTest->getKeypoint(i);
00607
00608
00609 uint matchIndex = bbf > 0 ?
00610 itsKDTree->nearestNeighborBBF(tstkey, bbf, distsq1, distsq2) :
00611 itsKDTree->nearestNeighbor(tstkey, distsq1, distsq2);
00612
00613
00614 if (100U * distsq1 < thresh2 * distsq2)
00615 {
00616 KeypointMatch m;
00617 m.refkp = itsVoRef->getKeypoint(matchIndex); m.tstkp = tstkey;
00618 m.distSq = distsq1; m.distSq2 = distsq2;
00619 itsMatches.push_back(m);
00620 ++ nmatches;
00621 }
00622 }
00623
00624
00625 return nmatches;
00626 }
00627
00628
00629 Image< PixRGB<byte> >
00630 VisualObjectMatch::getMatchImage(const float scale) const
00631 {
00632
00633 Image< PixRGB<byte> > refimg = itsVoRef->getKeypointImage(scale, 0.0F);
00634 Image< PixRGB<byte> > tstimg = itsVoTest->getKeypointImage(scale, 0.0F);
00635 LDEBUG("r[%d %d] t[%d %d]",
00636 refimg.getWidth(), refimg.getHeight(),
00637 tstimg.getWidth(), tstimg.getHeight());
00638
00639
00640 int refdx, tstdx;
00641 const int w = std::max(refimg.getWidth(), tstimg.getWidth());
00642 const int dy = refimg.getHeight();
00643 const PixRGB<byte> greycol(128), linkcol(255, 200, 100);
00644 Image< PixRGB<byte> > combo(w, dy + tstimg.getHeight(), ZEROS);
00645
00646 if (refimg.getWidth() > tstimg.getWidth())
00647 { refdx = 0; tstdx = (w - tstimg.getWidth()) / 2; }
00648 else
00649 { refdx = (w - refimg.getWidth()) / 2; tstdx = 0; }
00650
00651 if (refimg.coordsOk(itsVoRef->getSalPoint()))
00652 drawDisk(refimg, itsVoRef->getSalPoint(), 3, PixRGB<byte>(255,255,0));
00653
00654 if (tstimg.coordsOk(itsVoTest->getSalPoint()))
00655 drawDisk(tstimg, itsVoTest->getSalPoint(), 3, PixRGB<byte>(255,255,0));
00656
00657 inplacePaste(combo, refimg, Point2D<int>(refdx, 0));
00658 inplacePaste(combo, tstimg, Point2D<int>(tstdx, dy));
00659
00660 drawLine(combo, Point2D<int>(0, dy-1), Point2D<int>(w-1, dy-1), greycol);
00661 drawLine(combo, Point2D<int>(0, dy), Point2D<int>(w-1, dy), greycol);
00662
00663
00664 const uint nm = itsMatches.size();
00665 for (uint i = 0; i < nm; i ++)
00666 {
00667 rutz::shared_ptr<Keypoint> refk = itsMatches[i].refkp;
00668 rutz::shared_ptr<Keypoint> tstk = itsMatches[i].tstkp;
00669
00670 drawLine(combo,
00671 Point2D<int>(int(refk->getX() * scale + 0.5F) + refdx,
00672 int(refk->getY() * scale + 0.5F)),
00673 Point2D<int>(int(tstk->getX() * scale + 0.5F) + tstdx,
00674 int(tstk->getY() * scale + 0.5F) + dy),
00675 linkcol);
00676 }
00677
00678 return combo;
00679 }
00680
00681
00682 Image< PixRGB<byte> >
00683 VisualObjectMatch::getMatchImage(Dims frameSize,
00684 Point2D<int> refOffset, Point2D<int> testOffset,
00685 const float scale) const
00686 {
00687 int w = frameSize.w();
00688 int h = frameSize.h();
00689
00690
00691 Image< PixRGB<byte> > refimg =
00692 getVoRef()->getKeypointImage(scale, 0.0F);
00693 Image< PixRGB<byte> > tstimg =
00694 getVoTest()->getKeypointImage(scale, 0.0F);
00695
00696
00697 drawDisk(refimg, getVoRef()->getSalPoint(), 3, PixRGB<byte>(255,255,0));
00698 drawDisk(tstimg, getVoTest()->getSalPoint(), 3, PixRGB<byte>(255,255,0));
00699
00700 const PixRGB<byte> greycol(128), linkcol(255, 200, 100);
00701 Image< PixRGB<byte> > combo(w, 2*h, ZEROS);
00702
00703
00704 inplacePaste(combo, refimg, Point2D<int>(0, 0)+ refOffset);
00705 inplacePaste(combo, tstimg, Point2D<int>(0, h)+ testOffset);
00706
00707 drawLine(combo, Point2D<int>(0, h-1), Point2D<int>(w-1, h-1), greycol);
00708
00709
00710 std::vector<KeypointMatch> matches = getKeypointMatches();
00711
00712 const uint nm = matches.size();
00713 for (uint i = 0; i < nm; i ++)
00714 {
00715 rutz::shared_ptr<Keypoint> refk = matches[i].refkp;
00716 rutz::shared_ptr<Keypoint> tstk = matches[i].tstkp;
00717
00718 drawLine
00719 (combo,
00720 Point2D<int>(int(refk->getX() * scale + 0.5F),
00721 int(refk->getY() * scale + 0.5F))
00722 + refOffset,
00723 Point2D<int>(int(tstk->getX() * scale + 0.5F),
00724 int(tstk->getY() * scale + 0.5F))
00725 + testOffset + Point2D<int>(0,h),
00726 linkcol);
00727 }
00728
00729 return combo;
00730 }
00731
00732
00733 Image< PixRGB<byte> > VisualObjectMatch::
00734 getTransfTestImage(const Image< PixRGB<byte> >& im)
00735 {
00736 SIFTaffine aff = getSIFTaffine();
00737
00738
00739
00740
00741 Image< PixRGB<byte> > result(im);
00742 if (result.initialized() == false)
00743 result.resize(itsVoRef->getImage().getDims(), true);
00744 Image< PixRGB<byte> > tsti = itsVoTest->getImage();
00745
00746 uint w = result.getWidth(), h = result.getHeight();
00747 Image< PixRGB<byte> >::iterator dptr = result.beginw();
00748
00749 for (uint j = 0; j < h; j ++)
00750 for (uint i = 0; i < w; i ++)
00751 {
00752 float u, v;
00753 aff.transform(float(i), float(j), u, v);
00754
00755 if (tsti.coordsOk(u, v))
00756 *dptr++ = tsti.getValInterp(u, v);
00757 else
00758 ++dptr;
00759 }
00760 return result;
00761 }
00762
00763
00764 void VisualObjectMatch::
00765 getTransfTestOutline(Point2D<int>& tl, Point2D<int>& tr, Point2D<int>& br, Point2D<int>& bl)
00766 {
00767 SIFTaffine a = getSIFTaffine();
00768 SIFTaffine aff = a.inverse();
00769
00770
00771
00772 const Dims objSize = itsVoTest->getObjectSize();
00773 const uint w = objSize.w();
00774 const uint h = objSize.h();
00775 float u, v;
00776
00777 aff.transform(0.0F, 0.0F, u, v);
00778 tl.i = int(u + 0.5F); tl.j = int(v + 0.5F);
00779
00780 aff.transform(float(w-1), 0.0F, u, v);
00781 tr.i = int(u + 0.5F); tr.j = int(v + 0.5F);
00782
00783 aff.transform(float(w-1), float(h-1), u, v);
00784 br.i = int(u + 0.5F); br.j = int(v + 0.5F);
00785
00786 aff.transform(0.0F, float(h-1), u, v);
00787 bl.i = int(u + 0.5F); bl.j = int(v + 0.5F);
00788 }
00789
00790
00791 Image< PixRGB<byte> > VisualObjectMatch::getFusedImage(const float mix)
00792 {
00793 SIFTaffine aff = getSIFTaffine();
00794
00795
00796
00797
00798 Image< PixRGB<byte> > refi = itsVoRef->getImage();
00799 Image< PixRGB<byte> > tsti = itsVoTest->getImage();
00800
00801 uint w = refi.getWidth(), h = refi.getHeight();
00802 Image< PixRGB<byte> > result(w, h, NO_INIT);
00803 Image< PixRGB<byte> >::const_iterator rptr = refi.begin();
00804 Image< PixRGB<byte> >::iterator dptr = result.beginw();
00805
00806 for (uint j = 0; j < h; j ++)
00807 for (uint i = 0; i < w; i ++)
00808 {
00809 float u, v;
00810 aff.transform(float(i), float(j), u, v);
00811 PixRGB<byte> rval = *rptr++;
00812
00813 if (tsti.coordsOk(u, v))
00814 {
00815 PixRGB<byte> tval = tsti.getValInterp(u, v);
00816 PixRGB<byte> mval = PixRGB<byte>(rval * mix + tval * (1.0F - mix));
00817 *dptr++ = mval;
00818 }
00819 else
00820 *dptr++ = PixRGB<byte>(rval * mix);
00821 }
00822 return result;
00823 }
00824
00825
00826 Point2D<int> VisualObjectMatch::getSpatialDist
00827 ( Point2D<int> offset1, Point2D<int> offset2)
00828 {
00829
00830
00831
00832
00833 SIFTaffine a = getSIFTaffine();
00834
00835
00836 Image<double> A(2,2, ZEROS);
00837 A.setVal(0, 0, a.m1); A.setVal(1, 0, a.m2);
00838 A.setVal(0, 1, a.m3); A.setVal(1, 1, a.m4);
00839
00840 Image<double> b(1,2, ZEROS);
00841 b.setVal(0, 0, a.tx);
00842 b.setVal(0, 1, a.ty);
00843
00844 Image<double> r(1,2,ZEROS);
00845 r.setVal(0,0, -offset1.i);
00846 r.setVal(0,1, -offset1.j);
00847
00848 LINFO("[ %7.3f %7.3f ][ %7.3f ] [ %7.3f ]",
00849 A.getVal(0,0), A.getVal(1,0), r.getVal(0,0), b.getVal(0,0));
00850 LINFO("[ %7.3f %7.3f ][ %7.3f ] + [ %7.3f ]",
00851 A.getVal(0,1), A.getVal(1,1), r.getVal(0,1), b.getVal(0,1));
00852
00853 Image<double> diff = matrixMult(A,r) + b;
00854 Point2D<int> diffPt = Point2D<int>(int(diff.getVal(0,0)),
00855 int(diff.getVal(0,1)));
00856 Point2D<int> res = diffPt + offset2;
00857
00858 LINFO("diff:[%d %d] + offset:[%d %d] = [%d %d]",
00859 diffPt.i, diffPt.j, offset2.i, offset2.j, res.i, res.j);
00860 return res;
00861 }
00862
00863
00864 Rectangle VisualObjectMatch::getOverlapRect()
00865 {
00866
00867
00868 SIFTaffine aff = getSIFTaffine();
00869
00870
00871
00872
00873
00874 Image< PixRGB<byte> > refi = getVoRef()->getImage();
00875 Image< PixRGB<byte> > tsti = getVoTest()->getImage();
00876
00877 uint wr = refi.getWidth(), hr = refi.getHeight();
00878 uint wt = tsti.getWidth(), ht = tsti.getHeight();
00879 LDEBUG("r[%d,%d] t[%d,%d]", wr, hr, wt, ht);
00880
00881
00882
00883 float u, v;
00884 aff.transform(float(0), float(0), u, v);
00885 float t = v, l = u, b = v, r = u;
00886 LDEBUG("t-l: [%f,%f]: [%f,%f,%f,%f]", u, v, t, l, b, r);
00887
00888
00889 aff.transform(float(wr-1), float(0), u, v);
00890 if(u < l) l = u; if(u > r) r = u;
00891 if(v < t) t = v; if(v > b) b = v;
00892 LDEBUG("t-r: [%f,%f]: [%f,%f,%f,%f]", u, v, t, l, b, r);
00893
00894
00895 aff.transform(float(0), float(hr-1), u, v);
00896 if(u < l) l = u; if(u > r) r = u;
00897 if(v < t) t = v; if(v > b) b = v;
00898 LDEBUG("b-l: [%f,%f]: [%f,%f,%f,%f]", u, v, t, l, b, r);
00899
00900
00901 aff.transform(float(wr-1), float(hr-1), u, v);
00902 if(u < l) l = u; if(u > r) r = u;
00903 if(v < t) t = v; if(v > b) b = v;
00904 LDEBUG("b-r: [%f,%f]: [%f,%f,%f,%f]", u, v, t, l, b, r);
00905
00906 const Rectangle refr = Rectangle::tlbrI(int(t+0.5F), int(l+0.5F),
00907 int(b+0.5F), int(r+0.5F));
00908 const Rectangle tstr = tsti.getBounds();
00909 const Rectangle ovl = refr.getOverlap(tstr);
00910 LINFO("overlap at: [%d, %d, %d, %d], %d, %d",
00911 ovl.left(), ovl.top(), ovl.rightI(), ovl.bottomI(),
00912 ovl.width(),ovl.height());
00913
00914 return ovl;
00915 }
00916
00917
00918 bool VisualObjectMatch::isOverlapping()
00919 {
00920 Image< PixRGB<byte> > refi = getVoRef()->getImage();
00921 Image< PixRGB<byte> > tsti = getVoTest()->getImage();
00922
00923 const Rectangle a = refi.getBounds();
00924 const Rectangle b = tsti.getBounds();
00925
00926
00927 const Rectangle ovl = getOverlapRect();
00928
00929 LINFO("o:[%d, %d, %d, %d], [%d,%d] dbo:[%d, %d, %d, %d], [%d,%d]",
00930 a.left(), a.top(), a.rightI(), a.bottomI(), a.width(), a.height(),
00931 b.left(), b.top(), b.rightI(), b.bottomI(), b.width(), b.height() );
00932 LINFO("overlap at: [%d, %d, %d, %d], %d, %d",
00933 ovl.left(), ovl.top(), ovl.rightI(), ovl.bottomI(),
00934 ovl.width(),ovl.height());
00935
00936
00937 if(!ovl.isValid()) { LINFO("do not overlap"); return false; }
00938 float ovlA = (ovl.width() * ovl.height())/(a.width() * a.height()+0.0);
00939 float ovlB = (ovl.width() * ovl.height())/(b.width() * b.height()+0.0);
00940 LINFO("ovl: a= %f, b= %f", ovlA, ovlB);
00941 if((ovlA > .5 && ovlB > .5) || (ovlA > .8) || (ovlB > .8))
00942 { LINFO("the objects overlap"); return true; }
00943 else
00944 { LINFO("objects do not significantly overlap"); return false; }
00945
00946 }
00947
00948
00949 bool VisualObjectMatch::isOverlapping2()
00950 {
00951 rutz::shared_ptr<VisualObject> obj1 = getVoRef();
00952 rutz::shared_ptr<VisualObject> obj2 = getVoTest();
00953 LINFO("obj1 size %d, obj2 size: %d match size: %d",
00954 obj1->numKeypoints(), obj2->numKeypoints(), size());
00955
00956
00957 float t1 = -1.0f, b1 = -1.0f, l1 = -1.0f, r1 = -1.0f;
00958 if(obj1->numKeypoints() > 0)
00959 {
00960 float x = obj1->getKeypoint(0)->getX();
00961 float y = obj1->getKeypoint(0)->getY();
00962 t1 = y, b1 = y, l1 = x, r1 = x;
00963
00964 }
00965 for(uint i = 1; i < obj1->numKeypoints(); i++)
00966 {
00967 float x = obj1->getKeypoint(i)->getX();
00968 float y = obj1->getKeypoint(i)->getY();
00969
00970 if(t1 > y) t1 = y; else if(b1 < y) b1 = y;
00971 if(l1 > x) l1 = x; else if(r1 < x) r1 = x;
00972
00973 }
00974 float area1 = (b1 - t1)*(r1 - l1);
00975 LINFO("obj1[%d]: t1: %f, b1: %f, l1: %f, r1: %f; A: %f",
00976 obj1->numKeypoints(), t1, b1, l1, r1, area1);
00977
00978
00979 float t2 = -1.0f, b2 = -1.0f, l2 = -1.0f, r2 = -1.0f;
00980 if(obj2->numKeypoints() > 0)
00981 {
00982 float x = obj2->getKeypoint(0)->getX();
00983 float y = obj2->getKeypoint(0)->getY();
00984 t2 = y, b2 = y, l2 = x, r2 = x;
00985
00986 }
00987 for(uint i = 1; i < obj2->numKeypoints(); i++)
00988 {
00989 float x = obj2->getKeypoint(i)->getX();
00990 float y = obj2->getKeypoint(i)->getY();
00991
00992 if(t2 > y) t2 = y; else if(b2 < y) b2 = y;
00993 if(l2 > x) l2 = x; else if(r2 < x) r2 = x;
00994
00995 }
00996 float area2 = (b2 - t2)*(r2 - l2);
00997 LINFO("obj2[%d]: t2: %f, b2: %f, l2: %f, r2: %f; A: %f",
00998 obj2->numKeypoints(), t2, b2, l2, r2, area2);
00999
01000
01001 float tm1 = -1.0f, bm1 = -1.0f, lm1 = -1.0f, rm1 = -1.0f;
01002 if(size() > 0)
01003 {
01004 float x = itsMatches[0].refkp->getX();
01005 float y = itsMatches[0].refkp->getY();
01006 tm1 = y, bm1 = y, lm1 = x, rm1 = x;
01007
01008 }
01009 for(uint i = 1; i < size(); i++)
01010 {
01011 float x = itsMatches[i].refkp->getX();
01012 float y = itsMatches[i].refkp->getY();
01013
01014 if(tm1 > y) tm1 = y; else if(bm1 < y) bm1 = y;
01015 if(lm1 > x) lm1 = x; else if(rm1 < x) rm1 = x;
01016
01017 }
01018 float aream1 = (bm1 - tm1)*(rm1 - lm1);
01019 LINFO("m1[%d]: tm1: %f, bm1: %f, lm1: %f, rm1: %f; Am: %f",
01020 size(), tm1, bm1, lm1, rm1, aream1);
01021
01022
01023 float tm2 = -1.0f, bm2 = -1.0f, lm2 = -1.0f, rm2 = -1.0f;
01024 if(size() > 0)
01025 {
01026 float x = itsMatches[0].tstkp->getX();
01027 float y = itsMatches[0].tstkp->getY();
01028 tm2 = y, bm2 = y, lm2 = x, rm2 = x;
01029
01030 }
01031 for(uint i = 1; i < size(); i++)
01032 {
01033 float x = itsMatches[i].tstkp->getX();
01034 float y = itsMatches[i].tstkp->getY();
01035
01036 if(tm2 > y) tm2 = y; else if(bm2 < y) bm2 = y;
01037 if(lm2 > x) lm2 = x; else if(rm2 < x) rm2 = x;
01038
01039 }
01040 float aream2 = (bm2 - tm2)*(rm2 - lm2);
01041 LINFO("m2[%d]: tm2: %f, bm2: %f, lm2: %f, rm2: %f; Am: %f",
01042 size(), tm2, bm2, lm2, rm2, aream2);
01043
01044
01045
01046 float lmid = l2 + (r2-l2)/4; float rmid = r2 - (r2-l2)/4;
01047 float lo = lm2; if(lo < lmid) lo = lmid;
01048 float ro = rm2; if(ro > rmid) ro = rmid;
01049 float wo = 0.0; if(ro > lo) wo = ro - lo;
01050
01051 float tmid = t2 + (b2-t2)/4; float bmid = b2 - (b2-t2)/4;
01052 float to = tm2; if(to < tmid) to = tmid;
01053 float bo = bm2; if(bo > bmid) bo = bmid;
01054 float ho = 0.0; if(bo > to) ho = bo - to;
01055 float ao = ho * wo;
01056
01057 bool ret = true; if(ao/(area2/4.0) < .3) ret = false;
01058 LINFO("wo: %f ho: %f ao/(a2/4) = %f/%f = %f < .4 -> ret = %d",
01059 wo,ho, ao, area2/4.0, ao/(area2/4.0), ret);
01060
01061 bool ret2 = true; if(ao/aream2 < .4) ret2 = false;
01062 LINFO("wo: %f ho: %f ao/(am2) = %f/%f = %f < .5 -> ret = %d",
01063 wo,ho, ao, aream2, ao/aream2, ret2);
01064 return ret || ret2;
01065 }
01066
01067
01068
01069
01070
01071