find_obj_calonder.cpp 5.51 KB
Newer Older
1 2 3 4
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
5 6
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
7

8 9
#include <iostream>
#include <fstream>
10

11 12
using namespace std;
using namespace cv;
13

14
static void help()
Gary Bradski's avatar
Gary Bradski committed
15
{
16
    cout << "This program shows the use of the Calonder point descriptor classifier"
17 18
            "SURF is used to detect interest points, Calonder is used to describe/match these points\n"
            "Format:" << endl <<
19 20
            "   classifier_file(to write) test_image file_with_train_images_filenames(txt)" <<
            "   or" << endl <<
21 22 23 24
            "   classifier_file(to read) test_image" << "\n" << endl <<
            "Using OpenCV version " << CV_VERSION << "\n" << endl;

    return;
Gary Bradski's avatar
Gary Bradski committed
25
}
26 27


28 29 30
/*
 * Generates random perspective transform of image
 */
31
static void warpPerspectiveRand( const Mat& src, Mat& dst, Mat& H, RNG& rng )
32
{
33 34 35 36 37 38 39 40 41 42 43 44
    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() );
45 46
}

47 48 49 50 51 52 53
/*
 * 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.
 */
54
static void trainCalonderClassifier( const string& classifierFilename, const string& imgFilename )
55
{
56 57 58 59
    // Reads train images
    ifstream is( imgFilename.c_str(), ifstream::in );
    vector<Mat> trainImgs;
    while( !is.eof() )
60
    {
61 62 63
        string str;
        getline( is, str );
        if (str.empty()) break;
64
        Mat img = imread( str, IMREAD_GRAYSCALE );
65 66
        if( !img.empty() )
            trainImgs.push_back( img );
67
    }
68 69 70 71 72 73
    if( trainImgs.empty() )
    {
        cout << "All train images can not be read." << endl;
        exit(-1);
    }
    cout << trainImgs.size() << " train images were read." << endl;
74

75 76 77 78 79
    // Extracts keypoints from train images
    SurfFeatureDetector detector;
    vector<BaseKeypoint> trainPoints;
    vector<IplImage> iplTrainImgs(trainImgs.size());
    for( size_t imgIdx = 0; imgIdx < trainImgs.size(); imgIdx++ )
80
    {
81 82
        iplTrainImgs[imgIdx] = trainImgs[imgIdx];
        vector<KeyPoint> kps; detector.detect( trainImgs[imgIdx], kps );
83

84
        for( size_t pointIdx = 0; pointIdx < kps.size(); pointIdx++ )
85
        {
86 87
            Point2f p = kps[pointIdx].pt;
            trainPoints.push_back( BaseKeypoint(cvRound(p.x), cvRound(p.y), &iplTrainImgs[imgIdx]) );
88
        }
89 90
    }

91 92 93 94 95 96
    // Trains Calonder classifier on extracted points
    RTreeClassifier classifier;
    classifier.train( trainPoints, theRNG(), 48, 9, 100 );
    // Writes classifier
    classifier.write( classifierFilename.c_str() );
}
97

98 99 100 101 102 103 104 105 106
/*
 * 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.
 */
107
static void testCalonderClassifier( const string& classifierFilename, const string& imgFilename )
108
{
109
    Mat img1 = imread( imgFilename, IMREAD_GRAYSCALE ), img2, H12;
110
    if( img1.empty() )
111 112
    {
        cout << "Test image can not be read." << endl;
113
        exit(-1);
114
    }
115
    warpPerspectiveRand( img1, img2, H12, theRNG() );
116

117 118
    // Exstract keypoints from test images
    SurfFeatureDetector detector;
119 120
    vector<KeyPoint> keypoints1; detector.detect( img1, keypoints1 );
    vector<KeyPoint> keypoints2; detector.detect( img2, keypoints2 );
121

122 123 124 125
    // Compute descriptors
    CalonderDescriptorExtractor<float> de( classifierFilename );
    Mat descriptors1;  de.compute( img1, keypoints1, descriptors1 );
    Mat descriptors2;  de.compute( img2, keypoints2, descriptors2 );
126 127

    // Match descriptors
128
    BFMatcher matcher(NORM_L1);
129 130
    vector<DMatch> matches;
    matcher.match( descriptors1, descriptors2, matches );
131 132 133

    // Prepare inlier mask
    vector<char> matchesMask( matches.size(), 0 );
134 135 136
    vector<Point2f> points1; KeyPoint::convert( keypoints1, points1 );
    vector<Point2f> points2; KeyPoint::convert( keypoints2, points2 );
    Mat points1t; perspectiveTransform(Mat(points1), points1t, H12);
137 138
    for( size_t mi = 0; mi < matches.size(); mi++ )
    {
139
        if( norm(points2[matches[mi].trainIdx] - points1t.at<Point2f>((int)mi,0)) < 4 ) // inlier
140 141
            matchesMask[mi] = 1;
    }
142

143 144
    // Draw
    Mat drawImg;
145
    drawMatches( img1, keypoints1, img2, keypoints2, matches, drawImg, CV_RGB(0, 255, 0), CV_RGB(0, 0, 255), matchesMask );
146 147 148 149
    string winName = "Matches";
    namedWindow( winName, WINDOW_AUTOSIZE );
    imshow( winName, drawImg );
    waitKey();
150 151
}

152
int main( int argc, char **argv )
153
{
154
    if( argc != 4 && argc != 3 )
155
    {
156
        help();
157 158 159
        return -1;
    }

160 161
    if( argc == 4 )
        trainCalonderClassifier( argv[1], argv[3] );
162

163
    testCalonderClassifier( argv[1], argv[2] );
164

165
    return 0;
166
}