cascadedetect.cpp 40.1 KB
Newer Older
wester committed
1 2 3 4 5 6 7 8 9
/*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.
//
//
wester committed
10
//                        Intel License Agreement
wester committed
11 12
//                For Open Source Computer Vision Library
//
wester committed
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
wester committed
14 15 16 17 18 19 20 21 22 23 24 25
// 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.
//
wester committed
26
//   * The name of Intel Corporation may not be used to endorse or promote products
wester committed
27 28 29 30 31
//     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.
wester committed
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
wester committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46
// 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>

#include "cascadedetect.hpp"

wester committed
47 48 49 50
#include <string>

#if defined (LOG_CASCADE_STATISTIC)
struct Logger
wester committed
51
{
wester committed
52 53 54 55 56 57 58 59 60 61 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
    enum { STADIES_NUM = 20 };

    int gid;
    cv::Mat mask;
    cv::Size sz0;
    int step;


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

wester committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
        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);
    }

} logger;
#endif

namespace cv
wester committed
115 116
{

wester committed
117
void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights)
wester committed
118 119 120 121 122 123 124 125 126 127 128 129 130
{
    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;
    }

wester committed
131
    vector<int> labels;
wester committed
132 133
    int nclasses = partition(rectList, labels, SimilarRects(eps));

wester committed
134 135 136 137
    vector<Rect> rrects(nclasses);
    vector<int> rweights(nclasses, 0);
    vector<int> rejectLevels(nclasses, 0);
    vector<double> rejectWeights(nclasses, DBL_MIN);
wester committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    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]++;
    }
    if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
    {
        for( i = 0; i < nlabels; i++ )
        {
            int cls = labels[i];
            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];
        }
    }

    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));
    }

    rectList.clear();
    if( weights )
        weights->clear();
    if( levelWeights )
        levelWeights->clear();

    for( i = 0; i < nclasses; i++ )
    {
        Rect r1 = rrects[i];
wester committed
182
        int n1 = levelWeights ? rejectLevels[i] : rweights[i];
wester committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        double w1 = rejectWeights[i];
        if( n1 <= groupThreshold )
            continue;
        // filter out small face rectangles inside large rectangles
        for( j = 0; j < nclasses; j++ )
        {
            int n2 = rweights[j];

            if( j == i || n2 <= groupThreshold )
                continue;
            Rect r2 = rrects[j];

            int dx = saturate_cast<int>( r2.width * eps );
            int dy = saturate_cast<int>( r2.height * eps );

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

        if( j == nclasses )
        {
            rectList.push_back(r1);
            if( weights )
wester committed
211
                weights->push_back(n1);
wester committed
212 213 214 215 216 217 218 219 220
            if( levelWeights )
                levelWeights->push_back(w1);
        }
    }
}

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

        for (unsigned i = 0; i<positionsV.size(); i++)
        {
            meanshiftV[i] = getNewValue(positionsV[i]);
            distanceV[i] = moveToMode(meanshiftV[i]);
            meanshiftV[i] -= positionsV[i];
        }
    }

wester committed
241
    void getModes(vector<Point3d>& modesV, vector<double>& resWeightsV, const double eps)
wester committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    {
        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]);
        }
    }

protected:
wester committed
269 270
    vector<Point3d> positionsV;
    vector<double> weightsV;
wester committed
271 272 273 274

    Point3d densityKernel;
    int positionsCount;

wester committed
275 276
    vector<Point3d> meanshiftV;
    vector<Point3d> distanceV;
wester committed
277 278 279 280 281 282 283 284 285 286 287 288 289
    int iterMax;
    double modeEps;

    Point3d getNewValue(const Point3d& inPt) const
    {
        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;

wester committed
290 291
            sPt.x *= exp(aPt.z);
            sPt.y *= exp(aPt.z);
wester committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

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

    double getResultWeight(const Point3d& inPt) const
    {
        double sumW=0;
        for (size_t i=0; i<positionsV.size(); i++)
        {
            Point3d aPt = positionsV[i];
            Point3d sPt = densityKernel;

wester committed
323 324
            sPt.x *= exp(aPt.z);
            sPt.y *= exp(aPt.z);
wester committed
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354

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

    Point3d moveToMode(Point3d aPt) const
    {
        Point3d bPt;
        for (int i = 0; i<iterMax; i++)
        {
            bPt = aPt;
            aPt = getNewValue(bPt);
            if ( getDistance(aPt, bPt) <= modeEps )
            {
                break;
            }
        }
        return aPt;
    }

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

    for (int i=0; i < detectionCount; i++)
    {
a  
Kai Westerkamp committed
375
        hitWeights[i] = (*foundWeights)[i];
wester committed
376 377 378 379 380
        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();
a  
Kai Westerkamp committed
381 382
    if (foundWeights)
        foundWeights->clear();
wester committed
383 384 385 386 387 388 389 390 391 392 393

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

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

    msGrouping.getModes(resultHits, resultWeights, 1);

    for (unsigned i=0; i < resultHits.size(); ++i)
    {

wester committed
394
        double scale = exp(resultHits[i].z);
wester committed
395 396 397 398 399 400 401 402 403
        hitCenter.x = resultHits[i].x;
        hitCenter.y = resultHits[i].y;
        Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
        Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
            int(s.width), int(s.height) );

        if (resultWeights[i] > detectThreshold)
        {
            rectList.push_back(resultRect);
a  
Kai Westerkamp committed
404
            foundWeights->push_back(resultWeights[i]);
wester committed
405 406 407 408
        }
    }
}

wester committed
409
void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps)
wester committed
410 411 412 413
{
    groupRectangles(rectList, groupThreshold, eps, 0, 0);
}

wester committed
414
void groupRectangles(vector<Rect>& rectList, vector<int>& weights, int groupThreshold, double eps)
wester committed
415 416 417 418
{
    groupRectangles(rectList, groupThreshold, eps, &weights, 0);
}
//used for cascade detection algorithm for ROC-curve calculating
wester committed
419
void groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels, vector<double>& levelWeights, int groupThreshold, double eps)
wester committed
420 421 422 423
{
    groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
}
//can be used for HOG detection algorithm only
wester committed
424 425
void groupRectangles_meanshift(vector<Rect>& rectList, vector<double>& foundWeights,
                               vector<double>& foundScales, double detectThreshold, Size winDetSize)
wester committed
426
{
a  
Kai Westerkamp committed
427
    groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
wester committed
428 429 430 431
}



wester committed
432 433
FeatureEvaluator::~FeatureEvaluator() {}
bool FeatureEvaluator::read(const FileNode&) {return true;}
wester committed
434 435
Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }
int FeatureEvaluator::getFeatureType() const {return -1;}
wester committed
436 437 438
bool FeatureEvaluator::setImage(const Mat&, Size) {return true;}
bool FeatureEvaluator::setWindow(Point) { return true; }
double FeatureEvaluator::calcOrd(int) const { return 0.; }
wester committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
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();

    int ri;
    for( ri = 0; ri < RECT_NUM; ri++ )
    {
        rect[ri].r = Rect();
        rect[ri].weight = 0.f;
    }

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

    tilted = (int)node[CC_TILTED] != 0;
    return true;
}

HaarEvaluator::HaarEvaluator()
{
wester committed
468
    features = new vector<Feature>();
wester committed
469 470 471 472 473
}
HaarEvaluator::~HaarEvaluator()
{
}

wester committed
474
bool HaarEvaluator::read(const FileNode& node)
wester committed
475
{
wester committed
476 477 478
    features->resize(node.size());
    featuresPtr = &(*features)[0];
    FileNodeIterator it = node.begin(), it_end = node.end();
wester committed
479 480
    hasTiltedFeatures = false;

wester committed
481
    for(int i = 0; it != it_end; ++it, i++)
wester committed
482
    {
wester committed
483
        if(!featuresPtr[i].read(*it))
wester committed
484
            return false;
wester committed
485
        if( featuresPtr[i].tilted )
wester committed
486 487 488 489 490 491 492
            hasTiltedFeatures = true;
    }
    return true;
}

Ptr<FeatureEvaluator> HaarEvaluator::clone() const
{
wester committed
493 494 495 496 497 498 499 500 501 502 503 504
    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;
    ret->varianceNormFactor = varianceNormFactor;
wester committed
505 506 507
    return ret;
}

wester committed
508
bool HaarEvaluator::setImage( const Mat &image, Size _origWinSize )
wester committed
509
{
wester committed
510 511 512
    int rn = image.rows+1, cn = image.cols+1;
    origWinSize = _origWinSize;
    normrect = Rect(1, 1, origWinSize.width-2, origWinSize.height-2);
wester committed
513

wester committed
514 515
    if (image.cols < origWinSize.width || image.rows < origWinSize.height)
        return false;
wester committed
516

wester committed
517
    if( sum0.rows < rn || sum0.cols < cn )
wester committed
518
    {
wester committed
519 520
        sum0.create(rn, cn, CV_32S);
        sqsum0.create(rn, cn, CV_64F);
wester committed
521
        if (hasTiltedFeatures)
wester committed
522
            tilted0.create( rn, cn, CV_32S);
wester committed
523
    }
wester committed
524 525
    sum = Mat(rn, cn, CV_32S, sum0.data);
    sqsum = Mat(rn, cn, CV_64F, sqsum0.data);
wester committed
526

wester committed
527 528 529 530 531 532 533 534 535 536 537
    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]);
wester committed
538

wester committed
539 540
    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 );
wester committed
541 542 543 544

    size_t fi, nfeatures = features->size();

    for( fi = 0; fi < nfeatures; fi++ )
wester committed
545 546
        featuresPtr[fi].updatePtrs( !featuresPtr[fi].tilted ? sum : tilted );
    return true;
wester committed
547 548
}

wester committed
549
bool  HaarEvaluator::setWindow( Point pt )
wester committed
550 551
{
    if( pt.x < 0 || pt.y < 0 ||
wester committed
552 553
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
wester committed
554 555
        return false;

wester committed
556 557 558 559
    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);
wester committed
560

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

    return true;
wester committed
570 571
}

wester committed
572 573 574 575 576 577 578 579
//----------------------------------------------  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;
}
wester committed
580

wester committed
581 582 583 584 585
LBPEvaluator::LBPEvaluator()
{
    features = new vector<Feature>();
}
LBPEvaluator::~LBPEvaluator()
wester committed
586
{
wester committed
587
}
wester committed
588

wester committed
589 590 591 592 593 594
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++)
wester committed
595
    {
wester committed
596 597
        if(!featuresPtr[i].read(*it))
            return false;
wester committed
598
    }
wester committed
599
    return true;
wester committed
600 601
}

wester committed
602
Ptr<FeatureEvaluator> LBPEvaluator::clone() const
wester committed
603
{
wester committed
604 605 606 607 608 609 610 611
    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;
wester committed
612 613
}

wester committed
614
bool LBPEvaluator::setImage( const Mat& image, Size _origWinSize )
wester committed
615
{
wester committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
    int rn = image.rows+1, cn = image.cols+1;
    origWinSize = _origWinSize;

    if( image.cols < origWinSize.width || image.rows < origWinSize.height )
        return false;

    if( sum0.rows < rn || sum0.cols < cn )
        sum0.create(rn, cn, CV_32S);
    sum = Mat(rn, cn, CV_32S, sum0.data);
    integral(image, sum);

    size_t fi, nfeatures = features->size();

    for( fi = 0; fi < nfeatures; fi++ )
        featuresPtr[fi].updatePtrs( sum );
    return true;
wester committed
632 633
}

wester committed
634 635 636 637 638 639 640 641 642 643 644 645
bool LBPEvaluator::setWindow( Point pt )
{
    if( pt.x < 0 || pt.y < 0 ||
        pt.x + origWinSize.width >= sum.cols ||
        pt.y + origWinSize.height >= sum.rows )
        return false;
    offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
    return true;
}

//----------------------------------------------  HOGEvaluator ---------------------------------------
bool HOGEvaluator::Feature :: read( const FileNode& node )
wester committed
646 647 648
{
    FileNode rnode = node[CC_RECT];
    FileNodeIterator it = rnode.begin();
wester committed
649 650 651 652 653 654 655 656 657
    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;
wester committed
658 659 660
    return true;
}

wester committed
661
HOGEvaluator::HOGEvaluator()
wester committed
662
{
wester committed
663
    features = new vector<Feature>();
wester committed
664 665
}

wester committed
666
HOGEvaluator::~HOGEvaluator()
wester committed
667 668 669
{
}

wester committed
670
bool HOGEvaluator::read( const FileNode& node )
wester committed
671 672
{
    features->resize(node.size());
wester committed
673
    featuresPtr = &(*features)[0];
wester committed
674 675 676
    FileNodeIterator it = node.begin(), it_end = node.end();
    for(int i = 0; it != it_end; ++it, i++)
    {
wester committed
677
        if(!featuresPtr[i].read(*it))
wester committed
678 679 680 681 682
            return false;
    }
    return true;
}

wester committed
683
Ptr<FeatureEvaluator> HOGEvaluator::clone() const
wester committed
684
{
wester committed
685 686 687 688 689 690 691
    HOGEvaluator* ret = new HOGEvaluator;
    ret->origWinSize = origWinSize;
    ret->features = features;
    ret->featuresPtr = &(*ret->features)[0];
    ret->offset = offset;
    ret->hist = hist;
    ret->normSum = normSum;
wester committed
692 693 694
    return ret;
}

wester committed
695
bool HOGEvaluator::setImage( const Mat& image, Size winSize )
wester committed
696
{
wester committed
697 698 699 700 701 702 703
    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++ )
wester committed
704
    {
wester committed
705
        hist.push_back( Mat(rows, cols, CV_32FC1) );
wester committed
706
    }
wester committed
707 708 709 710 711 712 713
    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++ )
wester committed
714
    {
wester committed
715
        featuresPtr[featIdx].updatePtrs( hist, normSum );
wester committed
716
    }
wester committed
717
    return true;
wester committed
718 719
}

wester committed
720
bool HOGEvaluator::setWindow(Point pt)
wester committed
721
{
wester committed
722 723 724 725 726 727
    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;
wester committed
728 729
}

wester committed
730
void HOGEvaluator::integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const
wester committed
731
{
wester committed
732 733
    CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
    int x, y, binIdx;
wester committed
734

wester committed
735 736 737 738
    Size gradSize(img.size());
    Size histSize(histogram[0].size());
    Mat grad(gradSize, CV_32F);
    Mat qangle(gradSize, CV_8U);
wester committed
739

wester committed
740 741 742
    AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
    int* xmap = (int*)mapbuf + 1;
    int* ymap = xmap + gradSize.width + 2;
wester committed
743

wester committed
744
    const int borderType = (int)BORDER_REPLICATE;
wester committed
745

wester committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759
    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);
wester committed
760

wester committed
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
    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++ )
        {
            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;
        }
    }
}
wester committed
824 825 826 827 828

Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
{
    return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
        featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
wester committed
829
        featureType == HOG ? Ptr<FeatureEvaluator>(new HOGEvaluator) :
wester committed
830 831 832 833 834
        Ptr<FeatureEvaluator>();
}

//---------------------------------------- Classifier Cascade --------------------------------------------

wester committed
835 836 837 838 839
CascadeClassifier::CascadeClassifier()
{
}

CascadeClassifier::CascadeClassifier(const string& filename)
wester committed
840
{
wester committed
841
    load(filename);
wester committed
842 843
}

wester committed
844
CascadeClassifier::~CascadeClassifier()
wester committed
845 846 847
{
}

wester committed
848
bool CascadeClassifier::empty() const
wester committed
849
{
wester committed
850
    return oldCascade.empty() && data.stages.empty();
wester committed
851 852
}

wester committed
853
bool CascadeClassifier::load(const string& filename)
wester committed
854 855 856 857 858 859 860 861 862
{
    oldCascade.release();
    data = Data();
    featureEvaluator.release();

    FileStorage fs(filename, FileStorage::READ);
    if( !fs.isOpened() )
        return false;

wester committed
863
    if( read(fs.getFirstTopLevelNode()) )
wester committed
864 865 866 867
        return true;

    fs.release();

wester committed
868
    oldCascade = Ptr<CvHaarClassifierCascade>((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
wester committed
869 870 871
    return !oldCascade.empty();
}

wester committed
872
int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
wester committed
873
{
wester committed
874
    CV_Assert( oldCascade.empty() );
wester committed
875

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

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

wester committed
906 907 908 909 910 911
bool CascadeClassifier::setImage( Ptr<FeatureEvaluator>& evaluator, const Mat& image )
{
    return empty() ? false : evaluator->setImage(image, data.origWinSize);
}

void CascadeClassifier::setMaskGenerator(Ptr<MaskGenerator> _maskGenerator)
wester committed
912 913 914
{
    maskGenerator=_maskGenerator;
}
wester committed
915
Ptr<CascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
wester committed
916 917 918 919
{
    return maskGenerator;
}

wester committed
920
void CascadeClassifier::setFaceDetectionMaskGenerator()
wester committed
921 922
{
#ifdef HAVE_TEGRA_OPTIMIZATION
wester committed
923 924 925
    setMaskGenerator(tegra::getCascadeClassifierMaskGenerator(*this));
#else
    setMaskGenerator(Ptr<CascadeClassifier::MaskGenerator>());
wester committed
926 927 928 929 930 931
#endif
}

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

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

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

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

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

#if defined (LOG_CASCADE_STATISTIC)

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

wester committed
996 997 998 999 1000 1001 1002
    CascadeClassifier* classifier;
    vector<Rect>* rectangles;
    Size processingRectSize;
    int stripSize, yStep;
    double scalingFactor;
    vector<int> *rejectLevels;
    vector<double> *levelWeights;
wester committed
1003 1004 1005 1006 1007 1008
    Mat mask;
    Mutex* mtx;
};

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

wester committed
1009 1010 1011 1012

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

wester committed
1017 1018 1019
#if defined (LOG_CASCADE_STATISTIC)
    logger.setImage(image);
#endif
wester committed
1020

wester committed
1021 1022 1023
    Mat currentMask;
    if (!maskGenerator.empty()) {
        currentMask=maskGenerator->generateMask(image);
wester committed
1024 1025
    }

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

wester committed
1044 1045 1046
#if defined (LOG_CASCADE_STATISTIC)
    logger.write();
#endif
wester committed
1047

wester committed
1048
    return true;
wester committed
1049 1050
}

wester committed
1051
bool CascadeClassifier::isOldFormatCascade() const
wester committed
1052 1053 1054 1055
{
    return !oldCascade.empty();
}

wester committed
1056 1057

int CascadeClassifier::getFeatureType() const
wester committed
1058 1059 1060 1061
{
    return featureEvaluator->getFeatureType();
}

wester committed
1062
Size CascadeClassifier::getOriginalWindowSize() const
wester committed
1063 1064 1065 1066
{
    return data.origWinSize;
}

wester committed
1067
bool CascadeClassifier::setImage(const Mat& image)
wester committed
1068
{
wester committed
1069
    return featureEvaluator->setImage(image, data.origWinSize);
wester committed
1070 1071
}

wester committed
1072 1073 1074 1075 1076 1077
void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
                                          vector<int>& rejectLevels,
                                          vector<double>& levelWeights,
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minObjectSize, Size maxObjectSize,
                                          bool outputRejectLevels )
wester committed
1078
{
wester committed
1079
    const double GROUP_EPS = 0.2;
wester committed
1080

wester committed
1081
    CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
a  
Kai Westerkamp committed
1082

wester committed
1083 1084
    if( empty() )
        return;
wester committed
1085

wester committed
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
    if( isOldFormatCascade() )
    {
        MemStorage storage(cvCreateMemStorage(0));
        CvMat _image = image;
        CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
                                              minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
        vector<CvAvgComp> vecAvgComp;
        Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
        objects.resize(vecAvgComp.size());
        std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
        return;
    }

    objects.clear();

    if (!maskGenerator.empty()) {
        maskGenerator->initializeMask(image);
    }
wester committed
1104 1105


a  
Kai Westerkamp committed
1106
    if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
wester committed
1107
        maxObjectSize = image.size();
wester committed
1108

wester committed
1109 1110
    Mat grayImage = image;
    if( grayImage.channels() > 1 )
a  
Kai Westerkamp committed
1111
    {
wester committed
1112 1113 1114
        Mat temp;
        cvtColor(grayImage, temp, CV_BGR2GRAY);
        grayImage = temp;
a  
Kai Westerkamp committed
1115
    }
wester committed
1116

wester committed
1117 1118
    Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
    vector<Rect> candidates;
wester committed
1119

a  
Kai Westerkamp committed
1120 1121 1122 1123 1124
    for( double factor = 1; ; factor *= scaleFactor )
    {
        Size originalWindowSize = getOriginalWindowSize();

        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
wester committed
1125 1126 1127 1128 1129 1130
        Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
        Size processingRectSize( scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height );

        if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
            break;
        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
a  
Kai Westerkamp committed
1131 1132 1133
            break;
        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
            continue;
wester committed
1134

wester committed
1135 1136
        Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
        resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
wester committed
1137

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

wester committed
1148
        int stripCount, stripSize;
wester committed
1149

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

wester committed
1155 1156 1157 1158
        if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
            rejectLevels, levelWeights, outputRejectLevels ) )
            break;
    }
wester committed
1159

wester committed
1160 1161 1162 1163 1164

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

    if( outputRejectLevels )
wester committed
1165
    {
wester committed
1166
        groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
wester committed
1167 1168 1169
    }
    else
    {
wester committed
1170
        groupRectangles( objects, minNeighbors, GROUP_EPS );
wester committed
1171 1172 1173
    }
}

wester committed
1174 1175 1176
void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
                                          double scaleFactor, int minNeighbors,
                                          int flags, Size minObjectSize, Size maxObjectSize)
wester committed
1177
{
wester committed
1178 1179 1180 1181
    vector<int> fakeLevels;
    vector<double> fakeWeights;
    detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
        minNeighbors, flags, minObjectSize, maxObjectSize, false );
wester committed
1182 1183
}

wester committed
1184
bool CascadeClassifier::Data::read(const FileNode &root)
wester committed
1185 1186 1187 1188
{
    static const float THRESHOLD_EPS = 1e-5f;

    // load stage params
wester committed
1189
    string stageTypeStr = (string)root[CC_STAGE_TYPE];
wester committed
1190 1191 1192 1193 1194
    if( stageTypeStr == CC_BOOST )
        stageType = BOOST;
    else
        return false;

wester committed
1195
    string featureTypeStr = (string)root[CC_FEATURE_TYPE];
wester committed
1196 1197 1198 1199 1200 1201
    if( featureTypeStr == CC_HAAR )
        featureType = FeatureEvaluator::HAAR;
    else if( featureTypeStr == CC_LBP )
        featureType = FeatureEvaluator::LBP;
    else if( featureTypeStr == CC_HOG )
        featureType = FeatureEvaluator::HOG;
wester committed
1202

wester committed
1203 1204 1205 1206 1207 1208 1209
    else
        return false;

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

wester committed
1210 1211
    isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;

wester committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
    // load feature params
    FileNode fn = root[CC_FEATURE_PARAMS];
    if( fn.empty() )
        return false;

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

    // load stages
    fn = root[CC_STAGES];
    if( fn.empty() )
        return false;

    stages.reserve(fn.size());
    classifiers.clear();
    nodes.clear();

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

    for( int si = 0; it != it_end; si++, ++it )
    {
        FileNode fns = *it;
        Stage stage;
        stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
        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);

        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;

            DTree tree;
            tree.nodeCount = (int)internalNodes.size()/nodeStep;
            classifiers.push_back(tree);

            nodes.reserve(nodes.size() + tree.nodeCount);
            leaves.reserve(leaves.size() + leafValues.size());
            if( subsetSize > 0 )
                subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);

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

            for( ; internalNodesIter != internalNodesEnd; ) // nodes
            {
                DTreeNode node;
                node.left = (int)*internalNodesIter; ++internalNodesIter;
                node.right = (int)*internalNodesIter; ++internalNodesIter;
                node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
                if( subsetSize > 0 )
                {
                    for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
                        subsets.push_back((int)*internalNodesIter);
                    node.threshold = 0.f;
                }
                else
                {
                    node.threshold = (float)*internalNodesIter; ++internalNodesIter;
                }
                nodes.push_back(node);
            }

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

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

    return true;
}

wester committed
1294
bool CascadeClassifier::read(const FileNode& root)
wester committed
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
{
    if( !data.read(root) )
        return false;

    // load features
    featureEvaluator = FeatureEvaluator::create(data.featureType);
    FileNode fn = root[CC_FEATURES];
    if( fn.empty() )
        return false;

wester committed
1305
    return featureEvaluator->read(fn);
wester committed
1306 1307
}

wester committed
1308
template<> void Ptr<CvHaarClassifierCascade>::delete_obj()
wester committed
1309 1310 1311
{ cvReleaseHaarClassifierCascade(&obj); }

} // namespace cv