I am using OpenCV 3.2
I am trying to use FLANN to match features descriptors in a faster way than brute force.
// Ratio to the second neighbor to consider a good match.
#define RATIO 0.75
void matchFeatures(const cv::Mat &query, const cv::Mat &target,
std::vector<cv::DMatch> &goodMatches) {
std::vector<std::vector<cv::DMatch>> matches;
cv::Ptr<cv::FlannBasedMatcher> matcher = cv::FlannBasedMatcher::create();
// Find 2 best matches for each descriptor to make later the second neighbor test.
matcher->knnMatch(query, target, matches, 2);
// Second neighbor ratio test.
for (unsigned int i = 0; i < matches.size(); ++i) {
if (matches[i][0].distance < matches[i][1].distance * RATIO)
goodMatches.push_back(matches[i][0]);
}
}
This code is working me with SURF and SIFT descriptors, but not with ORB.
OpenCV Error: Unsupported format or combination of formats (type=0) in buildIndex
As it's said here, FLANN needs the descriptors to be of type CV_32F so we need to convert them.
if (query.type() != CV_32F) query.convertTo(query, CV_32F);
if (target.type() != CV_32F) target.convertTo(target, CV_32F);
However, this supposed fix is returning me another error in convertTo function.
OpenCV Error: Assertion failed (!fixedType() || ((Mat*)obj)->type() == mtype) in create
This assertion is in opencv/modules/core/src/matrix.cpp file, line 2277.
What's happening?
Code to replicate issue.
#include <opencv2/opencv.hpp>
int main(int argc, char **argv) {
// Read both images.
cv::Mat image1 = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
if (image1.empty()) {
std::cerr << "Couldn't read image in " << argv[1] << std::endl;
return 1;
}
cv::Mat image2 = cv::imread(argv[2], cv::IMREAD_GRAYSCALE);
if (image2.empty()) {
std::cerr << "Couldn't read image in " << argv[2] << std::endl;
return 1;
}
// Detect the keyPoints and compute its descriptors using ORB Detector.
std::vector<cv::KeyPoint> keyPoints1, keyPoints2;
cv::Mat descriptors1, descriptors2;
cv::Ptr<cv::ORB> detector = cv::ORB::create();
detector->detectAndCompute(image1, cv::Mat(), keyPoints1, descriptors1);
detector->detectAndCompute(image2, cv::Mat(), keyPoints2, descriptors2);
// Match features.
std::vector<cv::DMatch> matches;
matchFeatures(descriptors1, descriptors2, matches);
// Draw matches.
cv::Mat image_matches;
cv::drawMatches(image1, keyPoints1, image2, keyPoints2, matches, image_matches);
cv::imshow("Matches", image_matches);
}
Did you adjust the FLANN parameters?
Taken from http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.html
While using ORB, you can pass the following. The commented values are recommended as per the docs, but it didn’t provide required results in some cases. Other values worked fine.:
index_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
Probably you can convert that to C++ api?
According to the comment, the C++ way is:
cv::FlannBasedMatcher matcher = cv::FlannBasedMatcher(cv::makePtr<cv::flann::LshIndexParams>(12, 20, 2));
Binary-string descriptors: ORB, BRIEF, BRISK, FREAK, AKAZE, etc.
Floating-point descriptors: SIFT, SURF, GLOH, etc.
Feature matching of binary descriptors can be efficiently done by comparing their Hamming distance as opposed to Euclidean distance used for floating-point descriptors.
For comparing binary descriptors in OpenCV, use FLANN + LSH index or Brute Force + Hamming distance.
http://answers.opencv.org/question/59996/flann-error-in-opencv-3/
By default, FlannBasedMatcher works as KDTreeIndex with L2 norm. This is the reason why it works well with SIFT/SURF descriptors and throws an exception for ORB descriptor.
Binary features and Locality Sensitive Hashing (LSH)
Performance comparison between binary and floating-point descriptors
I believe there is a bug in the OpenCV3 version: FLANN error in OpenCV 3
You need to convert your descriptors to a 'CV_32F'.
Related
I'm currently working on real-time feature matching using OpenCV3.4.0, c++ in QT creator.
My code matches features between the first frame that I got by webcam and current frame input from webcam.
Mat frame1, frame2, img1, img2, img1_gray, img2_gray;
int n = 0;
VideoCapture cap1(0);
namedWindow("Video Capture1", WINDOW_NORMAL);
namedWindow("Reference img", WINDOW_NORMAL);
namedWindow("matches1", WINDOW_NORMAL);
moveWindow("Video Capture1",50, 0);
moveWindow("Reference img",50, 100);
moveWindow("matches1",100,100);
while((char)waitKey(1)!='q'){
//raw image saved in frame
cap1>>frame1;
n=n+1;
if (n ==1){
imwrite("frame1.jpg", frame1);
cout<<"First frame saved as 'frame1'!!"<<endl;
}
if(frame1.empty())
break;
imshow("Video Capture1",frame1);
img1 = imread("frame1.jpg");
img2 = frame1;
cvtColor(img1, img1_gray, cv::COLOR_BGR2GRAY);
cvtColor(img2, img2_gray, cv::COLOR_BGR2GRAY);
imshow("Reference img",img1);
// detecting keypoints
int minHessian = 400;
Ptr<Feature2D> detector = xfeatures2d::SurfFeatureDetector::create();
vector<KeyPoint> keypoints1, keypoints2;
detector->detect(img1_gray,keypoints1);
detector->detect(img2_gray,keypoints2);
// computing descriptors
Ptr<DescriptorExtractor> extractor = xfeatures2d::SurfFeatureDetector::create();
Mat descriptors1, descriptors2;
extractor->compute(img1_gray,keypoints1,descriptors1);
extractor->compute(img2_gray,keypoints2,descriptors2);
// matching descriptors
BFMatcher matcher(NORM_L2);
vector<DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
// drawing the results
Mat img_matches;
drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches);
imshow("matches1", img_matches);
But the code returns so many matched points that I cannot distinguish which one matches which.
So, are there any methods to get high-quality matched points only?
And how can I get each matched point's pixel coordinates in QT creator just like MATLAB?
So, are there any methods to get high-quality matched points only?
I bet there are a lot of different methods. I am using e.g. a symmetry test. So matches from img1 to img2 also have to exist when matching from img2 to img1. I am using the test of Improve matching of feature points with OpenCV. Multiple other tests are shown there.
void symmetryTest(const std::vector<cv::DMatch> &matches1,const std::vector<cv::DMatch> &matches2,std::vector<cv::DMatch>& symMatches)
{
symMatches.clear();
for (vector<DMatch>::const_iterator matchIterator1= matches1.begin();matchIterator1!= matches1.end(); ++matchIterator1)
{
for (vector<DMatch>::const_iterator matchIterator2= matches2.begin();matchIterator2!= matches2.end();++matchIterator2)
{
if ((*matchIterator1).queryIdx ==(*matchIterator2).trainIdx &&(*matchIterator2).queryIdx ==(*matchIterator1).trainIdx)
{
symMatches.push_back(DMatch((*matchIterator1).queryIdx,(*matchIterator1).trainIdx,(*matchIterator1).distance));
break;
}
}
}
}
Like András Kovács says in the related answer you can also calculate a Fundamental Matrix with RANSAC to eliminate outliers using cv::findFundamentalMat.
And how can I get each matched point's pixel coordinates in QT creator just like MATLAB?
I hope I understood it right that you want to have the point coordinates of homologue points that match. I am extracting the coordinates of the points after the symmetryTest.
The coordinates are inside the keypoints.
for (size_t rows = 0; rows < sym_matches.size(); rows++) {
float x1 = keypoints_1[sym_matches[rows].queryIdx].pt.x;
float y1 = keypoints_1[sym_matches[rows].queryIdx].pt.y;
float x2 = keypoints_2[sym_matches[rows].trainIdx].pt.x;
float y2 = keypoints_2[sym_matches[rows].trainIdx].pt.y;
// Push the coordinates in a vector e.g. std:vector<cv::Point2f>>
}
You can do the same with your matches, keypoints1 and keypoint2.
My goal is to match an image captured from a camera with some models and find the closest one. However I think I am missing something.
This is what I'm doing: first I get a frame from the camera, select a portion, extract keypoints and compute descriptors using SURF and store them in a xml file (I also store the model as model.png). This is my model.
Then I take another frame (in few seconds), select the same portion, compute descriptors and match these against the previously stored one.
The result is not close to 100% (I use the ratio between good matches and number of keypoints) like I would expect.
To have a comparison, if I load model.png, compute its descriptors and match against the stored descriptors I get 100% matching (more or less), and this is reasonable.
This is my code:
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/nonfree/nonfree.hpp"
using namespace std;
std::vector<cv::KeyPoint> detectKeypoints(cv::Mat image, int hessianTh, int nOctaves, int nOctaveLayers, bool extended, bool upright) {
std::vector<cv::KeyPoint> keypoints;
cv::SurfFeatureDetector detector(hessianTh,nOctaves,nOctaveLayers,extended,upright);
detector.detect(image,keypoints);
return keypoints; }
cv::Mat computeDescriptors(cv::Mat image,std::vector<cv::KeyPoint> keypoints, int hessianTh, int nOctaves, int nOctaveLayers, bool extended, bool upright) {
cv::SurfDescriptorExtractor extractor(hessianTh,nOctaves,nOctaveLayers,extended,upright);
cv::Mat imageDescriptors;
extractor.compute(image,keypoints,imageDescriptors);
return imageDescriptors; }
int main(int argc, char *argv[]) {
cv::VideoCapture cap(0);
cap.set(CV_CAP_PROP_FRAME_WIDTH, 2304);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 1536);
cap >> frame;
cv::Rect selection(939,482,1063-939,640-482);
cv::Mat roi = frame(selection).clone();
//cv::Mat roi=cv::imread("model.png");
cv::cvtColor(roi,roi,CV_BGR2GRAY);
cv::equalizeHist(roi,roi);
if (std::stoi(argv[1])==1)
{
std::vector<cv::KeyPoint> keypoints = detectKeypoints(roi,400,4,2,true,false);
cv::FileStorage fs("model.xml", cv::FileStorage::WRITE);
cv::write(fs,"keypoints",keypoints);
cv::write(fs,"descriptors",computeDescriptors(roi,keypoints,400,4,2,true,false));
fs.release();
cv::imwrite("model.png",roi);
}
else
{
cv::FileStorage fs("model.xml", cv::FileStorage::READ);
std::vector<cv::KeyPoint> modelkeypoints;
cv::Mat modeldescriptor;
cv::FileNode filenode = fs["keypoints"];
cv::read(filenode,modelkeypoints);
filenode = fs["descriptors"];
cv::read(filenode, modeldescriptor);
fs.release();
std::vector<cv::KeyPoint> roikeypoints = detectKeypoints(roi,400,4,2,true,false);
cv::Mat roidescriptor = computeDescriptors(roi,roikeypoints,400,4,2,true,false);
std::vector<std::vector<cv::DMatch>> matches;
cv::BFMatcher matcher(cv::NORM_L2);
if(roikeypoints.size()<modelkeypoints.size())
matcher.knnMatch(roidescriptor, modeldescriptor, matches, 2); // Find two nearest matches
else
matcher.knnMatch(modeldescriptor, roidescriptor, matches, 2);
vector<cv::DMatch> good_matches;
for (int i = 0; i < matches.size(); ++i)
{
const float ratio = 0.7;
if (matches[i][0].distance < ratio * matches[i][1].distance)
{
good_matches.push_back(matches[i][0]);
}
}
cv::Mat matching;
cv::Mat model = cv::imread("model.png");
if(roikeypoints.size()<modelkeypoints.size())
cv::drawMatches(roi,roikeypoints,model,modelkeypoints,good_matches,matching);
else
cv::drawMatches(model,modelkeypoints,roi,roikeypoints,good_matches,matching);
cv::imwrite("matches.png",matching);
float result = static_cast<float>(good_matches.size())/static_cast<float>(roikeypoints.size());
std::cout << result << std::endl;
}
return 0; }
Any suggestion will be appreciated, this is driving me crazy..
This is expected, the small change between the two frames is the reason you don't get 100% matches. But on the same image, the SURF features are going to be exactly at the same points and the computed descriptors are going to be identical. So tune your method for your camera, plot the distance between features when they are supposed to be identical. Set a threshold on the distance such that most (maybe 95%) of the matches are accepted. This way you will have a low false match rate and still have a large rate of true matches.
I am new to openCV. I am trying to follow the OpenCV3.4.0 tutorial on AKAZE and ORB planar tracking tutorial to perform feature matching.
I am using the android NDK environment to write c++ code.
I managed to get the program to detect features from both the template and the camera pictures, and matched them using the FlannBasedMatcher. The returned matches are stored in the "matched1" and "matched2" array.
How can I get the keypoints stored in matche1 and match2 out? The tutorial suggest to use the function: Points(matched1), which is not available in my coding environment. I checked that I have included all the header files in the tutorial.
Mat& mScene= *(Mat *) scene;
Mat& mTempl = *(Mat *) templ;
vector<Point2f> object_bb;
Mat descriptorsTemplate,descriptorsCamera;
vector<KeyPoint> keypointsTemplate,keypointsCamera;
Ptr<ORB> fd_de = ORB::create();
fd_de->detectAndCompute( mScene, Mat(), keypointsCamera,
descriptorsCamera);
fd_de->detectAndCompute( mTempl, Mat(), keypointsTemplate,
descriptorsTemplate );
Mat img_keypoints_1,img_keypoints_2;
cv::FlannBasedMatcher matcher =
cv::FlannBasedMatcher(cv::makePtr<cv::flann::LshIndexParams>(12, 20,
2));
vector< vector<DMatch> > matches;
vector<KeyPoint> matched1, matched2;
matcher.knnMatch(descriptorsTemplate, descriptorsCamera, matches,2);
for( int i = 0; i < matches.size(); i++ ){
if(matches[i][0].distance < nn_match_ratio * matches[i]
[1].distance) {
matched1.push_back(keypointsTemplate[matches[i][0].queryIdx]);
matched2.push_back(keypointsCamera[matches[i][0].trainIdx]);
}
}
Mat inlier_mask, homography;
vector<KeyPoint> inliers1, inliers2;
vector<DMatch> inlier_matches;
if(matched1.size() >= 4) {
homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);
}
The Points function can be found in utils.h - remember to import it.
Does someone know of the link to BRIEF implementation with OpenCV 2.4? Regards.
PS: I know such questions are generally not welcome on SO, as the primary focus is what work you have done. But there was a similar question which was quite well received.
One of the answers to that questions suggests a generic manner for SIFT, which could be extended to BRIEF. Here is my slightly modified code.
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/highgui/highgui.hpp>
//using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
Mat image = imread("load02.jpg", CV_LOAD_IMAGE_GRAYSCALE);
cv::initModule_nonfree();
// Create smart pointer for SIFT feature detector.
Ptr<FeatureDetector> featureDetector = FeatureDetector::create("HARRIS"); // "BRIEF was initially written. Changed after answer."
vector<KeyPoint> keypoints;
// Detect the keypoints
featureDetector->detect(image, keypoints); // NOTE: featureDetector is a pointer hence the '->'.
//Similarly, we create a smart pointer to the SIFT extractor.
Ptr<DescriptorExtractor> featureExtractor = DescriptorExtractor::create("BRIEF");
// Compute the 128 dimension SIFT descriptor at each keypoint.
// Each row in "descriptors" correspond to the SIFT descriptor for each keypoint
Mat descriptors;
featureExtractor->compute(image, keypoints, descriptors);
// If you would like to draw the detected keypoint just to check
Mat outputImage;
Scalar keypointColor = Scalar(255, 0, 0); // Blue keypoints.
drawKeypoints(image, keypoints, outputImage, keypointColor, DrawMatchesFlags::DEFAULT);
namedWindow("Output");
imshow("Output", outputImage);
char c = ' ';
while ((c = waitKey(0)) != 'q'); // Keep window there until user presses 'q' to quit.
return 0;
}
The issue with this code is that it gives an error: First-chance exception at 0x00007FFB84698B9C in Project2.exe: Microsoft C++ exception: cv::Exception at memory location 0x00000071F4FBF8E0.
The error results in the function execution breaking. A tag says that execution will resume at the namedWindow("Output"); line.
Could someone please help fix this issue, or suggest a new code altogether? Thanks.
EDIT: The terminal now shows an error: Assertion failed (!outImage.empty()) in cv::drawKeypoints, file ..\..\..\..opencv\modules\features2d\src\draw.cpp, line 115. The next statement from where the code will resume remains the same, as drawKepoints is called just before it.
In OpenCV, BRIEF is a DescriptorExtractor, not a FeatureDetector. According to FeatureDetector::create, this factory method does not support "BRIEF" algorithm. In other words, FeatureDetector::create("BRIEF") returns a null pointer and your program crashes.
The general steps in feature matching are:
Find some interesting (feature) points in an image: FeatureDetector
Find a way to describe those points: DescriptorExtractor
Try to match descriptors (feature vectors) in two images: DescriptorMatcher
BRIEF is an algorithm only for step 2. You can use some other methods, HARRIS, ORB, ..., in step 1 and pass the result to step 2 using BRIEF. Besides, SIFT can be used in both step 1 and 2 because the algorithm provides methods for both steps.
Here's a simple example to use BRIEF in OpenCV. First step, find points that looks interesting (key points) in an image:
vector<KeyPoint> DetectKeyPoints(const Mat &image)
{
auto featureDetector = FeatureDetector::create("HARRIS");
vector<KeyPoint> keyPoints;
featureDetector->detect(image, keyPoints);
return keyPoints;
}
You can try any FeatureDetector algorithm instead of "HARRIS". Next step, compute the descriptors from key points:
Mat ComputeDescriptors(const Mat &image, vector<KeyPoint> &keyPoints)
{
auto featureExtractor = DescriptorExtractor::create("BRIEF");
Mat descriptors;
featureExtractor->compute(image, keyPoints, descriptors);
return descriptors;
}
You can use algorithm different than "BRIEF", too. And you can see that the algorithms in DescriptorExtractor is not the same as the algorithms in FeatureDetector. The last step, match two descriptors:
vector<DMatch> MatchTwoImage(const Mat &descriptor1, const Mat &descriptor2)
{
auto matcher = DescriptorMatcher::create("BruteForce");
vector<DMatch> matches;
matcher->match(descriptor1, descriptor2, matches);
return matches;
}
Similarly, you can try different matching algorithm other than "BruteForce". Finally back to main program, you can build the application from those functions:
auto img1 = cv::imread("image1.jpg");
auto img2 = cv::imread("image2.jpg");
auto keyPoints1 = DetectKeyPoints(img1);
auto keyPoints2 = DetectKeyPoints(img2);
auto descriptor1 = ComputeDescriptors(img1, keyPoints1);
auto descriptor2 = ComputeDescriptors(img2, keyPoints2);
auto matches = MatchTwoImage(descriptor1, descriptor2);
and use matches vector to complete your application. If you want to check the results, OpenCV also provides functions to draw results of step 1 & 3 in an image. For example, draw the matches in the final step:
Mat result;
drawMatches(img1, keyPoints1, img2, keyPoints2, matches, result);
imshow("result", result);
waitKey(0);
I'm working on a project where I will use homography as features in a classifier. My problem is in automatically calculating homographies, i'm using SIFT descriptors to find the points between the two images on which to calculate homography but SIFT are giving me very poor results, hence i can't use them in my work.
I'm using OpenCV 2.4.3.
At first I was using SURF, but I had similar results and I decided to use SIFT which are slower but more precise. My first guess was that the image resolution in my dataset was too low but i ran my algorithm on a state-of-the-art dataset (Pointing 04) and I obtained pretty much the same results, so the problem lies in what I do and not in my dataset.
The match between the SIFT keypoints found in each image is done with the FlannBased matcher, i tried the BruteForce one but the results were again pretty much the same.
This is an example of the match I found (image from Pointing 04 dataset)
The above image shows how poor is the match found with my program. Only 1 point is a correct match. I need (at least) 4 correct matches for what I have to do.
Here is the code i use:
This is the function that extracts SIFT descriptors from each image
void extract_sift(const Mat &img, vector<KeyPoint> &keypoints, Mat &descriptors, Rect* face_rec) {
// Create masks for ROI on the original image
Mat mask1 = Mat::zeros(img.size(), CV_8U); // type of mask is CV_8U
Mat roi1(mask1, *face_rec);
roi1 = Scalar(255, 255, 255);
// Extracts keypoints in ROIs only
Ptr<DescriptorExtractor> featExtractor;
Ptr<FeatureDetector> featDetector;
Ptr<DescriptorMatcher> featMatcher;
featExtractor = new SIFT();
featDetector = FeatureDetector::create("SIFT");
featDetector->detect(img,keypoints,mask1);
featExtractor->compute(img,keypoints,descriptors);
}
This is the function that matches two images' descriptors
void match_sift(const Mat &img1, const Mat &img2, const vector<KeyPoint> &kp1,
const vector<KeyPoint> &kp2, const Mat &descriptors1, const Mat &descriptors2,
vector<Point2f> &p_im1, vector<Point2f> &p_im2) {
// Matching descriptor vectors using FLANN matcher
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("FlannBased");
std::vector< DMatch > matches;
matcher->match( descriptors1, descriptors2, matches );
double max_dist = 0; double min_dist = 100;
// Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors1.rows; ++i ){
double dist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
// Draw only the 4 best matches
std::vector< DMatch > good_matches;
// XXX: DMatch has no sort method, maybe a more efficent min extraction algorithm can be used here?
double min=matches[0].distance;
int min_i = 0;
for( int i = 0; i < (matches.size()>4?4:matches.size()); ++i ) {
for(int j=0;j<matches.size();++j)
if(matches[j].distance < min) {
min = matches[j].distance;
min_i = j;
}
good_matches.push_back( matches[min_i]);
matches.erase(matches.begin() + min_i);
min=matches[0].distance;
min_i = 0;
}
Mat img_matches;
drawMatches( img1, kp1, img2, kp2,
good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
imwrite("imgMatch.jpeg",img_matches);
imshow("",img_matches);
waitKey();
for( int i = 0; i < good_matches.size(); i++ )
{
// Get the points from the best matches
p_im1.push_back( kp1[ good_matches[i].queryIdx ].pt );
p_im2.push_back( kp2[ good_matches[i].trainIdx ].pt );
}
}
And these functions are called here:
extract_sift(dataset[i].img,dataset[i].keypoints,dataset[i].descriptors,face_rec);
[...]
// Extract keypoints from i+1 image and calculate homography
extract_sift(dataset[i+1].img,dataset[i+1].keypoints,dataset[i+1].descriptors,face_rec);
dataset[front].points_r.clear(); // XXX: dunno if clearing the points every time is the best way to do it..
match_sift(dataset[front].img,dataset[i+1].img,dataset[front].keypoints,dataset[i+1].keypoints,
dataset[front].descriptors,dataset[i+1].descriptors,dataset[front].points_r,dataset[i+1].points_r);
dataset[i+1].H = findHomography(dataset[front].points_r,dataset[i+1].points_r, RANSAC);
Any help on how to improve the matching performance would be really appreciated, thanks.
You apparently use the "best four points" in your code w.r.t. the distance of the matches. In other words, you consider that a match is valid if both descriptors are really similar. I believe this is wrong. Did you try to draw all of the matches? Many of them should be wrong, but many should be good as well.
The distance of a match just tells how similar both points are. This doesn't tell if the match is coherent geometrically. Selecting the best matches should definitely consider the geometry.
Here is how I would do:
Detect the corners (you already do this)
Find the matches (you already do this)
Try to find a homography transform between both images by using the matches (don't filter them before!) using findHomography(...)
findHomography(...) will tell you which are the inliers. Those are your good_matches.