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