596e646517d4e1692ebdedf8061b0326eea9c303
[profile/ivi/opencv.git] / modules / objdetect / test / test_cascadeandhog.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 "test_precomp.hpp"
43 #include "opencv2/imgproc/imgproc.hpp"
44
45 using namespace cv;
46 using namespace std;
47
48 //#define GET_STAT
49
50 #define DIST_E              "distE"
51 #define S_E                 "sE"
52 #define NO_PAIR_E           "noPairE"
53 //#define TOTAL_NO_PAIR_E     "totalNoPairE"
54
55 #define DETECTOR_NAMES      "detector_names"
56 #define DETECTORS           "detectors"
57 #define IMAGE_FILENAMES     "image_filenames"
58 #define VALIDATION          "validation"
59 #define FILENAME            "fn"
60
61 #define C_SCALE_CASCADE     "scale_cascade"
62
63 class CV_DetectorTest : public cvtest::BaseTest
64 {
65 public:
66     CV_DetectorTest();
67 protected:
68     virtual int prepareData( FileStorage& fs );
69     virtual void run( int startFrom );
70     virtual string& getValidationFilename();
71
72     virtual void readDetector( const FileNode& fn ) = 0;
73     virtual void writeDetector( FileStorage& fs, int di ) = 0;
74     int runTestCase( int detectorIdx, vector<vector<Rect> >& objects );
75     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ) = 0;
76     int validate( int detectorIdx, vector<vector<Rect> >& objects );
77
78     struct
79     {
80         float dist;
81         float s;
82         float noPair;
83         //float totalNoPair;
84     } eps;
85     vector<string> detectorNames;
86     vector<string> detectorFilenames;
87     vector<string> imageFilenames;
88     vector<Mat> images;
89     string validationFilename;
90     string configFilename;
91     FileStorage validationFS;
92     bool write_results;
93 };
94
95 CV_DetectorTest::CV_DetectorTest()
96 {
97     configFilename = "dummy";
98     write_results = false;
99 }
100
101 string& CV_DetectorTest::getValidationFilename()
102 {
103     return validationFilename;
104 }
105
106 int CV_DetectorTest::prepareData( FileStorage& _fs )
107 {
108     if( !_fs.isOpened() )
109         test_case_count = -1;
110     else
111     {
112         FileNode fn = _fs.getFirstTopLevelNode();
113
114         fn[DIST_E] >> eps.dist;
115         fn[S_E] >> eps.s;
116         fn[NO_PAIR_E] >> eps.noPair;
117 //        fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair;
118
119         // read detectors
120         if( fn[DETECTOR_NAMES].node->data.seq != 0 )
121         {
122             FileNodeIterator it = fn[DETECTOR_NAMES].begin();
123             for( ; it != fn[DETECTOR_NAMES].end(); )
124             {
125                 string _name;
126                 it >> _name;
127                 detectorNames.push_back(_name);
128                 readDetector(fn[DETECTORS][_name]);
129             }
130         }
131         test_case_count = (int)detectorNames.size();
132
133         // read images filenames and images
134         string dataPath = ts->get_data_path();
135         if( fn[IMAGE_FILENAMES].node->data.seq != 0 )
136         {
137             for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); )
138             {
139                 string filename;
140                 it >> filename;
141                 imageFilenames.push_back(filename);
142                 Mat img = imread( dataPath+filename, 1 );
143                 images.push_back( img );
144             }
145         }
146     }
147     return cvtest::TS::OK;
148 }
149
150 void CV_DetectorTest::run( int )
151 {
152     string dataPath = ts->get_data_path();
153     string vs_filename = dataPath + getValidationFilename();
154
155     write_results = !validationFS.open( vs_filename, FileStorage::READ );
156
157     int code;
158     if( !write_results )
159     {
160         code = prepareData( validationFS );
161     }
162     else
163     {
164         FileStorage fs0(dataPath + configFilename, FileStorage::READ );
165         code = prepareData(fs0);
166     }
167
168     if( code < 0 )
169     {
170         ts->set_failed_test_info( code );
171         return;
172     }
173
174     if( write_results )
175     {
176         validationFS.release();
177         validationFS.open( vs_filename, FileStorage::WRITE );
178         validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{";
179
180         validationFS << DIST_E << eps.dist;
181         validationFS << S_E << eps.s;
182         validationFS << NO_PAIR_E << eps.noPair;
183     //    validationFS << TOTAL_NO_PAIR_E << eps.totalNoPair;
184
185         // write detector names
186         validationFS << DETECTOR_NAMES << "[";
187         vector<string>::const_iterator nit = detectorNames.begin();
188         for( ; nit != detectorNames.end(); ++nit )
189         {
190             validationFS << *nit;
191         }
192         validationFS << "]"; // DETECTOR_NAMES
193
194         // write detectors
195         validationFS << DETECTORS << "{";
196         assert( detectorNames.size() == detectorFilenames.size() );
197         nit = detectorNames.begin();
198         for( int di = 0; nit != detectorNames.end(); ++nit, di++ )
199         {
200             validationFS << *nit << "{";
201             writeDetector( validationFS, di );
202             validationFS << "}";
203         }
204         validationFS << "}";
205
206         // write image filenames
207         validationFS << IMAGE_FILENAMES << "[";
208         vector<string>::const_iterator it = imageFilenames.begin();
209         for( int ii = 0; it != imageFilenames.end(); ++it, ii++ )
210         {
211             char buf[10];
212             sprintf( buf, "%s%d", "img_", ii );
213             cvWriteComment( validationFS.fs, buf, 0 );
214             validationFS << *it;
215         }
216         validationFS << "]"; // IMAGE_FILENAMES
217
218         validationFS << VALIDATION << "{";
219     }
220
221     int progress = 0;
222     for( int di = 0; di < test_case_count; di++ )
223     {
224         progress = update_progress( progress, di, test_case_count, 0 );
225         if( write_results )
226             validationFS << detectorNames[di] << "{";
227         vector<vector<Rect> > objects;
228         int temp_code = runTestCase( di, objects );
229
230         if (!write_results && temp_code == cvtest::TS::OK)
231             temp_code = validate( di, objects );
232
233         if (temp_code != cvtest::TS::OK)
234             code = temp_code;
235
236         if( write_results )
237             validationFS << "}"; // detectorNames[di]
238     }
239
240     if( write_results )
241     {
242         validationFS << "}"; // VALIDATION
243         validationFS << "}"; // getDefaultObjectName
244     }
245
246     if ( test_case_count <= 0 || imageFilenames.size() <= 0 )
247     {
248         ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" );
249         code = cvtest::TS::FAIL_INVALID_TEST_DATA;
250     }
251     ts->set_failed_test_info( code );
252 }
253
254 int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects )
255 {
256     string dataPath = ts->get_data_path(), detectorFilename;
257     if( !detectorFilenames[detectorIdx].empty() )
258         detectorFilename = dataPath + detectorFilenames[detectorIdx];
259
260     for( int ii = 0; ii < (int)imageFilenames.size(); ++ii )
261     {
262         vector<Rect> imgObjects;
263         Mat image = images[ii];
264         if( image.empty() )
265         {
266             char msg[30];
267             sprintf( msg, "%s %d %s", "image ", ii, " can not be read" );
268             ts->printf( cvtest::TS::LOG, msg );
269             return cvtest::TS::FAIL_INVALID_TEST_DATA;
270         }
271         int code = detectMultiScale( detectorIdx, image, imgObjects );
272         if( code != cvtest::TS::OK )
273             return code;
274
275         objects.push_back( imgObjects );
276
277         if( write_results )
278         {
279             char buf[10];
280             sprintf( buf, "%s%d", "img_", ii );
281             string imageIdxStr = buf;
282             validationFS << imageIdxStr << "[:";
283             for( vector<Rect>::const_iterator it = imgObjects.begin();
284                     it != imgObjects.end(); ++it )
285             {
286                 validationFS << it->x << it->y << it->width << it->height;
287             }
288             validationFS << "]"; // imageIdxStr
289         }
290     }
291     return cvtest::TS::OK;
292 }
293
294
295 bool isZero( uchar i ) {return i == 0;}
296
297 int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects )
298 {
299     assert( imageFilenames.size() == objects.size() );
300     int imageIdx = 0;
301     int totalNoPair = 0, totalValRectCount = 0;
302
303     for( vector<vector<Rect> >::const_iterator it = objects.begin();
304         it != objects.end(); ++it, imageIdx++ ) // for image
305     {
306         Size imgSize = images[imageIdx].size();
307         float dist = min(imgSize.height, imgSize.width) * eps.dist;
308         float wDiff = imgSize.width * eps.s;
309         float hDiff = imgSize.height * eps.s;
310
311         int noPair = 0;
312
313         // read validation rectangles
314         char buf[10];
315         sprintf( buf, "%s%d", "img_", imageIdx );
316         string imageIdxStr = buf;
317         FileNode node = validationFS.getFirstTopLevelNode()[VALIDATION][detectorNames[detectorIdx]][imageIdxStr];
318         vector<Rect> valRects;
319         if( node.node->data.seq != 0 )
320         {
321             for( FileNodeIterator it2 = node.begin(); it2 != node.end(); )
322             {
323                 Rect r;
324                 it2 >> r.x >> r.y >> r.width >> r.height;
325                 valRects.push_back(r);
326             }
327         }
328         totalValRectCount += (int)valRects.size();
329
330         // compare rectangles
331         vector<uchar> map(valRects.size(), 0);
332         for( vector<Rect>::const_iterator cr = it->begin();
333             cr != it->end(); ++cr )
334         {
335             // find nearest rectangle
336             Point2f cp1 = Point2f( cr->x + (float)cr->width/2.0f, cr->y + (float)cr->height/2.0f );
337             int minIdx = -1, vi = 0;
338             float minDist = (float)norm( Point(imgSize.width, imgSize.height) );
339             for( vector<Rect>::const_iterator vr = valRects.begin();
340                 vr != valRects.end(); ++vr, vi++ )
341             {
342                 Point2f cp2 = Point2f( vr->x + (float)vr->width/2.0f, vr->y + (float)vr->height/2.0f );
343                 float curDist = (float)norm(cp1-cp2);
344                 if( curDist < minDist )
345                 {
346                     minIdx = vi;
347                     minDist = curDist;
348                 }
349             }
350             if( minIdx == -1 )
351             {
352                 noPair++;
353             }
354             else
355             {
356                 Rect vr = valRects[minIdx];
357                 if( map[minIdx] != 0 || (minDist > dist) || (abs(cr->width - vr.width) > wDiff) ||
358                                                         (abs(cr->height - vr.height) > hDiff) )
359                     noPair++;
360                 else
361                     map[minIdx] = 1;
362             }
363         }
364         noPair += (int)count_if( map.begin(), map.end(), isZero );
365         totalNoPair += noPair;
366         if( noPair > cvRound(valRects.size()*eps.noPair)+1 )
367             break;
368     }
369     if( imageIdx < (int)imageFilenames.size() )
370     {
371         char msg[500];
372         sprintf( msg, "detector %s has overrated count of rectangles without pair on %s-image\n",
373             detectorNames[detectorIdx].c_str(), imageFilenames[imageIdx].c_str() );
374         ts->printf( cvtest::TS::LOG, msg );
375         return cvtest::TS::FAIL_BAD_ACCURACY;
376     }
377     if ( totalNoPair > cvRound(totalValRectCount*eps./*total*/noPair)+1 )
378     {
379         ts->printf( cvtest::TS::LOG, "overrated count of rectangles without pair on all images set" );
380         return cvtest::TS::FAIL_BAD_ACCURACY;
381     }
382
383     return cvtest::TS::OK;
384 }
385
386 //----------------------------------------------- CascadeDetectorTest -----------------------------------
387 class CV_CascadeDetectorTest : public CV_DetectorTest
388 {
389 public:
390     CV_CascadeDetectorTest();
391 protected:
392     virtual void readDetector( const FileNode& fn );
393     virtual void writeDetector( FileStorage& fs, int di );
394     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );
395     virtual int detectMultiScale_C( const string& filename, int di, const Mat& img, vector<Rect>& objects );
396     vector<int> flags;
397 };
398
399 CV_CascadeDetectorTest::CV_CascadeDetectorTest()
400 {
401     validationFilename = "cascadeandhog/cascade.xml";
402     configFilename = "cascadeandhog/_cascade.xml";
403 }
404
405 void CV_CascadeDetectorTest::readDetector( const FileNode& fn )
406 {
407     string filename;
408     int flag;
409     fn[FILENAME] >> filename;
410     detectorFilenames.push_back(filename);
411     fn[C_SCALE_CASCADE] >> flag;
412     if( flag )
413         flags.push_back( 0 );
414     else
415         flags.push_back( CV_HAAR_SCALE_IMAGE );
416 }
417
418 void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di )
419 {
420     int sc = flags[di] & CV_HAAR_SCALE_IMAGE ? 0 : 1;
421     fs << FILENAME << detectorFilenames[di];
422     fs << C_SCALE_CASCADE << sc;
423 }
424
425
426 int CV_CascadeDetectorTest::detectMultiScale_C( const string& filename,
427                                                 int di, const Mat& img,
428                                                 vector<Rect>& objects )
429 {
430     Ptr<CvHaarClassifierCascade> c_cascade = cvLoadHaarClassifierCascade(filename.c_str(), cvSize(0,0));
431     Ptr<CvMemStorage> storage = cvCreateMemStorage();
432
433     if( c_cascade.empty() )
434     {
435         ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
436         return cvtest::TS::FAIL_INVALID_TEST_DATA;
437     }
438     Mat grayImg;
439     cvtColor( img, grayImg, CV_BGR2GRAY );
440     equalizeHist( grayImg, grayImg );
441
442     CvMat c_gray = grayImg;
443     CvSeq* rs = cvHaarDetectObjects(&c_gray, c_cascade, storage, 1.1, 3, flags[di] );
444     
445     objects.clear();
446     for( int i = 0; i < rs->total; i++ )
447     {
448         Rect r = *(Rect*)cvGetSeqElem(rs, i);
449         objects.push_back(r);
450     }
451
452     return cvtest::TS::OK;
453 }
454
455 int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img,
456                                               vector<Rect>& objects)
457 {
458     string dataPath = ts->get_data_path(), filename;
459     filename = dataPath + detectorFilenames[di];
460     const string pattern = "haarcascade_frontalface_default.xml";
461
462     if( filename.size() >= pattern.size() &&
463         strcmp(filename.c_str() + (filename.size() - pattern.size()),
464               pattern.c_str()) == 0 )
465         return detectMultiScale_C(filename, di, img, objects);
466
467     CascadeClassifier cascade( filename );
468     if( cascade.empty() )
469     {
470         ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
471         return cvtest::TS::FAIL_INVALID_TEST_DATA;
472     }
473     Mat grayImg;
474     cvtColor( img, grayImg, CV_BGR2GRAY );
475     equalizeHist( grayImg, grayImg );
476     cascade.detectMultiScale( grayImg, objects, 1.1, 3, flags[di] );
477     return cvtest::TS::OK;
478 }
479
480 //----------------------------------------------- HOGDetectorTest -----------------------------------
481 class CV_HOGDetectorTest : public CV_DetectorTest
482 {
483 public:
484     CV_HOGDetectorTest();
485 protected:
486     virtual void readDetector( const FileNode& fn );
487     virtual void writeDetector( FileStorage& fs, int di );
488     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );
489 };
490
491 CV_HOGDetectorTest::CV_HOGDetectorTest()
492 {
493     validationFilename = "cascadeandhog/hog.xml";
494 }
495
496 void CV_HOGDetectorTest::readDetector( const FileNode& fn )
497 {
498     string filename;
499     if( fn[FILENAME].node->data.seq != 0 )
500         fn[FILENAME] >> filename;
501     detectorFilenames.push_back( filename);
502 }
503
504 void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di )
505 {
506     fs << FILENAME << detectorFilenames[di];
507 }
508
509 int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img,
510                                               vector<Rect>& objects)
511 {
512     HOGDescriptor hog;
513     if( detectorFilenames[di].empty() )
514         hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
515     else
516         assert(0);
517     hog.detectMultiScale(img, objects);
518     return cvtest::TS::OK;
519 }
520
521 //----------------------------------------------- HOGDetectorReadWriteTest -----------------------------------
522 TEST(Objdetect_HOGDetectorReadWrite, regression)
523 {
524     // Inspired by bug #2607
525     Mat img;
526     img = imread(cvtest::TS::ptr()->get_data_path() + "/cascadeandhog/images/karen-and-rob.png");
527     ASSERT_FALSE(img.empty());
528
529     HOGDescriptor hog;
530     hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
531
532     string tempfilename = cv::tempfile(".xml");
533     FileStorage fs(tempfilename, FileStorage::WRITE);
534     hog.write(fs, "myHOG");
535
536     fs.open(tempfilename, FileStorage::READ);
537     remove(tempfilename.c_str());
538
539     FileNode n = fs["opencv_storage"]["myHOG"];
540
541     ASSERT_NO_THROW(hog.read(n));
542 }
543
544
545
546 TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); }
547 TEST(Objdetect_HOGDetector, regression) { CV_HOGDetectorTest test; test.safe_run(); }