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