]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/tests/cv/src/acascadeandhog.cpp
d446a287ead2a5f7371e28f7ea954e68e666de5f
[opencv.git] / opencv / tests / cv / src / acascadeandhog.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 "cvtest.h"
43 using namespace cv;
44 using namespace std;
45
46 //#define GET_STAT
47
48 #define DIST_E              "distE"\r
49 #define S_E                 "sE"\r
50 #define NO_PAIR_E           "noPairE"\r
51 //#define TOTAL_NO_PAIR_E     "totalNoPairE"
52
53 #define DETECTOR_NAMES      "detector_names"
54 #define DETECTORS                       "detectors"
55 #define IMAGE_FILENAMES     "image_filenames"
56 #define VALIDATION          "validation"
57 #define FILENAME                        "fn"
58
59 #define C_SCALE_CASCADE         "scale_cascade"
60
61 class CV_DetectorTest : public CvTest
62 {
63 public:\r
64     CV_DetectorTest( const char* test_name );
65     virtual int init( CvTS* system );\r
66 protected:\r
67     virtual int prepareData( FileStorage& fs );\r
68     virtual void run( int startFrom );\r
69     virtual string& getValidationFilename();\r
70         \r
71         virtual void readDetector( const FileNode& fn ) = 0;\r
72         virtual void writeDetector( FileStorage& fs, int di ) = 0;\r
73     int runTestCase( int detectorIdx, vector<vector<Rect> >& objects );\r
74     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ) = 0;\r
75     int validate( int detectorIdx, vector<vector<Rect> >& objects );\r
76 \r
77     struct\r
78     {\r
79         float dist;\r
80         float s;\r
81         float noPair;\r
82         //float totalNoPair;\r
83     } eps;\r
84     vector<string> detectorNames;\r
85     vector<string> detectorFilenames;\r
86     vector<string> imageFilenames;\r
87     vector<Mat> images;\r
88     string validationFilename;\r
89     FileStorage validationFS;\r
90 };
91
92 CV_DetectorTest::CV_DetectorTest( const char* test_name ) : CvTest( test_name, "detectMultiScale" )
93 {
94 }
95
96 int CV_DetectorTest::init(CvTS *system)
97 {
98     clear();
99     ts = system;
100     string dataPath = ts->get_data_path();
101     validationFS.open( dataPath + getValidationFilename(), FileStorage::READ );
102     return prepareData( validationFS );
103 }
104
105 string& CV_DetectorTest::getValidationFilename()
106 {
107     return validationFilename;
108 }
109
110 int CV_DetectorTest::prepareData( FileStorage& _fs )
111 {
112     if( !_fs.isOpened() )\r
113         test_case_count = -1;\r
114     else\r
115     {\r
116         FileNode fn = _fs.getFirstTopLevelNode();\r
117 \r
118         fn[DIST_E] >> eps.dist;\r
119         fn[S_E] >> eps.s;\r
120         fn[NO_PAIR_E] >> eps.noPair;\r
121 //        fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair;\r
122 \r
123         // read detectors\r
124         if( fn[DETECTOR_NAMES].node->data.seq != 0 )\r
125         {\r
126             FileNodeIterator it = fn[DETECTOR_NAMES].begin();\r
127             for( ; it != fn[DETECTOR_NAMES].end(); )\r
128             {\r
129                 string name;\r
130                 it >> name; \r
131                 detectorNames.push_back(name);\r
132                                 readDetector(fn[DETECTORS][name]);\r
133             }\r
134         }\r
135         test_case_count = (int)detectorNames.size();\r
136 \r
137         // read images filenames and images\r
138         string dataPath = ts->get_data_path();\r
139         if( fn[IMAGE_FILENAMES].node->data.seq != 0 )\r
140         {\r
141             for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); )
142             {
143                 string filename;
144                 it >> filename;
145                 imageFilenames.push_back(filename);
146                 Mat img = imread( dataPath+filename, 1 );
147                 images.push_back( img );
148             }\r
149         }\r
150     }\r
151     return CvTS::OK;
152 }
153
154 void CV_DetectorTest::run( int start_from )
155 {
156     int code = CvTS::OK;
157     start_from = 0;
158
159 #ifdef GET_STAT
160     validationFS.release();
161     string filename = ts->get_data_path();
162     filename += getValidationFilename();
163     validationFS.open( filename, FileStorage::WRITE );
164     validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{";
165
166     validationFS << DIST_E << eps.dist;\r
167     validationFS << S_E << eps.s;\r
168     validationFS << NO_PAIR_E << eps.noPair;\r
169 //    validationFS << TOTAL_NO_PAIR_E << eps.totalNoPair;
170
171     // write detector names
172     validationFS << DETECTOR_NAMES << "[";
173     vector<string>::const_iterator nit = detectorNames.begin();
174     for( ; nit != detectorNames.end(); ++nit )
175     {
176         validationFS << *nit;
177     }
178     validationFS << "]"; // DETECTOR_NAMES
179
180         // write detectors
181         validationFS << DETECTORS << "{";
182         assert( detectorNames.size() == detectorFilenames.size() );
183         nit = detectorNames.begin();
184         for( int di = 0; di < detectorNames.size(), nit != detectorNames.end(); ++nit, di++ )
185         {
186                 validationFS << *nit << "{";
187                 writeDetector( validationFS, di );
188                 validationFS << "}";
189         }
190         validationFS << "}";
191     
192     // write image filenames
193     validationFS << IMAGE_FILENAMES << "[";
194     vector<string>::const_iterator it = imageFilenames.begin();
195     for( int ii = 0; it != imageFilenames.end(); ++it, ii++ )
196     {
197         char buf[10];
198         sprintf( buf, "%s%d", "img_", ii );
199         cvWriteComment( validationFS.fs, buf, 0 );
200         validationFS << *it;
201     }
202     validationFS << "]"; // IMAGE_FILENAMES
203
204     validationFS << VALIDATION << "{";
205 #endif
206
207     for( int di = 0; di < test_case_count; di++ )
208     {
209 #ifdef GET_STAT
210         validationFS << detectorNames[di] << "{";
211 #endif
212         vector<vector<Rect> > objects;
213         int temp_code = runTestCase( di, objects );
214 #ifndef GET_STAT
215         if (temp_code == CvTS::OK)
216             temp_code = validate( di, objects );
217 #endif
218         if (temp_code != CvTS::OK)
219             code = temp_code;
220 #ifdef GET_STAT
221         validationFS << "}"; // detectorNames[di]
222 #endif
223     }
224
225 #ifdef GET_STAT
226     validationFS << "}"; // VALIDATION
227     validationFS << "}"; // getDefaultObjectName
228 #endif
229     if ( test_case_count <= 0 || imageFilenames.size() <= 0 )
230     {\r
231         ts->printf( CvTS::LOG, "validation file is not determined or not correct" );
232         code = CvTS::FAIL_INVALID_TEST_DATA;\r
233     }
234     ts->set_failed_test_info( code );
235 }
236
237 int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects )\r
238 {\r
239     string dataPath = ts->get_data_path(), detectorFilename;\r
240     if( !detectorFilenames[detectorIdx].empty() )\r
241         detectorFilename = dataPath + detectorFilenames[detectorIdx];\r
242 \r
243     for( int ii = 0; ii < (int)imageFilenames.size(); ++ii )
244     {
245         vector<Rect> imgObjects;
246         Mat image = images[ii];\r
247         if( image.empty() )
248         {
249             char msg[30];
250             sprintf( msg, "%s %d %s", "image ", ii, " can not be read" );
251             ts->printf( CvTS::LOG, msg );
252             return CvTS::FAIL_INVALID_TEST_DATA;
253         }\r
254         int code = detectMultiScale( detectorIdx, image, imgObjects );\r
255                 if( code != CvTS::OK )\r
256                         return code;\r
257 \r
258         objects.push_back( imgObjects );\r
259
260 #ifdef GET_STAT
261         char buf[10];
262         sprintf( buf, "%s%d", "img_", ii );
263         string imageIdxStr = buf;
264         validationFS << imageIdxStr << "[:";
265         for( vector<Rect>::const_iterator it = imgObjects.begin();
266                 it != imgObjects.end(); ++it )
267         {
268             validationFS << it->x << it->y << it->width << it->height;
269         }
270         validationFS << "]"; // imageIdxStr
271 #endif\r
272     }\r
273     return CvTS::OK;\r
274 }\r
275 \r
276 \r
277 bool isZero( uchar i ) {return i == 0;}\r
278 \r
279 int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects )
280 {
281     assert( imageFilenames.size() == objects.size() );
282     int imageIdx = 0;
283     int totalNoPair = 0, totalValRectCount = 0;
284
285     for( vector<vector<Rect> >::const_iterator it = objects.begin();
286         it != objects.end(); ++it, imageIdx++ ) // for image
287     {
288         Size imgSize = images[imageIdx].size();
289         float dist = min(imgSize.height, imgSize.width) * eps.dist;
290         float wDiff = imgSize.width * eps.s;
291         float hDiff = imgSize.height * eps.s;
292
293         int noPair = 0;
294
295         // read validation rectangles\r
296         char buf[10];
297         sprintf( buf, "%s%d", "img_", imageIdx );
298         string imageIdxStr = buf;\r
299         FileNode node = validationFS.getFirstTopLevelNode()[VALIDATION][detectorNames[detectorIdx]][imageIdxStr];
300         vector<Rect> valRects;\r
301         if( node.node->data.seq != 0 )\r
302         {\r
303             for( FileNodeIterator it = node.begin(); it != node.end(); )
304             {
305                 Rect r;
306                 it >> r.x >> r.y >> r.width >> r.height;
307                 valRects.push_back(r);
308             }\r
309         }
310         totalValRectCount += (int)valRects.size();
311                 
312         // compare rectangles
313                 vector<uchar> map(valRects.size(), 0);
314         for( vector<Rect>::const_iterator cr = it->begin();
315             cr != it->end(); ++cr )
316         {
317             // find nearest rectangle
318             Point2f cp1 = Point2f( cr->x + (float)cr->width/2.0f, cr->y + (float)cr->height/2.0f );
319             int minIdx = -1, vi = 0;
320             float minDist = (float)norm( Point(imgSize.width, imgSize.height) );
321             for( vector<Rect>::const_iterator vr = valRects.begin();
322                 vr != valRects.end(); ++vr, vi++ )
323             {
324                 Point2f cp2 = Point2f( vr->x + (float)vr->width/2.0f, vr->y + (float)vr->height/2.0f );
325                 float curDist = (float)norm(cp1-cp2);
326                 if( curDist < minDist )
327                 {
328                     minIdx = vi;
329                     minDist = curDist;
330                 }
331             }
332             if( minIdx == -1 )
333             {
334                 noPair++;
335             }
336             else
337             {
338                 Rect vr = valRects[minIdx];
339                 if( map[minIdx] != 0 || (minDist > dist) || (abs(cr->width - vr.width) > wDiff) ||
340                                                                                                                 (abs(cr->height - vr.height) > hDiff) )
341                     noPair++;
342                                 else
343                                         map[minIdx] = 1;
344             }
345         }
346         noPair += (int)count_if( map.begin(), map.end(), isZero );
347         totalNoPair += noPair;
348         if( noPair > valRects.size()*eps.noPair+1 )
349             break;
350     }
351     if( imageIdx < (int)imageFilenames.size() )
352     {
353         char msg[500];
354         sprintf( msg, "detector %s has overrated count of rectangles without pair on %s-image",
355             detectorNames[detectorIdx].c_str(), imageFilenames[imageIdx].c_str() );
356         ts->printf( CvTS::LOG, msg );
357         return CvTS::FAIL_BAD_ACCURACY;
358     }\r
359     if ( totalNoPair > totalValRectCount*eps./*total*/noPair+1 )\r
360     {\r
361         ts->printf( CvTS::LOG, "overrated count of rectangles without pair on all images set" );
362         return CvTS::FAIL_BAD_ACCURACY;\r
363     }\r
364
365     return CvTS::OK;\r
366 }\r
367 \r
368 //----------------------------------------------- CascadeDetectorTest -----------------------------------\r
369 class CV_CascadeDetectorTest : public CV_DetectorTest\r
370 {\r
371 public:\r
372     CV_CascadeDetectorTest( const char* test_name );
373 protected:\r
374         virtual void readDetector( const FileNode& fn );\r
375         virtual void writeDetector( FileStorage& fs, int di );\r
376     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );\r
377         vector<int> flags;\r
378 };\r
379 \r
380 CV_CascadeDetectorTest::CV_CascadeDetectorTest(const char *test_name)\r
381     : CV_DetectorTest( test_name )\r
382 {\r
383     validationFilename = "cascadeandhog/cascade.xml";\r
384 }\r
385 \r
386 void CV_CascadeDetectorTest::readDetector( const FileNode& fn )\r
387 {\r
388         string filename;\r
389         int flag;\r
390         fn[FILENAME] >> filename;\r
391         detectorFilenames.push_back(filename);\r
392         fn[C_SCALE_CASCADE] >> flag;\r
393         if( flag )
394                 flags.push_back( 0 );\r
395         else\r
396                 flags.push_back( CV_HAAR_SCALE_IMAGE );\r
397 }\r
398 \r
399 void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di )\r
400 {\r
401         int sc = flags[di] & CV_HAAR_SCALE_IMAGE ? 0 : 1;
402         fs << FILENAME << detectorFilenames[di];
403         fs << C_SCALE_CASCADE << sc;
404 }\r
405 \r
406 int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img,\r
407                                               vector<Rect>& objects)\r
408 {\r
409         string dataPath = ts->get_data_path(), filename;\r
410         filename = dataPath + detectorFilenames[di];\r
411     CascadeClassifier cascade( filename );\r
412         if( cascade.empty() )\r
413         {\r
414                 ts->printf( CvTS::LOG, "cascade %s can not be opened");\r
415                 return CvTS::FAIL_INVALID_TEST_DATA;\r
416         }\r
417     Mat grayImg;\r
418     cvtColor( img, grayImg, CV_BGR2GRAY );
419     equalizeHist( grayImg, grayImg );\r
420     cascade.detectMultiScale( grayImg, objects, 1.1, 3, flags[di] );\r
421         return CvTS::OK;\r
422 }\r
423 \r
424 //----------------------------------------------- HOGDetectorTest -----------------------------------\r
425 class CV_HOGDetectorTest : public CV_DetectorTest\r
426 {\r
427 public:\r
428     CV_HOGDetectorTest( const char* test_name );
429 protected:\r
430         virtual void readDetector( const FileNode& fn );\r
431         virtual void writeDetector( FileStorage& fs, int di );\r
432     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );\r
433 };\r
434 \r
435 CV_HOGDetectorTest::CV_HOGDetectorTest(const char *test_name)\r
436 : CV_DetectorTest( test_name )\r
437 {\r
438     validationFilename = "cascadeandhog/hog.xml";\r
439 }\r
440 \r
441 void CV_HOGDetectorTest::readDetector( const FileNode& fn )\r
442 {\r
443         string filename;\r
444         if( fn[FILENAME].node->data.seq != 0 )\r
445                 fn[FILENAME] >> filename;\r
446         detectorFilenames.push_back( filename);\r
447 }\r
448 \r
449 void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di )\r
450 {\r
451         fs << FILENAME << detectorFilenames[di];
452 }\r
453 \r
454 int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img,\r
455                                               vector<Rect>& objects)\r
456 {\r
457     HOGDescriptor hog;\r
458     if( detectorFilenames[di].empty() )
459         hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
460     else
461         assert(0);
462     hog.detectMultiScale(img, objects);\r
463         return CvTS::OK;\r
464 }\r
465 \r
466 CV_CascadeDetectorTest cascadeTest("cascade-detector");\r
467 CV_HOGDetectorTest hogTest("hog-detector");\r