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