• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CV_DetectorTest()96 CV_DetectorTest::CV_DetectorTest()
97 {
98     configFilename = "dummy";
99     write_results = false;
100 }
101 
getValidationFilename()102 string& CV_DetectorTest::getValidationFilename()
103 {
104     return validationFilename;
105 }
106 
prepareData(FileStorage & _fs)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 
run(int)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 
runTestCase(int detectorIdx,vector<vector<Rect>> & objects)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 
isZero(uchar i)297 bool isZero( uchar i ) {return i == 0;}
298 
validate(int detectorIdx,vector<vector<Rect>> & objects)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 
CV_CascadeDetectorTest()399 CV_CascadeDetectorTest::CV_CascadeDetectorTest()
400 {
401     validationFilename = "cascadeandhog/cascade.xml";
402     configFilename = "cascadeandhog/_cascade.xml";
403 }
404 
readDetector(const FileNode & fn)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 
writeDetector(FileStorage & fs,int di)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 
detectMultiScale_C(const string & filename,int di,const Mat & img,vector<Rect> & objects)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 
detectMultiScale(int di,const Mat & img,vector<Rect> & objects)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 
CV_HOGDetectorTest()491 CV_HOGDetectorTest::CV_HOGDetectorTest()
492 {
493     validationFilename = "cascadeandhog/hog.xml";
494 }
495 
readDetector(const FileNode & fn)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 
writeDetector(FileStorage & fs,int di)504 void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di )
505 {
506     fs << FILENAME << detectorFilenames[di];
507 }
508 
detectMultiScale(int di,const Mat & img,vector<Rect> & objects)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 -----------------------------------
TEST(Objdetect_HOGDetectorReadWrite,regression)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 
TEST(Objdetect_CascadeDetector,regression)546 TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); }
TEST(Objdetect_HOGDetector,regression)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:
HOGDescriptorTester(HOGDescriptor & instance)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     {
BlockDataHOGCacheTester::BlockData588         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);
~HOGCacheTesterHOGCacheTester605     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 
HOGCacheTester()633 HOGCacheTester::HOGCacheTester()
634 {
635     useCache = false;
636     blockHistogramSize = count1 = count2 = count4 = 0;
637     descriptor = 0;
638 }
639 
HOGCacheTester(const HOGDescriptorTester * _descriptor,const Mat & _img,Size _paddingTL,Size _paddingBR,bool _useCache,Size _cacheStride)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 
init(const HOGDescriptorTester * _descriptor,const Mat & _img,Size _paddingTL,Size _paddingBR,bool _useCache,Size _cacheStride)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 
getBlock(Point pt,float * buf)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 
normalizeBlockHistogram(float * _hist) const936 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 
windowsInImage(Size imageSize,Size winStride) const981 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 
getWindow(Size imageSize,Size winStride,int idx) const987 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 
is_failed() const995 inline bool HOGDescriptorTester::is_failed() const
996 {
997     return failed;
998 }
999 
gcd(int a,int b)1000 static inline int gcd(int a, int b) { return (a % b == 0) ? b : gcd (b, a % b); }
1001 
detect(const Mat & img,vector<Point> & hits,vector<double> & weights,double hitThreshold,Size winStride,Size padding,const vector<Point> & locations) const1002 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 
detect(const Mat & img,vector<Point> & hits,double hitThreshold,Size winStride,Size padding,const vector<Point> & locations) const1103 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 
compute(InputArray _img,vector<float> & descriptors,Size winStride,Size padding,const vector<Point> & locations) const1110 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 
computeGradient(const Mat & img,Mat & grad,Mat & qangle,Size paddingTL,Size paddingBR) const1185 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 
TEST(Objdetect_HOGDetector_Strict,accuracy)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 
TEST(Objdetect_CascadeDetector,small_img)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