2018-02-09 18:24:18 +08:00
# include <opencv2/core.hpp>
# include <opencv2/imgproc.hpp>
# include <opencv2/features2d.hpp>
# include <opencv2/highgui.hpp>
2015-05-03 17:42:27 +08:00
# include <vector>
# include <iostream>
using namespace std ;
using namespace cv ;
2015-05-04 16:36:24 +08:00
static void help ( )
{
cout < < " \n This program demonstrates how to detect compute and match ORB BRISK and AKAZE descriptors \n "
" Usage: \n "
2018-10-31 20:48:56 +08:00
" ./matchmethod_orb_akaze_brisk --image1=<image1(basketball1.png as default)> --image2=<image2(basketball2.png as default)> \n "
2015-05-04 16:36:24 +08:00
" Press a key when image window is active to change algorithm or descriptor " ;
}
2015-05-03 17:42:27 +08:00
2015-05-04 16:36:24 +08:00
int main ( int argc , char * argv [ ] )
2015-05-03 17:42:27 +08:00
{
2015-05-04 16:36:24 +08:00
vector < String > typeDesc ;
2015-05-03 17:42:27 +08:00
vector < String > typeAlgoMatch ;
2015-05-04 16:36:24 +08:00
vector < String > fileName ;
2015-05-04 16:59:43 +08:00
// This descriptor are going to be detect and compute
2019-04-06 03:02:24 +08:00
typeDesc . push_back ( " AKAZE-DESCRIPTOR_KAZE_UPRIGHT " ) ; // see https://docs.opencv.org/master/d8/d30/classcv_1_1AKAZE.html
typeDesc . push_back ( " AKAZE " ) ; // see http://docs.opencv.org/master/d8/d30/classcv_1_1AKAZE.html
typeDesc . push_back ( " ORB " ) ; // see http://docs.opencv.org/master/de/dbf/classcv_1_1BRISK.html
typeDesc . push_back ( " BRISK " ) ; // see http://docs.opencv.org/master/db/d95/classcv_1_1ORB.html
// This algorithm would be used to match descriptors see http://docs.opencv.org/master/db/d39/classcv_1_1DescriptorMatcher.html#ab5dc5036569ecc8d47565007fa518257
2015-05-03 17:42:27 +08:00
typeAlgoMatch . push_back ( " BruteForce " ) ;
2015-05-04 16:36:24 +08:00
typeAlgoMatch . push_back ( " BruteForce-L1 " ) ;
2015-05-03 17:42:27 +08:00
typeAlgoMatch . push_back ( " BruteForce-Hamming " ) ;
typeAlgoMatch . push_back ( " BruteForce-Hamming(2) " ) ;
2015-08-01 23:24:23 +08:00
cv : : CommandLineParser parser ( argc , argv ,
2018-10-31 20:48:56 +08:00
" { @image1 | basketball1.png | } "
" { @image2 | basketball2.png | } "
2015-08-01 23:24:23 +08:00
" {help h ||} " ) ;
if ( parser . has ( " help " ) )
2015-05-04 16:36:24 +08:00
{
help ( ) ;
2015-08-01 23:24:23 +08:00
return 0 ;
2015-05-04 16:36:24 +08:00
}
2018-10-31 20:48:56 +08:00
fileName . push_back ( samples : : findFile ( parser . get < string > ( 0 ) ) ) ;
fileName . push_back ( samples : : findFile ( parser . get < string > ( 1 ) ) ) ;
2015-05-04 16:36:24 +08:00
Mat img1 = imread ( fileName [ 0 ] , IMREAD_GRAYSCALE ) ;
Mat img2 = imread ( fileName [ 1 ] , IMREAD_GRAYSCALE ) ;
2018-10-31 20:48:56 +08:00
if ( img1 . empty ( ) )
{
cerr < < " Image " < < fileName [ 0 ] < < " is empty or cannot be found " < < endl ;
return 1 ;
}
if ( img2 . empty ( ) )
{
cerr < < " Image " < < fileName [ 1 ] < < " is empty or cannot be found " < < endl ;
return 1 ;
}
2015-05-03 17:58:32 +08:00
2015-05-04 16:36:24 +08:00
vector < double > desMethCmp ;
2015-05-03 17:42:27 +08:00
Ptr < Feature2D > b ;
2015-05-04 16:36:24 +08:00
// Descriptor loop
2015-05-03 17:42:27 +08:00
vector < String > : : iterator itDesc ;
2016-04-29 21:41:39 +08:00
for ( itDesc = typeDesc . begin ( ) ; itDesc ! = typeDesc . end ( ) ; + + itDesc )
2015-05-04 16:36:24 +08:00
{
2015-05-03 17:42:27 +08:00
Ptr < DescriptorMatcher > descriptorMatcher ;
2015-05-04 16:36:24 +08:00
// Match between img1 and img2
2015-05-04 16:59:43 +08:00
vector < DMatch > matches ;
// keypoint for img1 and img2
2015-05-04 16:36:24 +08:00
vector < KeyPoint > keyImg1 , keyImg2 ;
2015-05-04 16:59:43 +08:00
// Descriptor for img1 and img2
Mat descImg1 , descImg2 ;
2015-05-03 17:42:27 +08:00
vector < String > : : iterator itMatcher = typeAlgoMatch . end ( ) ;
2015-05-08 04:48:13 +08:00
if ( * itDesc = = " AKAZE-DESCRIPTOR_KAZE_UPRIGHT " ) {
b = AKAZE : : create ( AKAZE : : DESCRIPTOR_KAZE_UPRIGHT ) ;
2018-10-31 20:48:56 +08:00
}
2015-05-03 17:42:27 +08:00
if ( * itDesc = = " AKAZE " ) {
b = AKAZE : : create ( ) ;
2018-10-31 20:48:56 +08:00
}
2015-05-03 17:42:27 +08:00
if ( * itDesc = = " ORB " ) {
b = ORB : : create ( ) ;
}
else if ( * itDesc = = " BRISK " ) {
b = BRISK : : create ( ) ;
}
2015-05-08 15:57:05 +08:00
try
{
2015-05-04 16:36:24 +08:00
// We can detect keypoint with detect method
2015-05-03 17:42:27 +08:00
b - > detect ( img1 , keyImg1 , Mat ( ) ) ;
2015-05-04 16:36:24 +08:00
// and compute their descriptors with method compute
2015-05-03 17:42:27 +08:00
b - > compute ( img1 , keyImg1 , descImg1 ) ;
2015-05-04 16:36:24 +08:00
// or detect and compute descriptors in one step
2015-05-03 17:42:27 +08:00
b - > detectAndCompute ( img2 , Mat ( ) , keyImg2 , descImg2 , false ) ;
2015-05-04 16:36:24 +08:00
// Match method loop
2016-04-29 21:41:39 +08:00
for ( itMatcher = typeAlgoMatch . begin ( ) ; itMatcher ! = typeAlgoMatch . end ( ) ; + + itMatcher ) {
2015-05-03 17:42:27 +08:00
descriptorMatcher = DescriptorMatcher : : create ( * itMatcher ) ;
2015-05-09 16:29:53 +08:00
if ( ( * itMatcher = = " BruteForce-Hamming " | | * itMatcher = = " BruteForce-Hamming(2) " ) & & ( b - > descriptorType ( ) = = CV_32F | | b - > defaultNorm ( ) < = NORM_L2SQR ) )
2015-05-04 16:36:24 +08:00
{
2015-05-08 04:48:13 +08:00
cout < < " ************************************************************************** \n " ;
cout < < " It's strange. You should use Hamming distance only for a binary descriptor \n " ;
cout < < " ************************************************************************** \n " ;
2015-05-04 16:36:24 +08:00
}
2015-05-09 16:29:53 +08:00
if ( ( * itMatcher = = " BruteForce " | | * itMatcher = = " BruteForce-L1 " ) & & ( b - > defaultNorm ( ) > = NORM_HAMMING ) )
{
cout < < " ************************************************************************** \n " ;
cout < < " It's strange. You shouldn't use L1 or L2 distance for a binary descriptor \n " ;
cout < < " ************************************************************************** \n " ;
}
2015-05-08 04:55:38 +08:00
try
2015-05-04 16:36:24 +08:00
{
2015-05-08 04:48:13 +08:00
descriptorMatcher - > match ( descImg1 , descImg2 , matches , Mat ( ) ) ;
// Keep best matches only to have a nice drawing.
// We sort distance between descriptor matches
Mat index ;
int nbMatch = int ( matches . size ( ) ) ;
Mat tab ( nbMatch , 1 , CV_32F ) ;
for ( int i = 0 ; i < nbMatch ; i + + )
{
tab . at < float > ( i , 0 ) = matches [ i ] . distance ;
}
sortIdx ( tab , index , SORT_EVERY_COLUMN + SORT_ASCENDING ) ;
vector < DMatch > bestMatches ;
for ( int i = 0 ; i < 30 ; i + + )
{
bestMatches . push_back ( matches [ index . at < int > ( i , 0 ) ] ) ;
}
Mat result ;
drawMatches ( img1 , keyImg1 , img2 , keyImg2 , bestMatches , result ) ;
namedWindow ( * itDesc + " : " + * itMatcher , WINDOW_AUTOSIZE ) ;
imshow ( * itDesc + " : " + * itMatcher , result ) ;
// Saved result could be wrong due to bug 4308
FileStorage fs ( * itDesc + " _ " + * itMatcher + " .yml " , FileStorage : : WRITE ) ;
fs < < " Matches " < < matches ;
vector < DMatch > : : iterator it ;
cout < < " **********Match results********** \n " ;
cout < < " Index \t Index \t distance \n " ;
cout < < " in img1 \t in img2 \n " ;
// Use to compute distance between keyPoint matches and to evaluate match algorithm
double cumSumDist2 = 0 ;
2016-04-29 21:41:39 +08:00
for ( it = bestMatches . begin ( ) ; it ! = bestMatches . end ( ) ; + + it )
2015-05-08 04:48:13 +08:00
{
cout < < it - > queryIdx < < " \t " < < it - > trainIdx < < " \t " < < it - > distance < < " \n " ;
Point2d p = keyImg1 [ it - > queryIdx ] . pt - keyImg2 [ it - > trainIdx ] . pt ;
cumSumDist2 = p . x * p . x + p . y * p . y ;
}
desMethCmp . push_back ( cumSumDist2 ) ;
waitKey ( ) ;
2015-05-04 16:36:24 +08:00
}
2018-10-20 04:21:20 +08:00
catch ( const Exception & e )
{
2015-05-08 15:57:05 +08:00
cout < < e . msg < < endl ;
cout < < " Cumulative distance cannot be computed. " < < endl ;
2015-05-08 04:48:13 +08:00
desMethCmp . push_back ( - 1 ) ;
2015-05-04 16:36:24 +08:00
}
2018-10-20 04:21:20 +08:00
}
2015-05-03 17:42:27 +08:00
}
2018-10-20 04:21:20 +08:00
catch ( const Exception & e )
2015-05-04 16:36:24 +08:00
{
2018-10-31 20:48:56 +08:00
cerr < < " Exception: " < < e . what ( ) < < endl ;
2015-05-03 17:42:27 +08:00
cout < < " Feature : " < < * itDesc < < " \n " ;
if ( itMatcher ! = typeAlgoMatch . end ( ) )
2015-05-04 16:36:24 +08:00
{
2015-05-03 17:42:27 +08:00
cout < < " Matcher : " < < * itMatcher < < " \n " ;
2015-05-04 16:36:24 +08:00
}
2015-05-03 17:42:27 +08:00
}
}
2015-05-04 16:36:24 +08:00
int i = 0 ;
cout < < " Cumulative distance between keypoint match for different algorithm and feature detector \n \t " ;
2018-02-08 18:51:54 +08:00
cout < < " We cannot say which is the best but we can say results are different! \n \t " ;
2016-04-29 21:41:39 +08:00
for ( vector < String > : : iterator itMatcher = typeAlgoMatch . begin ( ) ; itMatcher ! = typeAlgoMatch . end ( ) ; + + itMatcher )
2015-05-04 16:36:24 +08:00
{
cout < < * itMatcher < < " \t " ;
}
cout < < " \n " ;
2016-04-29 21:41:39 +08:00
for ( itDesc = typeDesc . begin ( ) ; itDesc ! = typeDesc . end ( ) ; + + itDesc )
2015-05-04 16:36:24 +08:00
{
cout < < * itDesc < < " \t " ;
2016-04-29 21:41:39 +08:00
for ( vector < String > : : iterator itMatcher = typeAlgoMatch . begin ( ) ; itMatcher ! = typeAlgoMatch . end ( ) ; + + itMatcher , + + i )
2015-05-04 16:36:24 +08:00
{
cout < < desMethCmp [ i ] < < " \t " ;
}
cout < < " \n " ;
}
2015-05-03 17:42:27 +08:00
return 0 ;
}