cascadedetect.cpp 41.1 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 46
#include "cascadedetect.hpp"

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

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

57 58 59 60

    Logger() : gid (0), step(2) {}
    void setImage(const cv::Mat& image)
    {
61
     if (gid == 0)
62 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
         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);
    }
108 109

} logger;
110
#endif
111

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

115 116 117 118
// class for grouping object candidates, detected by Cascade Classifier, HOG etc.
// instance of the class is to be passed to cv::partition (see cxoperations.hpp)
class CV_EXPORTS SimilarRects
{
A
Andrey Kamaev 已提交
119
public:
120 121 122 123 124 125 126 127 128 129
    SimilarRects(double _eps) : eps(_eps) {}
    inline bool operator()(const Rect& r1, const Rect& r2) const
    {
        double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;
        return std::abs(r1.x - r2.x) <= delta &&
        std::abs(r1.y - r2.y) <= delta &&
        std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
        std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
    }
    double eps;
A
Andrey Kamaev 已提交
130 131
};

132

133
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps, std::vector<int>* weights, std::vector<double>* levelWeights)
134 135 136 137 138 139 140 141 142 143 144 145
{
    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 已提交
146

147
    std::vector<int> labels;
148
    int nclasses = partition(rectList, labels, SimilarRects(eps));
A
Andrey Kamaev 已提交
149

150 151 152 153
    std::vector<Rect> rrects(nclasses);
    std::vector<int> rweights(nclasses, 0);
    std::vector<int> rejectLevels(nclasses, 0);
    std::vector<double> rejectWeights(nclasses, DBL_MIN);
154 155 156 157 158 159 160 161 162 163
    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]++;
    }
164
    if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
A
Andrey Kamaev 已提交
165 166 167 168
    {
        for( i = 0; i < nlabels; i++ )
        {
            int cls = labels[i];
169 170 171 172 173 174 175
            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 已提交
176 177 178
        }
    }

179 180 181 182 183 184 185 186 187
    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 已提交
188

189 190 191
    rectList.clear();
    if( weights )
        weights->clear();
A
Andrey Kamaev 已提交
192 193 194
    if( levelWeights )
        levelWeights->clear();

195 196 197
    for( i = 0; i < nclasses; i++ )
    {
        Rect r1 = rrects[i];
198
        int n1 = levelWeights ? rejectLevels[i] : rweights[i];
A
Andrey Kamaev 已提交
199
        double w1 = rejectWeights[i];
200 201 202 203 204 205
        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 已提交
206

207 208 209
            if( j == i || n2 <= groupThreshold )
                continue;
            Rect r2 = rrects[j];
A
Andrey Kamaev 已提交
210

211 212
            int dx = saturate_cast<int>( r2.width * eps );
            int dy = saturate_cast<int>( r2.height * eps );
A
Andrey Kamaev 已提交
213

214 215 216 217 218 219 220 221
            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 已提交
222

223 224 225 226 227
        if( j == nclasses )
        {
            rectList.push_back(r1);
            if( weights )
                weights->push_back(n1);
A
Andrey Kamaev 已提交
228 229
            if( levelWeights )
                levelWeights->push_back(w1);
230 231 232 233
        }
    }
}

234 235 236
class MeanshiftGrouping
{
public:
237 238
    MeanshiftGrouping(const Point3d& densKer, const std::vector<Point3d>& posV,
        const std::vector<double>& wV, double eps, int maxIter = 20)
239
    {
A
Andrey Kamaev 已提交
240
        densityKernel = densKer;
241 242
        weightsV = wV;
        positionsV = posV;
243
        positionsCount = (int)posV.size();
A
Andrey Kamaev 已提交
244
        meanshiftV.resize(positionsCount);
245
        distanceV.resize(positionsCount);
A
Andrey Kamaev 已提交
246
        iterMax = maxIter;
S
Siegfried Hochdorfer 已提交
247
        modeEps = eps;
A
Andrey Kamaev 已提交
248 249 250 251 252 253 254

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

257
    void getModes(std::vector<Point3d>& modesV, std::vector<double>& resWeightsV, const double eps)
258
    {
A
Andrey Kamaev 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
        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]);
        }
282 283 284
    }

protected:
285 286
    std::vector<Point3d> positionsV;
    std::vector<double> weightsV;
287

A
Andrey Kamaev 已提交
288 289
    Point3d densityKernel;
    int positionsCount;
290

291 292
    std::vector<Point3d> meanshiftV;
    std::vector<Point3d> distanceV;
A
Andrey Kamaev 已提交
293 294
    int iterMax;
    double modeEps;
295

A
Andrey Kamaev 已提交
296
    Point3d getNewValue(const Point3d& inPt) const
297
    {
A
Andrey Kamaev 已提交
298 299 300 301 302 303 304 305
        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;

306 307
            sPt.x *= std::exp(aPt.z);
            sPt.y *= std::exp(aPt.z);
A
Andrey Kamaev 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

            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;
329 330
    }

A
Andrey Kamaev 已提交
331
    double getResultWeight(const Point3d& inPt) const
332
    {
A
Andrey Kamaev 已提交
333 334 335 336 337 338
        double sumW=0;
        for (size_t i=0; i<positionsV.size(); i++)
        {
            Point3d aPt = positionsV[i];
            Point3d sPt = densityKernel;

339 340
            sPt.x *= std::exp(aPt.z);
            sPt.y *= std::exp(aPt.z);
A
Andrey Kamaev 已提交
341 342 343 344 345 346 347 348 349 350

            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;
351
    }
A
Andrey Kamaev 已提交
352 353

    Point3d moveToMode(Point3d aPt) const
354
    {
A
Andrey Kamaev 已提交
355 356 357 358 359 360 361 362 363 364 365
        Point3d bPt;
        for (int i = 0; i<iterMax; i++)
        {
            bPt = aPt;
            aPt = getNewValue(bPt);
            if ( getDistance(aPt, bPt) <= modeEps )
            {
                break;
            }
        }
        return aPt;
366 367 368 369
    }

    double getDistance(Point3d p1, Point3d p2) const
    {
A
Andrey Kamaev 已提交
370
        Point3d ns = densityKernel;
371 372
        ns.x *= std::exp(p2.z);
        ns.y *= std::exp(p2.z);
A
Andrey Kamaev 已提交
373 374 375 376 377
        p2 -= p1;
        p2.x /= ns.x;
        p2.y /= ns.y;
        p2.z /= ns.z;
        return p2.dot(p2);
378 379
    }
};
380
//new grouping function with using meanshift
381 382
static void groupRectangles_meanshift(std::vector<Rect>& rectList, double detectThreshold, std::vector<double>* foundWeights,
                                      std::vector<double>& scales, Size winDetSize)
383
{
384
    int detectionCount = (int)rectList.size();
385 386
    std::vector<Point3d> hits(detectionCount), resultHits;
    std::vector<double> hitWeights(detectionCount), resultWeights;
387 388
    Point2d hitCenter;

A
Andrey Kamaev 已提交
389
    for (int i=0; i < detectionCount; i++)
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
    {
        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 已提交
407
    for (unsigned i=0; i < resultHits.size(); ++i)
408 409
    {

410
        double scale = std::exp(resultHits[i].z);
411 412 413
        hitCenter.x = resultHits[i].x;
        hitCenter.y = resultHits[i].y;
        Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
A
Andrey Kamaev 已提交
414 415
        Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
            int(s.width), int(s.height) );
416

A
Andrey Kamaev 已提交
417
        if (resultWeights[i] > detectThreshold)
418 419 420 421 422 423
        {
            rectList.push_back(resultRect);
            foundWeights->push_back(resultWeights[i]);
        }
    }
}
424

425
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps)
426
{
427
    groupRectangles(rectList, groupThreshold, eps, 0, 0);
428
}
429

430
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& weights, int groupThreshold, double eps)
431
{
432 433
    groupRectangles(rectList, groupThreshold, eps, &weights, 0);
}
434
//used for cascade detection algorithm for ROC-curve calculating
435
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels, std::vector<double>& levelWeights, int groupThreshold, double eps)
436
{
437
    groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
438
}
439
//can be used for HOG detection algorithm only
440 441
void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,
                               std::vector<double>& foundScales, double detectThreshold, Size winDetSize)
442
{
A
Andrey Kamaev 已提交
443
    groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
444 445
}

A
Andrey Kamaev 已提交
446

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462

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 已提交
463

464 465 466 467 468 469
    int ri;
    for( ri = 0; ri < RECT_NUM; ri++ )
    {
        rect[ri].r = Rect();
        rect[ri].weight = 0.f;
    }
A
Andrey Kamaev 已提交
470

471 472 473 474 475 476
    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 已提交
477

478 479 480 481 482 483
    tilted = (int)node[CC_TILTED] != 0;
    return true;
}

HaarEvaluator::HaarEvaluator()
{
484
    features = new std::vector<Feature>();
485 486 487 488 489 490 491 492 493 494 495
}
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 已提交
496

497 498 499 500 501 502 503 504 505
    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 已提交
506

507 508 509 510 511 512 513 514 515 516 517 518 519
Ptr<FeatureEvaluator> HaarEvaluator::clone() const
{
    HaarEvaluator* ret = new HaarEvaluator;
    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 已提交
520
    ret->varianceNormFactor = varianceNormFactor;
521 522 523 524 525 526 527 528
    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 已提交
529

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

533 534 535 536 537 538 539 540
    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);
541
    sqsum = Mat(rn, cn, CV_64F, sqsum0.data);
542 543 544 545 546 547 548 549 550 551 552 553

    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 已提交
554

555 556
    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 已提交
557

558 559 560 561 562 563 564 565 566 567
    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 ||
568 569
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
570 571 572 573 574 575 576 577 578
        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. )
579
        nf = std::sqrt(nf);
580 581 582 583
    else
        nf = 1.;
    varianceNormFactor = 1./nf;
    offset = (int)pOffset;
584

585 586 587 588 589 590 591 592 593 594 595 596 597 598
    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()
{
599
    features = new std::vector<Feature>();
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
}
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
{
    LBPEvaluator* ret = new LBPEvaluator;
    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 已提交
637

638 639 640 641
    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 已提交
642

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

645 646 647 648
    for( fi = 0; fi < nfeatures; fi++ )
        featuresPtr[fi].updatePtrs( sum );
    return true;
}
A
Andrey Kamaev 已提交
649

650 651 652
bool LBPEvaluator::setWindow( Point pt )
{
    if( pt.x < 0 || pt.y < 0 ||
653 654
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
655 656 657
        return false;
    offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
    return true;
A
Andrey Kamaev 已提交
658
}
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

//----------------------------------------------  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()
{
679
    features = new std::vector<Feature>();
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
}

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
{
    HOGEvaluator* ret = new HOGEvaluator;
    ret->origWinSize = origWinSize;
    ret->features = features;
    ret->featuresPtr = &(*ret->features)[0];
    ret->offset = offset;
    ret->hist = hist;
A
Andrey Kamaev 已提交
707
    ret->normSum = normSum;
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
    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;
734 735
}

736 737 738 739 740 741 742 743 744 745
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;
}

746
void HOGEvaluator::integralHistogram(const Mat &img, std::vector<Mat> &histogram, Mat &norm, int nbins) const
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 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
{
    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 已提交
825
        {
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
            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 )
842 843
{
    return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
A
Andrey Kamaev 已提交
844
        featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
845 846
        featureType == HOG ? Ptr<FeatureEvaluator>(new HOGEvaluator) :
        Ptr<FeatureEvaluator>();
847
}
848

849 850 851 852 853 854
//---------------------------------------- Classifier Cascade --------------------------------------------

CascadeClassifier::CascadeClassifier()
{
}

855
CascadeClassifier::CascadeClassifier(const String& filename)
A
Andrey Kamaev 已提交
856 857
{
    load(filename);
858
}
859 860 861

CascadeClassifier::~CascadeClassifier()
{
A
Andrey Kamaev 已提交
862
}
863 864 865

bool CascadeClassifier::empty() const
{
866
    return oldCascade.empty() && data.stages.empty();
867 868
}

869
bool CascadeClassifier::load(const String& filename)
870 871
{
    oldCascade.release();
872 873
    data = Data();
    featureEvaluator.release();
A
Andrey Kamaev 已提交
874

875 876 877
    FileStorage fs(filename, FileStorage::READ);
    if( !fs.isOpened() )
        return false;
A
Andrey Kamaev 已提交
878

879 880
    if( read(fs.getFirstTopLevelNode()) )
        return true;
A
Andrey Kamaev 已提交
881

882
    fs.release();
A
Andrey Kamaev 已提交
883

884 885 886
    oldCascade = Ptr<CvHaarClassifierCascade>((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
    return !oldCascade.empty();
}
A
Andrey Kamaev 已提交
887 888

int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
889 890
{
    CV_Assert( oldCascade.empty() );
A
Andrey Kamaev 已提交
891

892 893 894
    assert( data.featureType == FeatureEvaluator::HAAR ||
            data.featureType == FeatureEvaluator::LBP ||
            data.featureType == FeatureEvaluator::HOG );
895

A
Andrey Kamaev 已提交
896
    if( !evaluator->setWindow(pt) )
897 898 899 900
        return -1;
    if( data.isStumpBased )
    {
        if( data.featureType == FeatureEvaluator::HAAR )
A
Andrey Kamaev 已提交
901
            return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
902
        else if( data.featureType == FeatureEvaluator::LBP )
A
Andrey Kamaev 已提交
903
            return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
904
        else if( data.featureType == FeatureEvaluator::HOG )
A
Andrey Kamaev 已提交
905
            return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );
906 907 908 909 910 911
        else
            return -2;
    }
    else
    {
        if( data.featureType == FeatureEvaluator::HAAR )
A
Andrey Kamaev 已提交
912
            return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
913
        else if( data.featureType == FeatureEvaluator::LBP )
A
Andrey Kamaev 已提交
914
            return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
915
        else if( data.featureType == FeatureEvaluator::HOG )
A
Andrey Kamaev 已提交
916
            return predictOrdered<HOGEvaluator>( *this, evaluator, weight );
917 918 919
        else
            return -2;
    }
920
}
A
Andrey Kamaev 已提交
921 922

bool CascadeClassifier::setImage( Ptr<FeatureEvaluator>& evaluator, const Mat& image )
923
{
A
Andrey Kamaev 已提交
924
    return empty() ? false : evaluator->setImage(image, data.origWinSize);
925
}
926

927 928 929 930 931 932 933 934 935
void CascadeClassifier::setMaskGenerator(Ptr<MaskGenerator> _maskGenerator)
{
    maskGenerator=_maskGenerator;
}
Ptr<CascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
{
    return maskGenerator;
}

936
void CascadeClassifier::setFaceDetectionMaskGenerator()
937 938
{
#ifdef HAVE_TEGRA_OPTIMIZATION
939
    setMaskGenerator(tegra::getCascadeClassifierMaskGenerator(*this));
940
#else
941
    setMaskGenerator(Ptr<CascadeClassifier::MaskGenerator>());
942 943 944
#endif
}

945
class CascadeClassifierInvoker : public ParallelLoopBody
946
{
947
public:
A
Andrey Kamaev 已提交
948
    CascadeClassifierInvoker( CascadeClassifier& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
949
        std::vector<Rect>& _vec, std::vector<int>& _levels, std::vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
950
    {
951
        classifier = &_cc;
952
        processingRectSize = _sz1;
953 954
        stripSize = _stripSize;
        yStep = _yStep;
955 956
        scalingFactor = _factor;
        rectangles = &_vec;
957 958 959 960
        rejectLevels = outputLevels ? &_levels : 0;
        levelWeights = outputLevels ? &_weights : 0;
        mask = _mask;
        mtx = _mtx;
961
    }
A
Andrey Kamaev 已提交
962

963
    void operator()(const Range& range) const
964
    {
965
        Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
966

967
        Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));
968

969
        int y1 = range.start * stripSize;
970
        int y2 = std::min(range.end * stripSize, processingRectSize.height);
971
        for( int y = y1; y < y2; y += yStep )
972
        {
973
            for( int x = 0; x < processingRectSize.width; x += yStep )
974
            {
975
                if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
976 977 978
                    continue;
                }

979 980
                double gypWeight;
                int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
981

982 983 984 985
#if defined (LOG_CASCADE_STATISTIC)

                logger.setPoint(Point(x, y), result);
#endif
986 987 988
                if( rejectLevels )
                {
                    if( result == 1 )
989
                        result =  -(int)classifier->data.stages.size();
990
                    if( classifier->data.stages.size() + result < 4 )
991
                    {
992
                        mtx->lock();
A
Andrey Kamaev 已提交
993
                        rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
994
                        rejectLevels->push_back(-result);
995
                        levelWeights->push_back(gypWeight);
996
                        mtx->unlock();
997
                    }
A
Andrey Kamaev 已提交
998
                }
999
                else if( result > 0 )
1000 1001
                {
                    mtx->lock();
1002
                    rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
1003
                                               winSize.width, winSize.height));
1004 1005
                    mtx->unlock();
                }
1006
                if( result == 0 )
1007 1008
                    x += yStep;
            }
1009
        }
1010
    }
A
Andrey Kamaev 已提交
1011

1012
    CascadeClassifier* classifier;
1013
    std::vector<Rect>* rectangles;
1014
    Size processingRectSize;
1015
    int stripSize, yStep;
1016
    double scalingFactor;
1017 1018
    std::vector<int> *rejectLevels;
    std::vector<double> *levelWeights;
1019
    Mat mask;
1020
    Mutex* mtx;
1021
};
A
Andrey Kamaev 已提交
1022

1023 1024
struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };

1025

1026
bool CascadeClassifier::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize,
1027 1028
                                           int stripSize, int yStep, double factor, std::vector<Rect>& candidates,
                                           std::vector<int>& levels, std::vector<double>& weights, bool outputRejectLevels )
1029 1030 1031 1032
{
    if( !featureEvaluator->setImage( image, data.origWinSize ) )
        return false;

1033
#if defined (LOG_CASCADE_STATISTIC)
1034
    logger.setImage(image);
1035
#endif
1036

1037 1038 1039 1040 1041
    Mat currentMask;
    if (!maskGenerator.empty()) {
        currentMask=maskGenerator->generateMask(image);
    }

1042 1043 1044
    std::vector<Rect> candidatesVector;
    std::vector<int> rejectLevels;
    std::vector<double> levelWeights;
1045
    Mutex mtx;
1046 1047
    if( outputRejectLevels )
    {
1048 1049
        parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
            candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
1050
        levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
1051
        weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
1052 1053 1054
    }
    else
    {
1055 1056
         parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
            candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
1057
    }
1058
    candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
1059

1060 1061 1062 1063 1064
#if defined (LOG_CASCADE_STATISTIC)
    logger.write();
#endif

    return true;
1065 1066 1067 1068 1069 1070 1071
}

bool CascadeClassifier::isOldFormatCascade() const
{
    return !oldCascade.empty();
}

1072

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
int CascadeClassifier::getFeatureType() const
{
    return featureEvaluator->getFeatureType();
}

Size CascadeClassifier::getOriginalWindowSize() const
{
    return data.origWinSize;
}

bool CascadeClassifier::setImage(const Mat& image)
{
K
Kirill Kornyakov 已提交
1085
    return featureEvaluator->setImage(image, data.origWinSize);
1086 1087
}

1088 1089 1090
void CascadeClassifier::detectMultiScale( const Mat& image, std::vector<Rect>& objects,
                                          std::vector<int>& rejectLevels,
                                          std::vector<double>& levelWeights,
1091
                                          double scaleFactor, int minNeighbors,
A
Andrey Kamaev 已提交
1092
                                          int flags, Size minObjectSize, Size maxObjectSize,
A
Alexey Kazakov 已提交
1093
                                          bool outputRejectLevels )
1094 1095
{
    const double GROUP_EPS = 0.2;
A
Andrey Kamaev 已提交
1096

1097
    CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
A
Andrey Kamaev 已提交
1098

1099 1100 1101
    if( empty() )
        return;

1102
    if( isOldFormatCascade() )
1103 1104 1105
    {
        MemStorage storage(cvCreateMemStorage(0));
        CvMat _image = image;
1106 1107
        CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
                                              minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
1108
        std::vector<CvAvgComp> vecAvgComp;
1109 1110 1111 1112 1113
        Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
        objects.resize(vecAvgComp.size());
        std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
        return;
    }
1114

1115
    objects.clear();
1116

1117 1118 1119 1120 1121
    if (!maskGenerator.empty()) {
        maskGenerator->initializeMask(image);
    }


1122 1123
    if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
        maxObjectSize = image.size();
A
Andrey Kamaev 已提交
1124

1125 1126
    Mat grayImage = image;
    if( grayImage.channels() > 1 )
1127 1128
    {
        Mat temp;
1129 1130
        cvtColor(grayImage, temp, CV_BGR2GRAY);
        grayImage = temp;
1131
    }
A
Andrey Kamaev 已提交
1132

1133
    Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
1134
    std::vector<Rect> candidates;
1135 1136 1137

    for( double factor = 1; ; factor *= scaleFactor )
    {
1138
        Size originalWindowSize = getOriginalWindowSize();
1139

1140
        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
1141
        Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
1142
        Size processingRectSize( scaledImageSize.width - originalWindowSize.width + 1, scaledImageSize.height - originalWindowSize.height + 1 );
A
Andrey Kamaev 已提交
1143

1144
        if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
1145
            break;
1146
        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
1147
            break;
1148
        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
1149
            continue;
A
Andrey Kamaev 已提交
1150

1151 1152 1153
        Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
        resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );

1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
        int yStep;
        if( getFeatureType() == cv::FeatureEvaluator::HOG )
        {
            yStep = 4;
        }
        else
        {
            yStep = factor > 2. ? 1 : 2;
        }

1164
        int stripCount, stripSize;
1165

1166
    #ifdef HAVE_TBB
1167
        const int PTS_PER_THREAD = 1000;
1168
        stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
1169
        stripCount = std::min(std::max(stripCount, 1), 100);
1170
        stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
1171 1172
    #else
        stripCount = 1;
1173
        stripSize = processingRectSize.height;
1174 1175
    #endif

A
Andrey Kamaev 已提交
1176
        if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
1177
            rejectLevels, levelWeights, outputRejectLevels ) )
1178 1179
            break;
    }
1180

A
Andrey Kamaev 已提交
1181

1182 1183 1184
    objects.resize(candidates.size());
    std::copy(candidates.begin(), candidates.end(), objects.begin());

1185 1186 1187 1188 1189 1190 1191 1192
    if( outputRejectLevels )
    {
        groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
    }
    else
    {
        groupRectangles( objects, minNeighbors, GROUP_EPS );
    }
A
Alexey Kazakov 已提交
1193 1194
}

1195
void CascadeClassifier::detectMultiScale( const Mat& image, std::vector<Rect>& objects,
A
Alexey Kazakov 已提交
1196 1197 1198
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minObjectSize, Size maxObjectSize)
{
1199 1200
    std::vector<int> fakeLevels;
    std::vector<double> fakeWeights;
A
Andrey Kamaev 已提交
1201
    detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
A
Alexey Kazakov 已提交
1202
        minNeighbors, flags, minObjectSize, maxObjectSize, false );
A
Andrey Kamaev 已提交
1203
}
1204

1205
bool CascadeClassifier::Data::read(const FileNode &root)
1206
{
1207
    static const float THRESHOLD_EPS = 1e-5f;
A
Andrey Kamaev 已提交
1208

1209
    // load stage params
1210
    String stageTypeStr = (String)root[CC_STAGE_TYPE];
1211 1212 1213 1214
    if( stageTypeStr == CC_BOOST )
        stageType = BOOST;
    else
        return false;
1215

1216
    String featureTypeStr = (String)root[CC_FEATURE_TYPE];
1217 1218 1219 1220
    if( featureTypeStr == CC_HAAR )
        featureType = FeatureEvaluator::HAAR;
    else if( featureTypeStr == CC_LBP )
        featureType = FeatureEvaluator::LBP;
1221 1222 1223
    else if( featureTypeStr == CC_HOG )
        featureType = FeatureEvaluator::HOG;

1224 1225
    else
        return false;
1226

1227 1228 1229
    origWinSize.width = (int)root[CC_WIDTH];
    origWinSize.height = (int)root[CC_HEIGHT];
    CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
1230

1231
    isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;
1232 1233 1234 1235 1236

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

1238 1239 1240
    ncategories = fn[CC_MAX_CAT_COUNT];
    int subsetSize = (ncategories + 31)/32,
        nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
1241

1242 1243 1244 1245
    // load stages
    fn = root[CC_STAGES];
    if( fn.empty() )
        return false;
1246

1247 1248 1249
    stages.reserve(fn.size());
    classifiers.clear();
    nodes.clear();
1250

1251
    FileNodeIterator it = fn.begin(), it_end = fn.end();
1252

1253 1254 1255 1256
    for( int si = 0; it != it_end; si++, ++it )
    {
        FileNode fns = *it;
        Stage stage;
1257
        stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
1258 1259 1260 1261 1262 1263 1264
        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);
1265

1266 1267 1268 1269 1270 1271 1272 1273
        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;
1274

1275 1276 1277
            DTree tree;
            tree.nodeCount = (int)internalNodes.size()/nodeStep;
            classifiers.push_back(tree);
1278

1279 1280 1281 1282
            nodes.reserve(nodes.size() + tree.nodeCount);
            leaves.reserve(leaves.size() + leafValues.size());
            if( subsetSize > 0 )
                subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
1283 1284 1285 1286

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

            for( ; internalNodesIter != internalNodesEnd; ) // nodes
1287 1288
            {
                DTreeNode node;
1289 1290 1291
                node.left = (int)*internalNodesIter; ++internalNodesIter;
                node.right = (int)*internalNodesIter; ++internalNodesIter;
                node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
1292 1293
                if( subsetSize > 0 )
                {
1294 1295
                    for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
                        subsets.push_back((int)*internalNodesIter);
1296 1297 1298 1299
                    node.threshold = 0.f;
                }
                else
                {
1300
                    node.threshold = (float)*internalNodesIter; ++internalNodesIter;
1301 1302 1303
                }
                nodes.push_back(node);
            }
1304 1305 1306 1307 1308

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

            for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
                leaves.push_back((float)*internalNodesIter);
1309 1310 1311
        }
    }

1312 1313 1314 1315 1316 1317 1318 1319
    return true;
}

bool CascadeClassifier::read(const FileNode& root)
{
    if( !data.read(root) )
        return false;

1320
    // load features
1321 1322
    featureEvaluator = FeatureEvaluator::create(data.featureType);
    FileNode fn = root[CC_FEATURES];
1323 1324
    if( fn.empty() )
        return false;
A
Andrey Kamaev 已提交
1325

1326
    return featureEvaluator->read(fn);
1327
}
A
Andrey Kamaev 已提交
1328

1329
template<> void Ptr<CvHaarClassifierCascade>::delete_obj()
A
Andrey Kamaev 已提交
1330
{ cvReleaseHaarClassifierCascade(&obj); }
1331 1332

} // namespace cv