1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
42 #include "test_precomp.hpp"
43 #include "opencv2/imgproc/imgproc.hpp"
50 #define DIST_E "distE"
52 #define NO_PAIR_E "noPairE"
53 //#define TOTAL_NO_PAIR_E "totalNoPairE"
55 #define DETECTOR_NAMES "detector_names"
56 #define DETECTORS "detectors"
57 #define IMAGE_FILENAMES "image_filenames"
58 #define VALIDATION "validation"
61 #define C_SCALE_CASCADE "scale_cascade"
63 class CV_DetectorTest : public cvtest::BaseTest
68 virtual int prepareData( FileStorage& fs );
69 virtual void run( int startFrom );
70 virtual string& getValidationFilename();
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 );
85 vector<string> detectorNames;
86 vector<string> detectorFilenames;
87 vector<string> imageFilenames;
89 string validationFilename;
90 string configFilename;
91 FileStorage validationFS;
95 CV_DetectorTest::CV_DetectorTest()
97 configFilename = "dummy";
98 write_results = false;
101 string& CV_DetectorTest::getValidationFilename()
103 return validationFilename;
106 int CV_DetectorTest::prepareData( FileStorage& _fs )
108 if( !_fs.isOpened() )
109 test_case_count = -1;
112 FileNode fn = _fs.getFirstTopLevelNode();
114 fn[DIST_E] >> eps.dist;
116 fn[NO_PAIR_E] >> eps.noPair;
117 // fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair;
120 if( fn[DETECTOR_NAMES].node->data.seq != 0 )
122 FileNodeIterator it = fn[DETECTOR_NAMES].begin();
123 for( ; it != fn[DETECTOR_NAMES].end(); )
127 detectorNames.push_back(_name);
128 readDetector(fn[DETECTORS][_name]);
131 test_case_count = (int)detectorNames.size();
133 // read images filenames and images
134 string dataPath = ts->get_data_path();
135 if( fn[IMAGE_FILENAMES].node->data.seq != 0 )
137 for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); )
141 imageFilenames.push_back(filename);
142 Mat img = imread( dataPath+filename, 1 );
143 images.push_back( img );
147 return cvtest::TS::OK;
150 void CV_DetectorTest::run( int )
152 string dataPath = ts->get_data_path();
153 string vs_filename = dataPath + getValidationFilename();
155 write_results = !validationFS.open( vs_filename, FileStorage::READ );
160 code = prepareData( validationFS );
164 FileStorage fs0(dataPath + configFilename, FileStorage::READ );
165 code = prepareData(fs0);
170 ts->set_failed_test_info( code );
176 validationFS.release();
177 validationFS.open( vs_filename, FileStorage::WRITE );
178 validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{";
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;
185 // write detector names
186 validationFS << DETECTOR_NAMES << "[";
187 vector<string>::const_iterator nit = detectorNames.begin();
188 for( ; nit != detectorNames.end(); ++nit )
190 validationFS << *nit;
192 validationFS << "]"; // DETECTOR_NAMES
195 validationFS << DETECTORS << "{";
196 assert( detectorNames.size() == detectorFilenames.size() );
197 nit = detectorNames.begin();
198 for( int di = 0; nit != detectorNames.end(); ++nit, di++ )
200 validationFS << *nit << "{";
201 writeDetector( validationFS, di );
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++ )
212 sprintf( buf, "%s%d", "img_", ii );
213 cvWriteComment( validationFS.fs, buf, 0 );
216 validationFS << "]"; // IMAGE_FILENAMES
218 validationFS << VALIDATION << "{";
222 for( int di = 0; di < test_case_count; di++ )
224 progress = update_progress( progress, di, test_case_count, 0 );
226 validationFS << detectorNames[di] << "{";
227 vector<vector<Rect> > objects;
228 int temp_code = runTestCase( di, objects );
230 if (!write_results && temp_code == cvtest::TS::OK)
231 temp_code = validate( di, objects );
233 if (temp_code != cvtest::TS::OK)
237 validationFS << "}"; // detectorNames[di]
242 validationFS << "}"; // VALIDATION
243 validationFS << "}"; // getDefaultObjectName
246 if ( test_case_count <= 0 || imageFilenames.size() <= 0 )
248 ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" );
249 code = cvtest::TS::FAIL_INVALID_TEST_DATA;
251 ts->set_failed_test_info( code );
254 int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects )
256 string dataPath = ts->get_data_path(), detectorFilename;
257 if( !detectorFilenames[detectorIdx].empty() )
258 detectorFilename = dataPath + detectorFilenames[detectorIdx];
260 for( int ii = 0; ii < (int)imageFilenames.size(); ++ii )
262 vector<Rect> imgObjects;
263 Mat image = images[ii];
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;
271 int code = detectMultiScale( detectorIdx, image, imgObjects );
272 if( code != cvtest::TS::OK )
275 objects.push_back( imgObjects );
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 )
286 validationFS << it->x << it->y << it->width << it->height;
288 validationFS << "]"; // imageIdxStr
291 return cvtest::TS::OK;
295 bool isZero( uchar i ) {return i == 0;}
297 int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects )
299 assert( imageFilenames.size() == objects.size() );
301 int totalNoPair = 0, totalValRectCount = 0;
303 for( vector<vector<Rect> >::const_iterator it = objects.begin();
304 it != objects.end(); ++it, imageIdx++ ) // for image
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;
313 // read validation rectangles
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 )
321 for( FileNodeIterator it2 = node.begin(); it2 != node.end(); )
324 it2 >> r.x >> r.y >> r.width >> r.height;
325 valRects.push_back(r);
328 totalValRectCount += (int)valRects.size();
330 // compare rectangles
331 vector<uchar> map(valRects.size(), 0);
332 for( vector<Rect>::const_iterator cr = it->begin();
333 cr != it->end(); ++cr )
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++ )
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 )
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) )
364 noPair += (int)count_if( map.begin(), map.end(), isZero );
365 totalNoPair += noPair;
366 if( noPair > cvRound(valRects.size()*eps.noPair)+1 )
369 if( imageIdx < (int)imageFilenames.size() )
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;
377 if ( totalNoPair > cvRound(totalValRectCount*eps./*total*/noPair)+1 )
379 ts->printf( cvtest::TS::LOG, "overrated count of rectangles without pair on all images set" );
380 return cvtest::TS::FAIL_BAD_ACCURACY;
383 return cvtest::TS::OK;
386 //----------------------------------------------- CascadeDetectorTest -----------------------------------
387 class CV_CascadeDetectorTest : public CV_DetectorTest
390 CV_CascadeDetectorTest();
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 );
399 CV_CascadeDetectorTest::CV_CascadeDetectorTest()
401 validationFilename = "cascadeandhog/cascade.xml";
402 configFilename = "cascadeandhog/_cascade.xml";
405 void CV_CascadeDetectorTest::readDetector( const FileNode& fn )
409 fn[FILENAME] >> filename;
410 detectorFilenames.push_back(filename);
411 fn[C_SCALE_CASCADE] >> flag;
413 flags.push_back( 0 );
415 flags.push_back( CV_HAAR_SCALE_IMAGE );
418 void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di )
420 int sc = flags[di] & CV_HAAR_SCALE_IMAGE ? 0 : 1;
421 fs << FILENAME << detectorFilenames[di];
422 fs << C_SCALE_CASCADE << sc;
426 int CV_CascadeDetectorTest::detectMultiScale_C( const string& filename,
427 int di, const Mat& img,
428 vector<Rect>& objects )
430 Ptr<CvHaarClassifierCascade> c_cascade = cvLoadHaarClassifierCascade(filename.c_str(), cvSize(0,0));
431 Ptr<CvMemStorage> storage = cvCreateMemStorage();
433 if( c_cascade.empty() )
435 ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
436 return cvtest::TS::FAIL_INVALID_TEST_DATA;
439 cvtColor( img, grayImg, CV_BGR2GRAY );
440 equalizeHist( grayImg, grayImg );
442 CvMat c_gray = grayImg;
443 CvSeq* rs = cvHaarDetectObjects(&c_gray, c_cascade, storage, 1.1, 3, flags[di] );
446 for( int i = 0; i < rs->total; i++ )
448 Rect r = *(Rect*)cvGetSeqElem(rs, i);
449 objects.push_back(r);
452 return cvtest::TS::OK;
455 int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img,
456 vector<Rect>& objects)
458 string dataPath = ts->get_data_path(), filename;
459 filename = dataPath + detectorFilenames[di];
460 const string pattern = "haarcascade_frontalface_default.xml";
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);
467 CascadeClassifier cascade( filename );
468 if( cascade.empty() )
470 ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
471 return cvtest::TS::FAIL_INVALID_TEST_DATA;
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;
480 //----------------------------------------------- HOGDetectorTest -----------------------------------
481 class CV_HOGDetectorTest : public CV_DetectorTest
484 CV_HOGDetectorTest();
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 );
491 CV_HOGDetectorTest::CV_HOGDetectorTest()
493 validationFilename = "cascadeandhog/hog.xml";
496 void CV_HOGDetectorTest::readDetector( const FileNode& fn )
499 if( fn[FILENAME].node->data.seq != 0 )
500 fn[FILENAME] >> filename;
501 detectorFilenames.push_back( filename);
504 void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di )
506 fs << FILENAME << detectorFilenames[di];
509 int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img,
510 vector<Rect>& objects)
513 if( detectorFilenames[di].empty() )
514 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
517 hog.detectMultiScale(img, objects);
518 return cvtest::TS::OK;
521 //----------------------------------------------- HOGDetectorReadWriteTest -----------------------------------
522 TEST(Objdetect_HOGDetectorReadWrite, regression)
524 // Inspired by bug #2607
526 img = imread(cvtest::TS::ptr()->get_data_path() + "/cascadeandhog/images/karen-and-rob.png");
527 ASSERT_FALSE(img.empty());
530 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
532 string tempfilename = cv::tempfile(".xml");
533 FileStorage fs(tempfilename, FileStorage::WRITE);
534 hog.write(fs, "myHOG");
536 fs.open(tempfilename, FileStorage::READ);
537 remove(tempfilename.c_str());
539 FileNode n = fs["opencv_storage"]["myHOG"];
541 ASSERT_NO_THROW(hog.read(n));
546 TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); }
547 TEST(Objdetect_HOGDetector, regression) { CV_HOGDetectorTest test; test.safe_run(); }