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 @@ ...@@ -71,6 +71,15 @@
publisher = {Kluwer Academic Publishers} 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, @article{Mikolajczyk2004,
title = {Scale \& affine invariant interest point detectors}, title = {Scale \& affine invariant interest point detectors},
author = {Mikolajczyk, Krystian and Schmid, Cordelia}, 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 ...@@ -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 @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 ...@@ -969,7 +971,7 @@ CV_EXPORTS void FASTForPointSet( InputArray image, CV_IN_OUT std::vector<KeyPoin
//! @addtogroup xfeatures2d_match //! @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 size1 Input size of image1.
@param size2 Input size of image2. @param size2 Input size of image2.
@param keypoints1 Input keypoints of image1. @param keypoints1 Input keypoints of image1.
...@@ -984,10 +986,24 @@ CV_EXPORTS void FASTForPointSet( InputArray image, CV_IN_OUT std::vector<KeyPoin ...@@ -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 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. 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,
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 std::vector<DMatch>& matches1to2, CV_OUT std::vector<DMatch>& matchesGMS, const bool withRotation = false,
const bool withScale = false, const double thresholdFactor = 6.0 ); 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