paella/Code/src/DetectionAndMatching/DetectionAndMatching.cpp

281 lines
9.6 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Paella
// Copyright (C) 2015 - Thomas FORGIONE, Emilie JALRAS, Marion LENFANT, Thierry MALON, Amandine PAILLOUX
// Authors :
// Thomas FORGIONE
// Emilie JALRAS
// Marion LENFANT
// Thierry MALON
// Amandine PAILLOUX
//
// This file is part of the project Paella
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <cmath>
#include <tuple>
#include <algorithm>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "DetectionAndMatching/DetectionAndMatching.hpp"
int main( int argc, char** argv )
{
cv::Mat img_1;
cv::Mat img_2;
cv::Mat masque_1;
cv::Mat masque_2;
cv::Size s1;
cv::Size s2;
if( argc != 5 )
{
if (argc==3) {
img_1 = cv::imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
img_2 = cv::imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );
s1 = img_1.size();
s2 = img_2.size();
masque_1 = cv::Mat::ones(s1.height,s1.width, CV_8UC1);
masque_2 = cv::Mat::ones(s2.height,s2.width, CV_8UC1);
} else {
readme(); return -1;
}
}
if (argc!=3)
{
img_1 = cv::imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
img_2 = cv::imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );
masque_1 = cv::imread( argv[3], CV_LOAD_IMAGE_GRAYSCALE );
masque_2 = cv::imread( argv[4], CV_LOAD_IMAGE_GRAYSCALE );
}
//std::cout << masque_1 << std::endl;
if( !img_1.data || !img_2.data )
{ std::cout<< " --(!) Error reading images " << std::endl; return -1; }
std::vector<std::pair<cv::Point, cv::Point>> matchPoints;
cv::Mat masque_1_resize = resizeMask(masque_1,img_1.size());
cv::Mat masque_2_resize = resizeMask(masque_2,img_2.size());
matchPoints = detectAndMatch(img_1, img_2, masque_1_resize, masque_2_resize);
return 0;
}
std::vector<std::pair<cv::Point,cv::Point>> detectAndMatch(cv::Mat const& img_1, cv::Mat const& img_2, cv::Mat const& masque_1, cv::Mat const& masque_2)
{
//-- Step 1: Detect the keypoints using BRISK Detector
int Threshl=3;
int Octaves=4; //(pyramid layer) from which the keypoint has been extracted
float PatternScales=1.5f;
std::vector<cv::KeyPoint> keypoints_1, keypoints_2;
cv::BRISK BRISKD(Threshl,Octaves,PatternScales);//initialize algoritm
BRISKD.create("Feature2D.BRISK");
BRISKD.detect(img_1, keypoints_1,masque_1);
BRISKD.detect( img_2, keypoints_2, masque_2);
//-- Draw keypoints
cv::Mat img_keypoints_1; cv::Mat img_keypoints_2;
cv::drawKeypoints( img_1, keypoints_1, img_keypoints_1, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT );
cv::drawKeypoints( img_2, keypoints_2, img_keypoints_2, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT );
//-- Show detected (drawn) keypoints
cv::imshow("Keypoints 1", img_keypoints_1 );
cv::imshow("Keypoints 2", img_keypoints_2 );
cv::waitKey(0);
//-- Step 2: Calculate descriptors (feature vectors)
//every raw from the descriptors contains a KeyPoint descriptor (128 columns per raw)
cv::Mat descriptors_1, descriptors_2;
BRISKD.compute( img_1, keypoints_1, descriptors_1 );
BRISKD.compute( img_2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
cv::BFMatcher matcher(cv::NORM_L2);
std::vector< cv::DMatch > matches1;
std::vector< cv::DMatch > matches2;
matcher.match( descriptors_1, descriptors_2, matches1 );
matcher.match( descriptors_2, descriptors_1, matches2 );
//symetric filtering
std::vector< cv::DMatch > symetricMatches = symetricFilter( matches1, matches2);
//order constraint
float proportion = 0.5;
std::vector< cv::DMatch > matches = orderConstraintFilter (symetricMatches, keypoints_1, keypoints_2, proportion);
/* //threshold filter
float distanceThreshold=1;
std::vector< cv::DMatch > correctedMatches = thresholdFilter (distanceThreshold, symetricMatches);
std::cout << correctedMatches.size() << std::endl;*/
// geometric filter
std::tuple<std::vector< cv::DMatch >, std::vector<cv::KeyPoint>, std::vector<cv::KeyPoint>> tuple = geometricFilter ( keypoints_1, keypoints_2,matches);
std::vector< cv::DMatch > geometricMatches = std::get<0>(tuple);
keypoints_1 = std::get<1>(tuple);
keypoints_2 = std::get<2>(tuple);
std::cout << keypoints_1.size() << std::endl;
//-- Draw matches
cv::Mat img_matches;
cv::drawMatches( img_1, keypoints_1, img_2, keypoints_2, geometricMatches, img_matches );
//-- Show detected matches
cv::imshow("correctedMatches", img_matches );
cv::waitKey(0);
// vector of pairs of matching points
std::vector<std::pair<cv::Point,cv::Point>> matchPoints;
for(unsigned int i=0;i<geometricMatches.size();i++)
{
//myPair pair of matching points
std::pair<cv::Point,cv::Point> myPair;
//queryIdx index of the point from image 1
myPair.first=keypoints_1[geometricMatches[i].queryIdx].pt;
//trainIdx index of the point from image 2
myPair.second=keypoints_2[geometricMatches[i].trainIdx].pt;
matchPoints.push_back(myPair);
}
return matchPoints;
}
std::vector< cv::DMatch > symetricFilter( std::vector< cv::DMatch > const& matches1, std::vector< cv::DMatch > const& matches2)
{
std::vector< cv::DMatch > symetricMatches;
unsigned int h;
for(unsigned int k=0;k<matches1.size();k++)
{
h = 0;
while((h<matches2.size()) & (matches2[h].queryIdx!=matches1[k].trainIdx))
{
h++;
}
if (matches2[h].queryIdx==matches1[k].trainIdx)
{
if (matches1[k].queryIdx==matches2[h].trainIdx) symetricMatches.push_back(matches1[k]);
}
}
return symetricMatches;
}
std::vector< cv::DMatch > orderConstraintFilter (std::vector< cv::DMatch > const& symetricMatches, std::vector<cv::KeyPoint> const& keypoints_1, std::vector<cv::KeyPoint> const& keypoints_2, float proportion)
{
std::vector< cv::DMatch > matches;
unsigned int counter;
for(unsigned int k=0;k<symetricMatches.size();k++)
{
for(unsigned int h=k+1;h<symetricMatches.size();h++)
{
if(((keypoints_1[symetricMatches[k].queryIdx].pt.y-keypoints_1[symetricMatches[h].queryIdx].pt.y)*(keypoints_2[symetricMatches[k].trainIdx].pt.y-keypoints_2[symetricMatches[h].trainIdx].pt.y)<0)||((keypoints_1[symetricMatches[k].queryIdx].pt.x-keypoints_1[symetricMatches[h].queryIdx].pt.x)*(keypoints_2[symetricMatches[k].trainIdx].pt.x-keypoints_2[symetricMatches[h].trainIdx].pt.x)<0))
{
counter++;
}
}
if(counter<symetricMatches.size()*proportion)
{
matches.push_back(symetricMatches[k]);
}
counter=0;
}
return matches;
}
std::vector< cv::DMatch > thresholdFilter (float distanceThreshold, std::vector< cv::DMatch > const& matches)
{
std::vector< cv::DMatch > correctedMatches;
for(unsigned int i=0;i<matches.size();i++)
{
std::cout << matches[i].distance << std::endl;
if(matches[i].distance<distanceThreshold) correctedMatches.push_back(matches[i]);
}
return correctedMatches;
}
std::tuple<std::vector<cv::DMatch>, std::vector<cv::KeyPoint>, std::vector<cv::KeyPoint>> geometricFilter ( std::vector<cv::KeyPoint> const& keypoints_1, std::vector<cv::KeyPoint> const& keypoints_2, std::vector< cv::DMatch > const& correctedMatches)
{
std::vector< cv::DMatch > matches;
std::vector<cv::KeyPoint> kpoints_1, kpoints_2, newkpoints_1, newkpoints_2;
std::vector<cv::Point2f> points_1, points_2;
for(unsigned int i=0;i<correctedMatches.size();i++)
{
kpoints_1.push_back(keypoints_1[correctedMatches[i].queryIdx]);
kpoints_2.push_back(keypoints_2[correctedMatches[i].trainIdx]);
points_1.push_back(kpoints_1[i].pt);
points_2.push_back(kpoints_2[i].pt);
}
cv::Mat masque;
cv::Mat F = cv::findFundamentalMat(points_1, points_2, CV_FM_RANSAC, 5, 0.99, masque);
unsigned int counter = 0;
for(unsigned int j=0;j<kpoints_1.size();j++)
{
if (static_cast<int>(masque.at<uchar>(j,0))==1)
{
newkpoints_1.push_back(kpoints_1[j]);
newkpoints_2.push_back(kpoints_2[j]);
matches.push_back(cv::DMatch(counter,counter,0));
counter++;
}
}
return std::make_tuple(std::move(matches), std::move(newkpoints_1), std::move(newkpoints_2));
}
cv::Mat resizeMask(cv::Mat const& mask, cv::Size const& sizeImage)
{
cv::Size sizeMask = mask.size();
cv::Mat newMask = cv::Mat::zeros(sizeImage, CV_8UC1);
unsigned int h = 0 ;
for(int j=sizeImage.width-sizeMask.width;j<sizeImage.width;j++)
{
mask.col(h).copyTo(newMask.col(j));
h = h +1;
}
return newMask;
}
void readme()
{
std::cout << " Usage: DetectionAndMatching.exe <img1> <img2> [<mask1> <mask2>]" << std::endl;
}