Add OpenCV source code
[platform/upstream/opencv.git] / modules / objdetect / src / cascadedetect.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43 #include <cstdio>
44
45 #include "cascadedetect.hpp"
46
47 #include <string>
48
49 #if defined (LOG_CASCADE_STATISTIC)
50 struct Logger
51 {
52     enum { STADIES_NUM = 20 };
53
54     int gid;
55     cv::Mat mask;
56     cv::Size sz0;
57     int step;
58
59
60     Logger() : gid (0), step(2) {}
61     void setImage(const cv::Mat& image)
62     {
63      if (gid == 0)
64          sz0 = image.size();
65
66       mask.create(image.rows, image.cols * (STADIES_NUM + 1) + STADIES_NUM, CV_8UC1);
67       mask = cv::Scalar(0);
68       cv::Mat roi = mask(cv::Rect(cv::Point(0,0), image.size()));
69       image.copyTo(roi);
70
71       printf("%d) Size = (%d, %d)\n", gid, image.cols, image.rows);
72
73       for(int i = 0; i < STADIES_NUM; ++i)
74       {
75           int x = image.cols + i * (image.cols + 1);
76           cv::line(mask, cv::Point(x, 0), cv::Point(x, mask.rows-1), cv::Scalar(255));
77       }
78
79       if (sz0.width/image.cols > 2 && sz0.height/image.rows > 2)
80           step = 1;
81     }
82
83     void setPoint(const cv::Point& p, int passed_stadies)
84     {
85         int cols = mask.cols / (STADIES_NUM + 1);
86
87         passed_stadies = -passed_stadies;
88         passed_stadies = (passed_stadies == -1) ? STADIES_NUM : passed_stadies;
89
90         unsigned char* ptr = mask.ptr<unsigned char>(p.y) + cols + 1 + p.x;
91         for(int i = 0; i < passed_stadies; ++i, ptr += cols + 1)
92         {
93             *ptr = 255;
94
95             if (step == 2)
96             {
97                 ptr[1] = 255;
98                 ptr[mask.step] = 255;
99                 ptr[mask.step + 1] = 255;
100             }
101         }
102     };
103
104     void write()
105     {
106         char buf[4096];
107         sprintf(buf, "%04d.png", gid++);
108         cv::imwrite(buf, mask);
109     }
110
111 } logger;
112 #endif
113
114 namespace cv
115 {
116
117 void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights)
118 {
119     if( groupThreshold <= 0 || rectList.empty() )
120     {
121         if( weights )
122         {
123             size_t i, sz = rectList.size();
124             weights->resize(sz);
125             for( i = 0; i < sz; i++ )
126                 (*weights)[i] = 1;
127         }
128         return;
129     }
130
131     vector<int> labels;
132     int nclasses = partition(rectList, labels, SimilarRects(eps));
133
134     vector<Rect> rrects(nclasses);
135     vector<int> rweights(nclasses, 0);
136     vector<int> rejectLevels(nclasses, 0);
137     vector<double> rejectWeights(nclasses, DBL_MIN);
138     int i, j, nlabels = (int)labels.size();
139     for( i = 0; i < nlabels; i++ )
140     {
141         int cls = labels[i];
142         rrects[cls].x += rectList[i].x;
143         rrects[cls].y += rectList[i].y;
144         rrects[cls].width += rectList[i].width;
145         rrects[cls].height += rectList[i].height;
146         rweights[cls]++;
147     }
148     if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
149     {
150         for( i = 0; i < nlabels; i++ )
151         {
152             int cls = labels[i];
153             if( (*weights)[i] > rejectLevels[cls] )
154             {
155                 rejectLevels[cls] = (*weights)[i];
156                 rejectWeights[cls] = (*levelWeights)[i];
157             }
158             else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
159                 rejectWeights[cls] = (*levelWeights)[i];
160         }
161     }
162
163     for( i = 0; i < nclasses; i++ )
164     {
165         Rect r = rrects[i];
166         float s = 1.f/rweights[i];
167         rrects[i] = Rect(saturate_cast<int>(r.x*s),
168              saturate_cast<int>(r.y*s),
169              saturate_cast<int>(r.width*s),
170              saturate_cast<int>(r.height*s));
171     }
172
173     rectList.clear();
174     if( weights )
175         weights->clear();
176     if( levelWeights )
177         levelWeights->clear();
178
179     for( i = 0; i < nclasses; i++ )
180     {
181         Rect r1 = rrects[i];
182         int n1 = levelWeights ? rejectLevels[i] : rweights[i];
183         double w1 = rejectWeights[i];
184         if( n1 <= groupThreshold )
185             continue;
186         // filter out small face rectangles inside large rectangles
187         for( j = 0; j < nclasses; j++ )
188         {
189             int n2 = rweights[j];
190
191             if( j == i || n2 <= groupThreshold )
192                 continue;
193             Rect r2 = rrects[j];
194
195             int dx = saturate_cast<int>( r2.width * eps );
196             int dy = saturate_cast<int>( r2.height * eps );
197
198             if( i != j &&
199                 r1.x >= r2.x - dx &&
200                 r1.y >= r2.y - dy &&
201                 r1.x + r1.width <= r2.x + r2.width + dx &&
202                 r1.y + r1.height <= r2.y + r2.height + dy &&
203                 (n2 > std::max(3, n1) || n1 < 3) )
204                 break;
205         }
206
207         if( j == nclasses )
208         {
209             rectList.push_back(r1);
210             if( weights )
211                 weights->push_back(n1);
212             if( levelWeights )
213                 levelWeights->push_back(w1);
214         }
215     }
216 }
217
218 class MeanshiftGrouping
219 {
220 public:
221     MeanshiftGrouping(const Point3d& densKer, const vector<Point3d>& posV,
222         const vector<double>& wV, double eps, int maxIter = 20)
223     {
224         densityKernel = densKer;
225         weightsV = wV;
226         positionsV = posV;
227         positionsCount = (int)posV.size();
228         meanshiftV.resize(positionsCount);
229         distanceV.resize(positionsCount);
230         iterMax = maxIter;
231         modeEps = eps;
232
233         for (unsigned i = 0; i<positionsV.size(); i++)
234         {
235             meanshiftV[i] = getNewValue(positionsV[i]);
236             distanceV[i] = moveToMode(meanshiftV[i]);
237             meanshiftV[i] -= positionsV[i];
238         }
239     }
240
241     void getModes(vector<Point3d>& modesV, vector<double>& resWeightsV, const double eps)
242     {
243         for (size_t i=0; i <distanceV.size(); i++)
244         {
245             bool is_found = false;
246             for(size_t j=0; j<modesV.size(); j++)
247             {
248                 if ( getDistance(distanceV[i], modesV[j]) < eps)
249                 {
250                     is_found=true;
251                     break;
252                 }
253             }
254             if (!is_found)
255             {
256                 modesV.push_back(distanceV[i]);
257             }
258         }
259
260         resWeightsV.resize(modesV.size());
261
262         for (size_t i=0; i<modesV.size(); i++)
263         {
264             resWeightsV[i] = getResultWeight(modesV[i]);
265         }
266     }
267
268 protected:
269     vector<Point3d> positionsV;
270     vector<double> weightsV;
271
272     Point3d densityKernel;
273     int positionsCount;
274
275     vector<Point3d> meanshiftV;
276     vector<Point3d> distanceV;
277     int iterMax;
278     double modeEps;
279
280     Point3d getNewValue(const Point3d& inPt) const
281     {
282         Point3d resPoint(.0);
283         Point3d ratPoint(.0);
284         for (size_t i=0; i<positionsV.size(); i++)
285         {
286             Point3d aPt= positionsV[i];
287             Point3d bPt = inPt;
288             Point3d sPt = densityKernel;
289
290             sPt.x *= exp(aPt.z);
291             sPt.y *= exp(aPt.z);
292
293             aPt.x /= sPt.x;
294             aPt.y /= sPt.y;
295             aPt.z /= sPt.z;
296
297             bPt.x /= sPt.x;
298             bPt.y /= sPt.y;
299             bPt.z /= sPt.z;
300
301             double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
302
303             resPoint += w*aPt;
304
305             ratPoint.x += w/sPt.x;
306             ratPoint.y += w/sPt.y;
307             ratPoint.z += w/sPt.z;
308         }
309         resPoint.x /= ratPoint.x;
310         resPoint.y /= ratPoint.y;
311         resPoint.z /= ratPoint.z;
312         return resPoint;
313     }
314
315     double getResultWeight(const Point3d& inPt) const
316     {
317         double sumW=0;
318         for (size_t i=0; i<positionsV.size(); i++)
319         {
320             Point3d aPt = positionsV[i];
321             Point3d sPt = densityKernel;
322
323             sPt.x *= exp(aPt.z);
324             sPt.y *= exp(aPt.z);
325
326             aPt -= inPt;
327
328             aPt.x /= sPt.x;
329             aPt.y /= sPt.y;
330             aPt.z /= sPt.z;
331
332             sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
333         }
334         return sumW;
335     }
336
337     Point3d moveToMode(Point3d aPt) const
338     {
339         Point3d bPt;
340         for (int i = 0; i<iterMax; i++)
341         {
342             bPt = aPt;
343             aPt = getNewValue(bPt);
344             if ( getDistance(aPt, bPt) <= modeEps )
345             {
346                 break;
347             }
348         }
349         return aPt;
350     }
351
352     double getDistance(Point3d p1, Point3d p2) const
353     {
354         Point3d ns = densityKernel;
355         ns.x *= exp(p2.z);
356         ns.y *= exp(p2.z);
357         p2 -= p1;
358         p2.x /= ns.x;
359         p2.y /= ns.y;
360         p2.z /= ns.z;
361         return p2.dot(p2);
362     }
363 };
364 //new grouping function with using meanshift
365 static void groupRectangles_meanshift(vector<Rect>& rectList, double detectThreshold, vector<double>* foundWeights,
366                                       vector<double>& scales, Size winDetSize)
367 {
368     int detectionCount = (int)rectList.size();
369     vector<Point3d> hits(detectionCount), resultHits;
370     vector<double> hitWeights(detectionCount), resultWeights;
371     Point2d hitCenter;
372
373     for (int i=0; i < detectionCount; i++)
374     {
375         hitWeights[i] = (*foundWeights)[i];
376         hitCenter = (rectList[i].tl() + rectList[i].br())*(0.5); //center of rectangles
377         hits[i] = Point3d(hitCenter.x, hitCenter.y, std::log(scales[i]));
378     }
379
380     rectList.clear();
381     if (foundWeights)
382         foundWeights->clear();
383
384     double logZ = std::log(1.3);
385     Point3d smothing(8, 16, logZ);
386
387     MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);
388
389     msGrouping.getModes(resultHits, resultWeights, 1);
390
391     for (unsigned i=0; i < resultHits.size(); ++i)
392     {
393
394         double scale = exp(resultHits[i].z);
395         hitCenter.x = resultHits[i].x;
396         hitCenter.y = resultHits[i].y;
397         Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
398         Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
399             int(s.width), int(s.height) );
400
401         if (resultWeights[i] > detectThreshold)
402         {
403             rectList.push_back(resultRect);
404             foundWeights->push_back(resultWeights[i]);
405         }
406     }
407 }
408
409 void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps)
410 {
411     groupRectangles(rectList, groupThreshold, eps, 0, 0);
412 }
413
414 void groupRectangles(vector<Rect>& rectList, vector<int>& weights, int groupThreshold, double eps)
415 {
416     groupRectangles(rectList, groupThreshold, eps, &weights, 0);
417 }
418 //used for cascade detection algorithm for ROC-curve calculating
419 void groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels, vector<double>& levelWeights, int groupThreshold, double eps)
420 {
421     groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
422 }
423 //can be used for HOG detection algorithm only
424 void groupRectangles_meanshift(vector<Rect>& rectList, vector<double>& foundWeights,
425                                vector<double>& foundScales, double detectThreshold, Size winDetSize)
426 {
427     groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
428 }
429
430
431
432 FeatureEvaluator::~FeatureEvaluator() {}
433 bool FeatureEvaluator::read(const FileNode&) {return true;}
434 Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }
435 int FeatureEvaluator::getFeatureType() const {return -1;}
436 bool FeatureEvaluator::setImage(const Mat&, Size) {return true;}
437 bool FeatureEvaluator::setWindow(Point) { return true; }
438 double FeatureEvaluator::calcOrd(int) const { return 0.; }
439 int FeatureEvaluator::calcCat(int) const { return 0; }
440
441 //----------------------------------------------  HaarEvaluator ---------------------------------------
442
443 bool HaarEvaluator::Feature :: read( const FileNode& node )
444 {
445     FileNode rnode = node[CC_RECTS];
446     FileNodeIterator it = rnode.begin(), it_end = rnode.end();
447
448     int ri;
449     for( ri = 0; ri < RECT_NUM; ri++ )
450     {
451         rect[ri].r = Rect();
452         rect[ri].weight = 0.f;
453     }
454
455     for(ri = 0; it != it_end; ++it, ri++)
456     {
457         FileNodeIterator it2 = (*it).begin();
458         it2 >> rect[ri].r.x >> rect[ri].r.y >>
459             rect[ri].r.width >> rect[ri].r.height >> rect[ri].weight;
460     }
461
462     tilted = (int)node[CC_TILTED] != 0;
463     return true;
464 }
465
466 HaarEvaluator::HaarEvaluator()
467 {
468     features = new vector<Feature>();
469 }
470 HaarEvaluator::~HaarEvaluator()
471 {
472 }
473
474 bool HaarEvaluator::read(const FileNode& node)
475 {
476     features->resize(node.size());
477     featuresPtr = &(*features)[0];
478     FileNodeIterator it = node.begin(), it_end = node.end();
479     hasTiltedFeatures = false;
480
481     for(int i = 0; it != it_end; ++it, i++)
482     {
483         if(!featuresPtr[i].read(*it))
484             return false;
485         if( featuresPtr[i].tilted )
486             hasTiltedFeatures = true;
487     }
488     return true;
489 }
490
491 Ptr<FeatureEvaluator> HaarEvaluator::clone() const
492 {
493     HaarEvaluator* ret = new HaarEvaluator;
494     ret->origWinSize = origWinSize;
495     ret->features = features;
496     ret->featuresPtr = &(*ret->features)[0];
497     ret->hasTiltedFeatures = hasTiltedFeatures;
498     ret->sum0 = sum0, ret->sqsum0 = sqsum0, ret->tilted0 = tilted0;
499     ret->sum = sum, ret->sqsum = sqsum, ret->tilted = tilted;
500     ret->normrect = normrect;
501     memcpy( ret->p, p, 4*sizeof(p[0]) );
502     memcpy( ret->pq, pq, 4*sizeof(pq[0]) );
503     ret->offset = offset;
504     ret->varianceNormFactor = varianceNormFactor;
505     return ret;
506 }
507
508 bool HaarEvaluator::setImage( const Mat &image, Size _origWinSize )
509 {
510     int rn = image.rows+1, cn = image.cols+1;
511     origWinSize = _origWinSize;
512     normrect = Rect(1, 1, origWinSize.width-2, origWinSize.height-2);
513
514     if (image.cols < origWinSize.width || image.rows < origWinSize.height)
515         return false;
516
517     if( sum0.rows < rn || sum0.cols < cn )
518     {
519         sum0.create(rn, cn, CV_32S);
520         sqsum0.create(rn, cn, CV_64F);
521         if (hasTiltedFeatures)
522             tilted0.create( rn, cn, CV_32S);
523     }
524     sum = Mat(rn, cn, CV_32S, sum0.data);
525     sqsum = Mat(rn, cn, CV_64F, sqsum0.data);
526
527     if( hasTiltedFeatures )
528     {
529         tilted = Mat(rn, cn, CV_32S, tilted0.data);
530         integral(image, sum, sqsum, tilted);
531     }
532     else
533         integral(image, sum, sqsum);
534     const int* sdata = (const int*)sum.data;
535     const double* sqdata = (const double*)sqsum.data;
536     size_t sumStep = sum.step/sizeof(sdata[0]);
537     size_t sqsumStep = sqsum.step/sizeof(sqdata[0]);
538
539     CV_SUM_PTRS( p[0], p[1], p[2], p[3], sdata, normrect, sumStep );
540     CV_SUM_PTRS( pq[0], pq[1], pq[2], pq[3], sqdata, normrect, sqsumStep );
541
542     size_t fi, nfeatures = features->size();
543
544     for( fi = 0; fi < nfeatures; fi++ )
545         featuresPtr[fi].updatePtrs( !featuresPtr[fi].tilted ? sum : tilted );
546     return true;
547 }
548
549 bool  HaarEvaluator::setWindow( Point pt )
550 {
551     if( pt.x < 0 || pt.y < 0 ||
552         pt.x + origWinSize.width >= sum.cols ||
553         pt.y + origWinSize.height >= sum.rows )
554         return false;
555
556     size_t pOffset = pt.y * (sum.step/sizeof(int)) + pt.x;
557     size_t pqOffset = pt.y * (sqsum.step/sizeof(double)) + pt.x;
558     int valsum = CALC_SUM(p, pOffset);
559     double valsqsum = CALC_SUM(pq, pqOffset);
560
561     double nf = (double)normrect.area() * valsqsum - (double)valsum * valsum;
562     if( nf > 0. )
563         nf = sqrt(nf);
564     else
565         nf = 1.;
566     varianceNormFactor = 1./nf;
567     offset = (int)pOffset;
568
569     return true;
570 }
571
572 //----------------------------------------------  LBPEvaluator -------------------------------------
573 bool LBPEvaluator::Feature :: read(const FileNode& node )
574 {
575     FileNode rnode = node[CC_RECT];
576     FileNodeIterator it = rnode.begin();
577     it >> rect.x >> rect.y >> rect.width >> rect.height;
578     return true;
579 }
580
581 LBPEvaluator::LBPEvaluator()
582 {
583     features = new vector<Feature>();
584 }
585 LBPEvaluator::~LBPEvaluator()
586 {
587 }
588
589 bool LBPEvaluator::read( const FileNode& node )
590 {
591     features->resize(node.size());
592     featuresPtr = &(*features)[0];
593     FileNodeIterator it = node.begin(), it_end = node.end();
594     for(int i = 0; it != it_end; ++it, i++)
595     {
596         if(!featuresPtr[i].read(*it))
597             return false;
598     }
599     return true;
600 }
601
602 Ptr<FeatureEvaluator> LBPEvaluator::clone() const
603 {
604     LBPEvaluator* ret = new LBPEvaluator;
605     ret->origWinSize = origWinSize;
606     ret->features = features;
607     ret->featuresPtr = &(*ret->features)[0];
608     ret->sum0 = sum0, ret->sum = sum;
609     ret->normrect = normrect;
610     ret->offset = offset;
611     return ret;
612 }
613
614 bool LBPEvaluator::setImage( const Mat& image, Size _origWinSize )
615 {
616     int rn = image.rows+1, cn = image.cols+1;
617     origWinSize = _origWinSize;
618
619     if( image.cols < origWinSize.width || image.rows < origWinSize.height )
620         return false;
621
622     if( sum0.rows < rn || sum0.cols < cn )
623         sum0.create(rn, cn, CV_32S);
624     sum = Mat(rn, cn, CV_32S, sum0.data);
625     integral(image, sum);
626
627     size_t fi, nfeatures = features->size();
628
629     for( fi = 0; fi < nfeatures; fi++ )
630         featuresPtr[fi].updatePtrs( sum );
631     return true;
632 }
633
634 bool LBPEvaluator::setWindow( Point pt )
635 {
636     if( pt.x < 0 || pt.y < 0 ||
637         pt.x + origWinSize.width >= sum.cols ||
638         pt.y + origWinSize.height >= sum.rows )
639         return false;
640     offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
641     return true;
642 }
643
644 //----------------------------------------------  HOGEvaluator ---------------------------------------
645 bool HOGEvaluator::Feature :: read( const FileNode& node )
646 {
647     FileNode rnode = node[CC_RECT];
648     FileNodeIterator it = rnode.begin();
649     it >> rect[0].x >> rect[0].y >> rect[0].width >> rect[0].height >> featComponent;
650     rect[1].x = rect[0].x + rect[0].width;
651     rect[1].y = rect[0].y;
652     rect[2].x = rect[0].x;
653     rect[2].y = rect[0].y + rect[0].height;
654     rect[3].x = rect[0].x + rect[0].width;
655     rect[3].y = rect[0].y + rect[0].height;
656     rect[1].width = rect[2].width = rect[3].width = rect[0].width;
657     rect[1].height = rect[2].height = rect[3].height = rect[0].height;
658     return true;
659 }
660
661 HOGEvaluator::HOGEvaluator()
662 {
663     features = new vector<Feature>();
664 }
665
666 HOGEvaluator::~HOGEvaluator()
667 {
668 }
669
670 bool HOGEvaluator::read( const FileNode& node )
671 {
672     features->resize(node.size());
673     featuresPtr = &(*features)[0];
674     FileNodeIterator it = node.begin(), it_end = node.end();
675     for(int i = 0; it != it_end; ++it, i++)
676     {
677         if(!featuresPtr[i].read(*it))
678             return false;
679     }
680     return true;
681 }
682
683 Ptr<FeatureEvaluator> HOGEvaluator::clone() const
684 {
685     HOGEvaluator* ret = new HOGEvaluator;
686     ret->origWinSize = origWinSize;
687     ret->features = features;
688     ret->featuresPtr = &(*ret->features)[0];
689     ret->offset = offset;
690     ret->hist = hist;
691     ret->normSum = normSum;
692     return ret;
693 }
694
695 bool HOGEvaluator::setImage( const Mat& image, Size winSize )
696 {
697     int rows = image.rows + 1;
698     int cols = image.cols + 1;
699     origWinSize = winSize;
700     if( image.cols < origWinSize.width || image.rows < origWinSize.height )
701         return false;
702     hist.clear();
703     for( int bin = 0; bin < Feature::BIN_NUM; bin++ )
704     {
705         hist.push_back( Mat(rows, cols, CV_32FC1) );
706     }
707     normSum.create( rows, cols, CV_32FC1 );
708
709     integralHistogram( image, hist, normSum, Feature::BIN_NUM );
710
711     size_t featIdx, featCount = features->size();
712
713     for( featIdx = 0; featIdx < featCount; featIdx++ )
714     {
715         featuresPtr[featIdx].updatePtrs( hist, normSum );
716     }
717     return true;
718 }
719
720 bool HOGEvaluator::setWindow(Point pt)
721 {
722     if( pt.x < 0 || pt.y < 0 ||
723         pt.x + origWinSize.width >= hist[0].cols-2 ||
724         pt.y + origWinSize.height >= hist[0].rows-2 )
725         return false;
726     offset = pt.y * ((int)hist[0].step/sizeof(float)) + pt.x;
727     return true;
728 }
729
730 void HOGEvaluator::integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const
731 {
732     CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
733     int x, y, binIdx;
734
735     Size gradSize(img.size());
736     Size histSize(histogram[0].size());
737     Mat grad(gradSize, CV_32F);
738     Mat qangle(gradSize, CV_8U);
739
740     AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
741     int* xmap = (int*)mapbuf + 1;
742     int* ymap = xmap + gradSize.width + 2;
743
744     const int borderType = (int)BORDER_REPLICATE;
745
746     for( x = -1; x < gradSize.width + 1; x++ )
747         xmap[x] = borderInterpolate(x, gradSize.width, borderType);
748     for( y = -1; y < gradSize.height + 1; y++ )
749         ymap[y] = borderInterpolate(y, gradSize.height, borderType);
750
751     int width = gradSize.width;
752     AutoBuffer<float> _dbuf(width*4);
753     float* dbuf = _dbuf;
754     Mat Dx(1, width, CV_32F, dbuf);
755     Mat Dy(1, width, CV_32F, dbuf + width);
756     Mat Mag(1, width, CV_32F, dbuf + width*2);
757     Mat Angle(1, width, CV_32F, dbuf + width*3);
758
759     float angleScale = (float)(nbins/CV_PI);
760
761     for( y = 0; y < gradSize.height; y++ )
762     {
763         const uchar* currPtr = img.data + img.step*ymap[y];
764         const uchar* prevPtr = img.data + img.step*ymap[y-1];
765         const uchar* nextPtr = img.data + img.step*ymap[y+1];
766         float* gradPtr = (float*)grad.ptr(y);
767         uchar* qanglePtr = (uchar*)qangle.ptr(y);
768
769         for( x = 0; x < width; x++ )
770         {
771             dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);
772             dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);
773         }
774         cartToPolar( Dx, Dy, Mag, Angle, false );
775         for( x = 0; x < width; x++ )
776         {
777             float mag = dbuf[x+width*2];
778             float angle = dbuf[x+width*3];
779             angle = angle*angleScale - 0.5f;
780             int bidx = cvFloor(angle);
781             angle -= bidx;
782             if( bidx < 0 )
783                 bidx += nbins;
784             else if( bidx >= nbins )
785                 bidx -= nbins;
786
787             qanglePtr[x] = (uchar)bidx;
788             gradPtr[x] = mag;
789         }
790     }
791     integral(grad, norm, grad.depth());
792
793     float* histBuf;
794     const float* magBuf;
795     const uchar* binsBuf;
796
797     int binsStep = (int)( qangle.step / sizeof(uchar) );
798     int histStep = (int)( histogram[0].step / sizeof(float) );
799     int magStep = (int)( grad.step / sizeof(float) );
800     for( binIdx = 0; binIdx < nbins; binIdx++ )
801     {
802         histBuf = (float*)histogram[binIdx].data;
803         magBuf = (const float*)grad.data;
804         binsBuf = (const uchar*)qangle.data;
805
806         memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );
807         histBuf += histStep + 1;
808         for( y = 0; y < qangle.rows; y++ )
809         {
810             histBuf[-1] = 0.f;
811             float strSum = 0.f;
812             for( x = 0; x < qangle.cols; x++ )
813             {
814                 if( binsBuf[x] == binIdx )
815                     strSum += magBuf[x];
816                 histBuf[x] = histBuf[-histStep + x] + strSum;
817             }
818             histBuf += histStep;
819             binsBuf += binsStep;
820             magBuf += magStep;
821         }
822     }
823 }
824
825 Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
826 {
827     return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
828         featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
829         featureType == HOG ? Ptr<FeatureEvaluator>(new HOGEvaluator) :
830         Ptr<FeatureEvaluator>();
831 }
832
833 //---------------------------------------- Classifier Cascade --------------------------------------------
834
835 CascadeClassifier::CascadeClassifier()
836 {
837 }
838
839 CascadeClassifier::CascadeClassifier(const string& filename)
840 {
841     load(filename);
842 }
843
844 CascadeClassifier::~CascadeClassifier()
845 {
846 }
847
848 bool CascadeClassifier::empty() const
849 {
850     return oldCascade.empty() && data.stages.empty();
851 }
852
853 bool CascadeClassifier::load(const string& filename)
854 {
855     oldCascade.release();
856     data = Data();
857     featureEvaluator.release();
858
859     FileStorage fs(filename, FileStorage::READ);
860     if( !fs.isOpened() )
861         return false;
862
863     if( read(fs.getFirstTopLevelNode()) )
864         return true;
865
866     fs.release();
867
868     oldCascade = Ptr<CvHaarClassifierCascade>((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
869     return !oldCascade.empty();
870 }
871
872 int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
873 {
874     CV_Assert( oldCascade.empty() );
875
876     assert( data.featureType == FeatureEvaluator::HAAR ||
877             data.featureType == FeatureEvaluator::LBP ||
878             data.featureType == FeatureEvaluator::HOG );
879
880     if( !evaluator->setWindow(pt) )
881         return -1;
882     if( data.isStumpBased )
883     {
884         if( data.featureType == FeatureEvaluator::HAAR )
885             return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
886         else if( data.featureType == FeatureEvaluator::LBP )
887             return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
888         else if( data.featureType == FeatureEvaluator::HOG )
889             return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );
890         else
891             return -2;
892     }
893     else
894     {
895         if( data.featureType == FeatureEvaluator::HAAR )
896             return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
897         else if( data.featureType == FeatureEvaluator::LBP )
898             return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
899         else if( data.featureType == FeatureEvaluator::HOG )
900             return predictOrdered<HOGEvaluator>( *this, evaluator, weight );
901         else
902             return -2;
903     }
904 }
905
906 bool CascadeClassifier::setImage( Ptr<FeatureEvaluator>& evaluator, const Mat& image )
907 {
908     return empty() ? false : evaluator->setImage(image, data.origWinSize);
909 }
910
911 void CascadeClassifier::setMaskGenerator(Ptr<MaskGenerator> _maskGenerator)
912 {
913     maskGenerator=_maskGenerator;
914 }
915 Ptr<CascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
916 {
917     return maskGenerator;
918 }
919
920 void CascadeClassifier::setFaceDetectionMaskGenerator()
921 {
922 #ifdef HAVE_TEGRA_OPTIMIZATION
923     setMaskGenerator(tegra::getCascadeClassifierMaskGenerator(*this));
924 #else
925     setMaskGenerator(Ptr<CascadeClassifier::MaskGenerator>());
926 #endif
927 }
928
929 class CascadeClassifierInvoker : public ParallelLoopBody
930 {
931 public:
932     CascadeClassifierInvoker( CascadeClassifier& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
933         vector<Rect>& _vec, vector<int>& _levels, vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
934     {
935         classifier = &_cc;
936         processingRectSize = _sz1;
937         stripSize = _stripSize;
938         yStep = _yStep;
939         scalingFactor = _factor;
940         rectangles = &_vec;
941         rejectLevels = outputLevels ? &_levels : 0;
942         levelWeights = outputLevels ? &_weights : 0;
943         mask = _mask;
944         mtx = _mtx;
945     }
946
947     void operator()(const Range& range) const
948     {
949         Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
950
951         Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));
952
953         int y1 = range.start * stripSize;
954         int y2 = min(range.end * stripSize, processingRectSize.height);
955         for( int y = y1; y < y2; y += yStep )
956         {
957             for( int x = 0; x < processingRectSize.width; x += yStep )
958             {
959                 if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
960                     continue;
961                 }
962
963                 double gypWeight;
964                 int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
965
966 #if defined (LOG_CASCADE_STATISTIC)
967
968                 logger.setPoint(Point(x, y), result);
969 #endif
970                 if( rejectLevels )
971                 {
972                     if( result == 1 )
973                         result =  -(int)classifier->data.stages.size();
974                     if( classifier->data.stages.size() + result < 4 )
975                     {
976                         mtx->lock();
977                         rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
978                         rejectLevels->push_back(-result);
979                         levelWeights->push_back(gypWeight);
980                         mtx->unlock();
981                     }
982                 }
983                 else if( result > 0 )
984                 {
985                     mtx->lock();
986                     rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
987                                                winSize.width, winSize.height));
988                     mtx->unlock();
989                 }
990                 if( result == 0 )
991                     x += yStep;
992             }
993         }
994     }
995
996     CascadeClassifier* classifier;
997     vector<Rect>* rectangles;
998     Size processingRectSize;
999     int stripSize, yStep;
1000     double scalingFactor;
1001     vector<int> *rejectLevels;
1002     vector<double> *levelWeights;
1003     Mat mask;
1004     Mutex* mtx;
1005 };
1006
1007 struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };
1008
1009
1010 bool CascadeClassifier::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize,
1011                                            int stripSize, int yStep, double factor, vector<Rect>& candidates,
1012                                            vector<int>& levels, vector<double>& weights, bool outputRejectLevels )
1013 {
1014     if( !featureEvaluator->setImage( image, data.origWinSize ) )
1015         return false;
1016
1017 #if defined (LOG_CASCADE_STATISTIC)
1018     logger.setImage(image);
1019 #endif
1020
1021     Mat currentMask;
1022     if (!maskGenerator.empty()) {
1023         currentMask=maskGenerator->generateMask(image);
1024     }
1025
1026     vector<Rect> candidatesVector;
1027     vector<int> rejectLevels;
1028     vector<double> levelWeights;
1029     Mutex mtx;
1030     if( outputRejectLevels )
1031     {
1032         parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
1033             candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
1034         levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
1035         weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
1036     }
1037     else
1038     {
1039          parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
1040             candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
1041     }
1042     candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
1043
1044 #if defined (LOG_CASCADE_STATISTIC)
1045     logger.write();
1046 #endif
1047
1048     return true;
1049 }
1050
1051 bool CascadeClassifier::isOldFormatCascade() const
1052 {
1053     return !oldCascade.empty();
1054 }
1055
1056
1057 int CascadeClassifier::getFeatureType() const
1058 {
1059     return featureEvaluator->getFeatureType();
1060 }
1061
1062 Size CascadeClassifier::getOriginalWindowSize() const
1063 {
1064     return data.origWinSize;
1065 }
1066
1067 bool CascadeClassifier::setImage(const Mat& image)
1068 {
1069     return featureEvaluator->setImage(image, data.origWinSize);
1070 }
1071
1072 void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
1073                                           vector<int>& rejectLevels,
1074                                           vector<double>& levelWeights,
1075                                           double scaleFactor, int minNeighbors,
1076                                           int flags, Size minObjectSize, Size maxObjectSize,
1077                                           bool outputRejectLevels )
1078 {
1079     const double GROUP_EPS = 0.2;
1080
1081     CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
1082
1083     if( empty() )
1084         return;
1085
1086     if( isOldFormatCascade() )
1087     {
1088         MemStorage storage(cvCreateMemStorage(0));
1089         CvMat _image = image;
1090         CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
1091                                               minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
1092         vector<CvAvgComp> vecAvgComp;
1093         Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
1094         objects.resize(vecAvgComp.size());
1095         std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
1096         return;
1097     }
1098
1099     objects.clear();
1100
1101     if (!maskGenerator.empty()) {
1102         maskGenerator->initializeMask(image);
1103     }
1104
1105
1106     if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
1107         maxObjectSize = image.size();
1108
1109     Mat grayImage = image;
1110     if( grayImage.channels() > 1 )
1111     {
1112         Mat temp;
1113         cvtColor(grayImage, temp, CV_BGR2GRAY);
1114         grayImage = temp;
1115     }
1116
1117     Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
1118     vector<Rect> candidates;
1119
1120     for( double factor = 1; ; factor *= scaleFactor )
1121     {
1122         Size originalWindowSize = getOriginalWindowSize();
1123
1124         Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
1125         Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
1126         Size processingRectSize( scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height );
1127
1128         if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
1129             break;
1130         if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
1131             break;
1132         if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
1133             continue;
1134
1135         Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
1136         resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
1137
1138         int yStep;
1139         if( getFeatureType() == cv::FeatureEvaluator::HOG )
1140         {
1141             yStep = 4;
1142         }
1143         else
1144         {
1145             yStep = factor > 2. ? 1 : 2;
1146         }
1147
1148         int stripCount, stripSize;
1149
1150         const int PTS_PER_THREAD = 1000;
1151         stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
1152         stripCount = std::min(std::max(stripCount, 1), 100);
1153         stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
1154
1155         if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
1156             rejectLevels, levelWeights, outputRejectLevels ) )
1157             break;
1158     }
1159
1160
1161     objects.resize(candidates.size());
1162     std::copy(candidates.begin(), candidates.end(), objects.begin());
1163
1164     if( outputRejectLevels )
1165     {
1166         groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
1167     }
1168     else
1169     {
1170         groupRectangles( objects, minNeighbors, GROUP_EPS );
1171     }
1172 }
1173
1174 void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
1175                                           double scaleFactor, int minNeighbors,
1176                                           int flags, Size minObjectSize, Size maxObjectSize)
1177 {
1178     vector<int> fakeLevels;
1179     vector<double> fakeWeights;
1180     detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
1181         minNeighbors, flags, minObjectSize, maxObjectSize, false );
1182 }
1183
1184 bool CascadeClassifier::Data::read(const FileNode &root)
1185 {
1186     static const float THRESHOLD_EPS = 1e-5f;
1187
1188     // load stage params
1189     string stageTypeStr = (string)root[CC_STAGE_TYPE];
1190     if( stageTypeStr == CC_BOOST )
1191         stageType = BOOST;
1192     else
1193         return false;
1194
1195     string featureTypeStr = (string)root[CC_FEATURE_TYPE];
1196     if( featureTypeStr == CC_HAAR )
1197         featureType = FeatureEvaluator::HAAR;
1198     else if( featureTypeStr == CC_LBP )
1199         featureType = FeatureEvaluator::LBP;
1200     else if( featureTypeStr == CC_HOG )
1201         featureType = FeatureEvaluator::HOG;
1202
1203     else
1204         return false;
1205
1206     origWinSize.width = (int)root[CC_WIDTH];
1207     origWinSize.height = (int)root[CC_HEIGHT];
1208     CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
1209
1210     isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;
1211
1212     // load feature params
1213     FileNode fn = root[CC_FEATURE_PARAMS];
1214     if( fn.empty() )
1215         return false;
1216
1217     ncategories = fn[CC_MAX_CAT_COUNT];
1218     int subsetSize = (ncategories + 31)/32,
1219         nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
1220
1221     // load stages
1222     fn = root[CC_STAGES];
1223     if( fn.empty() )
1224         return false;
1225
1226     stages.reserve(fn.size());
1227     classifiers.clear();
1228     nodes.clear();
1229
1230     FileNodeIterator it = fn.begin(), it_end = fn.end();
1231
1232     for( int si = 0; it != it_end; si++, ++it )
1233     {
1234         FileNode fns = *it;
1235         Stage stage;
1236         stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
1237         fns = fns[CC_WEAK_CLASSIFIERS];
1238         if(fns.empty())
1239             return false;
1240         stage.ntrees = (int)fns.size();
1241         stage.first = (int)classifiers.size();
1242         stages.push_back(stage);
1243         classifiers.reserve(stages[si].first + stages[si].ntrees);
1244
1245         FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
1246         for( ; it1 != it1_end; ++it1 ) // weak trees
1247         {
1248             FileNode fnw = *it1;
1249             FileNode internalNodes = fnw[CC_INTERNAL_NODES];
1250             FileNode leafValues = fnw[CC_LEAF_VALUES];
1251             if( internalNodes.empty() || leafValues.empty() )
1252                 return false;
1253
1254             DTree tree;
1255             tree.nodeCount = (int)internalNodes.size()/nodeStep;
1256             classifiers.push_back(tree);
1257
1258             nodes.reserve(nodes.size() + tree.nodeCount);
1259             leaves.reserve(leaves.size() + leafValues.size());
1260             if( subsetSize > 0 )
1261                 subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
1262
1263             FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();
1264
1265             for( ; internalNodesIter != internalNodesEnd; ) // nodes
1266             {
1267                 DTreeNode node;
1268                 node.left = (int)*internalNodesIter; ++internalNodesIter;
1269                 node.right = (int)*internalNodesIter; ++internalNodesIter;
1270                 node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
1271                 if( subsetSize > 0 )
1272                 {
1273                     for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
1274                         subsets.push_back((int)*internalNodesIter);
1275                     node.threshold = 0.f;
1276                 }
1277                 else
1278                 {
1279                     node.threshold = (float)*internalNodesIter; ++internalNodesIter;
1280                 }
1281                 nodes.push_back(node);
1282             }
1283
1284             internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();
1285
1286             for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
1287                 leaves.push_back((float)*internalNodesIter);
1288         }
1289     }
1290
1291     return true;
1292 }
1293
1294 bool CascadeClassifier::read(const FileNode& root)
1295 {
1296     if( !data.read(root) )
1297         return false;
1298
1299     // load features
1300     featureEvaluator = FeatureEvaluator::create(data.featureType);
1301     FileNode fn = root[CC_FEATURES];
1302     if( fn.empty() )
1303         return false;
1304
1305     return featureEvaluator->read(fn);
1306 }
1307
1308 template<> void Ptr<CvHaarClassifierCascade>::delete_obj()
1309 { cvReleaseHaarClassifierCascade(&obj); }
1310
1311 } // namespace cv