Commit f9bbe706 authored by Alexander Alekhin's avatar Alexander Alekhin
Browse files

Merge pull request #2383 from catree:feat_LOGOS_matching

parents 8a7a625a a9c1cfcb
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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},
+21 −5
Original line number Diff line number Diff line
@@ -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,11 +986,25 @@ 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);

/** @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);

//! @}

}
+69 −0
Original line number Diff line number Diff line
// 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
+189 −0
Original line number Diff line number Diff line
// 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;
}
}
+87 −0
Original line number Diff line number Diff line
// 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
Loading