2

How to match keypoints in SIFT ?

I have calculated 128 size vector for each keypoint in an image.

let, I1 is original image, I2 is 45 degree rotated image.

I got 130 keypoints for I1 and 104 keypoints for I2.

i.e. 128x130 and 128x104.

I calculated euclidean distance between one keypoint of I1 and all keypoints of I2. so I got again euclidean distance matrix of size 128x104.

Now I need to choose nearest keypoint from this euclidean distance matrix. How I can select minimum distance 128 size vector out of 128 x 104 sized matrix?

Chaitali
  • 21
  • 1
  • 3
  • usually people use **RANSAC** to match the keypoints. It is not enough to chose the min distance keypoint but to find/match patterns of neighbor keypoints too. It is not that simple and involves clustering analysis and matching ... RANSAC do it statistically. Cant help further as My knowledge on the subject is VERY limited. but at least you know what to google ... – Spektre Mar 11 '16 at 09:25

1 Answers1

1

Since you have already calculated the distance between the keypoints, in order to match them, sort them in increasing order of Euclidean distance, and consider only those keypoints which are a constant*min_distance [i.e: select on some %age of the sorted distances] as 'good matches'.

There is also BruteForceMatcher, KNNMatch and FlannBasedMatcher in OpenCV (URL Below) http://docs.opencv.org/2.4/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.html#feature-flann-matcher

and

http://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html#descriptormatcher-knnmatch

Also, have a look at these questions and their responses.

1) Trying to match two images using sift in OpenCv, but too many matches

2) Efficient way for SIFT descriptor matching

Just for completeness, providing some very rough code for your reference.

If you have ;
class SIFTDemo
{
private:
    Mat image;
    vector<cv::KeyPoint> keypoints;
    Mat descriptors;
    Mat sift_output;
    vector<DMatch> matches;

public:
    SIFTDemo();
    ~SIFTDemo();
    SIFTDemo(Mat m);
    void extractSiftFeatures();
    vector <DMatch> FindMatchesEuclidian(SIFTDemo &m2);
};

Then one can have something like this;

void SIFTDemo::extractSiftFeatures()
{
    SIFT siftobject;
    siftobject.operator()(image, Mat(), keypoints, descriptors);
}

vector<DMatch> SIFTDemo::FindMatchesEuclidian(SIFTDemo &m2)
{
    // Calculate euclidian distance between keypoints to find best matching   pairs.
    // create two dimensional vector for storing euclidian distance
    vector< vector<float> > vec1, unsortedvec1;
    for (int i=0; i<this->keypoints.size(); i++)
    {
        vec1.push_back(vector<float>()); // Add an empty row
        unsortedvec1.push_back(vector<float>());
    }

    // create vector of DMatch for storing matxhes point
    vector<DMatch> matches1;
    DMatch dm1;

    // loop through keypoints1.size
    for (int i=0; i<this->keypoints.size(); i++)
    {
        // get 128 dimensions in a vector
        vector<float> k1;
        for(int x=0; x<128; x++)
        {
            k1.push_back((float)this->descriptors.at<float>(i,x));
        }

        // loop through keypoints2.size
        for (int j=0; j<m2.keypoints.size(); j++)
        {
            double temp=0;
            // calculate euclidian distance
            for(int x=0; x<128; x++)
            {
                temp += (pow((k1[x] - (float)m2.descriptors.at<float>(j,x)), 2.0));
            }
            vec1[i].push_back((float)sqrt(temp)); // store distance for each keypoints in image2
            unsortedvec1[i] = vec1[i];
        }
        sort(vec1[i].begin(),vec1[i].end()); // sort the vector distances to get shortest distance

        // find position of the shortest distance
        int pos = (int)(find(unsortedvec1[i].begin(), unsortedvec1[i].end(), vec1[i][0]) - unsortedvec1[i].begin());

        // assign that matchin feature to DMatch variable dm1
        dm1.queryIdx = i;
        dm1.trainIdx = pos;
        dm1.distance = vec1[i][0];
        matches1.push_back(dm1);
        this->matches.push_back(dm1);
        //cout << pos << endl;
    }

    // craete two dimensional vector for storing euclidian distance
    vector<vector<float>> vec2, unsortedvec2;
    for (int i=0; i<m2.keypoints.size(); i++)
    {
        vec2.push_back(vector<float>()); // Add an empty row
        unsortedvec2.push_back(vector<float>());
    }

    // create vector of DMatch for storing matxhes point
    vector<DMatch> matches2;
    DMatch dm2;
    // loop through keypoints2.size
    for (int i=0; i<m2.keypoints.size(); i++)
    {
        // get 128 dimensions in a vector
        vector<float> k1;
        for(int x=0; x<128; x++)
        {
            k1.push_back((float)m2.descriptors.at<float>(i,x));
        }

        // loop through keypoints1.size
        for (int j=0; j<this->keypoints.size(); j++)
        {
            double temp=0;
            // calculate euclidian distance
            for(int x=0; x<128; x++)
            {
                temp += (pow((k1[x] - (float)this->descriptors.at<float>(j,x)), 2.0));
            }
            vec2[i].push_back((float)sqrt(temp)); // store distance for each keypoints in image1
            unsortedvec2[i] = vec2[i];
        }
        sort(vec2[i].begin(),vec2[i].end()); // sort the vector distances to get shortest distance

        // find position of the shortest distance
        int pos = (int)(find(unsortedvec2[i].begin(), unsortedvec2[i].end(), vec2[i][0]) - unsortedvec2[i].begin());

        // assign that matchin feature to DMatch variable
        dm2.queryIdx = i;
        dm2.trainIdx = pos;
        dm2.distance = vec2[i][0];
        matches2.push_back(dm2);
        m2.matches.push_back(dm2);

        //cout << pos << endl;
    }

    // Ref : http://docs.opencv.org/2.4/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.html#feature-flann-matcher
    //-- Quick calculation of max and min distances between keypoints1
    double max_dist = 0;
    double min_dist = 500.0;
    for( int i = 0; i < matches1.size(); i++ )
    {
        double dist = matches1[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }

    // Draw only "good" matches1 (i.e. whose distance is less than 2*min_dist ) 
    vector<DMatch> good_matches1;
    for( int i = 0; i < matches1.size(); i++ )
    {
        if( matches1[i].distance <= 2*min_dist )
        {
            good_matches1.push_back( matches1[i]);
        }
    }

    // Quick calculation of max and min distances between keypoints2 but not used
    for( int i = 0; i < matches2.size(); i++ )
    {
        double dist = matches2[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }

    // Draw only "good" matches by comparing that (ft1 gives ft2) and (ft2 gives ft1)
    vector<DMatch> good_matches;
    for(unsigned int i=0; i<good_matches1.size(); i++)
    {
        // check ft1=ft2 and ft2=ft1
        if(good_matches1[i].queryIdx == matches2[good_matches1[i].trainIdx].trainIdx)
            good_matches.push_back(good_matches1[i]);
    }
    return good_matches;
}

FInally, as mentioned in the comment also look at RANSAC to do this. Not diving into that not to make the answer longer but you can find resources online and on SO.

Community
  • 1
  • 1
MananVyas
  • 236
  • 2
  • 4