Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/*
*.pyc
9 changes: 5 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
cmake_minimum_required(VERSION 2.8)
project( gms_match_demo)
set( CMAKE_CXX_STANDARD 11 )

# OpenCV
# OpenCV
find_package( OpenCV REQUIRED )

#
include_directories(
#
include_directories(
include
${OpenCV_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
)


Expand Down
12 changes: 12 additions & 0 deletions data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# README on Photos

This describes the photos kf_\*.png
They photos are drawn from a video sequence. The numbers denote the keyframe number. For example kf_972.png and kf_973.png are consecutive frames.

There are 3 separate physical locations

- kf_8, kf_12, kf_558, kf_589 are captured in same physical location.

- kf_972, kf_973, kf_2397 are captured in same physical location.

- kf_1140, kf_2582, kf_2583 are captured in same physical location.
Binary file added data/kf_1140.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_2397.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_2581.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_2582.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_2583.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_558.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_589.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_972.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/kf_973.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified data/nn_right.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion include/Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
#include <vector>
#include <iostream>
#include <ctime>
#include <string>
using namespace std;
using namespace cv;

#ifdef USE_GPU
#include <opencv2/cudafeatures2d.hpp>
using cuda::GpuMat;
#endif

52 changes: 31 additions & 21 deletions include/gms_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#define THRESH_FACTOR 6

// 8 possible rotation and each one is 3 X 3
// 8 possible rotation and each one is 3 X 3
const int mRotationPatterns[8][9] = {
1,2,3,
4,5,6,
Expand Down Expand Up @@ -45,8 +45,8 @@ const double mScaleRatios[5] = { 1.0, 1.0 / 2, 1.0 / sqrt(2.0), sqrt(2.0), 2.0 }
class gms_matcher
{
public:
// OpenCV Keypoints & Correspond Image Size & Nearest Neighbor Matches
gms_matcher(const vector<KeyPoint> &vkp1, const Size size1, const vector<KeyPoint> &vkp2, const Size size2, const vector<DMatch> &vDMatches)
// OpenCV Keypoints & Correspond Image Size & Nearest Neighbor Matches
gms_matcher(const vector<KeyPoint> &vkp1, const Size size1, const vector<KeyPoint> &vkp2, const Size size2, const vector<DMatch> &vDMatches)
{
// Input initialize
NormalizePoints(vkp1, size1, mvP1);
Expand All @@ -58,7 +58,7 @@ class gms_matcher
mGridSizeLeft = Size(20, 20);
mGridNumberLeft = mGridSizeLeft.width * mGridSizeLeft.height;

// Initialize the neihbor of left grid
// Initialize the neihbor of left grid
mGridNeighborLeft = Mat::zeros(mGridNumberLeft, 9, CV_32SC1);
InitalizeNiehbors(mGridNeighborLeft, mGridSizeLeft);
};
Expand Down Expand Up @@ -87,14 +87,14 @@ class gms_matcher
// value : how many matches from idx_left to idx_right
Mat mMotionStatistics;

//
//
vector<int> mNumberPointsInPerCellLeft;

// Inldex : grid_idx_left
// Value : grid_idx_right
vector<int> mCellPairs;

// Every Matches has a cell-pair
// Every Matches has a cell-pair
// first : grid_idx_left
// second : grid_idx_right
vector<pair<int, int> > mvMatchPairs;
Expand All @@ -110,7 +110,7 @@ class gms_matcher
public:

// Get Inlier Mask
// Return number of inliers
// Return number of inliers
int GetInlierMask(vector<bool> &vbInliers, bool WithScale = false, bool WithRotation = false);

private:
Expand Down Expand Up @@ -177,7 +177,7 @@ class gms_matcher
return x + y * mGridSizeRight.width;
}

// Assign Matches to Cell Pairs
// Assign Matches to Cell Pairs
void AssignMatchPairs(int GridType);

// Verify Cell Pairs
Expand All @@ -193,7 +193,7 @@ class gms_matcher
for (int yi = -1; yi <= 1; yi++)
{
for (int xi = -1; xi <= 1; xi++)
{
{
int idx_xx = idx_x + xi;
int idx_yy = idx_y + yi;

Expand Down Expand Up @@ -222,13 +222,13 @@ class gms_matcher
mGridSizeRight.height = mGridSizeLeft.height * mScaleRatios[Scale];
mGridNumberRight = mGridSizeRight.width * mGridSizeRight.height;

// Initialize the neihbor of right grid
// Initialize the neihbor of right grid
mGridNeighborRight = Mat::zeros(mGridNumberRight, 9, CV_32SC1);
InitalizeNiehbors(mGridNeighborRight, mGridSizeRight);
}


// Run
// Run
int run(int RotationType);
};

Expand Down Expand Up @@ -292,7 +292,7 @@ int gms_matcher::GetInlierMask(vector<bool> &vbInliers, bool WithScale, bool Wit
vbInliers = mvbInlierMask;
max_inlier = num_inlier;
}

}
return max_inlier;
}
Expand Down Expand Up @@ -356,7 +356,7 @@ void gms_matcher::VerifyCellPairs(int RotationType) {
int idx_grid_rt = mCellPairs[i];

const int *NB9_lt = mGridNeighborLeft.ptr<int>(i);
const int *NB9_rt = mGridNeighborRight.ptr<int>(idx_grid_rt);
const int *NB9_rt = mGridNeighborRight.ptr<int>(idx_grid_rt);

int score = 0;
double thresh = 0;
Expand Down Expand Up @@ -389,13 +389,13 @@ int gms_matcher::run(int RotationType) {
mMotionStatistics = Mat::zeros(mGridNumberLeft, mGridNumberRight, CV_32SC1);
mvMatchPairs.assign(mNumberMatches, pair<int, int>(0, 0));

for (int GridType = 1; GridType <= 4; GridType++)
for (int GridType = 1; GridType <= 4; GridType++)
{
// initialize
mMotionStatistics.setTo(0);
mCellPairs.assign(mGridNumberLeft, -1);
mNumberPointsInPerCellLeft.assign(mGridNumberLeft, 0);

AssignMatchPairs(GridType);
VerifyCellPairs(RotationType);

Expand All @@ -421,7 +421,7 @@ inline Mat DrawInlier(Mat &src1, Mat &src2, vector<KeyPoint> &kpt1, vector<KeyPo
src1.copyTo(output(Rect(0, 0, src1.cols, src1.rows)));
src2.copyTo(output(Rect(src1.cols, 0, src2.cols, src2.rows)));

if (type == 1)
if (type == 1) // Only Lines
{
for (size_t i = 0; i < inlier.size(); i++)
{
Expand All @@ -430,7 +430,7 @@ inline Mat DrawInlier(Mat &src1, Mat &src2, vector<KeyPoint> &kpt1, vector<KeyPo
line(output, left, right, Scalar(0, 255, 255));
}
}
else if (type == 2)
else if (type == 2) // Lines and Circles
{
for (size_t i = 0; i < inlier.size(); i++)
{
Expand All @@ -447,6 +447,20 @@ inline Mat DrawInlier(Mat &src1, Mat &src2, vector<KeyPoint> &kpt1, vector<KeyPo
circle(output, right, 1, Scalar(0, 255, 0), 2);
}
}
else // Circles and Text
{
for (size_t i = 0; i < inlier.size(); i++)
{
Point2f left = kpt1[inlier[i].queryIdx].pt;
Point2f right = (kpt2[inlier[i].trainIdx].pt + Point2f((float)src1.cols, 0.f));
circle(output, left, 1, Scalar(0, 255, 255), 2);
circle(output, right, 1, Scalar(0, 255, 0), 2);

cv::putText( output, to_string(i), left, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.3, cv::Scalar(255,0,255) );
cv::putText( output, to_string(i), right, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.3, cv::Scalar(255,0,255) );

}
}

return output;
}
Expand All @@ -456,7 +470,3 @@ inline void imresize(Mat &src, int height) {
int width = static_cast<int>(src.cols * 1.0 / ratio);
resize(src, src, Size(width, height));
}




42 changes: 42 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Usage of gms_matcher.py

Cntains 2 classes, viz. GmsMatcher and GmsRobe.

## GmsMatcher
This class is the original work as presented in the paper.
```
@inproceedings{bian2017gms,
title={GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence},
author={JiaWang Bian and Wen-Yan Lin and Yasuyuki Matsushita and Sai-Kit Yeung and Tan Dat Nguyen and Ming-Ming Cheng},
booktitle={IEEE Conference on Computer Vision and Pattern Recognition},
year={2017}
}
```

For demo usage see function, `demo_gms_original_implementation()` in `demo.py`


## GmsRobe
This class extents the functionality of GmsMatcher.
The GmsMatcher returns an array of DMatch. This might be sometimes convinent to use.
As you need to make reference to the keypoints to get to the image co-ordinates in question.

GmsRobe class defines the following functions
- match2( imC, imP )<br/>
Given current image(imC) and prev image(imP) returns GMS matches co-ordinates (x,y)_i
Essentially this is a thin wrapper around the original GmsMatcher class


- match3( imC, imP, imCm )<br/>
To find 3way correspondences, ie. a set of point co-ordinates in imC which also occur in imP and imCm. a<-->b<-->c. Note: The order in which the images is given is critical.


- match2_guided( imC, pt1, imP ) <br/>
Given current image (imC), with pts_C 2xN as input points on imC, the
objective is to find these points in previous image (imP)

The way we do this is to first compute all the matches between imC and imP.
Then filter these matches to include only those pysically close to input points.


Following functions return 2xN matrix with (x,y) of matches.
125 changes: 125 additions & 0 deletions python/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
## This script has demo use cases for the original GmsMatcher class as well as
## for the GmsRobe class.

import math
from enum import Enum

import cv2
import numpy as np
cv2.ocl.setUseOpenCL(False)

import time
import code


from gms_matcher import GmsMatcher
from gms_matcher import GmsRobe
from gms_matcher import DrawingType

def imresize(src, height):
ratio = src.shape[0] * 1.0/height
width = int(src.shape[1] * 1.0/ratio)
return cv2.resize(src, (width, height))

def demo_match3():

imgP = cv2.imread("../data/kf_1140.png") #curr
imgC = cv2.imread("../data/kf_2583.png") #prev
imgCm = cv2.imread("../data/kf_2581.png") #currm

gms = GmsRobe()

print 'Test gmsrobe.match3()'
startT = time.time()
pts_C, pts_P, pts_Cm = gms.match3( imgC, imgP, imgCm )
print 'gmsrobe.match3 took (ms): %4.2f' %(1000.*(time.time() - startT ) )

gridd = gms.plot_3way_match( imgC, pts_C, imgP, pts_P, imgCm, pts_Cm, show_random_points=40 )
gridd2 = gms.plot_3way_match( imgC, pts_C, imgP, pts_P, imgCm, pts_Cm, show_random_points=-1, enable_lines=False )
cv2.imshow( 'showing 3way match. Randomly selected 40 points only', gridd )
cv2.imshow( 'showing 3way match.', gridd2 )
cv2.waitKey(0)



def demo_match2_guided():

print 'Test gmsrobe.match2_guided()'
imgP = cv2.imread("../data/kf_1140.png") #curr
imgC = cv2.imread("../data/kf_2583.png") #prev

orb = cv2.ORB_create(50)
kp1 = orb.detect(imgC, None)
print 'nORB Pts:', len(kp1)
pts_C = np.transpose( np.array([ np.array(k.pt) for k in kp1 ]) ) #2xN


gms = GmsRobe()

startT = time.time()
ptC, ptP = gms.match2_guided( imgC, pts_C, imgP )
print 'Elapsed total (ms): %4.2f' %(1000.0 * (time.time() - startT ) )
print 'nGuided Pts: ', ptC.shape[1]

cv2.imshow( 'orb points on curr', gms.plot_points_on_image( imgC, pts_C ) )
cv2.imshow( 'xcanvas', gms.plot_point_sets( imgC, ptC, imgP, ptP ) )
cv2.waitKey(0)




def demo_match2():
print 'Test simple gmsrobe.match2()'
imgP = cv2.imread("../data/kf_972.png") #curr
imgC = cv2.imread("../data/kf_973.png") #prev

gms = GmsRobe()

startT = time.time()
ptC, ptP = gms.match2( imgC, imgP )
print 'Elapsed total (ms): %4.2f' %(1000.0 * (time.time() - startT ) )


xcanvas = gms.plot_point_sets( imgC, ptC, imgP, ptP, enable_lines=False )
xcanvas2 = gms.plot_point_sets( imgC, ptC, imgP, ptP, show_random_points=60 )

# r = np.random.randint( 0, ptC.shape[1], 50 )
# xcanvas = gms.plot_point_sets( imgC, ptC[:,r], imgCm, ptCm[:,r] )
cv2.imshow( 'All matches', xcanvas )
cv2.imshow( 'Only show 60 random matches', xcanvas2 )
cv2.waitKey(0)



def demo_gms_original_implementation():
print 'Test for Original GMS Implementation, ie. class GmsMatcher'
# img1 = cv2.imread("../data/nn_left.jpg")
# img2 = cv2.imread("../data/nn_right.jpg")

# img1 = imresize(img1, 240)
# img2 = imresize(img2, 240)

img1 = cv2.imread("../data/kf_2581.png")
img2 = cv2.imread("../data/kf_2583.png")


gms = GmsMatcher(verbosity=1)
startT = time.time()
matches = gms.compute_matches(img1, img2)
print 'gms.compute_matches took (ms): %4.2f' %(1000.*(time.time() - startT ) )
gms.draw_matches(img1, img2, DrawingType.POINTS_AND_TEXT)

if __name__ == '__main__':
# Try any of the 4 demos.
which_demo = raw_input('a:demo_match3\nb:demo_match2_guided\nc:demo_match2\nd:demo_gms_original_implementation\nWhich demo do you want to try? ')
print( '%s' %(which_demo))

if which_demo == 'a':
demo_match3()
if which_demo == 'b':
demo_match2_guided()
if which_demo == 'c':
demo_match2()

if which_demo == 'd':
demo_gms_original_implementation()
Loading