1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
42 #include "precomp.hpp"
45 #include "cascadedetect.hpp"
49 #if defined (LOG_CASCADE_STATISTIC)
52 enum { STADIES_NUM = 20 };
60 Logger() : gid (0), step(2) {}
61 void setImage(const cv::Mat& image)
66 mask.create(image.rows, image.cols * (STADIES_NUM + 1) + STADIES_NUM, CV_8UC1);
68 cv::Mat roi = mask(cv::Rect(cv::Point(0,0), image.size()));
71 printf("%d) Size = (%d, %d)\n", gid, image.cols, image.rows);
73 for(int i = 0; i < STADIES_NUM; ++i)
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));
79 if (sz0.width/image.cols > 2 && sz0.height/image.rows > 2)
83 void setPoint(const cv::Point& p, int passed_stadies)
85 int cols = mask.cols / (STADIES_NUM + 1);
87 passed_stadies = -passed_stadies;
88 passed_stadies = (passed_stadies == -1) ? STADIES_NUM : passed_stadies;
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)
99 ptr[mask.step + 1] = 255;
107 sprintf(buf, "%04d.png", gid++);
108 cv::imwrite(buf, mask);
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
122 SimilarRects(double _eps) : eps(_eps) {}
123 inline bool operator()(const Rect& r1, const Rect& r2) const
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;
135 void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights)
137 if( groupThreshold <= 0 || rectList.empty() )
141 size_t i, sz = rectList.size();
143 for( i = 0; i < sz; i++ )
150 int nclasses = partition(rectList, labels, SimilarRects(eps));
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++ )
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;
166 if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
168 for( i = 0; i < nlabels; i++ )
171 if( (*weights)[i] > rejectLevels[cls] )
173 rejectLevels[cls] = (*weights)[i];
174 rejectWeights[cls] = (*levelWeights)[i];
176 else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
177 rejectWeights[cls] = (*levelWeights)[i];
181 for( i = 0; i < nclasses; 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));
195 levelWeights->clear();
197 for( i = 0; i < nclasses; i++ )
200 int n1 = levelWeights ? rejectLevels[i] : rweights[i];
201 double w1 = rejectWeights[i];
202 if( n1 <= groupThreshold )
204 // filter out small face rectangles inside large rectangles
205 for( j = 0; j < nclasses; j++ )
207 int n2 = rweights[j];
209 if( j == i || n2 <= groupThreshold )
213 int dx = saturate_cast<int>( r2.width * eps );
214 int dy = saturate_cast<int>( r2.height * eps );
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) )
227 rectList.push_back(r1);
229 weights->push_back(n1);
231 levelWeights->push_back(w1);
236 class MeanshiftGrouping
239 MeanshiftGrouping(const Point3d& densKer, const vector<Point3d>& posV,
240 const vector<double>& wV, double eps, int maxIter = 20)
242 densityKernel = densKer;
245 positionsCount = (int)posV.size();
246 meanshiftV.resize(positionsCount);
247 distanceV.resize(positionsCount);
251 for (unsigned i = 0; i<positionsV.size(); i++)
253 meanshiftV[i] = getNewValue(positionsV[i]);
254 distanceV[i] = moveToMode(meanshiftV[i]);
255 meanshiftV[i] -= positionsV[i];
259 void getModes(vector<Point3d>& modesV, vector<double>& resWeightsV, const double eps)
261 for (size_t i=0; i <distanceV.size(); i++)
263 bool is_found = false;
264 for(size_t j=0; j<modesV.size(); j++)
266 if ( getDistance(distanceV[i], modesV[j]) < eps)
274 modesV.push_back(distanceV[i]);
278 resWeightsV.resize(modesV.size());
280 for (size_t i=0; i<modesV.size(); i++)
282 resWeightsV[i] = getResultWeight(modesV[i]);
287 vector<Point3d> positionsV;
288 vector<double> weightsV;
290 Point3d densityKernel;
293 vector<Point3d> meanshiftV;
294 vector<Point3d> distanceV;
298 Point3d getNewValue(const Point3d& inPt) const
300 Point3d resPoint(.0);
301 Point3d ratPoint(.0);
302 for (size_t i=0; i<positionsV.size(); i++)
304 Point3d aPt= positionsV[i];
306 Point3d sPt = densityKernel;
319 double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
323 ratPoint.x += w/sPt.x;
324 ratPoint.y += w/sPt.y;
325 ratPoint.z += w/sPt.z;
327 resPoint.x /= ratPoint.x;
328 resPoint.y /= ratPoint.y;
329 resPoint.z /= ratPoint.z;
333 double getResultWeight(const Point3d& inPt) const
336 for (size_t i=0; i<positionsV.size(); i++)
338 Point3d aPt = positionsV[i];
339 Point3d sPt = densityKernel;
350 sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
355 Point3d moveToMode(Point3d aPt) const
358 for (int i = 0; i<iterMax; i++)
361 aPt = getNewValue(bPt);
362 if ( getDistance(aPt, bPt) <= modeEps )
370 double getDistance(Point3d p1, Point3d p2) const
372 Point3d ns = densityKernel;
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)
386 int detectionCount = (int)rectList.size();
387 vector<Point3d> hits(detectionCount), resultHits;
388 vector<double> hitWeights(detectionCount), resultWeights;
391 for (int i=0; i < detectionCount; i++)
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]));
400 foundWeights->clear();
402 double logZ = std::log(1.3);
403 Point3d smothing(8, 16, logZ);
405 MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);
407 msGrouping.getModes(resultHits, resultWeights, 1);
409 for (unsigned i=0; i < resultHits.size(); ++i)
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) );
419 if (resultWeights[i] > detectThreshold)
421 rectList.push_back(resultRect);
422 foundWeights->push_back(resultWeights[i]);
427 void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps)
429 groupRectangles(rectList, groupThreshold, eps, 0, 0);
432 void groupRectangles(vector<Rect>& rectList, vector<int>& weights, int groupThreshold, double eps)
434 groupRectangles(rectList, groupThreshold, eps, &weights, 0);
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)
439 groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
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)
445 groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
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; }
459 //---------------------------------------------- HaarEvaluator ---------------------------------------
461 bool HaarEvaluator::Feature :: read( const FileNode& node )
463 FileNode rnode = node[CC_RECTS];
464 FileNodeIterator it = rnode.begin(), it_end = rnode.end();
467 for( ri = 0; ri < RECT_NUM; ri++ )
470 rect[ri].weight = 0.f;
473 for(ri = 0; it != it_end; ++it, ri++)
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;
480 tilted = (int)node[CC_TILTED] != 0;
484 HaarEvaluator::HaarEvaluator()
486 features = new vector<Feature>();
488 HaarEvaluator::~HaarEvaluator()
492 bool HaarEvaluator::read(const FileNode& node)
494 features->resize(node.size());
495 featuresPtr = &(*features)[0];
496 FileNodeIterator it = node.begin(), it_end = node.end();
497 hasTiltedFeatures = false;
499 for(int i = 0; it != it_end; ++it, i++)
501 if(!featuresPtr[i].read(*it))
503 if( featuresPtr[i].tilted )
504 hasTiltedFeatures = true;
509 Ptr<FeatureEvaluator> HaarEvaluator::clone() const
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;
526 bool HaarEvaluator::setImage( const Mat &image, Size _origWinSize )
528 int rn = image.rows+1, cn = image.cols+1;
529 origWinSize = _origWinSize;
530 normrect = Rect(1, 1, origWinSize.width-2, origWinSize.height-2);
532 if (image.cols < origWinSize.width || image.rows < origWinSize.height)
535 if( sum0.rows < rn || sum0.cols < cn )
537 sum0.create(rn, cn, CV_32S);
538 sqsum0.create(rn, cn, CV_64F);
539 if (hasTiltedFeatures)
540 tilted0.create( rn, cn, CV_32S);
542 sum = Mat(rn, cn, CV_32S, sum0.data);
543 sqsum = Mat(rn, cn, CV_64F, sqsum0.data);
545 if( hasTiltedFeatures )
547 tilted = Mat(rn, cn, CV_32S, tilted0.data);
548 integral(image, sum, sqsum, tilted);
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]);
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 );
560 size_t fi, nfeatures = features->size();
562 for( fi = 0; fi < nfeatures; fi++ )
563 featuresPtr[fi].updatePtrs( !featuresPtr[fi].tilted ? sum : tilted );
567 bool HaarEvaluator::setWindow( Point pt )
569 if( pt.x < 0 || pt.y < 0 ||
570 pt.x + origWinSize.width >= sum.cols ||
571 pt.y + origWinSize.height >= sum.rows )
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);
579 double nf = (double)normrect.area() * valsqsum - (double)valsum * valsum;
584 varianceNormFactor = 1./nf;
585 offset = (int)pOffset;
590 //---------------------------------------------- LBPEvaluator -------------------------------------
591 bool LBPEvaluator::Feature :: read(const FileNode& node )
593 FileNode rnode = node[CC_RECT];
594 FileNodeIterator it = rnode.begin();
595 it >> rect.x >> rect.y >> rect.width >> rect.height;
599 LBPEvaluator::LBPEvaluator()
601 features = new vector<Feature>();
603 LBPEvaluator::~LBPEvaluator()
607 bool LBPEvaluator::read( const FileNode& node )
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++)
614 if(!featuresPtr[i].read(*it))
620 Ptr<FeatureEvaluator> LBPEvaluator::clone() const
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;
632 bool LBPEvaluator::setImage( const Mat& image, Size _origWinSize )
634 int rn = image.rows+1, cn = image.cols+1;
635 origWinSize = _origWinSize;
637 if( image.cols < origWinSize.width || image.rows < origWinSize.height )
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);
645 size_t fi, nfeatures = features->size();
647 for( fi = 0; fi < nfeatures; fi++ )
648 featuresPtr[fi].updatePtrs( sum );
652 bool LBPEvaluator::setWindow( Point pt )
654 if( pt.x < 0 || pt.y < 0 ||
655 pt.x + origWinSize.width >= sum.cols ||
656 pt.y + origWinSize.height >= sum.rows )
658 offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
662 //---------------------------------------------- HOGEvaluator ---------------------------------------
663 bool HOGEvaluator::Feature :: read( const FileNode& node )
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;
679 HOGEvaluator::HOGEvaluator()
681 features = new vector<Feature>();
684 HOGEvaluator::~HOGEvaluator()
688 bool HOGEvaluator::read( const FileNode& node )
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++)
695 if(!featuresPtr[i].read(*it))
701 Ptr<FeatureEvaluator> HOGEvaluator::clone() const
703 HOGEvaluator* ret = new HOGEvaluator;
704 ret->origWinSize = origWinSize;
705 ret->features = features;
706 ret->featuresPtr = &(*ret->features)[0];
707 ret->offset = offset;
709 ret->normSum = normSum;
713 bool HOGEvaluator::setImage( const Mat& image, Size winSize )
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 )
721 for( int bin = 0; bin < Feature::BIN_NUM; bin++ )
723 hist.push_back( Mat(rows, cols, CV_32FC1) );
725 normSum.create( rows, cols, CV_32FC1 );
727 integralHistogram( image, hist, normSum, Feature::BIN_NUM );
729 size_t featIdx, featCount = features->size();
731 for( featIdx = 0; featIdx < featCount; featIdx++ )
733 featuresPtr[featIdx].updatePtrs( hist, normSum );
738 bool HOGEvaluator::setWindow(Point pt)
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 )
744 offset = pt.y * ((int)hist[0].step/sizeof(float)) + pt.x;
748 void HOGEvaluator::integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const
750 CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
753 Size gradSize(img.size());
754 Size histSize(histogram[0].size());
755 Mat grad(gradSize, CV_32F);
756 Mat qangle(gradSize, CV_8U);
758 AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
759 int* xmap = (int*)mapbuf + 1;
760 int* ymap = xmap + gradSize.width + 2;
762 const int borderType = (int)BORDER_REPLICATE;
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);
769 int width = gradSize.width;
770 AutoBuffer<float> _dbuf(width*4);
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);
777 float angleScale = (float)(nbins/CV_PI);
779 for( y = 0; y < gradSize.height; y++ )
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);
787 for( x = 0; x < width; x++ )
789 dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);
790 dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);
792 cartToPolar( Dx, Dy, Mag, Angle, false );
793 for( x = 0; x < width; x++ )
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);
802 else if( bidx >= nbins )
805 qanglePtr[x] = (uchar)bidx;
809 integral(grad, norm, grad.depth());
813 const uchar* binsBuf;
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++ )
820 histBuf = (float*)histogram[binIdx].data;
821 magBuf = (const float*)grad.data;
822 binsBuf = (const uchar*)qangle.data;
824 memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );
825 histBuf += histStep + 1;
826 for( y = 0; y < qangle.rows; y++ )
830 for( x = 0; x < qangle.cols; x++ )
832 if( binsBuf[x] == binIdx )
834 histBuf[x] = histBuf[-histStep + x] + strSum;
843 Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
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>();
851 //---------------------------------------- Classifier Cascade --------------------------------------------
853 CascadeClassifier::CascadeClassifier()
857 CascadeClassifier::CascadeClassifier(const string& filename)
862 CascadeClassifier::~CascadeClassifier()
866 bool CascadeClassifier::empty() const
868 return oldCascade.empty() && data.stages.empty();
871 bool CascadeClassifier::load(const string& filename)
873 oldCascade.release();
875 featureEvaluator.release();
877 FileStorage fs(filename, FileStorage::READ);
881 if( read(fs.getFirstTopLevelNode()) )
886 oldCascade = Ptr<CvHaarClassifierCascade>((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
887 return !oldCascade.empty();
890 int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
892 CV_Assert( oldCascade.empty() );
894 assert( data.featureType == FeatureEvaluator::HAAR ||
895 data.featureType == FeatureEvaluator::LBP ||
896 data.featureType == FeatureEvaluator::HOG );
898 if( !evaluator->setWindow(pt) )
900 if( data.isStumpBased )
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 );
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 );
924 bool CascadeClassifier::setImage( Ptr<FeatureEvaluator>& evaluator, const Mat& image )
926 return empty() ? false : evaluator->setImage(image, data.origWinSize);
929 void CascadeClassifier::setMaskGenerator(Ptr<MaskGenerator> _maskGenerator)
931 maskGenerator=_maskGenerator;
933 Ptr<CascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
935 return maskGenerator;
938 void CascadeClassifier::setFaceDetectionMaskGenerator()
940 #ifdef HAVE_TEGRA_OPTIMIZATION
941 setMaskGenerator(tegra::getCascadeClassifierMaskGenerator(*this));
943 setMaskGenerator(Ptr<CascadeClassifier::MaskGenerator>());
947 class CascadeClassifierInvoker : public ParallelLoopBody
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)
954 processingRectSize = _sz1;
955 stripSize = _stripSize;
957 scalingFactor = _factor;
959 rejectLevels = outputLevels ? &_levels : 0;
960 levelWeights = outputLevels ? &_weights : 0;
965 void operator()(const Range& range) const
967 Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
969 Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));
971 int y1 = range.start * stripSize;
972 int y2 = min(range.end * stripSize, processingRectSize.height);
973 for( int y = y1; y < y2; y += yStep )
975 for( int x = 0; x < processingRectSize.width; x += yStep )
977 if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
982 int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
984 #if defined (LOG_CASCADE_STATISTIC)
986 logger.setPoint(Point(x, y), result);
991 result = -(int)classifier->data.stages.size();
992 if( classifier->data.stages.size() + result < 4 )
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);
1001 else if( result > 0 )
1004 rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
1005 winSize.width, winSize.height));
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;
1025 struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };
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 )
1032 if( !featureEvaluator->setImage( image, data.origWinSize ) )
1035 #if defined (LOG_CASCADE_STATISTIC)
1036 logger.setImage(image);
1040 if (!maskGenerator.empty()) {
1041 currentMask=maskGenerator->generateMask(image);
1044 vector<Rect> candidatesVector;
1045 vector<int> rejectLevels;
1046 vector<double> levelWeights;
1048 if( outputRejectLevels )
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() );
1057 parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
1058 candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
1060 candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
1062 #if defined (LOG_CASCADE_STATISTIC)
1069 bool CascadeClassifier::isOldFormatCascade() const
1071 return !oldCascade.empty();
1075 int CascadeClassifier::getFeatureType() const
1077 return featureEvaluator->getFeatureType();
1080 Size CascadeClassifier::getOriginalWindowSize() const
1082 return data.origWinSize;
1085 bool CascadeClassifier::setImage(const Mat& image)
1087 return featureEvaluator->setImage(image, data.origWinSize);
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 )
1097 const double GROUP_EPS = 0.2;
1099 CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
1104 if( isOldFormatCascade() )
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());
1119 if (!maskGenerator.empty()) {
1120 maskGenerator->initializeMask(image);
1124 if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
1125 maxObjectSize = image.size();
1127 Mat grayImage = image;
1128 if( grayImage.channels() > 1 )
1131 cvtColor(grayImage, temp, CV_BGR2GRAY);
1135 Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
1136 vector<Rect> candidates;
1138 for( double factor = 1; ; factor *= scaleFactor )
1140 Size originalWindowSize = getOriginalWindowSize();
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 );
1146 if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
1148 if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
1150 if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
1153 Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
1154 resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
1157 if( getFeatureType() == cv::FeatureEvaluator::HOG )
1163 yStep = factor > 2. ? 1 : 2;
1166 int stripCount, stripSize;
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;
1173 if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
1174 rejectLevels, levelWeights, outputRejectLevels ) )
1179 objects.resize(candidates.size());
1180 std::copy(candidates.begin(), candidates.end(), objects.begin());
1182 if( outputRejectLevels )
1184 groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
1188 groupRectangles( objects, minNeighbors, GROUP_EPS );
1192 void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
1193 double scaleFactor, int minNeighbors,
1194 int flags, Size minObjectSize, Size maxObjectSize)
1196 vector<int> fakeLevels;
1197 vector<double> fakeWeights;
1198 detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
1199 minNeighbors, flags, minObjectSize, maxObjectSize, false );
1202 bool CascadeClassifier::Data::read(const FileNode &root)
1204 static const float THRESHOLD_EPS = 1e-5f;
1206 // load stage params
1207 string stageTypeStr = (string)root[CC_STAGE_TYPE];
1208 if( stageTypeStr == CC_BOOST )
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;
1224 origWinSize.width = (int)root[CC_WIDTH];
1225 origWinSize.height = (int)root[CC_HEIGHT];
1226 CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
1228 isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false;
1230 // load feature params
1231 FileNode fn = root[CC_FEATURE_PARAMS];
1235 ncategories = fn[CC_MAX_CAT_COUNT];
1236 int subsetSize = (ncategories + 31)/32,
1237 nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
1240 fn = root[CC_STAGES];
1244 stages.reserve(fn.size());
1245 classifiers.clear();
1248 FileNodeIterator it = fn.begin(), it_end = fn.end();
1250 for( int si = 0; it != it_end; si++, ++it )
1254 stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
1255 fns = fns[CC_WEAK_CLASSIFIERS];
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);
1263 FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
1264 for( ; it1 != it1_end; ++it1 ) // weak trees
1266 FileNode fnw = *it1;
1267 FileNode internalNodes = fnw[CC_INTERNAL_NODES];
1268 FileNode leafValues = fnw[CC_LEAF_VALUES];
1269 if( internalNodes.empty() || leafValues.empty() )
1273 tree.nodeCount = (int)internalNodes.size()/nodeStep;
1274 classifiers.push_back(tree);
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);
1281 FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();
1283 for( ; internalNodesIter != internalNodesEnd; ) // nodes
1286 node.left = (int)*internalNodesIter; ++internalNodesIter;
1287 node.right = (int)*internalNodesIter; ++internalNodesIter;
1288 node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
1289 if( subsetSize > 0 )
1291 for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
1292 subsets.push_back((int)*internalNodesIter);
1293 node.threshold = 0.f;
1297 node.threshold = (float)*internalNodesIter; ++internalNodesIter;
1299 nodes.push_back(node);
1302 internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();
1304 for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
1305 leaves.push_back((float)*internalNodesIter);
1312 bool CascadeClassifier::read(const FileNode& root)
1314 if( !data.read(root) )
1318 featureEvaluator = FeatureEvaluator::create(data.featureType);
1319 FileNode fn = root[CC_FEATURES];
1323 return featureEvaluator->read(fn);
1326 template<> void Ptr<CvHaarClassifierCascade>::delete_obj()
1327 { cvReleaseHaarClassifierCascade(&obj); }