Home | History | Annotate | Download | only in test
      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.hpp"
     44 #include "opencv2/objdetect/objdetect_c.h"
     45 
     46 using namespace cv;
     47 using namespace std;
     48 
     49 //#define GET_STAT
     50 
     51 #define DIST_E              "distE"
     52 #define S_E                 "sE"
     53 #define NO_PAIR_E           "noPairE"
     54 //#define TOTAL_NO_PAIR_E     "totalNoPairE"
     55 
     56 #define DETECTOR_NAMES      "detector_names"
     57 #define DETECTORS           "detectors"
     58 #define IMAGE_FILENAMES     "image_filenames"
     59 #define VALIDATION          "validation"
     60 #define FILENAME            "fn"
     61 
     62 #define C_SCALE_CASCADE     "scale_cascade"
     63 
     64 class CV_DetectorTest : public cvtest::BaseTest
     65 {
     66 public:
     67     CV_DetectorTest();
     68 protected:
     69     virtual int prepareData( FileStorage& fs );
     70     virtual void run( int startFrom );
     71     virtual string& getValidationFilename();
     72 
     73     virtual void readDetector( const FileNode& fn ) = 0;
     74     virtual void writeDetector( FileStorage& fs, int di ) = 0;
     75     int runTestCase( int detectorIdx, vector<vector<Rect> >& objects );
     76     virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ) = 0;
     77     int validate( int detectorIdx, vector<vector<Rect> >& objects );
     78 
     79     struct
     80     {
     81         float dist;
     82         float s;
     83         float noPair;
     84         //float totalNoPair;
     85     } eps;
     86     vector<string> detectorNames;
     87     vector<string> detectorFilenames;
     88     vector<string> imageFilenames;
     89     vector<Mat> images;
     90     string validationFilename;
     91     string configFilename;
     92     FileStorage validationFS;
     93     bool write_results;
     94 };
     95 
     96 CV_DetectorTest::CV_DetectorTest()
     97 {
     98     configFilename = "dummy";
     99     write_results = false;
    100 }
    101 
    102 string& CV_DetectorTest::getValidationFilename()
    103 {
    104     return validationFilename;
    105 }
    106 
    107 int CV_DetectorTest::prepareData( FileStorage& _fs )
    108 {
    109     if( !_fs.isOpened() )
    110         test_case_count = -1;
    111     else
    112     {
    113         FileNode fn = _fs.getFirstTopLevelNode();
    114 
    115         fn[DIST_E] >> eps.dist;
    116         fn[S_E] >> eps.s;
    117         fn[NO_PAIR_E] >> eps.noPair;
    118 //        fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair;
    119 
    120         // read detectors
    121         if( fn[DETECTOR_NAMES].size() != 0 )
    122         {
    123             FileNodeIterator it = fn[DETECTOR_NAMES].begin();
    124             for( ; it != fn[DETECTOR_NAMES].end(); )
    125             {
    126                 String _name;
    127                 it >> _name;
    128                 detectorNames.push_back(_name);
    129                 readDetector(fn[DETECTORS][_name]);
    130             }
    131         }
    132         test_case_count = (int)detectorNames.size();
    133 
    134         // read images filenames and images
    135         string dataPath = ts->get_data_path();
    136         if( fn[IMAGE_FILENAMES].size() != 0 )
    137         {
    138             for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); )
    139             {
    140                 String filename;
    141                 it >> filename;
    142                 imageFilenames.push_back(filename);
    143                 Mat img = imread( dataPath+filename, 1 );
    144                 images.push_back( img );
    145             }
    146         }
    147     }
    148     return cvtest::TS::OK;
    149 }
    150 
    151 void CV_DetectorTest::run( int )
    152 {
    153     string dataPath = ts->get_data_path();
    154     string vs_filename = dataPath + getValidationFilename();
    155 
    156     write_results = !validationFS.open( vs_filename, FileStorage::READ );
    157 
    158     int code;
    159     if( !write_results )
    160     {
    161         code = prepareData( validationFS );
    162     }
    163     else
    164     {
    165         FileStorage fs0(dataPath + configFilename, FileStorage::READ );
    166         code = prepareData(fs0);
    167     }
    168 
    169     if( code < 0 )
    170     {
    171         ts->set_failed_test_info( code );
    172         return;
    173     }
    174 
    175     if( write_results )
    176     {
    177         validationFS.release();
    178         validationFS.open( vs_filename, FileStorage::WRITE );
    179         validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{";
    180 
    181         validationFS << DIST_E << eps.dist;
    182         validationFS << S_E << eps.s;
    183         validationFS << NO_PAIR_E << eps.noPair;
    184     //    validationFS << TOTAL_NO_PAIR_E << eps.totalNoPair;
    185 
    186         // write detector names
    187         validationFS << DETECTOR_NAMES << "[";
    188         vector<string>::const_iterator nit = detectorNames.begin();
    189         for( ; nit != detectorNames.end(); ++nit )
    190         {
    191             validationFS << *nit;
    192         }
    193         validationFS << "]"; // DETECTOR_NAMES
    194 
    195         // write detectors
    196         validationFS << DETECTORS << "{";
    197         assert( detectorNames.size() == detectorFilenames.size() );
    198         nit = detectorNames.begin();
    199         for( int di = 0; nit != detectorNames.end(); ++nit, di++ )
    200         {
    201             validationFS << *nit << "{";
    202             writeDetector( validationFS, di );
    203             validationFS << "}";
    204         }
    205         validationFS << "}";
    206 
    207         // write image filenames
    208         validationFS << IMAGE_FILENAMES << "[";
    209         vector<string>::const_iterator it = imageFilenames.begin();
    210         for( int ii = 0; it != imageFilenames.end(); ++it, ii++ )
    211         {
    212             char buf[10];
    213             sprintf( buf, "%s%d", "img_", ii );
    214             //cvWriteComment( validationFS.fs, buf, 0 );
    215             validationFS << *it;
    216         }
    217         validationFS << "]"; // IMAGE_FILENAMES
    218 
    219         validationFS << VALIDATION << "{";
    220     }
    221 
    222     int progress = 0;
    223     for( int di = 0; di < test_case_count; di++ )
    224     {
    225         progress = update_progress( progress, di, test_case_count, 0 );
    226         if( write_results )
    227             validationFS << detectorNames[di] << "{";
    228         vector<vector<Rect> > objects;
    229         int temp_code = runTestCase( di, objects );
    230 
    231         if (!write_results && temp_code == cvtest::TS::OK)
    232             temp_code = validate( di, objects );
    233 
    234         if (temp_code != cvtest::TS::OK)
    235             code = temp_code;
    236 
    237         if( write_results )
    238             validationFS << "}"; // detectorNames[di]
    239     }
    240 
    241     if( write_results )
    242     {
    243         validationFS << "}"; // VALIDATION
    244         validationFS << "}"; // getDefaultObjectName
    245     }
    246 
    247     if ( test_case_count <= 0 || imageFilenames.size() <= 0 )
    248     {
    249         ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" );
    250         code = cvtest::TS::FAIL_INVALID_TEST_DATA;
    251     }
    252     ts->set_failed_test_info( code );
    253 }
    254 
    255 int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects )
    256 {
    257     string dataPath = ts->get_data_path(), detectorFilename;
    258     if( !detectorFilenames[detectorIdx].empty() )
    259         detectorFilename = dataPath + detectorFilenames[detectorIdx];
    260     printf("detector %s\n", detectorFilename.c_str());
    261 
    262     for( int ii = 0; ii < (int)imageFilenames.size(); ++ii )
    263     {
    264         vector<Rect> imgObjects;
    265         Mat image = images[ii];
    266         if( image.empty() )
    267         {
    268             char msg[30];
    269             sprintf( msg, "%s %d %s", "image ", ii, " can not be read" );
    270             ts->printf( cvtest::TS::LOG, msg );
    271             return cvtest::TS::FAIL_INVALID_TEST_DATA;
    272         }
    273         int code = detectMultiScale( detectorIdx, image, imgObjects );
    274         if( code != cvtest::TS::OK )
    275             return code;
    276 
    277         objects.push_back( imgObjects );
    278 
    279         if( write_results )
    280         {
    281             char buf[10];
    282             sprintf( buf, "%s%d", "img_", ii );
    283             string imageIdxStr = buf;
    284             validationFS << imageIdxStr << "[:";
    285             for( vector<Rect>::const_iterator it = imgObjects.begin();
    286                     it != imgObjects.end(); ++it )
    287             {
    288                 validationFS << it->x << it->y << it->width << it->height;
    289             }
    290             validationFS << "]"; // imageIdxStr
    291         }
    292     }
    293     return cvtest::TS::OK;
    294 }
    295 
    296 
    297 bool isZero( uchar i ) {return i == 0;}
    298 
    299 int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects )
    300 {
    301     assert( imageFilenames.size() == objects.size() );
    302     int imageIdx = 0;
    303     int totalNoPair = 0, totalValRectCount = 0;
    304 
    305     for( vector<vector<Rect> >::const_iterator it = objects.begin();
    306         it != objects.end(); ++it, imageIdx++ ) // for image
    307     {
    308         Size imgSize = images[imageIdx].size();
    309         float dist = min(imgSize.height, imgSize.width) * eps.dist;
    310         float wDiff = imgSize.width * eps.s;
    311         float hDiff = imgSize.height * eps.s;
    312 
    313         int noPair = 0;
    314 
    315         // read validation rectangles
    316         char buf[10];
    317         sprintf( buf, "%s%d", "img_", imageIdx );
    318         string imageIdxStr = buf;
    319         FileNode node = validationFS.getFirstTopLevelNode()[VALIDATION][detectorNames[detectorIdx]][imageIdxStr];
    320         vector<Rect> valRects;
    321         if( node.size() != 0 )
    322         {
    323             for( FileNodeIterator it2 = node.begin(); it2 != node.end(); )
    324             {
    325                 Rect r;
    326                 it2 >> r.x >> r.y >> r.width >> r.height;
    327                 valRects.push_back(r);
    328             }
    329         }
    330         totalValRectCount += (int)valRects.size();
    331 
    332         // compare rectangles
    333         vector<uchar> map(valRects.size(), 0);
    334         for( vector<Rect>::const_iterator cr = it->begin();
    335             cr != it->end(); ++cr )
    336         {
    337             // find nearest rectangle
    338             Point2f cp1 = Point2f( cr->x + (float)cr->width/2.0f, cr->y + (float)cr->height/2.0f );
    339             int minIdx = -1, vi = 0;
    340             float minDist = (float)norm( Point(imgSize.width, imgSize.height) );
    341             for( vector<Rect>::const_iterator vr = valRects.begin();
    342                 vr != valRects.end(); ++vr, vi++ )
    343             {
    344                 Point2f cp2 = Point2f( vr->x + (float)vr->width/2.0f, vr->y + (float)vr->height/2.0f );
    345                 float curDist = (float)norm(cp1-cp2);
    346                 if( curDist < minDist )
    347                 {
    348                     minIdx = vi;
    349                     minDist = curDist;
    350                 }
    351             }
    352             if( minIdx == -1 )
    353             {
    354                 noPair++;
    355             }
    356             else
    357             {
    358                 Rect vr = valRects[minIdx];
    359                 if( map[minIdx] != 0 || (minDist > dist) || (abs(cr->width - vr.width) > wDiff) ||
    360                                                         (abs(cr->height - vr.height) > hDiff) )
    361                     noPair++;
    362                 else
    363                     map[minIdx] = 1;
    364             }
    365         }
    366         noPair += (int)count_if( map.begin(), map.end(), isZero );
    367         totalNoPair += noPair;
    368 
    369         EXPECT_LE(noPair, cvRound(valRects.size()*eps.noPair)+1)
    370             << "detector " << detectorNames[detectorIdx] << " has overrated count of rectangles without pair on "
    371             << imageFilenames[imageIdx] << " image";
    372 
    373         if (::testing::Test::HasFailure())
    374             break;
    375     }
    376 
    377     EXPECT_LE(totalNoPair, cvRound(totalValRectCount*eps./*total*/noPair)+1)
    378         << "detector " << detectorNames[detectorIdx] << " has overrated count of rectangles without pair on all images set";
    379 
    380     if (::testing::Test::HasFailure())
    381         return cvtest::TS::FAIL_BAD_ACCURACY;
    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( CASCADE_SCALE_IMAGE );
    416 }
    417 
    418 void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di )
    419 {
    420     int sc = flags[di] & CASCADE_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 )
    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, COLOR_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, COLOR_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].size() != 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(); }
    548 
    549 
    550 //----------------------------------------------- HOG SSE2 compatible test -----------------------------------
    551 
    552 class HOGDescriptorTester :
    553     public cv::HOGDescriptor
    554 {
    555     HOGDescriptor* actual_hog;
    556     cvtest::TS* ts;
    557     mutable bool failed;
    558 
    559 public:
    560     HOGDescriptorTester(HOGDescriptor& instance) :
    561         cv::HOGDescriptor(instance), actual_hog(&instance),
    562         ts(cvtest::TS::ptr()), failed(false)
    563     { }
    564 
    565     virtual void computeGradient(const Mat& img, Mat& grad, Mat& qangle,
    566         Size paddingTL, Size paddingBR) const;
    567 
    568     virtual void detect(const Mat& img,
    569         vector<Point>& hits, vector<double>& weights, double hitThreshold = 0.0,
    570         Size winStride = Size(), Size padding = Size(),
    571         const vector<Point>& locations = vector<Point>()) const;
    572 
    573     virtual void detect(const Mat& img, vector<Point>& hits, double hitThreshold = 0.0,
    574         Size winStride = Size(), Size padding = Size(),
    575         const vector<Point>& locations = vector<Point>()) const;
    576 
    577     virtual void compute(InputArray img, vector<float>& descriptors,
    578         Size winStride = Size(), Size padding = Size(),
    579         const vector<Point>& locations = vector<Point>()) const;
    580 
    581     bool is_failed() const;
    582 };
    583 
    584 struct HOGCacheTester
    585 {
    586     struct BlockData
    587     {
    588         BlockData() : histOfs(0), imgOffset() {}
    589         int histOfs;
    590         Point imgOffset;
    591     };
    592 
    593     struct PixData
    594     {
    595         size_t gradOfs, qangleOfs;
    596         int histOfs[4];
    597         float histWeights[4];
    598         float gradWeight;
    599     };
    600 
    601     HOGCacheTester();
    602     HOGCacheTester(const HOGDescriptorTester* descriptor,
    603         const Mat& img, Size paddingTL, Size paddingBR,
    604         bool useCache, Size cacheStride);
    605     virtual ~HOGCacheTester() { }
    606     virtual void init(const HOGDescriptorTester* descriptor,
    607         const Mat& img, Size paddingTL, Size paddingBR,
    608         bool useCache, Size cacheStride);
    609 
    610     Size windowsInImage(Size imageSize, Size winStride) const;
    611     Rect getWindow(Size imageSize, Size winStride, int idx) const;
    612 
    613     const float* getBlock(Point pt, float* buf);
    614     virtual void normalizeBlockHistogram(float* histogram) const;
    615 
    616     vector<PixData> pixData;
    617     vector<BlockData> blockData;
    618 
    619     bool useCache;
    620     vector<int> ymaxCached;
    621     Size winSize, cacheStride;
    622     Size nblocks, ncells;
    623     int blockHistogramSize;
    624     int count1, count2, count4;
    625     Point imgoffset;
    626     Mat_<float> blockCache;
    627     Mat_<uchar> blockCacheFlags;
    628 
    629     Mat grad, qangle;
    630     const HOGDescriptorTester* descriptor;
    631 };
    632 
    633 HOGCacheTester::HOGCacheTester()
    634 {
    635     useCache = false;
    636     blockHistogramSize = count1 = count2 = count4 = 0;
    637     descriptor = 0;
    638 }
    639 
    640 HOGCacheTester::HOGCacheTester(const HOGDescriptorTester* _descriptor,
    641     const Mat& _img, Size _paddingTL, Size _paddingBR,
    642     bool _useCache, Size _cacheStride)
    643 {
    644     init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride);
    645 }
    646 
    647 void HOGCacheTester::init(const HOGDescriptorTester* _descriptor,
    648     const Mat& _img, Size _paddingTL, Size _paddingBR,
    649     bool _useCache, Size _cacheStride)
    650 {
    651     descriptor = _descriptor;
    652     cacheStride = _cacheStride;
    653     useCache = _useCache;
    654 
    655     descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR);
    656     imgoffset = _paddingTL;
    657 
    658     winSize = descriptor->winSize;
    659     Size blockSize = descriptor->blockSize;
    660     Size blockStride = descriptor->blockStride;
    661     Size cellSize = descriptor->cellSize;
    662     int i, j, nbins = descriptor->nbins;
    663     int rawBlockSize = blockSize.width*blockSize.height;
    664 
    665     nblocks = Size((winSize.width - blockSize.width)/blockStride.width + 1,
    666                    (winSize.height - blockSize.height)/blockStride.height + 1);
    667     ncells = Size(blockSize.width/cellSize.width, blockSize.height/cellSize.height);
    668     blockHistogramSize = ncells.width*ncells.height*nbins;
    669 
    670     if( useCache )
    671     {
    672         Size cacheSize((grad.cols - blockSize.width)/cacheStride.width+1,
    673                        (winSize.height/cacheStride.height)+1);
    674         blockCache.create(cacheSize.height, cacheSize.width*blockHistogramSize);
    675         blockCacheFlags.create(cacheSize);
    676         size_t cacheRows = blockCache.rows;
    677         ymaxCached.resize(cacheRows);
    678         for(size_t ii = 0; ii < cacheRows; ii++ )
    679             ymaxCached[ii] = -1;
    680     }
    681 
    682     Mat_<float> weights(blockSize);
    683     float sigma = (float)descriptor->getWinSigma();
    684     float scale = 1.f/(sigma*sigma*2);
    685 
    686     for(i = 0; i < blockSize.height; i++)
    687         for(j = 0; j < blockSize.width; j++)
    688         {
    689             float di = i - blockSize.height*0.5f;
    690             float dj = j - blockSize.width*0.5f;
    691             weights(i,j) = std::exp(-(di*di + dj*dj)*scale);
    692         }
    693 
    694     blockData.resize(nblocks.width*nblocks.height);
    695     pixData.resize(rawBlockSize*3);
    696 
    697     // Initialize 2 lookup tables, pixData & blockData.
    698     // Here is why:
    699     //
    700     // The detection algorithm runs in 4 nested loops (at each pyramid layer):
    701     //  loop over the windows within the input image
    702     //    loop over the blocks within each window
    703     //      loop over the cells within each block
    704     //        loop over the pixels in each cell
    705     //
    706     // As each of the loops runs over a 2-dimensional array,
    707     // we could get 8(!) nested loops in total, which is very-very slow.
    708     //
    709     // To speed the things up, we do the following:
    710     //   1. loop over windows is unrolled in the HOGDescriptor::{compute|detect} methods;
    711     //         inside we compute the current search window using getWindow() method.
    712     //         Yes, it involves some overhead (function call + couple of divisions),
    713     //         but it's tiny in fact.
    714     //   2. loop over the blocks is also unrolled. Inside we use pre-computed blockData[j]
    715     //         to set up gradient and histogram pointers.
    716     //   3. loops over cells and pixels in each cell are merged
    717     //       (since there is no overlap between cells, each pixel in the block is processed once)
    718     //      and also unrolled. Inside we use PixData[k] to access the gradient values and
    719     //      update the histogram
    720     //
    721     count1 = count2 = count4 = 0;
    722     for( j = 0; j < blockSize.width; j++ )
    723         for( i = 0; i < blockSize.height; i++ )
    724         {
    725             PixData* data = 0;
    726             float cellX = (j+0.5f)/cellSize.width - 0.5f;
    727             float cellY = (i+0.5f)/cellSize.height - 0.5f;
    728             int icellX0 = cvFloor(cellX);
    729             int icellY0 = cvFloor(cellY);
    730             int icellX1 = icellX0 + 1, icellY1 = icellY0 + 1;
    731             cellX -= icellX0;
    732             cellY -= icellY0;
    733 
    734             if( (unsigned)icellX0 < (unsigned)ncells.width &&
    735                 (unsigned)icellX1 < (unsigned)ncells.width )
    736             {
    737                 if( (unsigned)icellY0 < (unsigned)ncells.height &&
    738                     (unsigned)icellY1 < (unsigned)ncells.height )
    739                 {
    740                     data = &pixData[rawBlockSize*2 + (count4++)];
    741                     data->histOfs[0] = (icellX0*ncells.height + icellY0)*nbins;
    742                     data->histWeights[0] = (1.f - cellX)*(1.f - cellY);
    743                     data->histOfs[1] = (icellX1*ncells.height + icellY0)*nbins;
    744                     data->histWeights[1] = cellX*(1.f - cellY);
    745                     data->histOfs[2] = (icellX0*ncells.height + icellY1)*nbins;
    746                     data->histWeights[2] = (1.f - cellX)*cellY;
    747                     data->histOfs[3] = (icellX1*ncells.height + icellY1)*nbins;
    748                     data->histWeights[3] = cellX*cellY;
    749                 }
    750                 else
    751                 {
    752                     data = &pixData[rawBlockSize + (count2++)];
    753                     if( (unsigned)icellY0 < (unsigned)ncells.height )
    754                     {
    755                         icellY1 = icellY0;
    756                         cellY = 1.f - cellY;
    757                     }
    758                     data->histOfs[0] = (icellX0*ncells.height + icellY1)*nbins;
    759                     data->histWeights[0] = (1.f - cellX)*cellY;
    760                     data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins;
    761                     data->histWeights[1] = cellX*cellY;
    762                     data->histOfs[2] = data->histOfs[3] = 0;
    763                     data->histWeights[2] = data->histWeights[3] = 0;
    764                 }
    765             }
    766             else
    767             {
    768                 if( (unsigned)icellX0 < (unsigned)ncells.width )
    769                 {
    770                     icellX1 = icellX0;
    771                     cellX = 1.f - cellX;
    772                 }
    773 
    774                 if( (unsigned)icellY0 < (unsigned)ncells.height &&
    775                     (unsigned)icellY1 < (unsigned)ncells.height )
    776                 {
    777                     data = &pixData[rawBlockSize + (count2++)];
    778                     data->histOfs[0] = (icellX1*ncells.height + icellY0)*nbins;
    779                     data->histWeights[0] = cellX*(1.f - cellY);
    780                     data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins;
    781                     data->histWeights[1] = cellX*cellY;
    782                     data->histOfs[2] = data->histOfs[3] = 0;
    783                     data->histWeights[2] = data->histWeights[3] = 0;
    784                 }
    785                 else
    786                 {
    787                     data = &pixData[count1++];
    788                     if( (unsigned)icellY0 < (unsigned)ncells.height )
    789                     {
    790                         icellY1 = icellY0;
    791                         cellY = 1.f - cellY;
    792                     }
    793                     data->histOfs[0] = (icellX1*ncells.height + icellY1)*nbins;
    794                     data->histWeights[0] = cellX*cellY;
    795                     data->histOfs[1] = data->histOfs[2] = data->histOfs[3] = 0;
    796                     data->histWeights[1] = data->histWeights[2] = data->histWeights[3] = 0;
    797                 }
    798             }
    799             data->gradOfs = (grad.cols*i + j)*2;
    800             data->qangleOfs = (qangle.cols*i + j)*2;
    801             data->gradWeight = weights(i,j);
    802         }
    803 
    804     assert( count1 + count2 + count4 == rawBlockSize );
    805     // defragment pixData
    806     for( j = 0; j < count2; j++ )
    807         pixData[j + count1] = pixData[j + rawBlockSize];
    808     for( j = 0; j < count4; j++ )
    809         pixData[j + count1 + count2] = pixData[j + rawBlockSize*2];
    810     count2 += count1;
    811     count4 += count2;
    812 
    813     // initialize blockData
    814     for( j = 0; j < nblocks.width; j++ )
    815         for( i = 0; i < nblocks.height; i++ )
    816         {
    817             BlockData& data = blockData[j*nblocks.height + i];
    818             data.histOfs = (j*nblocks.height + i)*blockHistogramSize;
    819             data.imgOffset = Point(j*blockStride.width,i*blockStride.height);
    820         }
    821 }
    822 
    823 const float* HOGCacheTester::getBlock(Point pt, float* buf)
    824 {
    825     float* blockHist = buf;
    826     assert(descriptor != 0);
    827 
    828     Size blockSize = descriptor->blockSize;
    829     pt += imgoffset;
    830 
    831     CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) &&
    832                (unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) );
    833 
    834     if( useCache )
    835     {
    836         CV_Assert( pt.x % cacheStride.width == 0 &&
    837                    pt.y % cacheStride.height == 0 );
    838         Point cacheIdx(pt.x/cacheStride.width,
    839                       (pt.y/cacheStride.height) % blockCache.rows);
    840         if( pt.y != ymaxCached[cacheIdx.y] )
    841         {
    842             Mat_<uchar> cacheRow = blockCacheFlags.row(cacheIdx.y);
    843             cacheRow = (uchar)0;
    844             ymaxCached[cacheIdx.y] = pt.y;
    845         }
    846 
    847         blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize];
    848         uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x);
    849         if( computedFlag != 0 )
    850             return blockHist;
    851         computedFlag = (uchar)1; // set it at once, before actual computing
    852     }
    853 
    854     int k, C1 = count1, C2 = count2, C4 = count4;
    855     const float* gradPtr = grad.ptr<float>(pt.y) + pt.x*2;
    856     const uchar* qanglePtr = qangle.ptr(pt.y) + pt.x*2;
    857 
    858     CV_Assert( blockHist != 0 );
    859     for( k = 0; k < blockHistogramSize; k++ )
    860         blockHist[k] = 0.f;
    861 
    862     const PixData* _pixData = &pixData[0];
    863 
    864     for( k = 0; k < C1; k++ )
    865     {
    866         const PixData& pk = _pixData[k];
    867         const float* a = gradPtr + pk.gradOfs;
    868         float w = pk.gradWeight*pk.histWeights[0];
    869         const uchar* h = qanglePtr + pk.qangleOfs;
    870         int h0 = h[0], h1 = h[1];
    871         float* hist = blockHist + pk.histOfs[0];
    872         float t0 = hist[h0] + a[0]*w;
    873         float t1 = hist[h1] + a[1]*w;
    874         hist[h0] = t0; hist[h1] = t1;
    875     }
    876 
    877     for( ; k < C2; k++ )
    878     {
    879         const PixData& pk = _pixData[k];
    880         const float* a = gradPtr + pk.gradOfs;
    881         float w, t0, t1, a0 = a[0], a1 = a[1];
    882         const uchar* h = qanglePtr + pk.qangleOfs;
    883         int h0 = h[0], h1 = h[1];
    884 
    885         float* hist = blockHist + pk.histOfs[0];
    886         w = pk.gradWeight*pk.histWeights[0];
    887         t0 = hist[h0] + a0*w;
    888         t1 = hist[h1] + a1*w;
    889         hist[h0] = t0; hist[h1] = t1;
    890 
    891         hist = blockHist + pk.histOfs[1];
    892         w = pk.gradWeight*pk.histWeights[1];
    893         t0 = hist[h0] + a0*w;
    894         t1 = hist[h1] + a1*w;
    895         hist[h0] = t0; hist[h1] = t1;
    896     }
    897 
    898     for( ; k < C4; k++ )
    899     {
    900         const PixData& pk = _pixData[k];
    901         const float* a = gradPtr + pk.gradOfs;
    902         float w, t0, t1, a0 = a[0], a1 = a[1];
    903         const uchar* h = qanglePtr + pk.qangleOfs;
    904         int h0 = h[0], h1 = h[1];
    905 
    906         float* hist = blockHist + pk.histOfs[0];
    907         w = pk.gradWeight*pk.histWeights[0];
    908         t0 = hist[h0] + a0*w;
    909         t1 = hist[h1] + a1*w;
    910         hist[h0] = t0; hist[h1] = t1;
    911 
    912         hist = blockHist + pk.histOfs[1];
    913         w = pk.gradWeight*pk.histWeights[1];
    914         t0 = hist[h0] + a0*w;
    915         t1 = hist[h1] + a1*w;
    916         hist[h0] = t0; hist[h1] = t1;
    917 
    918         hist = blockHist + pk.histOfs[2];
    919         w = pk.gradWeight*pk.histWeights[2];
    920         t0 = hist[h0] + a0*w;
    921         t1 = hist[h1] + a1*w;
    922         hist[h0] = t0; hist[h1] = t1;
    923 
    924         hist = blockHist + pk.histOfs[3];
    925         w = pk.gradWeight*pk.histWeights[3];
    926         t0 = hist[h0] + a0*w;
    927         t1 = hist[h1] + a1*w;
    928         hist[h0] = t0; hist[h1] = t1;
    929     }
    930 
    931     normalizeBlockHistogram(blockHist);
    932 
    933     return blockHist;
    934 }
    935 
    936 void HOGCacheTester::normalizeBlockHistogram(float* _hist) const
    937 {
    938     float* hist = &_hist[0], partSum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
    939     size_t i, sz = blockHistogramSize;
    940 
    941     for (i = 0; i <= sz - 4; i += 4)
    942     {
    943         partSum[0] += hist[i] * hist[i];
    944         partSum[1] += hist[i+1] * hist[i+1];
    945         partSum[2] += hist[i+2] * hist[i+2];
    946         partSum[3] += hist[i+3] * hist[i+3];
    947     }
    948     float t0 = partSum[0] + partSum[1];
    949     float t1 = partSum[2] + partSum[3];
    950     float sum = t0 + t1;
    951     for( ; i < sz; i++ )
    952         sum += hist[i]*hist[i];
    953 
    954     float scale = 1.f/(std::sqrt(sum)+sz*0.1f), thresh = (float)descriptor->L2HysThreshold;
    955     partSum[0] = partSum[1] = partSum[2] = partSum[3] = 0.0f;
    956     for(i = 0; i <= sz - 4; i += 4)
    957     {
    958         hist[i] = std::min(hist[i]*scale, thresh);
    959         hist[i+1] = std::min(hist[i+1]*scale, thresh);
    960         hist[i+2] = std::min(hist[i+2]*scale, thresh);
    961         hist[i+3] = std::min(hist[i+3]*scale, thresh);
    962         partSum[0] += hist[i]*hist[i];
    963         partSum[1] += hist[i+1]*hist[i+1];
    964         partSum[2] += hist[i+2]*hist[i+2];
    965         partSum[3] += hist[i+3]*hist[i+3];
    966     }
    967     t0 = partSum[0] + partSum[1];
    968     t1 = partSum[2] + partSum[3];
    969     sum = t0 + t1;
    970     for( ; i < sz; i++ )
    971     {
    972         hist[i] = std::min(hist[i]*scale, thresh);
    973         sum += hist[i]*hist[i];
    974     }
    975 
    976     scale = 1.f/(std::sqrt(sum)+1e-3f);
    977     for( i = 0; i < sz; i++ )
    978         hist[i] *= scale;
    979 }
    980 
    981 Size HOGCacheTester::windowsInImage(Size imageSize, Size winStride) const
    982 {
    983     return Size((imageSize.width - winSize.width)/winStride.width + 1,
    984                 (imageSize.height - winSize.height)/winStride.height + 1);
    985 }
    986 
    987 Rect HOGCacheTester::getWindow(Size imageSize, Size winStride, int idx) const
    988 {
    989     int nwindowsX = (imageSize.width - winSize.width)/winStride.width + 1;
    990     int y = idx / nwindowsX;
    991     int x = idx - nwindowsX*y;
    992     return Rect( x*winStride.width, y*winStride.height, winSize.width, winSize.height );
    993 }
    994 
    995 inline bool HOGDescriptorTester::is_failed() const
    996 {
    997     return failed;
    998 }
    999 
   1000 static inline int gcd(int a, int b) { return (a % b == 0) ? b : gcd (b, a % b); }
   1001 
   1002 void HOGDescriptorTester::detect(const Mat& img,
   1003     vector<Point>& hits, vector<double>& weights, double hitThreshold,
   1004     Size winStride, Size padding, const vector<Point>& locations) const
   1005 {
   1006     if (failed)
   1007         return;
   1008 
   1009     hits.clear();
   1010     if( svmDetector.empty() )
   1011         return;
   1012 
   1013     if( winStride == Size() )
   1014         winStride = cellSize;
   1015     Size cacheStride(gcd(winStride.width, blockStride.width),
   1016                      gcd(winStride.height, blockStride.height));
   1017     size_t nwindows = locations.size();
   1018     padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width);
   1019     padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height);
   1020     Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2);
   1021 
   1022     HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride);
   1023 
   1024     if( !nwindows )
   1025         nwindows = cache.windowsInImage(paddedImgSize, winStride).area();
   1026 
   1027     const HOGCacheTester::BlockData* blockData = &cache.blockData[0];
   1028 
   1029     int nblocks = cache.nblocks.area();
   1030     int blockHistogramSize = cache.blockHistogramSize;
   1031     size_t dsize = getDescriptorSize();
   1032 
   1033     double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0;
   1034     vector<float> blockHist(blockHistogramSize);
   1035 
   1036     for( size_t i = 0; i < nwindows; i++ )
   1037     {
   1038         Point pt0;
   1039         if( !locations.empty() )
   1040         {
   1041             pt0 = locations[i];
   1042             if( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width ||
   1043                 pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height )
   1044                 continue;
   1045         }
   1046         else
   1047         {
   1048             pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding);
   1049             CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0);
   1050         }
   1051         double s = rho;
   1052         const float* svmVec = &svmDetector[0];
   1053         int j, k;
   1054         for( j = 0; j < nblocks; j++, svmVec += blockHistogramSize )
   1055         {
   1056             const HOGCacheTester::BlockData& bj = blockData[j];
   1057             Point pt = pt0 + bj.imgOffset;
   1058 
   1059             const float* vec = cache.getBlock(pt, &blockHist[0]);
   1060             for( k = 0; k <= blockHistogramSize - 4; k += 4 )
   1061                 s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] +
   1062                     vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3];
   1063             for( ; k < blockHistogramSize; k++ )
   1064                 s += vec[k]*svmVec[k];
   1065         }
   1066         if( s >= hitThreshold )
   1067         {
   1068             hits.push_back(pt0);
   1069             weights.push_back(s);
   1070         }
   1071     }
   1072 
   1073     // validation
   1074     std::vector<Point> actual_find_locations;
   1075     std::vector<double> actual_weights;
   1076     actual_hog->detect(img, actual_find_locations, actual_weights,
   1077         hitThreshold, winStride, padding, locations);
   1078 
   1079     if (!std::equal(hits.begin(), hits.end(),
   1080         actual_find_locations.begin()))
   1081     {
   1082         ts->printf(cvtest::TS::SUMMARY, "Found locations are not equal (see detect function)\n");
   1083         ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
   1084         ts->set_gtest_status();
   1085         failed = true;
   1086         return;
   1087     }
   1088 
   1089     const double eps = 0.0;
   1090     double diff_norm = cvtest::norm(actual_weights, weights, NORM_L2);
   1091     if (diff_norm > eps)
   1092     {
   1093         ts->printf(cvtest::TS::SUMMARY, "Weights for found locations aren't equal.\n"
   1094             "Norm of the difference is %lf\n", diff_norm);
   1095         ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels());
   1096         failed = true;
   1097         ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
   1098         ts->set_gtest_status();
   1099         return;
   1100     }
   1101 }
   1102 
   1103 void HOGDescriptorTester::detect(const Mat& img, vector<Point>& hits, double hitThreshold,
   1104     Size winStride, Size padding, const vector<Point>& locations) const
   1105 {
   1106     vector<double> weightsV;
   1107     detect(img, hits, weightsV, hitThreshold, winStride, padding, locations);
   1108 }
   1109 
   1110 void HOGDescriptorTester::compute(InputArray _img, vector<float>& descriptors,
   1111     Size winStride, Size padding, const vector<Point>& locations) const
   1112 {
   1113     Mat img = _img.getMat();
   1114 
   1115     if( winStride == Size() )
   1116         winStride = cellSize;
   1117     Size cacheStride(gcd(winStride.width, blockStride.width),
   1118         gcd(winStride.height, blockStride.height));
   1119     size_t nwindows = locations.size();
   1120     padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width);
   1121     padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height);
   1122     Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2);
   1123 
   1124     HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride);
   1125 
   1126     if( !nwindows )
   1127         nwindows = cache.windowsInImage(paddedImgSize, winStride).area();
   1128 
   1129     const HOGCacheTester::BlockData* blockData = &cache.blockData[0];
   1130 
   1131     int nblocks = cache.nblocks.area();
   1132     int blockHistogramSize = cache.blockHistogramSize;
   1133     size_t dsize = getDescriptorSize();
   1134     descriptors.resize(dsize*nwindows);
   1135 
   1136     for( size_t i = 0; i < nwindows; i++ )
   1137     {
   1138         float* descriptor = &descriptors[i*dsize];
   1139 
   1140         Point pt0;
   1141         if( !locations.empty() )
   1142         {
   1143             pt0 = locations[i];
   1144             if( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width ||
   1145                 pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height )
   1146                 continue;
   1147         }
   1148         else
   1149         {
   1150             pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding);
   1151             CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0);
   1152         }
   1153 
   1154         for( int j = 0; j < nblocks; j++ )
   1155         {
   1156             const HOGCacheTester::BlockData& bj = blockData[j];
   1157             Point pt = pt0 + bj.imgOffset;
   1158 
   1159             float* dst = descriptor + bj.histOfs;
   1160             const float* src = cache.getBlock(pt, dst);
   1161             if( src != dst )
   1162                 for( int k = 0; k < blockHistogramSize; k++ )
   1163                     dst[k] = src[k];
   1164         }
   1165     }
   1166 
   1167     // validation
   1168     std::vector<float> actual_descriptors;
   1169     actual_hog->compute(img, actual_descriptors, winStride, padding, locations);
   1170 
   1171     double diff_norm = cvtest::norm(actual_descriptors, descriptors, NORM_L2);
   1172     const double eps = 0.0;
   1173     if (diff_norm > eps)
   1174     {
   1175         ts->printf(cvtest::TS::SUMMARY, "Norm of the difference: %lf\n", diff_norm);
   1176         ts->printf(cvtest::TS::SUMMARY, "Found descriptors are not equal (see compute function)\n");
   1177         ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
   1178         ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels());
   1179         ts->set_gtest_status();
   1180         failed = true;
   1181         return;
   1182     }
   1183 }
   1184 
   1185 void HOGDescriptorTester::computeGradient(const Mat& img, Mat& grad, Mat& qangle,
   1186    Size paddingTL, Size paddingBR) const
   1187 {
   1188     CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
   1189 
   1190     Size gradsize(img.cols + paddingTL.width + paddingBR.width,
   1191        img.rows + paddingTL.height + paddingBR.height);
   1192     grad.create(gradsize, CV_32FC2);  // <magnitude*(1-alpha), magnitude*alpha>
   1193     qangle.create(gradsize, CV_8UC2); // [0..nbins-1] - quantized gradient orientation
   1194     Size wholeSize;
   1195     Point roiofs;
   1196     img.locateROI(wholeSize, roiofs);
   1197 
   1198     int i, x, y;
   1199     int cn = img.channels();
   1200 
   1201     Mat_<float> _lut(1, 256);
   1202     const float* lut = &_lut(0,0);
   1203 
   1204     if( gammaCorrection )
   1205        for( i = 0; i < 256; i++ )
   1206            _lut(0,i) = std::sqrt((float)i);
   1207     else
   1208        for( i = 0; i < 256; i++ )
   1209            _lut(0,i) = (float)i;
   1210 
   1211     AutoBuffer<int> mapbuf(gradsize.width + gradsize.height + 4);
   1212     int* xmap = (int*)mapbuf + 1;
   1213     int* ymap = xmap + gradsize.width + 2;
   1214 
   1215     const int borderType = (int)BORDER_REFLECT_101;
   1216 
   1217     for( x = -1; x < gradsize.width + 1; x++ )
   1218        xmap[x] = borderInterpolate(x - paddingTL.width + roiofs.x,
   1219            wholeSize.width, borderType) - roiofs.x;
   1220     for( y = -1; y < gradsize.height + 1; y++ )
   1221        ymap[y] = borderInterpolate(y - paddingTL.height + roiofs.y,
   1222            wholeSize.height, borderType) - roiofs.y;
   1223 
   1224     // x- & y- derivatives for the whole row
   1225     int width = gradsize.width;
   1226     AutoBuffer<float> _dbuf(width*4);
   1227     float* dbuf = _dbuf;
   1228     Mat Dx(1, width, CV_32F, dbuf);
   1229     Mat Dy(1, width, CV_32F, dbuf + width);
   1230     Mat Mag(1, width, CV_32F, dbuf + width*2);
   1231     Mat Angle(1, width, CV_32F, dbuf + width*3);
   1232 
   1233     int _nbins = nbins;
   1234     float angleScale = (float)(_nbins/CV_PI);
   1235     for( y = 0; y < gradsize.height; y++ )
   1236     {
   1237        const uchar* imgPtr  = img.ptr(ymap[y]);
   1238        const uchar* prevPtr = img.ptr(ymap[y-1]);
   1239        const uchar* nextPtr = img.ptr(ymap[y+1]);
   1240        float* gradPtr = (float*)grad.ptr(y);
   1241        uchar* qanglePtr = (uchar*)qangle.ptr(y);
   1242 
   1243        if( cn == 1 )
   1244        {
   1245            for( x = 0; x < width; x++ )
   1246            {
   1247                int x1 = xmap[x];
   1248                dbuf[x] = (float)(lut[imgPtr[xmap[x+1]]] - lut[imgPtr[xmap[x-1]]]);
   1249                dbuf[width + x] = (float)(lut[nextPtr[x1]] - lut[prevPtr[x1]]);
   1250            }
   1251        }
   1252        else
   1253        {
   1254            for( x = 0; x < width; x++ )
   1255            {
   1256                int x1 = xmap[x]*3;
   1257                float dx0, dy0, dx, dy, mag0, mag;
   1258                 const uchar* p2 = imgPtr + xmap[x+1]*3;
   1259                const uchar* p0 = imgPtr + xmap[x-1]*3;
   1260 
   1261                dx0 = lut[p2[2]] - lut[p0[2]];
   1262                dy0 = lut[nextPtr[x1+2]] - lut[prevPtr[x1+2]];
   1263                mag0 = dx0*dx0 + dy0*dy0;
   1264 
   1265                dx = lut[p2[1]] - lut[p0[1]];
   1266                dy = lut[nextPtr[x1+1]] - lut[prevPtr[x1+1]];
   1267                mag = dx*dx + dy*dy;
   1268 
   1269                if( mag0 < mag )
   1270                {
   1271                    dx0 = dx;
   1272                    dy0 = dy;
   1273                    mag0 = mag;
   1274                }
   1275 
   1276                dx = lut[p2[0]] - lut[p0[0]];
   1277                dy = lut[nextPtr[x1]] - lut[prevPtr[x1]];
   1278                mag = dx*dx + dy*dy;
   1279 
   1280                if( mag0 < mag )
   1281                {
   1282                    dx0 = dx;
   1283                    dy0 = dy;
   1284                    mag0 = mag;
   1285                }
   1286 
   1287                dbuf[x] = dx0;
   1288                dbuf[x+width] = dy0;
   1289            }
   1290        }
   1291 
   1292        cartToPolar( Dx, Dy, Mag, Angle, false );
   1293        for( x = 0; x < width; x++ )
   1294        {
   1295            float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f;
   1296            int hidx = cvFloor(angle);
   1297            angle -= hidx;
   1298            gradPtr[x*2] = mag*(1.f - angle);
   1299            gradPtr[x*2+1] = mag*angle;
   1300            if( hidx < 0 )
   1301                hidx += _nbins;
   1302            else if( hidx >= _nbins )
   1303                hidx -= _nbins;
   1304            assert( (unsigned)hidx < (unsigned)_nbins );
   1305 
   1306            qanglePtr[x*2] = (uchar)hidx;
   1307            hidx++;
   1308            hidx &= hidx < _nbins ? -1 : 0;
   1309            qanglePtr[x*2+1] = (uchar)hidx;
   1310        }
   1311     }
   1312 
   1313     // validation
   1314     Mat actual_mats[2], reference_mats[2] = { grad, qangle };
   1315     const char* args[] = { "Gradient's", "Qangles's" };
   1316     actual_hog->computeGradient(img, actual_mats[0], actual_mats[1], paddingTL, paddingBR);
   1317 
   1318     const double eps = 0.0;
   1319     for (i = 0; i < 2; ++i)
   1320     {
   1321        double diff_norm = cvtest::norm(reference_mats[i], actual_mats[i], NORM_L2);
   1322        if (diff_norm > eps)
   1323        {
   1324            ts->printf(cvtest::TS::LOG, "%s matrices are not equal\n"
   1325                "Norm of the difference is %lf\n", args[i], diff_norm);
   1326            ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels());
   1327            ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
   1328            ts->set_gtest_status();
   1329            failed = true;
   1330            return;
   1331        }
   1332     }
   1333 }
   1334 
   1335 TEST(Objdetect_HOGDetector_Strict, accuracy)
   1336 {
   1337     cvtest::TS* ts = cvtest::TS::ptr();
   1338     RNG& rng = ts->get_rng();
   1339 
   1340     HOGDescriptor actual_hog;
   1341     actual_hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
   1342     HOGDescriptorTester reference_hog(actual_hog);
   1343 
   1344     const unsigned int test_case_count = 5;
   1345     for (unsigned int i = 0; i < test_case_count && !reference_hog.is_failed(); ++i)
   1346     {
   1347         // creating a matrix
   1348         Size ssize(rng.uniform(1, 10) * actual_hog.winSize.width,
   1349             rng.uniform(1, 10) * actual_hog.winSize.height);
   1350         int type = rng.uniform(0, 1) > 0 ? CV_8UC1 : CV_8UC3;
   1351         Mat image(ssize, type);
   1352         rng.fill(image, RNG::UNIFORM, 0, 256, true);
   1353 
   1354         // checking detect
   1355         std::vector<Point> hits;
   1356         std::vector<double> weights;
   1357         reference_hog.detect(image, hits, weights);
   1358 
   1359         // checking compute
   1360         std::vector<float> descriptors;
   1361         reference_hog.compute(image, descriptors);
   1362     }
   1363 }
   1364 
   1365 TEST(Objdetect_CascadeDetector, small_img)
   1366 {
   1367     String root = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/cascades/";
   1368     String cascades[] =
   1369     {
   1370         root + "haarcascade_frontalface_alt.xml",
   1371         root + "lbpcascade_frontalface.xml",
   1372         String()
   1373     };
   1374 
   1375     vector<Rect> objects;
   1376     RNG rng((uint64)-1);
   1377 
   1378     for( int i = 0; !cascades[i].empty(); i++ )
   1379     {
   1380         printf("%d. %s\n", i, cascades[i].c_str());
   1381         CascadeClassifier cascade(cascades[i]);
   1382         for( int j = 0; j < 100; j++ )
   1383         {
   1384             int width = rng.uniform(1, 100);
   1385             int height = rng.uniform(1, 100);
   1386             Mat img(height, width, CV_8U);
   1387             randu(img, 0, 256);
   1388             cascade.detectMultiScale(img, objects);
   1389         }
   1390     }
   1391 }
   1392