194cddfe35481b64414574a59d5244ead6d6663a
[platform/upstream/opencv.git] / samples / cpp / detector_descriptor_matcher_evaluation.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "opencv2/core/utility.hpp"
43 #include "opencv2/imgproc.hpp"
44 #include "opencv2/highgui.hpp"
45 #include "opencv2/features2d.hpp"
46
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <sys/stat.h>
50
51 #include <limits>
52 #include <cstdio>
53 #include <iostream>
54 #include <fstream>
55
56 using namespace std;
57 using namespace cv;
58
59 /*
60 The algorithm:
61
62 for each tested combination of detector+descriptor+matcher:
63
64     create detector, descriptor and matcher,
65     load their params if they are there, otherwise use the default ones and save them
66
67     for each dataset:
68
69         load reference image
70         detect keypoints in it, compute descriptors
71
72         for each transformed image:
73             load the image
74             load the transformation matrix
75             detect keypoints in it too, compute descriptors
76
77             find matches
78             transform keypoints from the first image using the ground-truth matrix
79
80             compute the number of matched keypoints, i.e. for each pair (i,j) found by a matcher compare
81             j-th keypoint from the second image with the transformed i-th keypoint. If they are close, +1.
82
83             so, we have:
84                N - number of keypoints in the first image that are also visible
85                (after transformation) on the second image
86
87                N1 - number of keypoints in the first image that have been matched.
88
89                n - number of the correct matches found by the matcher
90
91                n/N1 - precision
92                n/N - recall (?)
93
94             we store (N, n/N1, n/N) (where N is stored primarily for tuning the detector's thresholds,
95                                      in order to semi-equalize their keypoints counts)
96
97 */
98
99 typedef Vec3f TVec; // (N, n/N1, n/N) - see above
100
101 static void saveloadDDM( const string& params_filename,
102                          Ptr<FeatureDetector>& detector,
103                          Ptr<DescriptorExtractor>& descriptor,
104                          Ptr<DescriptorMatcher>& matcher )
105 {
106     FileStorage fs(params_filename, FileStorage::READ);
107     if( fs.isOpened() )
108     {
109         detector->read(fs["detector"]);
110         descriptor->read(fs["descriptor"]);
111         matcher->read(fs["matcher"]);
112     }
113     else
114     {
115         fs.open(params_filename, FileStorage::WRITE);
116         fs << "detector" << "{";
117         detector->write(fs);
118         fs << "}" << "descriptor" << "{";
119         descriptor->write(fs);
120         fs << "}" << "matcher" << "{";
121         matcher->write(fs);
122         fs << "}";
123     }
124 }
125
126 static Mat loadMat(const string& fsname)
127 {
128     FileStorage fs(fsname, FileStorage::READ);
129     Mat m;
130     fs.getFirstTopLevelNode() >> m;
131     return m;
132 }
133
134 static void transformKeypoints( const vector<KeyPoint>& kp,
135                                 vector<vector<Point2f> >& contours,
136                                 const Mat& H )
137 {
138     const float scale = 256.f;
139     size_t i, n = kp.size();
140     contours.resize(n);
141     vector<Point> temp;
142
143     for( i = 0; i < n; i++ )
144     {
145         ellipse2Poly(Point2f(kp[i].pt.x*scale, kp[i].pt.y*scale),
146                      Size2f(kp[i].size*scale, kp[i].size*scale),
147                      0, 0, 360, 12, temp);
148         Mat(temp).convertTo(contours[i], CV_32F, 1./scale);
149         perspectiveTransform(contours[i], contours[i], H);
150     }
151 }
152
153
154 static TVec proccessMatches( Size imgsize,
155                              const vector<DMatch>& matches,
156                              const vector<vector<Point2f> >& kp1t_contours,
157                              const vector<vector<Point2f> >& kp_contours,
158                              double overlapThreshold )
159 {
160     const double visibilityThreshold = 0.6;
161
162     // 1. [preprocessing] find bounding rect for each element of kp1t_contours and kp_contours.
163     // 2. [cross-check] for each DMatch (iK, i1)
164     //        update best_match[i1] using DMatch::distance.
165     // 3. [compute overlapping] for each i1 (keypoint from the first image) do:
166     //        if i1-th keypoint is outside of image, skip it
167     //        increment N
168     //        if best_match[i1] is initialized, increment N1
169     //        if kp_contours[best_match[i1]] and kp1t_contours[i1] overlap by overlapThreshold*100%,
170     //        increment n. Use bounding rects to speedup this step
171
172     int i, size1 = (int)kp1t_contours.size(), size = (int)kp_contours.size(), msize = (int)matches.size();
173     vector<DMatch> best_match(size1);
174     vector<Rect> rects1(size1), rects(size);
175
176     // proprocess
177     for( i = 0; i < size1; i++ )
178         rects1[i] = boundingRect(kp1t_contours[i]);
179
180     for( i = 0; i < size; i++ )
181         rects[i] = boundingRect(kp_contours[i]);
182
183     // cross-check
184     for( i = 0; i < msize; i++ )
185     {
186         DMatch m = matches[i];
187         int i1 = m.trainIdx, iK = m.queryIdx;
188         CV_Assert( 0 <= i1 && i1 < size1 && 0 <= iK && iK < size );
189         if( best_match[i1].trainIdx < 0 || best_match[i1].distance > m.distance )
190             best_match[i1] = m;
191     }
192
193     int N = 0, N1 = 0, n = 0;
194
195     // overlapping
196     for( i = 0; i < size1; i++ )
197     {
198         int i1 = i, iK = best_match[i].queryIdx;
199         if( iK >= 0 )
200             N1++;
201
202         Rect r = rects1[i] & Rect(0, 0, imgsize.width, imgsize.height);
203         if( r.area() < visibilityThreshold*rects1[i].area() )
204             continue;
205         N++;
206
207         if( iK < 0 || (rects1[i1] & rects[iK]).area() == 0 )
208             continue;
209
210         double n_area = intersectConvexConvex(kp1t_contours[i1], kp_contours[iK], noArray(), true);
211         if( n_area == 0 )
212             continue;
213
214         double area1 = contourArea(kp1t_contours[i1], false);
215         double area = contourArea(kp_contours[iK], false);
216
217         double ratio = n_area/(area1 + area - n_area);
218         n += ratio >= overlapThreshold;
219     }
220
221     return TVec((float)N, (float)n/std::max(N1, 1), (float)n/std::max(N, 1));
222 }
223
224
225 static void saveResults(const string& dir, const string& name, const string& dsname,
226                         const vector<TVec>& results, const int* xvals)
227 {
228     string fname1 = format("%s%s_%s_precision.csv", dir.c_str(), name.c_str(), dsname.c_str());
229     string fname2 = format("%s%s_%s_recall.csv", dir.c_str(), name.c_str(), dsname.c_str());
230     FILE* f1 = fopen(fname1.c_str(), "wt");
231     FILE* f2 = fopen(fname2.c_str(), "wt");
232
233     for( size_t i = 0; i < results.size(); i++ )
234     {
235         fprintf(f1, "%d, %.1f\n", xvals[i], results[i][1]*100);
236         fprintf(f2, "%d, %.1f\n", xvals[i], results[i][2]*100);
237     }
238     fclose(f1);
239     fclose(f2);
240 }
241
242
243 int main(int argc, char** argv)
244 {
245     static const char* ddms[] =
246     {
247         "ORBX_BF", "ORB", "ORB", "BruteForce-Hamming",
248         //"ORB_BF", "ORB", "ORB", "BruteForce-Hamming",
249         //"ORB3_BF", "ORB", "ORB", "BruteForce-Hamming(2)",
250         //"ORB4_BF", "ORB", "ORB", "BruteForce-Hamming(2)",
251         //"ORB_LSH", "ORB", "ORB", "LSH"
252         //"SURF_BF", "SURF", "SURF", "BruteForce",
253         0
254     };
255
256     static const char* datasets[] =
257     {
258         "bark", "bikes", "boat", "graf", "leuven", "trees", "ubc", "wall", 0
259     };
260
261     static const int imgXVals[] = { 2, 3, 4, 5, 6 }; // if scale, blur or light changes
262     static const int viewpointXVals[] = { 20, 30, 40, 50, 60 }; // if viewpoint changes
263     static const int jpegXVals[] = { 60, 80, 90, 95, 98 }; // if jpeg compression
264
265     const double overlapThreshold = 0.6;
266
267     vector<vector<vector<TVec> > > results; // indexed as results[ddm][dataset][testcase]
268
269     string dataset_dir = string(getenv("OPENCV_TEST_DATA_PATH")) +
270         "/cv/detectors_descriptors_evaluation/images_datasets";
271
272     string dir=argc > 1 ? argv[1] : ".";
273
274     if( dir[dir.size()-1] != '\\' && dir[dir.size()-1] != '/' )
275         dir += "/";
276
277     int result = system(("mkdir " + dir).c_str());
278     CV_Assert(result == 0);
279
280     for( int i = 0; ddms[i*4] != 0; i++ )
281     {
282         const char* name = ddms[i*4];
283         const char* detector_name = ddms[i*4+1];
284         const char* descriptor_name = ddms[i*4+2];
285         const char* matcher_name = ddms[i*4+3];
286         string params_filename = dir + string(name) + "_params.yml";
287
288         cout << "Testing " << name << endl;
289
290         Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);
291         Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);
292         Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(matcher_name);
293
294         saveloadDDM( params_filename, detector, descriptor, matcher );
295
296         results.push_back(vector<vector<TVec> >());
297
298         for( int j = 0; datasets[j] != 0; j++ )
299         {
300             const char* dsname = datasets[j];
301
302             cout << "\ton " << dsname << " ";
303             cout.flush();
304
305             const int* xvals = strcmp(dsname, "ubc") == 0 ? jpegXVals :
306                 strcmp(dsname, "graf") == 0 || strcmp(dsname, "wall") == 0 ? viewpointXVals : imgXVals;
307
308             vector<KeyPoint> kp1, kp;
309             vector<DMatch> matches;
310             vector<vector<Point2f> > kp1t_contours, kp_contours;
311             Mat desc1, desc;
312
313             Mat img1 = imread(format("%s/%s/img1.png", dataset_dir.c_str(), dsname), 0);
314             CV_Assert( !img1.empty() );
315
316             detector->detect(img1, kp1);
317             descriptor->compute(img1, kp1, desc1);
318
319             results[i].push_back(vector<TVec>());
320
321             for( int k = 2; ; k++ )
322             {
323                 cout << ".";
324                 cout.flush();
325                 Mat imgK = imread(format("%s/%s/img%d.png", dataset_dir.c_str(), dsname, k), 0);
326                 if( imgK.empty() )
327                     break;
328
329                 detector->detect(imgK, kp);
330                 descriptor->compute(imgK, kp, desc);
331                 matcher->match( desc, desc1, matches );
332
333                 Mat H = loadMat(format("%s/%s/H1to%dp.xml", dataset_dir.c_str(), dsname, k));
334
335                 transformKeypoints( kp1, kp1t_contours, H );
336                 transformKeypoints( kp, kp_contours, Mat::eye(3, 3, CV_64F));
337
338                 TVec r = proccessMatches( imgK.size(), matches, kp1t_contours, kp_contours, overlapThreshold );
339                 results[i][j].push_back(r);
340             }
341
342             saveResults(dir, name, dsname, results[i][j], xvals);
343             cout << endl;
344         }
345     }
346 }