cascadedetect.cpp 46.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "precomp.hpp"
#include <cstdio>

45
#include "cascadedetect.hpp"
46
#include "opencv2/objdetect/objdetect_c.h"
47

48
#if defined (LOG_CASCADE_STATISTIC)
49 50
struct Logger
{
51
    enum { STADIES_NUM = 20 };
52

53 54 55 56
    int gid;
    cv::Mat mask;
    cv::Size sz0;
    int step;
57

58 59 60 61

    Logger() : gid (0), step(2) {}
    void setImage(const cv::Mat& image)
    {
62
     if (gid == 0)
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
         sz0 = image.size();

      mask.create(image.rows, image.cols * (STADIES_NUM + 1) + STADIES_NUM, CV_8UC1);
      mask = cv::Scalar(0);
      cv::Mat roi = mask(cv::Rect(cv::Point(0,0), image.size()));
      image.copyTo(roi);

      printf("%d) Size = (%d, %d)\n", gid, image.cols, image.rows);

      for(int i = 0; i < STADIES_NUM; ++i)
      {
          int x = image.cols + i * (image.cols + 1);
          cv::line(mask, cv::Point(x, 0), cv::Point(x, mask.rows-1), cv::Scalar(255));
      }

      if (sz0.width/image.cols > 2 && sz0.height/image.rows > 2)
          step = 1;
    }

    void setPoint(const cv::Point& p, int passed_stadies)
    {
        int cols = mask.cols / (STADIES_NUM + 1);

        passed_stadies = -passed_stadies;
        passed_stadies = (passed_stadies == -1) ? STADIES_NUM : passed_stadies;

        unsigned char* ptr = mask.ptr<unsigned char>(p.y) + cols + 1 + p.x;
        for(int i = 0; i < passed_stadies; ++i, ptr += cols + 1)
        {
            *ptr = 255;

            if (step == 2)
            {
                ptr[1] = 255;
                ptr[mask.step] = 255;
                ptr[mask.step + 1] = 255;
            }
        }
    };

    void write()
    {
        char buf[4096];
        sprintf(buf, "%04d.png", gid++);
        cv::imwrite(buf, mask);
    }
109 110

} logger;
111
#endif
112

113 114
namespace cv
{
A
Andrey Kamaev 已提交
115

116
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps, std::vector<int>* weights, std::vector<double>* levelWeights)
117 118 119 120 121 122 123 124 125 126 127 128
{
    if( groupThreshold <= 0 || rectList.empty() )
    {
        if( weights )
        {
            size_t i, sz = rectList.size();
            weights->resize(sz);
            for( i = 0; i < sz; i++ )
                (*weights)[i] = 1;
        }
        return;
    }
A
Andrey Kamaev 已提交
129

130
    std::vector<int> labels;
131
    int nclasses = partition(rectList, labels, SimilarRects(eps));
A
Andrey Kamaev 已提交
132

133 134 135 136
    std::vector<Rect> rrects(nclasses);
    std::vector<int> rweights(nclasses, 0);
    std::vector<int> rejectLevels(nclasses, 0);
    std::vector<double> rejectWeights(nclasses, DBL_MIN);
137 138 139 140 141 142 143 144 145 146
    int i, j, nlabels = (int)labels.size();
    for( i = 0; i < nlabels; i++ )
    {
        int cls = labels[i];
        rrects[cls].x += rectList[i].x;
        rrects[cls].y += rectList[i].y;
        rrects[cls].width += rectList[i].width;
        rrects[cls].height += rectList[i].height;
        rweights[cls]++;
    }
147
    if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
A
Andrey Kamaev 已提交
148 149 150 151
    {
        for( i = 0; i < nlabels; i++ )
        {
            int cls = labels[i];
152 153 154 155 156 157 158
            if( (*weights)[i] > rejectLevels[cls] )
            {
                rejectLevels[cls] = (*weights)[i];
                rejectWeights[cls] = (*levelWeights)[i];
            }
            else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
                rejectWeights[cls] = (*levelWeights)[i];
A
Andrey Kamaev 已提交
159 160 161
        }
    }

162 163 164 165 166 167 168 169 170
    for( i = 0; i < nclasses; i++ )
    {
        Rect r = rrects[i];
        float s = 1.f/rweights[i];
        rrects[i] = Rect(saturate_cast<int>(r.x*s),
             saturate_cast<int>(r.y*s),
             saturate_cast<int>(r.width*s),
             saturate_cast<int>(r.height*s));
    }
A
Andrey Kamaev 已提交
171

172 173 174
    rectList.clear();
    if( weights )
        weights->clear();
A
Andrey Kamaev 已提交
175 176 177
    if( levelWeights )
        levelWeights->clear();

178 179 180
    for( i = 0; i < nclasses; i++ )
    {
        Rect r1 = rrects[i];
181
        int n1 = rweights[i];
A
Andrey Kamaev 已提交
182
        double w1 = rejectWeights[i];
183
        int l1 = rejectLevels[i];
184

185
        // filter out rectangles which don't have enough similar rectangles
186 187 188 189 190 191
        if( n1 <= groupThreshold )
            continue;
        // filter out small face rectangles inside large rectangles
        for( j = 0; j < nclasses; j++ )
        {
            int n2 = rweights[j];
A
Andrey Kamaev 已提交
192

193 194 195
            if( j == i || n2 <= groupThreshold )
                continue;
            Rect r2 = rrects[j];
A
Andrey Kamaev 已提交
196

197 198
            int dx = saturate_cast<int>( r2.width * eps );
            int dy = saturate_cast<int>( r2.height * eps );
A
Andrey Kamaev 已提交
199

200 201 202 203 204 205 206 207
            if( i != j &&
                r1.x >= r2.x - dx &&
                r1.y >= r2.y - dy &&
                r1.x + r1.width <= r2.x + r2.width + dx &&
                r1.y + r1.height <= r2.y + r2.height + dy &&
                (n2 > std::max(3, n1) || n1 < 3) )
                break;
        }
A
Andrey Kamaev 已提交
208

209 210 211 212
        if( j == nclasses )
        {
            rectList.push_back(r1);
            if( weights )
213
                weights->push_back(l1);
A
Andrey Kamaev 已提交
214 215
            if( levelWeights )
                levelWeights->push_back(w1);
216 217 218 219
        }
    }
}

220 221 222
class MeanshiftGrouping
{
public:
223 224
    MeanshiftGrouping(const Point3d& densKer, const std::vector<Point3d>& posV,
        const std::vector<double>& wV, double eps, int maxIter = 20)
225
    {
A
Andrey Kamaev 已提交
226
        densityKernel = densKer;
227 228
        weightsV = wV;
        positionsV = posV;
229
        positionsCount = (int)posV.size();
A
Andrey Kamaev 已提交
230
        meanshiftV.resize(positionsCount);
231
        distanceV.resize(positionsCount);
A
Andrey Kamaev 已提交
232
        iterMax = maxIter;
S
Siegfried Hochdorfer 已提交
233
        modeEps = eps;
A
Andrey Kamaev 已提交
234 235 236 237 238 239 240

        for (unsigned i = 0; i<positionsV.size(); i++)
        {
            meanshiftV[i] = getNewValue(positionsV[i]);
            distanceV[i] = moveToMode(meanshiftV[i]);
            meanshiftV[i] -= positionsV[i];
        }
241
    }
A
Andrey Kamaev 已提交
242

243
    void getModes(std::vector<Point3d>& modesV, std::vector<double>& resWeightsV, const double eps)
244
    {
A
Andrey Kamaev 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
        for (size_t i=0; i <distanceV.size(); i++)
        {
            bool is_found = false;
            for(size_t j=0; j<modesV.size(); j++)
            {
                if ( getDistance(distanceV[i], modesV[j]) < eps)
                {
                    is_found=true;
                    break;
                }
            }
            if (!is_found)
            {
                modesV.push_back(distanceV[i]);
            }
        }

        resWeightsV.resize(modesV.size());

        for (size_t i=0; i<modesV.size(); i++)
        {
            resWeightsV[i] = getResultWeight(modesV[i]);
        }
268 269 270
    }

protected:
271 272
    std::vector<Point3d> positionsV;
    std::vector<double> weightsV;
273

A
Andrey Kamaev 已提交
274 275
    Point3d densityKernel;
    int positionsCount;
276

277 278
    std::vector<Point3d> meanshiftV;
    std::vector<Point3d> distanceV;
A
Andrey Kamaev 已提交
279 280
    int iterMax;
    double modeEps;
281

A
Andrey Kamaev 已提交
282
    Point3d getNewValue(const Point3d& inPt) const
283
    {
A
Andrey Kamaev 已提交
284 285 286 287 288 289 290 291
        Point3d resPoint(.0);
        Point3d ratPoint(.0);
        for (size_t i=0; i<positionsV.size(); i++)
        {
            Point3d aPt= positionsV[i];
            Point3d bPt = inPt;
            Point3d sPt = densityKernel;

292 293
            sPt.x *= std::exp(aPt.z);
            sPt.y *= std::exp(aPt.z);
A
Andrey Kamaev 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

            aPt.x /= sPt.x;
            aPt.y /= sPt.y;
            aPt.z /= sPt.z;

            bPt.x /= sPt.x;
            bPt.y /= sPt.y;
            bPt.z /= sPt.z;

            double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));

            resPoint += w*aPt;

            ratPoint.x += w/sPt.x;
            ratPoint.y += w/sPt.y;
            ratPoint.z += w/sPt.z;
        }
        resPoint.x /= ratPoint.x;
        resPoint.y /= ratPoint.y;
        resPoint.z /= ratPoint.z;
        return resPoint;
315 316
    }

A
Andrey Kamaev 已提交
317
    double getResultWeight(const Point3d& inPt) const
318
    {
A
Andrey Kamaev 已提交
319 320 321 322 323 324
        double sumW=0;
        for (size_t i=0; i<positionsV.size(); i++)
        {
            Point3d aPt = positionsV[i];
            Point3d sPt = densityKernel;

325 326
            sPt.x *= std::exp(aPt.z);
            sPt.y *= std::exp(aPt.z);
A
Andrey Kamaev 已提交
327 328 329 330 331 332 333 334 335 336

            aPt -= inPt;

            aPt.x /= sPt.x;
            aPt.y /= sPt.y;
            aPt.z /= sPt.z;

            sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
        }
        return sumW;
337
    }
A
Andrey Kamaev 已提交
338 339

    Point3d moveToMode(Point3d aPt) const
340
    {
A
Andrey Kamaev 已提交
341 342 343 344 345 346 347 348 349 350 351
        Point3d bPt;
        for (int i = 0; i<iterMax; i++)
        {
            bPt = aPt;
            aPt = getNewValue(bPt);
            if ( getDistance(aPt, bPt) <= modeEps )
            {
                break;
            }
        }
        return aPt;
352 353 354 355
    }

    double getDistance(Point3d p1, Point3d p2) const
    {
A
Andrey Kamaev 已提交
356
        Point3d ns = densityKernel;
357 358
        ns.x *= std::exp(p2.z);
        ns.y *= std::exp(p2.z);
A
Andrey Kamaev 已提交
359 360 361 362 363
        p2 -= p1;
        p2.x /= ns.x;
        p2.y /= ns.y;
        p2.z /= ns.z;
        return p2.dot(p2);
364 365
    }
};
366
//new grouping function with using meanshift
367 368
static void groupRectangles_meanshift(std::vector<Rect>& rectList, double detectThreshold, std::vector<double>* foundWeights,
                                      std::vector<double>& scales, Size winDetSize)
369
{
370
    int detectionCount = (int)rectList.size();
371 372
    std::vector<Point3d> hits(detectionCount), resultHits;
    std::vector<double> hitWeights(detectionCount), resultWeights;
373 374
    Point2d hitCenter;

A
Andrey Kamaev 已提交
375
    for (int i=0; i < detectionCount; i++)
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
    {
        hitWeights[i] = (*foundWeights)[i];
        hitCenter = (rectList[i].tl() + rectList[i].br())*(0.5); //center of rectangles
        hits[i] = Point3d(hitCenter.x, hitCenter.y, std::log(scales[i]));
    }

    rectList.clear();
    if (foundWeights)
        foundWeights->clear();

    double logZ = std::log(1.3);
    Point3d smothing(8, 16, logZ);

    MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);

    msGrouping.getModes(resultHits, resultWeights, 1);

A
Andrey Kamaev 已提交
393
    for (unsigned i=0; i < resultHits.size(); ++i)
394 395
    {

396
        double scale = std::exp(resultHits[i].z);
397 398 399
        hitCenter.x = resultHits[i].x;
        hitCenter.y = resultHits[i].y;
        Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
A
Andrey Kamaev 已提交
400 401
        Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
            int(s.width), int(s.height) );
402

A
Andrey Kamaev 已提交
403
        if (resultWeights[i] > detectThreshold)
404 405 406 407 408 409
        {
            rectList.push_back(resultRect);
            foundWeights->push_back(resultWeights[i]);
        }
    }
}
410

411
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps)
412
{
413
    groupRectangles(rectList, groupThreshold, eps, 0, 0);
414
}
415

416
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& weights, int groupThreshold, double eps)
417
{
418 419
    groupRectangles(rectList, groupThreshold, eps, &weights, 0);
}
420
//used for cascade detection algorithm for ROC-curve calculating
421
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels, std::vector<double>& levelWeights, int groupThreshold, double eps)
422
{
423
    groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
424
}
425
//can be used for HOG detection algorithm only
426 427
void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,
                               std::vector<double>& foundScales, double detectThreshold, Size winDetSize)
428
{
A
Andrey Kamaev 已提交
429
    groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
430 431
}

A
Andrey Kamaev 已提交
432

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
FeatureEvaluator::~FeatureEvaluator() {}
bool FeatureEvaluator::read(const FileNode&) {return true;}
Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }
int FeatureEvaluator::getFeatureType() const {return -1;}
bool FeatureEvaluator::setImage(const Mat&, Size) {return true;}
bool FeatureEvaluator::setWindow(Point) { return true; }
double FeatureEvaluator::calcOrd(int) const { return 0.; }
int FeatureEvaluator::calcCat(int) const { return 0; }

//----------------------------------------------  HaarEvaluator ---------------------------------------

bool HaarEvaluator::Feature :: read( const FileNode& node )
{
    FileNode rnode = node[CC_RECTS];
    FileNodeIterator it = rnode.begin(), it_end = rnode.end();
A
Andrey Kamaev 已提交
448

449 450 451 452 453 454
    int ri;
    for( ri = 0; ri < RECT_NUM; ri++ )
    {
        rect[ri].r = Rect();
        rect[ri].weight = 0.f;
    }
A
Andrey Kamaev 已提交
455

456 457 458 459 460 461
    for(ri = 0; it != it_end; ++it, ri++)
    {
        FileNodeIterator it2 = (*it).begin();
        it2 >> rect[ri].r.x >> rect[ri].r.y >>
            rect[ri].r.width >> rect[ri].r.height >> rect[ri].weight;
    }
A
Andrey Kamaev 已提交
462

463 464 465 466 467 468
    tilted = (int)node[CC_TILTED] != 0;
    return true;
}

HaarEvaluator::HaarEvaluator()
{
R
Roman Donchenko 已提交
469
    features = makePtr<std::vector<Feature> >();
470 471 472 473 474 475 476 477 478 479 480
}
HaarEvaluator::~HaarEvaluator()
{
}

bool HaarEvaluator::read(const FileNode& node)
{
    features->resize(node.size());
    featuresPtr = &(*features)[0];
    FileNodeIterator it = node.begin(), it_end = node.end();
    hasTiltedFeatures = false;
A
Andrey Kamaev 已提交
481

482 483 484 485 486 487 488 489 490
    for(int i = 0; it != it_end; ++it, i++)
    {
        if(!featuresPtr[i].read(*it))
            return false;
        if( featuresPtr[i].tilted )
            hasTiltedFeatures = true;
    }
    return true;
}
A
Andrey Kamaev 已提交
491

492 493
Ptr<FeatureEvaluator> HaarEvaluator::clone() const
{
R
Roman Donchenko 已提交
494
    Ptr<HaarEvaluator> ret = makePtr<HaarEvaluator>();
495 496 497 498 499 500 501 502 503 504
    ret->origWinSize = origWinSize;
    ret->features = features;
    ret->featuresPtr = &(*ret->features)[0];
    ret->hasTiltedFeatures = hasTiltedFeatures;
    ret->sum0 = sum0, ret->sqsum0 = sqsum0, ret->tilted0 = tilted0;
    ret->sum = sum, ret->sqsum = sqsum, ret->tilted = tilted;
    ret->normrect = normrect;
    memcpy( ret->p, p, 4*sizeof(p[0]) );
    memcpy( ret->pq, pq, 4*sizeof(pq[0]) );
    ret->offset = offset;
A
Andrey Kamaev 已提交
505
    ret->varianceNormFactor = varianceNormFactor;
506 507 508 509 510 511 512 513
    return ret;
}

bool HaarEvaluator::setImage( const Mat &image, Size _origWinSize )
{
    int rn = image.rows+1, cn = image.cols+1;
    origWinSize = _origWinSize;
    normrect = Rect(1, 1, origWinSize.width-2, origWinSize.height-2);
A
Andrey Kamaev 已提交
514

515 516
    if (image.cols < origWinSize.width || image.rows < origWinSize.height)
        return false;
A
Andrey Kamaev 已提交
517

518 519 520 521 522 523 524 525
    if( sum0.rows < rn || sum0.cols < cn )
    {
        sum0.create(rn, cn, CV_32S);
        sqsum0.create(rn, cn, CV_64F);
        if (hasTiltedFeatures)
            tilted0.create( rn, cn, CV_32S);
    }
    sum = Mat(rn, cn, CV_32S, sum0.data);
526
    sqsum = Mat(rn, cn, CV_64F, sqsum0.data);
527 528 529 530 531 532 533 534 535 536 537 538

    if( hasTiltedFeatures )
    {
        tilted = Mat(rn, cn, CV_32S, tilted0.data);
        integral(image, sum, sqsum, tilted);
    }
    else
        integral(image, sum, sqsum);
    const int* sdata = (const int*)sum.data;
    const double* sqdata = (const double*)sqsum.data;
    size_t sumStep = sum.step/sizeof(sdata[0]);
    size_t sqsumStep = sqsum.step/sizeof(sqdata[0]);
A
Andrey Kamaev 已提交
539

540 541
    CV_SUM_PTRS( p[0], p[1], p[2], p[3], sdata, normrect, sumStep );
    CV_SUM_PTRS( pq[0], pq[1], pq[2], pq[3], sqdata, normrect, sqsumStep );
A
Andrey Kamaev 已提交
542

543 544 545 546 547 548 549 550 551 552
    size_t fi, nfeatures = features->size();

    for( fi = 0; fi < nfeatures; fi++ )
        featuresPtr[fi].updatePtrs( !featuresPtr[fi].tilted ? sum : tilted );
    return true;
}

bool  HaarEvaluator::setWindow( Point pt )
{
    if( pt.x < 0 || pt.y < 0 ||
553 554
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
555 556 557 558 559 560 561 562 563
        return false;

    size_t pOffset = pt.y * (sum.step/sizeof(int)) + pt.x;
    size_t pqOffset = pt.y * (sqsum.step/sizeof(double)) + pt.x;
    int valsum = CALC_SUM(p, pOffset);
    double valsqsum = CALC_SUM(pq, pqOffset);

    double nf = (double)normrect.area() * valsqsum - (double)valsum * valsum;
    if( nf > 0. )
564
        nf = std::sqrt(nf);
565 566 567 568
    else
        nf = 1.;
    varianceNormFactor = 1./nf;
    offset = (int)pOffset;
569

570 571 572 573 574 575 576 577 578 579 580 581 582 583
    return true;
}

//----------------------------------------------  LBPEvaluator -------------------------------------
bool LBPEvaluator::Feature :: read(const FileNode& node )
{
    FileNode rnode = node[CC_RECT];
    FileNodeIterator it = rnode.begin();
    it >> rect.x >> rect.y >> rect.width >> rect.height;
    return true;
}

LBPEvaluator::LBPEvaluator()
{
R
Roman Donchenko 已提交
584
    features = makePtr<std::vector<Feature> >();
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
}
LBPEvaluator::~LBPEvaluator()
{
}

bool LBPEvaluator::read( const FileNode& node )
{
    features->resize(node.size());
    featuresPtr = &(*features)[0];
    FileNodeIterator it = node.begin(), it_end = node.end();
    for(int i = 0; it != it_end; ++it, i++)
    {
        if(!featuresPtr[i].read(*it))
            return false;
    }
    return true;
}

Ptr<FeatureEvaluator> LBPEvaluator::clone() const
{
R
Roman Donchenko 已提交
605
    Ptr<LBPEvaluator> ret = makePtr<LBPEvaluator>();
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
    ret->origWinSize = origWinSize;
    ret->features = features;
    ret->featuresPtr = &(*ret->features)[0];
    ret->sum0 = sum0, ret->sum = sum;
    ret->normrect = normrect;
    ret->offset = offset;
    return ret;
}

bool LBPEvaluator::setImage( const Mat& image, Size _origWinSize )
{
    int rn = image.rows+1, cn = image.cols+1;
    origWinSize = _origWinSize;

    if( image.cols < origWinSize.width || image.rows < origWinSize.height )
        return false;
A
Andrey Kamaev 已提交
622

623 624 625 626
    if( sum0.rows < rn || sum0.cols < cn )
        sum0.create(rn, cn, CV_32S);
    sum = Mat(rn, cn, CV_32S, sum0.data);
    integral(image, sum);
A
Andrey Kamaev 已提交
627

628
    size_t fi, nfeatures = features->size();
A
Andrey Kamaev 已提交
629

630 631 632 633
    for( fi = 0; fi < nfeatures; fi++ )
        featuresPtr[fi].updatePtrs( sum );
    return true;
}
A
Andrey Kamaev 已提交
634

635 636 637
bool LBPEvaluator::setWindow( Point pt )
{
    if( pt.x < 0 || pt.y < 0 ||
638 639
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
640 641 642
        return false;
    offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
    return true;
A
Andrey Kamaev 已提交
643
}
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663

//----------------------------------------------  HOGEvaluator ---------------------------------------
bool HOGEvaluator::Feature :: read( const FileNode& node )
{
    FileNode rnode = node[CC_RECT];
    FileNodeIterator it = rnode.begin();
    it >> rect[0].x >> rect[0].y >> rect[0].width >> rect[0].height >> featComponent;
    rect[1].x = rect[0].x + rect[0].width;
    rect[1].y = rect[0].y;
    rect[2].x = rect[0].x;
    rect[2].y = rect[0].y + rect[0].height;
    rect[3].x = rect[0].x + rect[0].width;
    rect[3].y = rect[0].y + rect[0].height;
    rect[1].width = rect[2].width = rect[3].width = rect[0].width;
    rect[1].height = rect[2].height = rect[3].height = rect[0].height;
    return true;
}

HOGEvaluator::HOGEvaluator()
{
R
Roman Donchenko 已提交
664
    features = makePtr<std::vector<Feature> >();
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
}

HOGEvaluator::~HOGEvaluator()
{
}

bool HOGEvaluator::read( const FileNode& node )
{
    features->resize(node.size());
    featuresPtr = &(*features)[0];
    FileNodeIterator it = node.begin(), it_end = node.end();
    for(int i = 0; it != it_end; ++it, i++)
    {
        if(!featuresPtr[i].read(*it))
            return false;
    }
    return true;
}

Ptr<FeatureEvaluator> HOGEvaluator::clone() const
{
R
Roman Donchenko 已提交
686
    Ptr<HOGEvaluator> ret = makePtr<HOGEvaluator>();
687 688 689 690 691
    ret->origWinSize = origWinSize;
    ret->features = features;
    ret->featuresPtr = &(*ret->features)[0];
    ret->offset = offset;
    ret->hist = hist;
A
Andrey Kamaev 已提交
692
    ret->normSum = normSum;
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
    return ret;
}

bool HOGEvaluator::setImage( const Mat& image, Size winSize )
{
    int rows = image.rows + 1;
    int cols = image.cols + 1;
    origWinSize = winSize;
    if( image.cols < origWinSize.width || image.rows < origWinSize.height )
        return false;
    hist.clear();
    for( int bin = 0; bin < Feature::BIN_NUM; bin++ )
    {
        hist.push_back( Mat(rows, cols, CV_32FC1) );
    }
    normSum.create( rows, cols, CV_32FC1 );

    integralHistogram( image, hist, normSum, Feature::BIN_NUM );

    size_t featIdx, featCount = features->size();

    for( featIdx = 0; featIdx < featCount; featIdx++ )
    {
        featuresPtr[featIdx].updatePtrs( hist, normSum );
    }
    return true;
719 720
}

721 722 723 724 725 726 727 728 729 730
bool HOGEvaluator::setWindow(Point pt)
{
    if( pt.x < 0 || pt.y < 0 ||
        pt.x + origWinSize.width >= hist[0].cols-2 ||
        pt.y + origWinSize.height >= hist[0].rows-2 )
        return false;
    offset = pt.y * ((int)hist[0].step/sizeof(float)) + pt.x;
    return true;
}

731
void HOGEvaluator::integralHistogram(const Mat &img, std::vector<Mat> &histogram, Mat &norm, int nbins) const
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
{
    CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
    int x, y, binIdx;

    Size gradSize(img.size());
    Size histSize(histogram[0].size());
    Mat grad(gradSize, CV_32F);
    Mat qangle(gradSize, CV_8U);

    AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
    int* xmap = (int*)mapbuf + 1;
    int* ymap = xmap + gradSize.width + 2;

    const int borderType = (int)BORDER_REPLICATE;

    for( x = -1; x < gradSize.width + 1; x++ )
        xmap[x] = borderInterpolate(x, gradSize.width, borderType);
    for( y = -1; y < gradSize.height + 1; y++ )
        ymap[y] = borderInterpolate(y, gradSize.height, borderType);

    int width = gradSize.width;
    AutoBuffer<float> _dbuf(width*4);
    float* dbuf = _dbuf;
    Mat Dx(1, width, CV_32F, dbuf);
    Mat Dy(1, width, CV_32F, dbuf + width);
    Mat Mag(1, width, CV_32F, dbuf + width*2);
    Mat Angle(1, width, CV_32F, dbuf + width*3);

    float angleScale = (float)(nbins/CV_PI);

    for( y = 0; y < gradSize.height; y++ )
    {
        const uchar* currPtr = img.data + img.step*ymap[y];
        const uchar* prevPtr = img.data + img.step*ymap[y-1];
        const uchar* nextPtr = img.data + img.step*ymap[y+1];
        float* gradPtr = (float*)grad.ptr(y);
        uchar* qanglePtr = (uchar*)qangle.ptr(y);

        for( x = 0; x < width; x++ )
        {
            dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);
            dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);
        }
        cartToPolar( Dx, Dy, Mag, Angle, false );
        for( x = 0; x < width; x++ )
        {
            float mag = dbuf[x+width*2];
            float angle = dbuf[x+width*3];
            angle = angle*angleScale - 0.5f;
            int bidx = cvFloor(angle);
            angle -= bidx;
            if( bidx < 0 )
                bidx += nbins;
            else if( bidx >= nbins )
                bidx -= nbins;

            qanglePtr[x] = (uchar)bidx;
            gradPtr[x] = mag;
        }
    }
    integral(grad, norm, grad.depth());

    float* histBuf;
    const float* magBuf;
    const uchar* binsBuf;

    int binsStep = (int)( qangle.step / sizeof(uchar) );
    int histStep = (int)( histogram[0].step / sizeof(float) );
    int magStep = (int)( grad.step / sizeof(float) );
    for( binIdx = 0; binIdx < nbins; binIdx++ )
    {
        histBuf = (float*)histogram[binIdx].data;
        magBuf = (const float*)grad.data;
        binsBuf = (const uchar*)qangle.data;

        memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );
        histBuf += histStep + 1;
        for( y = 0; y < qangle.rows; y++ )
A
Andrey Kamaev 已提交
810
        {
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
            histBuf[-1] = 0.f;
            float strSum = 0.f;
            for( x = 0; x < qangle.cols; x++ )
            {
                if( binsBuf[x] == binIdx )
                    strSum += magBuf[x];
                histBuf[x] = histBuf[-histStep + x] + strSum;
            }
            histBuf += histStep;
            binsBuf += binsStep;
            magBuf += magStep;
        }
    }
}

Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
827 828
{
    return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
A
Andrey Kamaev 已提交
829
        featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
830 831
        featureType == HOG ? Ptr<FeatureEvaluator>(new HOGEvaluator) :
        Ptr<FeatureEvaluator>();
832
}
833

834 835
//---------------------------------------- Classifier Cascade --------------------------------------------

836
CascadeClassifierImpl::CascadeClassifierImpl()
837 838 839
{
}

840
CascadeClassifierImpl::~CascadeClassifierImpl()
A
Andrey Kamaev 已提交
841
{
842
}
843

844
bool CascadeClassifierImpl::empty() const
845
{
R
Roman Donchenko 已提交
846
    return !oldCascade && data.stages.empty();
847 848
}

849
bool CascadeClassifierImpl::load(const String& filename)
850 851
{
    oldCascade.release();
852 853
    data = Data();
    featureEvaluator.release();
A
Andrey Kamaev 已提交
854

855 856 857
    FileStorage fs(filename, FileStorage::READ);
    if( !fs.isOpened() )
        return false;
A
Andrey Kamaev 已提交
858

859
    if( read_(fs.getFirstTopLevelNode()) )
860
        return true;
A
Andrey Kamaev 已提交
861

862
    fs.release();
A
Andrey Kamaev 已提交
863

R
Roman Donchenko 已提交
864
    oldCascade.reset((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
865 866
    return !oldCascade.empty();
}
A
Andrey Kamaev 已提交
867

868 869 870 871 872 873
void CascadeClassifierImpl::read(const FileNode& node)
{
    read_(node);
}

int CascadeClassifierImpl::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
874
{
R
Roman Donchenko 已提交
875
    CV_Assert( !oldCascade );
A
Andrey Kamaev 已提交
876

877 878 879
    assert( data.featureType == FeatureEvaluator::HAAR ||
            data.featureType == FeatureEvaluator::LBP ||
            data.featureType == FeatureEvaluator::HOG );
880

A
Andrey Kamaev 已提交
881
    if( !evaluator->setWindow(pt) )
882 883 884 885
        return -1;
    if( data.isStumpBased )
    {
        if( data.featureType == FeatureEvaluator::HAAR )
A
Andrey Kamaev 已提交
886
            return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
887
        else if( data.featureType == FeatureEvaluator::LBP )
A
Andrey Kamaev 已提交
888
            return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
889
        else if( data.featureType == FeatureEvaluator::HOG )
A
Andrey Kamaev 已提交
890
            return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );
891 892 893 894 895 896
        else
            return -2;
    }
    else
    {
        if( data.featureType == FeatureEvaluator::HAAR )
A
Andrey Kamaev 已提交
897
            return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
898
        else if( data.featureType == FeatureEvaluator::LBP )
A
Andrey Kamaev 已提交
899
            return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
900
        else if( data.featureType == FeatureEvaluator::HOG )
A
Andrey Kamaev 已提交
901
            return predictOrdered<HOGEvaluator>( *this, evaluator, weight );
902 903 904
        else
            return -2;
    }
905
}
A
Andrey Kamaev 已提交
906

907
bool CascadeClassifierImpl::setImage( Ptr<FeatureEvaluator>& evaluator, const Mat& image )
908
{
A
Andrey Kamaev 已提交
909
    return empty() ? false : evaluator->setImage(image, data.origWinSize);
910
}
911

912
void CascadeClassifierImpl::setMaskGenerator(const Ptr<MaskGenerator>& _maskGenerator)
913 914 915
{
    maskGenerator=_maskGenerator;
}
916
Ptr<CascadeClassifierImpl::MaskGenerator> CascadeClassifierImpl::getMaskGenerator()
917 918 919 920
{
    return maskGenerator;
}

921
Ptr<BaseCascadeClassifier::MaskGenerator> createFaceDetectionMaskGenerator()
922 923
{
#ifdef HAVE_TEGRA_OPTIMIZATION
924
    return tegra::getCascadeClassifierMaskGenerator(*this);
925
#else
926
    return Ptr<BaseCascadeClassifier::MaskGenerator>();
927 928 929
#endif
}

930
class CascadeClassifierInvoker : public ParallelLoopBody
931
{
932
public:
933
    CascadeClassifierInvoker( CascadeClassifierImpl& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
934
        std::vector<Rect>& _vec, std::vector<int>& _levels, std::vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
935
    {
936
        classifier = &_cc;
937
        processingRectSize = _sz1;
938 939
        stripSize = _stripSize;
        yStep = _yStep;
940 941
        scalingFactor = _factor;
        rectangles = &_vec;
942 943 944 945
        rejectLevels = outputLevels ? &_levels : 0;
        levelWeights = outputLevels ? &_weights : 0;
        mask = _mask;
        mtx = _mtx;
946
    }
A
Andrey Kamaev 已提交
947

948
    void operator()(const Range& range) const
949
    {
950
        Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
951

952 953
        Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor),
                     cvRound(classifier->data.origWinSize.height * scalingFactor));
954

955
        int y1 = range.start * stripSize;
956
        int y2 = std::min(range.end * stripSize, processingRectSize.height);
957
        for( int y = y1; y < y2; y += yStep )
958
        {
959
            for( int x = 0; x < processingRectSize.width; x += yStep )
960
            {
961
                if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
962 963 964
                    continue;
                }

965 966
                double gypWeight;
                int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
967

968 969 970 971
#if defined (LOG_CASCADE_STATISTIC)

                logger.setPoint(Point(x, y), result);
#endif
972 973 974
                if( rejectLevels )
                {
                    if( result == 1 )
975
                        result =  -(int)classifier->data.stages.size();
976
                    if( classifier->data.stages.size() + result == 0 )
977
                    {
978
                        mtx->lock();
A
Andrey Kamaev 已提交
979
                        rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
980
                        rejectLevels->push_back(-result);
981
                        levelWeights->push_back(gypWeight);
982
                        mtx->unlock();
983
                    }
A
Andrey Kamaev 已提交
984
                }
985
                else if( result > 0 )
986 987
                {
                    mtx->lock();
988
                    rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
989
                                               winSize.width, winSize.height));
990 991
                    mtx->unlock();
                }
992
                if( result == 0 )
993 994
                    x += yStep;
            }
995
        }
996
    }
A
Andrey Kamaev 已提交
997

998
    CascadeClassifierImpl* classifier;
999
    std::vector<Rect>* rectangles;
1000
    Size processingRectSize;
1001
    int stripSize, yStep;
1002
    double scalingFactor;
1003 1004
    std::vector<int> *rejectLevels;
    std::vector<double> *levelWeights;
1005
    Mat mask;
1006
    Mutex* mtx;
1007
};
A
Andrey Kamaev 已提交
1008

1009
struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };
1010
struct getNeighbors { int operator ()(const CvAvgComp& e) const { return e.neighbors; } };
1011

1012

1013
bool CascadeClassifierImpl::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize,
1014 1015
                                           int stripSize, int yStep, double factor, std::vector<Rect>& candidates,
                                           std::vector<int>& levels, std::vector<double>& weights, bool outputRejectLevels )
1016 1017 1018 1019
{
    if( !featureEvaluator->setImage( image, data.origWinSize ) )
        return false;

1020
#if defined (LOG_CASCADE_STATISTIC)
1021
    logger.setImage(image);
1022
#endif
1023

1024
    Mat currentMask;
R
Roman Donchenko 已提交
1025
    if (maskGenerator) {
1026 1027 1028
        currentMask=maskGenerator->generateMask(image);
    }

1029 1030 1031
    std::vector<Rect> candidatesVector;
    std::vector<int> rejectLevels;
    std::vector<double> levelWeights;
1032
    Mutex mtx;
1033 1034
    if( outputRejectLevels )
    {
1035 1036
        parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
            candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
1037
        levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
1038
        weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
1039 1040 1041
    }
    else
    {
1042 1043
         parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
            candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
1044
    }
1045
    candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
1046

1047 1048 1049 1050 1051
#if defined (LOG_CASCADE_STATISTIC)
    logger.write();
#endif

    return true;
1052 1053
}

1054
bool CascadeClassifierImpl::isOldFormatCascade() const
1055 1056 1057 1058
{
    return !oldCascade.empty();
}

1059

1060
int CascadeClassifierImpl::getFeatureType() const
1061 1062 1063 1064
{
    return featureEvaluator->getFeatureType();
}

1065
Size CascadeClassifierImpl::getOriginalWindowSize() const
1066 1067 1068 1069
{
    return data.origWinSize;
}

1070
bool CascadeClassifierImpl::setImage(InputArray _image)
1071
{
1072
    Mat image = _image.getMat();
K
Kirill Kornyakov 已提交
1073
    return featureEvaluator->setImage(image, data.origWinSize);
1074 1075
}

1076 1077 1078 1079 1080
void* CascadeClassifierImpl::getOldCascade()
{
    return oldCascade;
}

1081 1082 1083 1084 1085 1086 1087 1088
static void detectMultiScaleOldFormat( const Mat& image, Ptr<CvHaarClassifierCascade> oldCascade,
                                       std::vector<Rect>& objects,
                                       std::vector<int>& rejectLevels,
                                       std::vector<double>& levelWeights,
                                       std::vector<CvAvgComp>& vecAvgComp,
                                       double scaleFactor, int minNeighbors,
                                       int flags, Size minObjectSize, Size maxObjectSize,
                                       bool outputRejectLevels = false )
1089
{
1090 1091 1092 1093 1094 1095 1096 1097
    MemStorage storage(cvCreateMemStorage(0));
    CvMat _image = image;
    CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
                                                 minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
    Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
    objects.resize(vecAvgComp.size());
    std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
}
1098

1099
void CascadeClassifierImpl::detectMultiScaleNoGrouping( const Mat& image, std::vector<Rect>& candidates,
1100 1101 1102 1103 1104
                                                    std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
                                                    double scaleFactor, Size minObjectSize, Size maxObjectSize,
                                                    bool outputRejectLevels )
{
    candidates.clear();
1105

R
Roman Donchenko 已提交
1106
    if (maskGenerator)
1107 1108
        maskGenerator->initializeMask(image);

1109 1110
    if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
        maxObjectSize = image.size();
A
Andrey Kamaev 已提交
1111

1112 1113
    Mat grayImage = image;
    if( grayImage.channels() > 1 )
1114 1115
    {
        Mat temp;
1116
        cvtColor(grayImage, temp, COLOR_BGR2GRAY);
1117
        grayImage = temp;
1118
    }
A
Andrey Kamaev 已提交
1119

1120
    Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
1121 1122 1123

    for( double factor = 1; ; factor *= scaleFactor )
    {
1124
        Size originalWindowSize = getOriginalWindowSize();
1125

1126
        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
1127
        Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
1128
        Size processingRectSize( scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height );
A
Andrey Kamaev 已提交
1129

1130
        if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
1131
            break;
1132
        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
1133
            break;
1134
        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
1135
            continue;
A
Andrey Kamaev 已提交
1136

1137
        Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
1138
        resize( grayImage, scaledImage, scaledImageSize, 0, 0, INTER_LINEAR );
1139

1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
        int yStep;
        if( getFeatureType() == cv::FeatureEvaluator::HOG )
        {
            yStep = 4;
        }
        else
        {
            yStep = factor > 2. ? 1 : 2;
        }

1150
        int stripCount, stripSize;
1151

1152
        const int PTS_PER_THREAD = 1000;
1153
        stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
1154
        stripCount = std::min(std::max(stripCount, 1), 100);
1155
        stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
1156

A
Andrey Kamaev 已提交
1157
        if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
1158
            rejectLevels, levelWeights, outputRejectLevels ) )
1159 1160
            break;
    }
1161
}
1162

1163
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
1164 1165 1166 1167 1168 1169
                                          std::vector<int>& rejectLevels,
                                          std::vector<double>& levelWeights,
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minObjectSize, Size maxObjectSize,
                                          bool outputRejectLevels )
{
1170
    Mat image = _image.getMat();
1171
    CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
A
Andrey Kamaev 已提交
1172

1173 1174
    if( empty() )
        return;
1175

1176
    if( isOldFormatCascade() )
1177
    {
1178 1179 1180
        std::vector<CvAvgComp> fakeVecAvgComp;
        detectMultiScaleOldFormat( image, oldCascade, objects, rejectLevels, levelWeights, fakeVecAvgComp, scaleFactor,
                                   minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
1181 1182 1183
    }
    else
    {
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
        detectMultiScaleNoGrouping( image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize,
                                    outputRejectLevels );
        const double GROUP_EPS = 0.2;
        if( outputRejectLevels )
        {
            groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
        }
        else
        {
            groupRectangles( objects, minNeighbors, GROUP_EPS );
        }
1195
    }
A
Alexey Kazakov 已提交
1196 1197
}

1198
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
A
Alexey Kazakov 已提交
1199 1200 1201
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minObjectSize, Size maxObjectSize)
{
1202
    Mat image = _image.getMat();
1203 1204
    std::vector<int> fakeLevels;
    std::vector<double> fakeWeights;
A
Andrey Kamaev 已提交
1205
    detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
1206 1207 1208
        minNeighbors, flags, minObjectSize, maxObjectSize );
}

1209
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
1210 1211 1212 1213
                                          std::vector<int>& numDetections, double scaleFactor,
                                          int minNeighbors, int flags, Size minObjectSize,
                                          Size maxObjectSize )
{
1214
    Mat image = _image.getMat();
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
    CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );

    if( empty() )
        return;

    std::vector<int> fakeLevels;
    std::vector<double> fakeWeights;
    if( isOldFormatCascade() )
    {
        std::vector<CvAvgComp> vecAvgComp;
        detectMultiScaleOldFormat( image, oldCascade, objects, fakeLevels, fakeWeights, vecAvgComp, scaleFactor,
                                   minNeighbors, flags, minObjectSize, maxObjectSize );
        numDetections.resize(vecAvgComp.size());
        std::transform(vecAvgComp.begin(), vecAvgComp.end(), numDetections.begin(), getNeighbors());
    }
    else
    {
        detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize );
        const double GROUP_EPS = 0.2;
        groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS );
    }
A
Andrey Kamaev 已提交
1236
}
1237

1238
bool CascadeClassifierImpl::Data::read(const FileNode &root)
1239
{
1240
    static const float THRESHOLD_EPS = 1e-5f;
A
Andrey Kamaev 已提交
1241

1242
    // load stage params
1243
    String stageTypeStr = (String)root[CC_STAGE_TYPE];
1244 1245 1246 1247
    if( stageTypeStr == CC_BOOST )
        stageType = BOOST;
    else
        return false;
1248

1249
    String featureTypeStr = (String)root[CC_FEATURE_TYPE];
1250 1251 1252 1253
    if( featureTypeStr == CC_HAAR )
        featureType = FeatureEvaluator::HAAR;
    else if( featureTypeStr == CC_LBP )
        featureType = FeatureEvaluator::LBP;
1254 1255 1256
    else if( featureTypeStr == CC_HOG )
        featureType = FeatureEvaluator::HOG;

1257 1258
    else
        return false;
1259

1260 1261 1262
    origWinSize.width = (int)root[CC_WIDTH];
    origWinSize.height = (int)root[CC_HEIGHT];
    CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
1263

1264
    isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;
1265 1266 1267 1268 1269

    // load feature params
    FileNode fn = root[CC_FEATURE_PARAMS];
    if( fn.empty() )
        return false;
1270

1271 1272 1273
    ncategories = fn[CC_MAX_CAT_COUNT];
    int subsetSize = (ncategories + 31)/32,
        nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
1274

1275 1276 1277 1278
    // load stages
    fn = root[CC_STAGES];
    if( fn.empty() )
        return false;
1279

1280 1281 1282
    stages.reserve(fn.size());
    classifiers.clear();
    nodes.clear();
1283

1284
    FileNodeIterator it = fn.begin(), it_end = fn.end();
1285

1286 1287 1288 1289
    for( int si = 0; it != it_end; si++, ++it )
    {
        FileNode fns = *it;
        Stage stage;
1290
        stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
1291 1292 1293 1294 1295 1296 1297
        fns = fns[CC_WEAK_CLASSIFIERS];
        if(fns.empty())
            return false;
        stage.ntrees = (int)fns.size();
        stage.first = (int)classifiers.size();
        stages.push_back(stage);
        classifiers.reserve(stages[si].first + stages[si].ntrees);
1298

1299 1300 1301 1302 1303 1304 1305 1306
        FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
        for( ; it1 != it1_end; ++it1 ) // weak trees
        {
            FileNode fnw = *it1;
            FileNode internalNodes = fnw[CC_INTERNAL_NODES];
            FileNode leafValues = fnw[CC_LEAF_VALUES];
            if( internalNodes.empty() || leafValues.empty() )
                return false;
1307

1308 1309 1310
            DTree tree;
            tree.nodeCount = (int)internalNodes.size()/nodeStep;
            classifiers.push_back(tree);
1311

1312 1313 1314 1315
            nodes.reserve(nodes.size() + tree.nodeCount);
            leaves.reserve(leaves.size() + leafValues.size());
            if( subsetSize > 0 )
                subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
1316 1317 1318 1319

            FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();

            for( ; internalNodesIter != internalNodesEnd; ) // nodes
1320 1321
            {
                DTreeNode node;
1322 1323 1324
                node.left = (int)*internalNodesIter; ++internalNodesIter;
                node.right = (int)*internalNodesIter; ++internalNodesIter;
                node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
1325 1326
                if( subsetSize > 0 )
                {
1327 1328
                    for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
                        subsets.push_back((int)*internalNodesIter);
1329 1330 1331 1332
                    node.threshold = 0.f;
                }
                else
                {
1333
                    node.threshold = (float)*internalNodesIter; ++internalNodesIter;
1334 1335 1336
                }
                nodes.push_back(node);
            }
1337 1338 1339 1340 1341

            internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();

            for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
                leaves.push_back((float)*internalNodesIter);
1342 1343 1344
        }
    }

1345 1346 1347
    return true;
}

1348
bool CascadeClassifierImpl::read_(const FileNode& root)
1349 1350 1351 1352
{
    if( !data.read(root) )
        return false;

1353
    // load features
1354 1355
    featureEvaluator = FeatureEvaluator::create(data.featureType);
    FileNode fn = root[CC_FEATURES];
1356 1357
    if( fn.empty() )
        return false;
A
Andrey Kamaev 已提交
1358

1359
    return featureEvaluator->read(fn);
1360
}
A
Andrey Kamaev 已提交
1361

R
Roman Donchenko 已提交
1362
template<> void DefaultDeleter<CvHaarClassifierCascade>::operator ()(CvHaarClassifierCascade* obj) const
A
Andrey Kamaev 已提交
1363
{ cvReleaseHaarClassifierCascade(&obj); }
1364

1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392

BaseCascadeClassifier::~BaseCascadeClassifier()
{
}

CascadeClassifier::CascadeClassifier() {}
CascadeClassifier::CascadeClassifier(const String& filename)
{
    load(filename);
}

CascadeClassifier::~CascadeClassifier()
{
}

bool CascadeClassifier::empty() const
{
    return cc.empty() || cc->empty();
}

bool CascadeClassifier::load( const String& filename )
{
    cc = makePtr<CascadeClassifierImpl>();
    if(!cc->load(filename))
        cc.release();
    return !empty();
}

1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
bool CascadeClassifier::read(const FileNode &root)
{
    Ptr<CascadeClassifierImpl> ccimpl;
    bool ok = ccimpl->read_(root);
    if( ok )
        cc = ccimpl.staticCast<BaseCascadeClassifier>();
    else
        cc.release();
    return ok;
}

1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
void CascadeClassifier::detectMultiScale( InputArray image,
                      CV_OUT std::vector<Rect>& objects,
                      double scaleFactor,
                      int minNeighbors, int flags,
                      Size minSize,
                      Size maxSize )
{
    CV_Assert(!empty());
    cc->detectMultiScale(image, objects, scaleFactor, minNeighbors, flags, minSize, maxSize);
}

void CascadeClassifier::detectMultiScale( InputArray image,
                      CV_OUT std::vector<Rect>& objects,
                      CV_OUT std::vector<int>& numDetections,
                      double scaleFactor,
                      int minNeighbors, int flags,
                      Size minSize, Size maxSize )
{
    CV_Assert(!empty());
    cc->detectMultiScale(image, objects, numDetections,
                         scaleFactor, minNeighbors, flags, minSize, maxSize);
}

void CascadeClassifier::detectMultiScale( InputArray image,
                      CV_OUT std::vector<Rect>& objects,
                      CV_OUT std::vector<int>& rejectLevels,
                      CV_OUT std::vector<double>& levelWeights,
                      double scaleFactor,
                      int minNeighbors, int flags,
                      Size minSize, Size maxSize,
                      bool outputRejectLevels )
{
    CV_Assert(!empty());
    cc->detectMultiScale(image, objects, rejectLevels, levelWeights,
                         scaleFactor, minNeighbors, flags,
                         minSize, maxSize, outputRejectLevels);
}

bool CascadeClassifier::isOldFormatCascade() const
{
    CV_Assert(!empty());
    return cc->isOldFormatCascade();
}

Size CascadeClassifier::getOriginalWindowSize() const
{
    CV_Assert(!empty());
    return cc->getOriginalWindowSize();
}

int CascadeClassifier::getFeatureType() const
{
    CV_Assert(!empty());
    return cc->getFeatureType();
}

void* CascadeClassifier::getOldCascade()
{
    CV_Assert(!empty());
    return cc->getOldCascade();
}

1466
void CascadeClassifier::setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator)
1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
{
    CV_Assert(!empty());
    cc->setMaskGenerator(maskGenerator);
}

Ptr<BaseCascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
{
    CV_Assert(!empty());
    return cc->getMaskGenerator();
}

1478
} // namespace cv