~paparazzi-uav/paparazzi/v5.0-manual

« back to all changes in this revision

Viewing changes to sw/ext/opencv_bebop/opencv/modules/objdetect/test/test_cascadeandhog.cpp

  • Committer: Paparazzi buildbot
  • Date: 2016-05-18 15:00:29 UTC
  • Revision ID: felix.ruess+docbot@gmail.com-20160518150029-e8lgzi5kvb4p7un9
Manual import commit 4b8bbb730080dac23cf816b98908dacfabe2a8ec from v5.0 branch.

Show diffs side-by-side

added added

removed removed

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