mirror of
https://github.com/opencv/opencv.git
synced 2024-11-25 11:40:44 +08:00
174 lines
6.4 KiB
C++
174 lines
6.4 KiB
C++
#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-4f, 1e-4f);
|
|
H.at<float>(2,1) = rng.uniform( -1e-4f, 1e-4f);
|
|
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;
|
|
}
|