1
/*M///////////////////////////////////////////////////////////////////////////////////////
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
10
// Intel License Agreement
11
// For Open Source Computer Vision Library
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
26
// * The name of Intel Corporation may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
42
#include "test_precomp.hpp"
43
#include "opencv2/imgproc.hpp"
44
#include "opencv2/objdetect/objdetect_c.h"
51
#define DIST_E "distE"
53
#define NO_PAIR_E "noPairE"
54
//#define TOTAL_NO_PAIR_E "totalNoPairE"
56
#define DETECTOR_NAMES "detector_names"
57
#define DETECTORS "detectors"
58
#define IMAGE_FILENAMES "image_filenames"
59
#define VALIDATION "validation"
62
#define C_SCALE_CASCADE "scale_cascade"
64
class CV_DetectorTest : public cvtest::BaseTest
69
virtual int prepareData( FileStorage& fs );
70
virtual void run( int startFrom );
71
virtual string& getValidationFilename();
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 );
86
vector<string> detectorNames;
87
vector<string> detectorFilenames;
88
vector<string> imageFilenames;
90
string validationFilename;
91
string configFilename;
92
FileStorage validationFS;
96
CV_DetectorTest::CV_DetectorTest()
98
configFilename = "dummy";
99
write_results = false;
102
string& CV_DetectorTest::getValidationFilename()
104
return validationFilename;
107
int CV_DetectorTest::prepareData( FileStorage& _fs )
109
if( !_fs.isOpened() )
110
test_case_count = -1;
113
FileNode fn = _fs.getFirstTopLevelNode();
115
fn[DIST_E] >> eps.dist;
117
fn[NO_PAIR_E] >> eps.noPair;
118
// fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair;
121
if( fn[DETECTOR_NAMES].size() != 0 )
123
FileNodeIterator it = fn[DETECTOR_NAMES].begin();
124
for( ; it != fn[DETECTOR_NAMES].end(); )
128
detectorNames.push_back(_name);
129
readDetector(fn[DETECTORS][_name]);
132
test_case_count = (int)detectorNames.size();
134
// read images filenames and images
135
string dataPath = ts->get_data_path();
136
if( fn[IMAGE_FILENAMES].size() != 0 )
138
for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); )
142
imageFilenames.push_back(filename);
143
Mat img = imread( dataPath+filename, 1 );
144
images.push_back( img );
148
return cvtest::TS::OK;
151
void CV_DetectorTest::run( int )
153
string dataPath = ts->get_data_path();
154
string vs_filename = dataPath + getValidationFilename();
156
write_results = !validationFS.open( vs_filename, FileStorage::READ );
161
code = prepareData( validationFS );
165
FileStorage fs0(dataPath + configFilename, FileStorage::READ );
166
code = prepareData(fs0);
171
ts->set_failed_test_info( code );
177
validationFS.release();
178
validationFS.open( vs_filename, FileStorage::WRITE );
179
validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{";
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;
186
// write detector names
187
validationFS << DETECTOR_NAMES << "[";
188
vector<string>::const_iterator nit = detectorNames.begin();
189
for( ; nit != detectorNames.end(); ++nit )
191
validationFS << *nit;
193
validationFS << "]"; // DETECTOR_NAMES
196
validationFS << DETECTORS << "{";
197
assert( detectorNames.size() == detectorFilenames.size() );
198
nit = detectorNames.begin();
199
for( int di = 0; nit != detectorNames.end(); ++nit, di++ )
201
validationFS << *nit << "{";
202
writeDetector( validationFS, di );
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++ )
213
sprintf( buf, "%s%d", "img_", ii );
214
//cvWriteComment( validationFS.fs, buf, 0 );
217
validationFS << "]"; // IMAGE_FILENAMES
219
validationFS << VALIDATION << "{";
223
for( int di = 0; di < test_case_count; di++ )
225
progress = update_progress( progress, di, test_case_count, 0 );
227
validationFS << detectorNames[di] << "{";
228
vector<vector<Rect> > objects;
229
int temp_code = runTestCase( di, objects );
231
if (!write_results && temp_code == cvtest::TS::OK)
232
temp_code = validate( di, objects );
234
if (temp_code != cvtest::TS::OK)
238
validationFS << "}"; // detectorNames[di]
243
validationFS << "}"; // VALIDATION
244
validationFS << "}"; // getDefaultObjectName
247
if ( test_case_count <= 0 || imageFilenames.size() <= 0 )
249
ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" );
250
code = cvtest::TS::FAIL_INVALID_TEST_DATA;
252
ts->set_failed_test_info( code );
255
int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects )
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());
262
for( int ii = 0; ii < (int)imageFilenames.size(); ++ii )
264
vector<Rect> imgObjects;
265
Mat image = images[ii];
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;
273
int code = detectMultiScale( detectorIdx, image, imgObjects );
274
if( code != cvtest::TS::OK )
277
objects.push_back( imgObjects );
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 )
288
validationFS << it->x << it->y << it->width << it->height;
290
validationFS << "]"; // imageIdxStr
293
return cvtest::TS::OK;
297
bool isZero( uchar i ) {return i == 0;}
299
int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects )
301
assert( imageFilenames.size() == objects.size() );
303
int totalNoPair = 0, totalValRectCount = 0;
305
for( vector<vector<Rect> >::const_iterator it = objects.begin();
306
it != objects.end(); ++it, imageIdx++ ) // for image
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;
315
// read validation rectangles
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 )
323
for( FileNodeIterator it2 = node.begin(); it2 != node.end(); )
326
it2 >> r.x >> r.y >> r.width >> r.height;
327
valRects.push_back(r);
330
totalValRectCount += (int)valRects.size();
332
// compare rectangles
333
vector<uchar> map(valRects.size(), 0);
334
for( vector<Rect>::const_iterator cr = it->begin();
335
cr != it->end(); ++cr )
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++ )
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 )
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) )
366
noPair += (int)count_if( map.begin(), map.end(), isZero );
367
totalNoPair += noPair;
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";
373
if (::testing::Test::HasFailure())
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";
380
if (::testing::Test::HasFailure())
381
return cvtest::TS::FAIL_BAD_ACCURACY;
383
return cvtest::TS::OK;
386
//----------------------------------------------- CascadeDetectorTest -----------------------------------
387
class CV_CascadeDetectorTest : public CV_DetectorTest
390
CV_CascadeDetectorTest();
392
virtual void readDetector( const FileNode& fn );
393
virtual void writeDetector( FileStorage& fs, int di );
394
virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );
395
virtual int detectMultiScale_C( const string& filename, int di, const Mat& img, vector<Rect>& objects );
399
CV_CascadeDetectorTest::CV_CascadeDetectorTest()
401
validationFilename = "cascadeandhog/cascade.xml";
402
configFilename = "cascadeandhog/_cascade.xml";
405
void CV_CascadeDetectorTest::readDetector( const FileNode& fn )
409
fn[FILENAME] >> filename;
410
detectorFilenames.push_back(filename);
411
fn[C_SCALE_CASCADE] >> flag;
413
flags.push_back( 0 );
415
flags.push_back( CASCADE_SCALE_IMAGE );
418
void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di )
420
int sc = flags[di] & CASCADE_SCALE_IMAGE ? 0 : 1;
421
fs << FILENAME << detectorFilenames[di];
422
fs << C_SCALE_CASCADE << sc;
426
int CV_CascadeDetectorTest::detectMultiScale_C( const string& filename,
427
int di, const Mat& img,
428
vector<Rect>& objects )
430
Ptr<CvHaarClassifierCascade> c_cascade(cvLoadHaarClassifierCascade(filename.c_str(), cvSize(0,0)));
431
Ptr<CvMemStorage> storage(cvCreateMemStorage());
435
ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
436
return cvtest::TS::FAIL_INVALID_TEST_DATA;
439
cvtColor( img, grayImg, COLOR_BGR2GRAY );
440
equalizeHist( grayImg, grayImg );
442
CvMat c_gray = grayImg;
443
CvSeq* rs = cvHaarDetectObjects(&c_gray, c_cascade, storage, 1.1, 3, flags[di] );
446
for( int i = 0; i < rs->total; i++ )
448
Rect r = *(Rect*)cvGetSeqElem(rs, i);
449
objects.push_back(r);
452
return cvtest::TS::OK;
455
int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img,
456
vector<Rect>& objects)
458
string dataPath = ts->get_data_path(), filename;
459
filename = dataPath + detectorFilenames[di];
460
const string pattern = "haarcascade_frontalface_default.xml";
462
if( filename.size() >= pattern.size() &&
463
strcmp(filename.c_str() + (filename.size() - pattern.size()),
464
pattern.c_str()) == 0 )
465
return detectMultiScale_C(filename, di, img, objects);
467
CascadeClassifier cascade( filename );
468
if( cascade.empty() )
470
ts->printf( cvtest::TS::LOG, "cascade %s can not be opened");
471
return cvtest::TS::FAIL_INVALID_TEST_DATA;
474
cvtColor( img, grayImg, COLOR_BGR2GRAY );
475
equalizeHist( grayImg, grayImg );
476
cascade.detectMultiScale( grayImg, objects, 1.1, 3, flags[di] );
477
return cvtest::TS::OK;
480
//----------------------------------------------- HOGDetectorTest -----------------------------------
481
class CV_HOGDetectorTest : public CV_DetectorTest
484
CV_HOGDetectorTest();
486
virtual void readDetector( const FileNode& fn );
487
virtual void writeDetector( FileStorage& fs, int di );
488
virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects );
491
CV_HOGDetectorTest::CV_HOGDetectorTest()
493
validationFilename = "cascadeandhog/hog.xml";
496
void CV_HOGDetectorTest::readDetector( const FileNode& fn )
499
if( fn[FILENAME].size() != 0 )
500
fn[FILENAME] >> filename;
501
detectorFilenames.push_back( filename);
504
void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di )
506
fs << FILENAME << detectorFilenames[di];
509
int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img,
510
vector<Rect>& objects)
513
if( detectorFilenames[di].empty() )
514
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
517
hog.detectMultiScale(img, objects);
518
return cvtest::TS::OK;
521
//----------------------------------------------- HOGDetectorReadWriteTest -----------------------------------
522
TEST(Objdetect_HOGDetectorReadWrite, regression)
524
// Inspired by bug #2607
526
img = imread(cvtest::TS::ptr()->get_data_path() + "/cascadeandhog/images/karen-and-rob.png");
527
ASSERT_FALSE(img.empty());
530
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
532
string tempfilename = cv::tempfile(".xml");
533
FileStorage fs(tempfilename, FileStorage::WRITE);
534
hog.write(fs, "myHOG");
536
fs.open(tempfilename, FileStorage::READ);
537
remove(tempfilename.c_str());
539
FileNode n = fs["opencv_storage"]["myHOG"];
541
ASSERT_NO_THROW(hog.read(n));
546
TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); }
547
TEST(Objdetect_HOGDetector, regression) { CV_HOGDetectorTest test; test.safe_run(); }
550
//----------------------------------------------- HOG SSE2 compatible test -----------------------------------
552
class HOGDescriptorTester :
553
public cv::HOGDescriptor
555
HOGDescriptor* actual_hog;
560
HOGDescriptorTester(HOGDescriptor& instance) :
561
cv::HOGDescriptor(instance), actual_hog(&instance),
562
ts(cvtest::TS::ptr()), failed(false)
565
virtual void computeGradient(const Mat& img, Mat& grad, Mat& qangle,
566
Size paddingTL, Size paddingBR) const;
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;
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;
577
virtual void compute(InputArray img, vector<float>& descriptors,
578
Size winStride = Size(), Size padding = Size(),
579
const vector<Point>& locations = vector<Point>()) const;
581
bool is_failed() const;
584
struct HOGCacheTester
588
BlockData() : histOfs(0), imgOffset() {}
595
size_t gradOfs, qangleOfs;
597
float histWeights[4];
602
HOGCacheTester(const HOGDescriptorTester* descriptor,
603
const Mat& img, Size paddingTL, Size paddingBR,
604
bool useCache, Size cacheStride);
605
virtual ~HOGCacheTester() { }
606
virtual void init(const HOGDescriptorTester* descriptor,
607
const Mat& img, Size paddingTL, Size paddingBR,
608
bool useCache, Size cacheStride);
610
Size windowsInImage(Size imageSize, Size winStride) const;
611
Rect getWindow(Size imageSize, Size winStride, int idx) const;
613
const float* getBlock(Point pt, float* buf);
614
virtual void normalizeBlockHistogram(float* histogram) const;
616
vector<PixData> pixData;
617
vector<BlockData> blockData;
620
vector<int> ymaxCached;
621
Size winSize, cacheStride;
622
Size nblocks, ncells;
623
int blockHistogramSize;
624
int count1, count2, count4;
626
Mat_<float> blockCache;
627
Mat_<uchar> blockCacheFlags;
630
const HOGDescriptorTester* descriptor;
633
HOGCacheTester::HOGCacheTester()
636
blockHistogramSize = count1 = count2 = count4 = 0;
640
HOGCacheTester::HOGCacheTester(const HOGDescriptorTester* _descriptor,
641
const Mat& _img, Size _paddingTL, Size _paddingBR,
642
bool _useCache, Size _cacheStride)
644
init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride);
647
void HOGCacheTester::init(const HOGDescriptorTester* _descriptor,
648
const Mat& _img, Size _paddingTL, Size _paddingBR,
649
bool _useCache, Size _cacheStride)
651
descriptor = _descriptor;
652
cacheStride = _cacheStride;
653
useCache = _useCache;
655
descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR);
656
imgoffset = _paddingTL;
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;
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;
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++ )
682
Mat_<float> weights(blockSize);
683
float sigma = (float)descriptor->getWinSigma();
684
float scale = 1.f/(sigma*sigma*2);
686
for(i = 0; i < blockSize.height; i++)
687
for(j = 0; j < blockSize.width; j++)
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);
694
blockData.resize(nblocks.width*nblocks.height);
695
pixData.resize(rawBlockSize*3);
697
// Initialize 2 lookup tables, pixData & blockData.
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
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.
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
721
count1 = count2 = count4 = 0;
722
for( j = 0; j < blockSize.width; j++ )
723
for( i = 0; i < blockSize.height; i++ )
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;
734
if( (unsigned)icellX0 < (unsigned)ncells.width &&
735
(unsigned)icellX1 < (unsigned)ncells.width )
737
if( (unsigned)icellY0 < (unsigned)ncells.height &&
738
(unsigned)icellY1 < (unsigned)ncells.height )
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;
752
data = &pixData[rawBlockSize + (count2++)];
753
if( (unsigned)icellY0 < (unsigned)ncells.height )
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;
768
if( (unsigned)icellX0 < (unsigned)ncells.width )
774
if( (unsigned)icellY0 < (unsigned)ncells.height &&
775
(unsigned)icellY1 < (unsigned)ncells.height )
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;
787
data = &pixData[count1++];
788
if( (unsigned)icellY0 < (unsigned)ncells.height )
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;
799
data->gradOfs = (grad.cols*i + j)*2;
800
data->qangleOfs = (qangle.cols*i + j)*2;
801
data->gradWeight = weights(i,j);
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];
813
// initialize blockData
814
for( j = 0; j < nblocks.width; j++ )
815
for( i = 0; i < nblocks.height; i++ )
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);
823
const float* HOGCacheTester::getBlock(Point pt, float* buf)
825
float* blockHist = buf;
826
assert(descriptor != 0);
828
Size blockSize = descriptor->blockSize;
831
CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) &&
832
(unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) );
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] )
842
Mat_<uchar> cacheRow = blockCacheFlags.row(cacheIdx.y);
844
ymaxCached[cacheIdx.y] = pt.y;
847
blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize];
848
uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x);
849
if( computedFlag != 0 )
851
computedFlag = (uchar)1; // set it at once, before actual computing
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;
858
CV_Assert( blockHist != 0 );
859
for( k = 0; k < blockHistogramSize; k++ )
862
const PixData* _pixData = &pixData[0];
864
for( k = 0; k < C1; k++ )
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;
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];
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;
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;
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];
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;
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;
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;
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;
931
normalizeBlockHistogram(blockHist);
936
void HOGCacheTester::normalizeBlockHistogram(float* _hist) const
938
float* hist = &_hist[0], partSum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
939
size_t i, sz = blockHistogramSize;
941
for (i = 0; i <= sz - 4; i += 4)
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];
948
float t0 = partSum[0] + partSum[1];
949
float t1 = partSum[2] + partSum[3];
952
sum += hist[i]*hist[i];
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)
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];
967
t0 = partSum[0] + partSum[1];
968
t1 = partSum[2] + partSum[3];
972
hist[i] = std::min(hist[i]*scale, thresh);
973
sum += hist[i]*hist[i];
976
scale = 1.f/(std::sqrt(sum)+1e-3f);
977
for( i = 0; i < sz; i++ )
981
Size HOGCacheTester::windowsInImage(Size imageSize, Size winStride) const
983
return Size((imageSize.width - winSize.width)/winStride.width + 1,
984
(imageSize.height - winSize.height)/winStride.height + 1);
987
Rect HOGCacheTester::getWindow(Size imageSize, Size winStride, int idx) const
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 );
995
inline bool HOGDescriptorTester::is_failed() const
1000
static inline int gcd(int a, int b) { return (a % b == 0) ? b : gcd (b, a % b); }
1002
void HOGDescriptorTester::detect(const Mat& img,
1003
vector<Point>& hits, vector<double>& weights, double hitThreshold,
1004
Size winStride, Size padding, const vector<Point>& locations) const
1010
if( svmDetector.empty() )
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);
1022
HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride);
1025
nwindows = cache.windowsInImage(paddedImgSize, winStride).area();
1027
const HOGCacheTester::BlockData* blockData = &cache.blockData[0];
1029
int nblocks = cache.nblocks.area();
1030
int blockHistogramSize = cache.blockHistogramSize;
1031
size_t dsize = getDescriptorSize();
1033
double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0;
1034
vector<float> blockHist(blockHistogramSize);
1036
for( size_t i = 0; i < nwindows; i++ )
1039
if( !locations.empty() )
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 )
1048
pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding);
1049
CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0);
1052
const float* svmVec = &svmDetector[0];
1054
for( j = 0; j < nblocks; j++, svmVec += blockHistogramSize )
1056
const HOGCacheTester::BlockData& bj = blockData[j];
1057
Point pt = pt0 + bj.imgOffset;
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];
1066
if( s >= hitThreshold )
1068
hits.push_back(pt0);
1069
weights.push_back(s);
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);
1079
if (!std::equal(hits.begin(), hits.end(),
1080
actual_find_locations.begin()))
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();
1089
const double eps = 0.0;
1090
double diff_norm = cvtest::norm(actual_weights, weights, NORM_L2);
1091
if (diff_norm > eps)
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());
1097
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
1098
ts->set_gtest_status();
1103
void HOGDescriptorTester::detect(const Mat& img, vector<Point>& hits, double hitThreshold,
1104
Size winStride, Size padding, const vector<Point>& locations) const
1106
vector<double> weightsV;
1107
detect(img, hits, weightsV, hitThreshold, winStride, padding, locations);
1110
void HOGDescriptorTester::compute(InputArray _img, vector<float>& descriptors,
1111
Size winStride, Size padding, const vector<Point>& locations) const
1113
Mat img = _img.getMat();
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);
1124
HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride);
1127
nwindows = cache.windowsInImage(paddedImgSize, winStride).area();
1129
const HOGCacheTester::BlockData* blockData = &cache.blockData[0];
1131
int nblocks = cache.nblocks.area();
1132
int blockHistogramSize = cache.blockHistogramSize;
1133
size_t dsize = getDescriptorSize();
1134
descriptors.resize(dsize*nwindows);
1136
for( size_t i = 0; i < nwindows; i++ )
1138
float* descriptor = &descriptors[i*dsize];
1141
if( !locations.empty() )
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 )
1150
pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding);
1151
CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0);
1154
for( int j = 0; j < nblocks; j++ )
1156
const HOGCacheTester::BlockData& bj = blockData[j];
1157
Point pt = pt0 + bj.imgOffset;
1159
float* dst = descriptor + bj.histOfs;
1160
const float* src = cache.getBlock(pt, dst);
1162
for( int k = 0; k < blockHistogramSize; k++ )
1168
std::vector<float> actual_descriptors;
1169
actual_hog->compute(img, actual_descriptors, winStride, padding, locations);
1171
double diff_norm = cvtest::norm(actual_descriptors, descriptors, NORM_L2);
1172
const double eps = 0.0;
1173
if (diff_norm > eps)
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();
1185
void HOGDescriptorTester::computeGradient(const Mat& img, Mat& grad, Mat& qangle,
1186
Size paddingTL, Size paddingBR) const
1188
CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
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
1196
img.locateROI(wholeSize, roiofs);
1199
int cn = img.channels();
1201
Mat_<float> _lut(1, 256);
1202
const float* lut = &_lut(0,0);
1204
if( gammaCorrection )
1205
for( i = 0; i < 256; i++ )
1206
_lut(0,i) = std::sqrt((float)i);
1208
for( i = 0; i < 256; i++ )
1209
_lut(0,i) = (float)i;
1211
AutoBuffer<int> mapbuf(gradsize.width + gradsize.height + 4);
1212
int* xmap = (int*)mapbuf + 1;
1213
int* ymap = xmap + gradsize.width + 2;
1215
const int borderType = (int)BORDER_REFLECT_101;
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;
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);
1234
float angleScale = (float)(_nbins/CV_PI);
1235
for( y = 0; y < gradsize.height; y++ )
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);
1245
for( x = 0; x < width; 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]]);
1254
for( x = 0; x < width; x++ )
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;
1261
dx0 = lut[p2[2]] - lut[p0[2]];
1262
dy0 = lut[nextPtr[x1+2]] - lut[prevPtr[x1+2]];
1263
mag0 = dx0*dx0 + dy0*dy0;
1265
dx = lut[p2[1]] - lut[p0[1]];
1266
dy = lut[nextPtr[x1+1]] - lut[prevPtr[x1+1]];
1267
mag = dx*dx + dy*dy;
1276
dx = lut[p2[0]] - lut[p0[0]];
1277
dy = lut[nextPtr[x1]] - lut[prevPtr[x1]];
1278
mag = dx*dx + dy*dy;
1288
dbuf[x+width] = dy0;
1292
cartToPolar( Dx, Dy, Mag, Angle, false );
1293
for( x = 0; x < width; x++ )
1295
float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f;
1296
int hidx = cvFloor(angle);
1298
gradPtr[x*2] = mag*(1.f - angle);
1299
gradPtr[x*2+1] = mag*angle;
1302
else if( hidx >= _nbins )
1304
assert( (unsigned)hidx < (unsigned)_nbins );
1306
qanglePtr[x*2] = (uchar)hidx;
1308
hidx &= hidx < _nbins ? -1 : 0;
1309
qanglePtr[x*2+1] = (uchar)hidx;
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);
1318
const double eps = 0.0;
1319
for (i = 0; i < 2; ++i)
1321
double diff_norm = cvtest::norm(reference_mats[i], actual_mats[i], NORM_L2);
1322
if (diff_norm > eps)
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();
1335
TEST(Objdetect_HOGDetector_Strict, accuracy)
1337
cvtest::TS* ts = cvtest::TS::ptr();
1338
RNG& rng = ts->get_rng();
1340
HOGDescriptor actual_hog;
1341
actual_hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
1342
HOGDescriptorTester reference_hog(actual_hog);
1344
const unsigned int test_case_count = 5;
1345
for (unsigned int i = 0; i < test_case_count && !reference_hog.is_failed(); ++i)
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);
1355
std::vector<Point> hits;
1356
std::vector<double> weights;
1357
reference_hog.detect(image, hits, weights);
1360
std::vector<float> descriptors;
1361
reference_hog.compute(image, descriptors);
1365
TEST(Objdetect_CascadeDetector, small_img)
1367
String root = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/cascades/";
1370
root + "haarcascade_frontalface_alt.xml",
1371
root + "lbpcascade_frontalface.xml",
1375
vector<Rect> objects;
1376
RNG rng((uint64)-1);
1378
for( int i = 0; !cascades[i].empty(); i++ )
1380
printf("%d. %s\n", i, cascades[i].c_str());
1381
CascadeClassifier cascade(cascades[i]);
1382
for( int j = 0; j < 100; j++ )
1384
int width = rng.uniform(1, 100);
1385
int height = rng.uniform(1, 100);
1386
Mat img(height, width, CV_8U);
1388
cascade.detectMultiScale(img, objects);