281 lines
9.6 KiB
C++
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;
|
|
}
|
|
|
|
|