Add LOGOS (Local geometric support for high-outlier spatial verification) features matching strategy.
parent
8a7a625a3c
commit
a9c1cfcb5d
12 changed files with 1050 additions and 5 deletions
@ -0,0 +1,69 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
#include "precomp.hpp" |
||||
#include "logos/Logos.hpp" |
||||
|
||||
namespace cv |
||||
{ |
||||
namespace xfeatures2d |
||||
{ |
||||
void matchLOGOS(const std::vector<KeyPoint>& keypoints1, const std::vector<KeyPoint>& keypoints2, |
||||
const std::vector<int>& nn1, const std::vector<int>& nn2, std::vector<DMatch>& matches1to2) |
||||
{ |
||||
CV_CheckEQ(keypoints1.size(), nn1.size(), "Number of keypoints1 must be equal to the number of nn1."); |
||||
CV_CheckEQ(keypoints2.size(), nn2.size(), "Number of keypoints2 must be equal to the number of nn2."); |
||||
if (keypoints1.empty() || keypoints2.empty()) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
std::vector<logos::Point*> vP1, vP2; |
||||
vP1.reserve(keypoints1.size()); |
||||
vP2.reserve(keypoints2.size()); |
||||
|
||||
for (size_t i = 0; i < keypoints1.size(); i++) |
||||
{ |
||||
logos::Point* pt1 = new logos::Point(keypoints1[i].pt.x, keypoints1[i].pt.y, |
||||
static_cast<float>(keypoints1[i].angle*CV_PI/180), |
||||
keypoints1[i].size, nn1[i]); |
||||
vP1.push_back(pt1); |
||||
} |
||||
|
||||
for (size_t i = 0; i < keypoints2.size(); i++) |
||||
{ |
||||
logos::Point* pt2 = new logos::Point(keypoints2[i].pt.x, keypoints2[i].pt.y, |
||||
static_cast<float>(keypoints2[i].angle*CV_PI/180), |
||||
keypoints2[i].size, nn2[i]); |
||||
vP2.push_back(pt2); |
||||
} |
||||
|
||||
logos::Logos logos; |
||||
std::vector<logos::PointPair*> globalMatches; |
||||
logos.estimateMatches(vP1, vP2, globalMatches); |
||||
|
||||
matches1to2.clear(); |
||||
matches1to2.reserve(globalMatches.size()); |
||||
for (size_t i = 0; i < globalMatches.size(); i++) |
||||
{ |
||||
logos::PointPair* pp = globalMatches[i]; |
||||
matches1to2.push_back(DMatch(pp->getPos1(), pp->getPos2(), 0)); |
||||
} |
||||
|
||||
for (size_t i = 0; i < globalMatches.size(); i++) |
||||
{ |
||||
delete globalMatches[i]; |
||||
} |
||||
|
||||
for (size_t i = 0; i < vP1.size(); i++) |
||||
{ |
||||
delete vP1[i]; |
||||
} |
||||
|
||||
for (size_t i = 0; i < vP2.size(); i++) |
||||
{ |
||||
delete vP2[i]; |
||||
} |
||||
} |
||||
} //namespace xfeatures2d
|
||||
} //namespace cv
|
@ -0,0 +1,189 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#include <cmath> |
||||
#include "Logos.hpp" |
||||
#include <opencv2/core.hpp> |
||||
|
||||
namespace logos |
||||
{ |
||||
Logos::Logos() |
||||
{ |
||||
LogosParameters defaultParams; |
||||
init(defaultParams); |
||||
} |
||||
|
||||
Logos::Logos(const LogosParameters& p) |
||||
{ |
||||
init(p); |
||||
} |
||||
|
||||
void Logos::init(const LogosParameters& p) |
||||
{ |
||||
setParams(p); |
||||
LB = static_cast<float>(-CV_PI); |
||||
BINSIZE = logosParams.GLOBALORILIMIT/3; |
||||
BINNUMBER = static_cast<unsigned int>(ceil(2*CV_PI/BINSIZE)); |
||||
bins.resize(BINNUMBER); |
||||
std::fill(bins.begin(), bins.end(), 0); |
||||
} |
||||
|
||||
int Logos::estimateMatches(std::vector<Point*> vP1, std::vector<Point*> vP2, std::vector<PointPair*>& globalmatches) |
||||
{ |
||||
matches.clear(); |
||||
|
||||
// for each point
|
||||
int count1 = 0; |
||||
|
||||
for (std::vector<Point*>::iterator pit1 = vP1.begin(); pit1 != vP1.end(); ++pit1, count1++) |
||||
{ |
||||
(*pit1)->nearestNeighbours(vP1, count1, getNum1()); |
||||
int count2 = 0; |
||||
|
||||
// find possible matches
|
||||
for (std::vector<Point*>::iterator pit2 = vP2.begin(); pit2 != vP2.end(); ++pit2, count2++) |
||||
{ |
||||
if ((*pit1)->getLabel() != (*pit2)->getLabel()) |
||||
{ |
||||
continue; |
||||
} |
||||
// this is a possible match in Image 2
|
||||
// get nearest neighbours
|
||||
(*pit2)->nearestNeighbours(vP2, count2, getNum2()); |
||||
|
||||
PointPair* ptpr = new PointPair(*pit1, *pit2); |
||||
ptpr->addPositions(count1, count2); |
||||
ptpr->computeLocalSupport(pp, getNum2()); |
||||
|
||||
// calc matches
|
||||
int support = 0; |
||||
for (std::vector<PointPair*>::const_iterator it = pp.begin(); it < pp.end(); ++it) |
||||
{ |
||||
Match m(ptpr, *it); |
||||
if (evaluateMatch(m)) |
||||
{ |
||||
support++; |
||||
} |
||||
} |
||||
for (size_t i = 0; i < pp.size(); i++) |
||||
{ |
||||
delete pp[i]; |
||||
} |
||||
pp.clear(); |
||||
if (support > 0) |
||||
{ |
||||
ptpr->setSupport(support); |
||||
matches.push_back(ptpr); |
||||
updateBin(ptpr->getRelOri()); |
||||
} |
||||
else |
||||
{ |
||||
delete ptpr; |
||||
ptpr = NULL; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// do global orientation
|
||||
double maxang = calcGlobalOrientation(); |
||||
|
||||
// find which matches are within global orientation limit
|
||||
int numinliers = 0; |
||||
globalmatches.clear(); |
||||
for (std::vector<PointPair*>::iterator it = matches.begin(); it != matches.end(); ++it) |
||||
{ |
||||
if (std::fabs((*it)->getRelOri() - maxang) < logosParams.GLOBALORILIMIT) |
||||
{ |
||||
numinliers++; |
||||
globalmatches.push_back(*it); |
||||
} |
||||
else |
||||
{ |
||||
delete *it; |
||||
*it = NULL; |
||||
} |
||||
} |
||||
|
||||
return numinliers; |
||||
} |
||||
|
||||
bool Logos::evaluateMatch(const Match& m) const |
||||
{ |
||||
return ((m.getRelOrientation() < getIntraOriLimit()) && |
||||
(m.getRelScale() < getIntraScaleLimit()) && |
||||
(m.getInterOrientation() < getInterOriLimit()) && |
||||
(m.getInterScale() < getInterScaleLimit())); |
||||
} |
||||
|
||||
void Logos::updateBin(float input) |
||||
{ |
||||
unsigned int binnumber = static_cast<unsigned int>(cvFloor((input-LB) / BINSIZE)); |
||||
// compare binnumber to BINNUMBER
|
||||
if (binnumber < BINNUMBER) |
||||
{ |
||||
bins[binnumber]++; |
||||
} |
||||
else |
||||
{ |
||||
bins[BINNUMBER-1]++; |
||||
} |
||||
} |
||||
|
||||
float Logos::calcGlobalOrientation() |
||||
{ |
||||
// find max bin
|
||||
// check BINNUMBER is big enough
|
||||
if (BINNUMBER < 3) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
std::vector<int> bins2(BINNUMBER); |
||||
int maxval = 0; |
||||
unsigned int maxix = 0; |
||||
bins2[0] = bins[0] + bins[1] + bins[BINNUMBER-1]; |
||||
maxval = bins2[0]; |
||||
for (unsigned int i = 1; i < BINNUMBER; i++) |
||||
{ |
||||
if (i == BINNUMBER-1) |
||||
{ |
||||
bins2[i] = bins[i]+bins[i-1]+bins[0]; |
||||
} |
||||
else |
||||
{ |
||||
bins2[i] = bins[i]+bins[i-1]+bins[i+1]; |
||||
} |
||||
if (bins2[i] > maxval) |
||||
{ |
||||
maxval = bins2[i]; |
||||
maxix = i; |
||||
} |
||||
} |
||||
|
||||
// convert to an angle
|
||||
return LB + maxix*BINSIZE + BINSIZE/2; |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#ifndef LOGOS_HPP |
||||
#define LOGOS_HPP |
||||
|
||||
#include "Point.hpp" |
||||
#include "Match.hpp" |
||||
#include "PointPair.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
struct LogosParameters |
||||
{ |
||||
LogosParameters() : |
||||
INTRAORILIMIT(0.1f), INTRASCALELIMIT(0.1f), INTERORILIMIT(0.1f), INTERSCALELIMIT(0.1f), GLOBALORILIMIT(0.1f), |
||||
NUM1(5), NUM2(5) {} |
||||
|
||||
float INTRAORILIMIT; |
||||
float INTRASCALELIMIT; |
||||
float INTERORILIMIT; |
||||
float INTERSCALELIMIT; |
||||
float GLOBALORILIMIT; |
||||
int NUM1; |
||||
int NUM2; |
||||
}; |
||||
|
||||
class Logos |
||||
{ |
||||
private: |
||||
std::vector<PointPair*> pp; |
||||
std::vector<PointPair*> matches; |
||||
|
||||
LogosParameters logosParams; |
||||
float LB; |
||||
float BINSIZE; |
||||
unsigned int BINNUMBER; |
||||
std::vector<int> bins; |
||||
|
||||
public: |
||||
Logos(); |
||||
Logos(const LogosParameters& p); |
||||
|
||||
void init(const LogosParameters& p); |
||||
|
||||
int estimateMatches(std::vector<Point*> vP1, std::vector<Point*> vP2, std::vector<PointPair*>& globalmatches); |
||||
bool evaluateMatch(const Match& m) const; |
||||
|
||||
inline float getIntraOriLimit() const { return logosParams.INTRAORILIMIT; } |
||||
inline float getIntraScaleLimit() const { return logosParams.INTRASCALELIMIT; } |
||||
inline float getInterOriLimit() const { return logosParams.INTERORILIMIT; } |
||||
inline float getInterScaleLimit() const { return logosParams.INTERSCALELIMIT; } |
||||
inline float getGlobalOriLimit() const { return logosParams.GLOBALORILIMIT; } |
||||
inline int getNum1() const { return logosParams.NUM1; } |
||||
inline int getNum2() const { return logosParams.NUM2; } |
||||
|
||||
void updateBin(float input); |
||||
float calcGlobalOrientation(); |
||||
|
||||
inline void setParams(const LogosParameters& p) { logosParams = p; } |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,103 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#include <algorithm> // std::min |
||||
#include <cmath> // log, acos |
||||
#include <iostream> |
||||
#include <opencv2/core.hpp> |
||||
#include "Match.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
Match::Match(PointPair* r_, PointPair* s_) : |
||||
r(r_), s(s_) |
||||
{ |
||||
calculateInternalVariables(); |
||||
setRelOrientation(); |
||||
setRelScale(); |
||||
interOrientationAndScale(); |
||||
} |
||||
|
||||
void Match::calculateInternalVariables() |
||||
{ |
||||
vijx = r->getx1() - s->getx1(); |
||||
vijy = r->gety1() - s->gety1(); |
||||
vmnx = r->getx2() - s->getx2(); |
||||
vmny = r->gety2() - s->gety2(); |
||||
} |
||||
|
||||
void Match::setRelOrientation() |
||||
{ |
||||
relOrientation = angleAbsDiff(r->getRelOri(), s->getRelOri()); |
||||
} |
||||
|
||||
void Match::setRelScale() |
||||
{ |
||||
relScale = std::fabs(r->getRelScale() - s->getRelScale()); |
||||
} |
||||
|
||||
float Match::angleAbsDiff(float a1, float a2) |
||||
{ |
||||
float ad = std::fabs(a1-a2); |
||||
while (ad > 2*CV_PI) |
||||
{ |
||||
ad = static_cast<float>(ad-2*CV_PI); |
||||
} |
||||
ad = std::min(std::fabs(ad), std::fabs(static_cast<float>(2*CV_PI-std::fabs(ad)))); |
||||
|
||||
return ad; |
||||
} |
||||
|
||||
void Match::interOrientationAndScale() |
||||
{ |
||||
float cp = vijx*vmny - vijy*vmnx; // analogous to 2D cross product
|
||||
float nmij = std::sqrt(vijx*vijx + vijy*vijy); |
||||
float nmnm = std::sqrt(vmnx*vmnx + vmny*vmny); |
||||
|
||||
float fr = (vijx*vmnx+vijy*vmny) / (nmij*nmnm); // numerator equivalent to dot product
|
||||
fr = std::min(std::max(fr, -1.0f), 1.0f); |
||||
ro3 = std::acos(fr)*sign(cp); |
||||
|
||||
rs3 = std::log(nmij) - std::log(nmnm); |
||||
|
||||
interOrientation = angleAbsDiff(r->getRelOri(), ro3); |
||||
interScale = std::fabs(r->getRelScale() - rs3); |
||||
} |
||||
|
||||
void Match::printMatch() const |
||||
{ |
||||
std::cout << "Relative Orientation: " << relOrientation << std::endl; |
||||
std::cout << "Relative Scale: " << relScale << std::endl; |
||||
std::cout << "Inter Orientation: " << interOrientation << std::endl; |
||||
std::cout << "Inter Scale: " << interScale << std::endl; |
||||
std::cout << "Global Relative Orientation: " << r->getRelOri() << std::endl; |
||||
} |
||||
|
||||
int Match::sign(float x) |
||||
{ |
||||
return (x > 0) - (x < 0); |
||||
} |
||||
} |
@ -0,0 +1,73 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#ifndef MATCH_HPP |
||||
#define MATCH_HPP |
||||
|
||||
#include "PointPair.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
class Match |
||||
{ |
||||
private: |
||||
PointPair* r; |
||||
PointPair* s; |
||||
float relOrientation; |
||||
float relScale; |
||||
float interOrientation; |
||||
float interScale; |
||||
|
||||
// Internal variables
|
||||
float ro3; |
||||
float rs3; |
||||
float vijx; |
||||
float vijy; |
||||
float vmnx; |
||||
float vmny; |
||||
|
||||
// Internal functions
|
||||
void calculateInternalVariables(); |
||||
void setRelOrientation(); |
||||
void setRelScale(); |
||||
float angleDiff(float a1,float a2); |
||||
float angleAbsDiff(float a1, float a2); |
||||
void interOrientationAndScale(); |
||||
int sign(float x); |
||||
|
||||
public: |
||||
Match(PointPair* r, PointPair* s); |
||||
|
||||
inline float getRelOrientation() const { return relOrientation; } |
||||
inline float getRelScale() const { return relScale; } |
||||
inline float getInterOrientation() const { return interOrientation; } |
||||
inline float getInterScale() const { return interScale; } |
||||
|
||||
void printMatch() const; |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,120 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#include <iostream> |
||||
#include <algorithm> // std::sort |
||||
#include "Point.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
static bool cMP(const MatchPoint& m, const MatchPoint& n) |
||||
{ |
||||
return (m.sd < n.sd); |
||||
} |
||||
|
||||
Point::Point() : |
||||
x(0), y(0), orientation(0), scale(1), nnVector(), nnFound(false), label(0) |
||||
{ |
||||
} |
||||
|
||||
Point::Point(float x_, float y_, float orientation_, float scale_, int label_) : |
||||
x(x_), y(y_), orientation(orientation_), scale(scale_), nnVector(), nnFound(false), label(label_) |
||||
{ |
||||
} |
||||
|
||||
void Point::nearestNeighbours(const std::vector<Point*>& vP, int index, int N) |
||||
{ |
||||
nearestNeighboursNaive(vP, index, N); |
||||
} |
||||
|
||||
void Point::nearestNeighboursNaive(const std::vector<Point*>& vP, int index, int N) |
||||
{ |
||||
// only want to calculate once.
|
||||
if (nnFound) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
std::vector<MatchPoint> minMatch; |
||||
minMatch.reserve(vP.size()); |
||||
|
||||
int i = 0; |
||||
for (std::vector<Point*>::const_iterator it = vP.begin(); it != vP.end(); ++it, i++) |
||||
{ |
||||
// A point is not it's own neighbour
|
||||
if (i == index) |
||||
{ |
||||
continue; |
||||
} |
||||
float sd = squareDist(getx(), gety(), (*it)->getx(), (*it)->gety()); |
||||
MatchPoint mP(sd, i); |
||||
minMatch.push_back(mP); |
||||
} |
||||
|
||||
std::sort(minMatch.begin(), minMatch.end(), cMP); |
||||
nnVector.resize(static_cast<size_t>(N)); |
||||
int count = 0; |
||||
for (std::vector<MatchPoint>::const_iterator mmit = minMatch.begin(); count < N; ++mmit, count++) |
||||
{ |
||||
nnVector[static_cast<size_t>(count)] = vP[static_cast<size_t>(mmit->index)]; |
||||
} |
||||
|
||||
nnFound = true; |
||||
} |
||||
|
||||
void Point::matchLabel(int label_, std::vector<Point*>& matchNN) |
||||
{ |
||||
for (std::vector<Point*>::const_iterator nnIterator = nnVector.begin(); |
||||
nnIterator != nnVector.end(); ++nnIterator) |
||||
{ |
||||
if ((*nnIterator)->label == label_) |
||||
{ |
||||
matchNN.push_back(*nnIterator); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Point::printPoint() const |
||||
{ |
||||
std::cout << getx() << " " |
||||
<< gety() << " " << getOrientation() << " " |
||||
<< getScale() << " " << getLabel() << std::endl; |
||||
} |
||||
|
||||
void Point::printNN() const |
||||
{ |
||||
for(std::vector<Point*>::const_iterator nnIterator = nnVector.begin(); |
||||
nnIterator != nnVector.end(); ++nnIterator) |
||||
{ |
||||
(*nnIterator)->printPoint(); |
||||
} |
||||
} |
||||
|
||||
float Point::squareDist(float x1, float y1, float x2, float y2) |
||||
{ |
||||
return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); |
||||
} |
||||
} |
@ -0,0 +1,79 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#ifndef POINT_HPP |
||||
#define POINT_HPP |
||||
|
||||
#include <vector> |
||||
|
||||
namespace logos |
||||
{ |
||||
struct MatchPoint |
||||
{ |
||||
float sd; |
||||
int index; |
||||
|
||||
MatchPoint(float sd_, int idx) : |
||||
sd(sd_), index(idx) {} |
||||
}; |
||||
|
||||
class Point |
||||
{ |
||||
private: |
||||
float x; |
||||
float y; |
||||
float orientation; |
||||
float scale; |
||||
std::vector<Point*> nnVector; |
||||
bool nnFound; |
||||
int label; |
||||
|
||||
public: |
||||
Point(); |
||||
Point(float x_, float y_, float orientation_, float scale_, int label_ = 0); |
||||
|
||||
inline float getx() const { return x; } |
||||
inline float gety() const { return y; } |
||||
inline float getOrientation() const { return orientation; } |
||||
inline float getScale() const { return scale; } |
||||
|
||||
inline int getLabel() const { return label; } |
||||
inline void setLabel(int label_) { label = label_; } |
||||
|
||||
inline void getNNVector(std::vector<Point*>& nnv) const { nnv = nnVector; } |
||||
void matchLabel(int label, std::vector<Point*>& mNN); |
||||
|
||||
void nearestNeighbours(const std::vector<Point*>& vP, int index, int N); |
||||
void nearestNeighboursNaive(const std::vector<Point*>& vP, int index, int N); |
||||
|
||||
void printPoint() const; |
||||
void printNN() const; |
||||
|
||||
float squareDist(float x1, float y1, float x2, float y2); |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,77 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#include <opencv2/core.hpp> |
||||
#include "PointPair.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
PointPair::PointPair(Point* p_, Point* q_) : |
||||
p(p_), q(q_), support(0), pos1(0), pos2(0) |
||||
{ |
||||
calculateInternalVariables(); |
||||
} |
||||
|
||||
void PointPair::computeLocalSupport(std::vector<PointPair*>& pp, int N) |
||||
{ |
||||
std::vector<Point*> nnVector; |
||||
p->getNNVector(nnVector); // Exposes the nearest neighbours
|
||||
|
||||
// for each nearest neighbour
|
||||
for (std::vector<Point*>::iterator nnIterator = nnVector.begin(); nnIterator != nnVector.end(); ++nnIterator) |
||||
{ |
||||
// is there a matching nearestNeighbour?
|
||||
std::vector<Point*> matchNN; |
||||
matchNN.reserve(static_cast<size_t>(N)); |
||||
q->matchLabel((*nnIterator)->getLabel(), matchNN); |
||||
for (std::vector<Point*>::const_iterator mit = matchNN.begin(); mit != matchNN.end(); ++mit) |
||||
{ |
||||
PointPair* m = new PointPair(*nnIterator, *mit); |
||||
pp.push_back(m); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void PointPair::calculateInternalVariables() |
||||
{ |
||||
relOri = angleDiff(p->getOrientation(), q->getOrientation()); |
||||
relScale = std::log(p->getScale()) - std::log(q->getScale()); |
||||
} |
||||
|
||||
float PointPair::angleDiff(float a1, float a2) |
||||
{ |
||||
float ad = a1 - a2; |
||||
while (ad > CV_PI) |
||||
{ |
||||
ad = static_cast<float>(ad-2*CV_PI); |
||||
} |
||||
while (ad < -CV_PI) |
||||
{ |
||||
ad = static_cast<float>(ad + 2*CV_PI); |
||||
} |
||||
return ad; |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
/*
|
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2018 Stephanie Lowry |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all |
||||
* copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
#ifndef POINTPAIR_HPP |
||||
#define POINTPAIR_HPP |
||||
|
||||
#include "Point.hpp" |
||||
|
||||
namespace logos |
||||
{ |
||||
class PointPair |
||||
{ |
||||
private: |
||||
Point* p; |
||||
Point* q; |
||||
int support; |
||||
float relOri; |
||||
float relScale; |
||||
int pos1; |
||||
int pos2; |
||||
|
||||
float angleDiff(float a1, float a2); |
||||
|
||||
public: |
||||
PointPair(Point* p_, Point* q_); |
||||
|
||||
void computeLocalSupport(std::vector<PointPair*>& pp, int N); |
||||
|
||||
void calculateInternalVariables(); |
||||
|
||||
float getRelOri() const { return relOri; } |
||||
float getRelScale() const { return relScale; } |
||||
|
||||
float getx1() const { return p->getx(); } |
||||
float getx2() const { return q->getx(); } |
||||
float gety1() const { return p->gety(); } |
||||
float gety2() const { return q->gety(); } |
||||
|
||||
void addPositions(int pos1_, int pos2_) { pos1 = pos1_; pos2 = pos2_; } |
||||
int getPos1() const { return pos1; } |
||||
int getPos2() const { return pos2; } |
||||
|
||||
int getSupport() const { return support; } |
||||
void setSupport(int support_) { support = support_; } |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,152 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
static void loadKeypoints(const std::string& vP_path, |
||||
const std::string& oP_path, |
||||
const std::string& sP_path, |
||||
const std::string& w_path, |
||||
std::vector<cv::KeyPoint>& keypoints, |
||||
std::vector<int>& nn) |
||||
{ |
||||
{ |
||||
std::ifstream file(vP_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
float x = 0, y = 0; |
||||
while (file >> x >> y) |
||||
{ |
||||
keypoints.push_back(cv::KeyPoint(x, y, 0)); |
||||
} |
||||
} |
||||
} |
||||
{ |
||||
std::ifstream file(oP_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
float orientation = 0; |
||||
size_t idx = 0; |
||||
while (file >> orientation) |
||||
{ |
||||
keypoints[idx].angle = static_cast<float>(orientation * 180.0 / CV_PI); |
||||
idx++; |
||||
} |
||||
} |
||||
} |
||||
{ |
||||
std::ifstream file(sP_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
float scale = 0; |
||||
size_t idx = 0; |
||||
while (file >> scale) |
||||
{ |
||||
keypoints[idx].size = scale; |
||||
idx++; |
||||
} |
||||
} |
||||
} |
||||
{ |
||||
std::ifstream file(w_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
int neighborIdx = 0; |
||||
while (file >> neighborIdx) |
||||
{ |
||||
nn.push_back(neighborIdx); |
||||
} |
||||
} |
||||
} |
||||
|
||||
ASSERT_TRUE(!keypoints.empty()); |
||||
} |
||||
|
||||
static void loadGroundTruth(const std::string& d1_path, |
||||
const std::string& b1_path, |
||||
std::vector<cv::DMatch>& groundTruth) |
||||
{ |
||||
std::vector<int> d1_vec; |
||||
{ |
||||
std::ifstream file(d1_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
int idx = 0; |
||||
while (file >> idx) |
||||
{ |
||||
d1_vec.push_back(idx-1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
std::vector<int> b1_vec; |
||||
{ |
||||
std::ifstream file(b1_path.c_str()); |
||||
if (file.is_open()) |
||||
{ |
||||
int idx = 0; |
||||
while (file >> idx) |
||||
{ |
||||
b1_vec.push_back(idx-1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
ASSERT_TRUE(!d1_vec.empty()); |
||||
ASSERT_EQ(d1_vec.size(), b1_vec.size()); |
||||
|
||||
for (size_t i = 0; i < d1_vec.size(); i++) |
||||
{ |
||||
groundTruth.push_back(cv::DMatch(d1_vec[i], b1_vec[i], 0)); |
||||
} |
||||
} |
||||
|
||||
TEST(XFeatures2d_LogosMatcher, logos_matcher_regression) |
||||
{ |
||||
const std::string vP1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/vP1.txt"); |
||||
const std::string oP1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/oP1.txt"); |
||||
const std::string sP1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/sP1.txt"); |
||||
const std::string w1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/w1.txt"); |
||||
|
||||
const std::string vP2_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/vP2.txt"); |
||||
const std::string oP2_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/oP2.txt"); |
||||
const std::string sP2_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/sP2.txt"); |
||||
const std::string w2_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/w2.txt"); |
||||
|
||||
std::vector<cv::KeyPoint> keypoints1, keypoints2; |
||||
std::vector<int> nn1, nn2; |
||||
loadKeypoints(vP1_path, oP1_path, sP1_path, w1_path, keypoints1, nn1); |
||||
loadKeypoints(vP2_path, oP2_path, sP2_path, w2_path, keypoints2, nn2); |
||||
|
||||
std::vector<cv::DMatch> matchesLogos; |
||||
matchLOGOS(keypoints1, keypoints2, nn1, nn2, matchesLogos); |
||||
|
||||
std::vector<cv::DMatch> groundTruth; |
||||
const std::string d1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/d1.txt"); |
||||
const std::string b1_path = cvtest::findDataFile("detectors_descriptors_evaluation/matching/LOGOS/b1.txt"); |
||||
loadGroundTruth(d1_path, b1_path, groundTruth); |
||||
|
||||
int correctMatches = 0; |
||||
for (size_t i = 0; i < matchesLogos.size(); i++) |
||||
{ |
||||
for (size_t j = 0; j < groundTruth.size(); j++) |
||||
{ |
||||
if (groundTruth[j].queryIdx == matchesLogos[i].queryIdx && |
||||
groundTruth[j].trainIdx == matchesLogos[j].trainIdx) |
||||
{ |
||||
correctMatches++; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
ASSERT_EQ(static_cast<int>(groundTruth.size()), correctMatches) |
||||
<< "groundTruth: " << groundTruth.size() |
||||
<< " ; matchesLogos: " << matchesLogos.size() |
||||
<< " ; correctMatches: " << correctMatches; |
||||
} |
||||
|
||||
}} // namespace
|
Loading…
Reference in new issue