# include "opencv2/highgui/highgui.hpp"
# include "opencv2/core/core.hpp"
# include "opencv2/imgproc/imgproc.hpp"
# include "opencv2/features2d/features2d.hpp"
# include <iostream>
# include <fstream>
using namespace std ;
using namespace cv ;
void help ( )
{
printf ( " \n This program shows the use of the Calonder point descriptor classifier \n "
" SURF is used to detect interest points, Calonder is used to describe/match these points \n "
" Usage: \n "
" ./find_obj_calonder --classifier_file=<classifier file, there is no default classifier file. You should create it at first and when you can use it for test> \n "
" --test_image=<image file for test, lena.jpg as default> \n "
" [--train_container]=<txt file with train images filenames> \n "
" Example: \n "
" --classifier_file=test_classifier --test_image=lena.jpg --train_container=one_way_train_images.txt \n "
" the test_classifier is created here using --train_container and tested witn --test_image at the end \n "
" --classifier_file=test_classifier --test_image=lena.jpg \n "
" the test classifier is tested here using lena.jpg \n " ) ;
}
/*
* Generates random perspective transform of image
*/
void warpPerspectiveRand ( const Mat & src , Mat & dst , Mat & H , RNG & rng )
{
H . create ( 3 , 3 , CV_32FC1 ) ;
H . at < float > ( 0 , 0 ) = rng . uniform ( 0.8f , 1.2f ) ;
H . at < float > ( 0 , 1 ) = rng . uniform ( - 0.1f , 0.1f ) ;
H . at < float > ( 0 , 2 ) = rng . uniform ( - 0.1f , 0.1f ) * src . cols ;
H . at < float > ( 1 , 0 ) = rng . uniform ( - 0.1f , 0.1f ) ;
H . at < float > ( 1 , 1 ) = rng . uniform ( 0.8f , 1.2f ) ;
H . at < float > ( 1 , 2 ) = rng . uniform ( - 0.1f , 0.1f ) * src . rows ;
H . at < float > ( 2 , 0 ) = rng . uniform ( - 1e-4 f , 1e-4 f ) ;
H . at < float > ( 2 , 1 ) = rng . uniform ( - 1e-4 f , 1e-4 f ) ;
H . at < float > ( 2 , 2 ) = rng . uniform ( 0.8f , 1.2f ) ;
warpPerspective ( src , dst , H , src . size ( ) ) ;
}
/*
* Trains Calonder classifier and writes trained classifier in file :
* imgFilename - name of . txt file which contains list of full filenames of train images ,
* classifierFilename - name of binary file in which classifier will be written .
*
* To train Calonder classifier RTreeClassifier class need to be used .
*/
void trainCalonderClassifier ( const string & classifierFilename , const string & imgFilename )
{
// Reads train images
ifstream is ( imgFilename . c_str ( ) , ifstream : : in ) ;
vector < Mat > trainImgs ;
while ( ! is . eof ( ) )
{
string str ;
getline ( is , str ) ;
if ( str . empty ( ) ) break ;
Mat img = imread ( str , CV_LOAD_IMAGE_GRAYSCALE ) ;
if ( ! img . empty ( ) )
trainImgs . push_back ( img ) ;
}
if ( trainImgs . empty ( ) )
{
cout < < " All train images can not be read. " < < endl ;
exit ( - 1 ) ;
}
cout < < trainImgs . size ( ) < < " train images were read. " < < endl ;
// Extracts keypoints from train images
SurfFeatureDetector detector ;
vector < BaseKeypoint > trainPoints ;
vector < IplImage > iplTrainImgs ( trainImgs . size ( ) ) ;
for ( size_t imgIdx = 0 ; imgIdx < trainImgs . size ( ) ; imgIdx + + )
{
iplTrainImgs [ imgIdx ] = trainImgs [ imgIdx ] ;
vector < KeyPoint > kps ; detector . detect ( trainImgs [ imgIdx ] , kps ) ;
for ( size_t pointIdx = 0 ; pointIdx < kps . size ( ) ; pointIdx + + )
{
Point2f p = kps [ pointIdx ] . pt ;
trainPoints . push_back ( BaseKeypoint ( cvRound ( p . x ) , cvRound ( p . y ) , & iplTrainImgs [ imgIdx ] ) ) ;
}
}
// Trains Calonder classifier on extracted points
RTreeClassifier classifier ;
classifier . train ( trainPoints , theRNG ( ) , 48 , 9 , 100 ) ;
// Writes classifier
classifier . write ( classifierFilename . c_str ( ) ) ;
}
/*
* Test Calonder classifier to match keypoints on given image :
* classifierFilename - name of file from which classifier will be read ,
* imgFilename - test image filename .
*
* To calculate keypoint descriptors you may use RTreeClassifier class ( as to train ) ,
* but it is convenient to use CalonderDescriptorExtractor class which is wrapper of
* RTreeClassifier .
*/
void testCalonderClassifier ( const string & classifierFilename , const string & imgFilename )
{
Mat img1 = imread ( imgFilename , CV_LOAD_IMAGE_GRAYSCALE ) , img2 , H12 ;
if ( img1 . empty ( ) )
{
cout < < " Test image can not be read. " < < endl ;
exit ( - 1 ) ;
}
warpPerspectiveRand ( img1 , img2 , H12 , theRNG ( ) ) ;
// Exstract keypoints from test images
SurfFeatureDetector detector ;
vector < KeyPoint > keypoints1 ; detector . detect ( img1 , keypoints1 ) ;
vector < KeyPoint > keypoints2 ; detector . detect ( img2 , keypoints2 ) ;
// Compute descriptors
CalonderDescriptorExtractor < float > de ( classifierFilename ) ;
Mat descriptors1 ; de . compute ( img1 , keypoints1 , descriptors1 ) ;
Mat descriptors2 ; de . compute ( img2 , keypoints2 , descriptors2 ) ;
// Match descriptors
BruteForceMatcher < L1 < float > > matcher ;
vector < DMatch > matches ;
matcher . match ( descriptors1 , descriptors2 , matches ) ;
// Prepare inlier mask
vector < char > matchesMask ( matches . size ( ) , 0 ) ;
vector < Point2f > points1 ; KeyPoint : : convert ( keypoints1 , points1 ) ;
vector < Point2f > points2 ; KeyPoint : : convert ( keypoints2 , points2 ) ;
Mat points1t ; perspectiveTransform ( Mat ( points1 ) , points1t , H12 ) ;
for ( size_t mi = 0 ; mi < matches . size ( ) ; mi + + )
{
if ( norm ( points2 [ matches [ mi ] . trainIdx ] - points1t . at < Point2f > ( mi , 0 ) ) < 4 ) // inlier
matchesMask [ mi ] = 1 ;
}
// Draw
Mat drawImg ;
drawMatches ( img1 , keypoints1 , img2 , keypoints2 , matches , drawImg , CV_RGB ( 0 , 255 , 0 ) , CV_RGB ( 0 , 0 , 255 ) , matchesMask ) ;
string winName = " Matches " ;
namedWindow ( winName , WINDOW_AUTOSIZE ) ;
imshow ( winName , drawImg ) ;
waitKey ( ) ;
}
int main ( int argc , const char * * argv )
{
help ( ) ;
CommandLineParser parser ( argc , argv ) ;
string classifierFileName = parser . get < string > ( " classifier_file " ) ;
string testImageFileName = parser . get < string > ( " test_image " , " lena.jpg " ) ;
string trainContainerFileName = parser . get < string > ( " train_container " ) ;
if ( classifierFileName . empty ( ) )
{
printf ( " \n Can't find classifier file, please select file for --classifier_file parameter \n " ) ;
help ( ) ;
return - 1 ;
}
if ( ! trainContainerFileName . empty ( ) )
trainCalonderClassifier ( classifierFileName . c_str ( ) , trainContainerFileName . c_str ( ) ) ;
testCalonderClassifier ( classifierFileName . c_str ( ) , testImageFileName . c_str ( ) ) ;
return 0 ;
}