Commit f9bbe706 authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #2383 from catree:feat_LOGOS_matching

parents 8a7a625a a9c1cfcb
......@@ -71,6 +71,15 @@
publisher = {Kluwer Academic Publishers}
}
@article{Lowry2018LOGOSLG,
title = {LOGOS: Local Geometric Support for High-Outlier Spatial Verification},
author = {Stephanie Lowry and Henrik Andreasson},
journal = {2018 IEEE International Conference on Robotics and Automation (ICRA)},
year = {2018},
pages = {7262-7269},
doi = {10.1109/ICRA.2018.8460988},
}
@article{Mikolajczyk2004,
title = {Scale \& affine invariant interest point detectors},
author = {Mikolajczyk, Krystian and Schmid, Cordelia},
......
......@@ -55,7 +55,9 @@ known to be patented. You need to set the OPENCV_ENABLE_NONFREE option in cmake
@defgroup xfeatures2d_match Experimental 2D Features Matching Algorithm
This section describes the GMS (Grid-based Motion Statistics) matching strategy.
This section describes the following matching strategies:
- GMS: Grid-based Motion Statistics, @cite Bian2017gms
- LOGOS: Local geometric support for high-outlier spatial verification, @cite Lowry2018LOGOSLG
@}
*/
......@@ -969,7 +971,7 @@ CV_EXPORTS void FASTForPointSet( InputArray image, CV_IN_OUT std::vector<KeyPoin
//! @addtogroup xfeatures2d_match
//! @{
/** @brief GMS (Grid-based Motion Statistics) feature matching strategy by @cite Bian2017gms .
/** @brief GMS (Grid-based Motion Statistics) feature matching strategy described in @cite Bian2017gms .
@param size1 Input size of image1.
@param size2 Input size of image2.
@param keypoints1 Input keypoints of image1.
......@@ -984,10 +986,24 @@ CV_EXPORTS void FASTForPointSet( InputArray image, CV_IN_OUT std::vector<KeyPoin
If matching results are not satisfying, please add more features. (We use 10000 for images with 640 X 480).
If your images have big rotation and scale changes, please set withRotation or withScale to true.
*/
CV_EXPORTS_W void matchGMS(const Size& size1, const Size& size2, const std::vector<KeyPoint>& keypoints1, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, CV_OUT std::vector<DMatch>& matchesGMS, const bool withRotation = false,
const bool withScale = false, const double thresholdFactor = 6.0);
CV_EXPORTS_W void matchGMS( const Size& size1, const Size& size2, const std::vector<KeyPoint>& keypoints1, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, CV_OUT std::vector<DMatch>& matchesGMS, const bool withRotation = false,
const bool withScale = false, const double thresholdFactor = 6.0 );
/** @brief LOGOS (Local geometric support for high-outlier spatial verification) feature matching strategy described in @cite Lowry2018LOGOSLG .
@param keypoints1 Input keypoints of image1.
@param keypoints2 Input keypoints of image2.
@param nn1 Index to the closest BoW centroid for each descriptors of image1.
@param nn2 Index to the closest BoW centroid for each descriptors of image2.
@param matches1to2 Matches returned by the LOGOS matching strategy.
@note
This matching strategy is suitable for features matching against large scale database.
First step consists in constructing the bag-of-words (BoW) from a representative image database.
Image descriptors are then represented by their closest codevector (nearest BoW centroid).
*/
CV_EXPORTS_W 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);
//! @}
......
// 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
// 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;
}
}
// 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
// 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);
}
}
// 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
// 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);
}
}
// 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
// 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;
}
}
// 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
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment