Path: blob/master/modules/objdetect/src/cascadedetect.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2008-2013, Itseez Inc., all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Itseez Inc. may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the copyright holders or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "precomp.hpp"42#include <cstdio>43#include <iostream>4445#include "cascadedetect.hpp"46#include "opencv2/objdetect/objdetect_c.h"47#include "opencl_kernels_objdetect.hpp"4849namespace cv50{5152template<typename _Tp> void copyVectorToUMat(const std::vector<_Tp>& v, UMat& um)53{54if(v.empty())55um.release();56Mat(1, (int)(v.size()*sizeof(v[0])), CV_8U, (void*)&v[0]).copyTo(um);57}5859void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps,60std::vector<int>* weights, std::vector<double>* levelWeights)61{62CV_INSTRUMENT_REGION();6364if( groupThreshold <= 0 || rectList.empty() )65{66if( weights && !levelWeights )67{68size_t i, sz = rectList.size();69weights->resize(sz);70for( i = 0; i < sz; i++ )71(*weights)[i] = 1;72}73return;74}7576std::vector<int> labels;77int nclasses = partition(rectList, labels, SimilarRects(eps));7879std::vector<Rect> rrects(nclasses);80std::vector<int> rweights(nclasses, 0);81std::vector<int> rejectLevels(nclasses, 0);82std::vector<double> rejectWeights(nclasses, DBL_MIN);83int i, j, nlabels = (int)labels.size();84for( i = 0; i < nlabels; i++ )85{86int cls = labels[i];87rrects[cls].x += rectList[i].x;88rrects[cls].y += rectList[i].y;89rrects[cls].width += rectList[i].width;90rrects[cls].height += rectList[i].height;91rweights[cls]++;92}9394bool useDefaultWeights = false;9596if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )97{98for( i = 0; i < nlabels; i++ )99{100int cls = labels[i];101if( (*weights)[i] > rejectLevels[cls] )102{103rejectLevels[cls] = (*weights)[i];104rejectWeights[cls] = (*levelWeights)[i];105}106else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )107rejectWeights[cls] = (*levelWeights)[i];108}109}110else111useDefaultWeights = true;112113for( i = 0; i < nclasses; i++ )114{115Rect r = rrects[i];116float s = 1.f/rweights[i];117rrects[i] = Rect(saturate_cast<int>(r.x*s),118saturate_cast<int>(r.y*s),119saturate_cast<int>(r.width*s),120saturate_cast<int>(r.height*s));121}122123rectList.clear();124if( weights )125weights->clear();126if( levelWeights )127levelWeights->clear();128129for( i = 0; i < nclasses; i++ )130{131Rect r1 = rrects[i];132int n1 = rweights[i];133double w1 = rejectWeights[i];134int l1 = rejectLevels[i];135136// filter out rectangles which don't have enough similar rectangles137if( n1 <= groupThreshold )138continue;139// filter out small face rectangles inside large rectangles140for( j = 0; j < nclasses; j++ )141{142int n2 = rweights[j];143144if( j == i || n2 <= groupThreshold )145continue;146Rect r2 = rrects[j];147148int dx = saturate_cast<int>( r2.width * eps );149int dy = saturate_cast<int>( r2.height * eps );150151if( i != j &&152r1.x >= r2.x - dx &&153r1.y >= r2.y - dy &&154r1.x + r1.width <= r2.x + r2.width + dx &&155r1.y + r1.height <= r2.y + r2.height + dy &&156(n2 > std::max(3, n1) || n1 < 3) )157break;158}159160if( j == nclasses )161{162rectList.push_back(r1);163if( weights )164weights->push_back(useDefaultWeights ? n1 : l1);165if( levelWeights )166levelWeights->push_back(w1);167}168}169}170171class MeanshiftGrouping172{173public:174MeanshiftGrouping(const Point3d& densKer, const std::vector<Point3d>& posV,175const std::vector<double>& wV, double eps, int maxIter = 20)176{177densityKernel = densKer;178weightsV = wV;179positionsV = posV;180positionsCount = (int)posV.size();181meanshiftV.resize(positionsCount);182distanceV.resize(positionsCount);183iterMax = maxIter;184modeEps = eps;185186for (unsigned i = 0; i<positionsV.size(); i++)187{188meanshiftV[i] = getNewValue(positionsV[i]);189distanceV[i] = moveToMode(meanshiftV[i]);190meanshiftV[i] -= positionsV[i];191}192}193194void getModes(std::vector<Point3d>& modesV, std::vector<double>& resWeightsV, const double eps)195{196for (size_t i=0; i <distanceV.size(); i++)197{198bool is_found = false;199for(size_t j=0; j<modesV.size(); j++)200{201if ( getDistance(distanceV[i], modesV[j]) < eps)202{203is_found=true;204break;205}206}207if (!is_found)208{209modesV.push_back(distanceV[i]);210}211}212213resWeightsV.resize(modesV.size());214215for (size_t i=0; i<modesV.size(); i++)216{217resWeightsV[i] = getResultWeight(modesV[i]);218}219}220221protected:222std::vector<Point3d> positionsV;223std::vector<double> weightsV;224225Point3d densityKernel;226int positionsCount;227228std::vector<Point3d> meanshiftV;229std::vector<Point3d> distanceV;230int iterMax;231double modeEps;232233Point3d getNewValue(const Point3d& inPt) const234{235Point3d resPoint(.0);236Point3d ratPoint(.0);237for (size_t i=0; i<positionsV.size(); i++)238{239Point3d aPt= positionsV[i];240Point3d bPt = inPt;241Point3d sPt = densityKernel;242243sPt.x *= std::exp(aPt.z);244sPt.y *= std::exp(aPt.z);245246aPt.x /= sPt.x;247aPt.y /= sPt.y;248aPt.z /= sPt.z;249250bPt.x /= sPt.x;251bPt.y /= sPt.y;252bPt.z /= sPt.z;253254double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));255256resPoint += w*aPt;257258ratPoint.x += w/sPt.x;259ratPoint.y += w/sPt.y;260ratPoint.z += w/sPt.z;261}262resPoint.x /= ratPoint.x;263resPoint.y /= ratPoint.y;264resPoint.z /= ratPoint.z;265return resPoint;266}267268double getResultWeight(const Point3d& inPt) const269{270double sumW=0;271for (size_t i=0; i<positionsV.size(); i++)272{273Point3d aPt = positionsV[i];274Point3d sPt = densityKernel;275276sPt.x *= std::exp(aPt.z);277sPt.y *= std::exp(aPt.z);278279aPt -= inPt;280281aPt.x /= sPt.x;282aPt.y /= sPt.y;283aPt.z /= sPt.z;284285sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));286}287return sumW;288}289290Point3d moveToMode(Point3d aPt) const291{292Point3d bPt;293for (int i = 0; i<iterMax; i++)294{295bPt = aPt;296aPt = getNewValue(bPt);297if ( getDistance(aPt, bPt) <= modeEps )298{299break;300}301}302return aPt;303}304305double getDistance(Point3d p1, Point3d p2) const306{307Point3d ns = densityKernel;308ns.x *= std::exp(p2.z);309ns.y *= std::exp(p2.z);310p2 -= p1;311p2.x /= ns.x;312p2.y /= ns.y;313p2.z /= ns.z;314return p2.dot(p2);315}316};317//new grouping function with using meanshift318static void groupRectangles_meanshift(std::vector<Rect>& rectList, double detectThreshold, std::vector<double>& foundWeights,319std::vector<double>& scales, Size winDetSize)320{321int detectionCount = (int)rectList.size();322std::vector<Point3d> hits(detectionCount), resultHits;323std::vector<double> hitWeights(detectionCount), resultWeights;324Point2d hitCenter;325326for (int i=0; i < detectionCount; i++)327{328hitWeights[i] = foundWeights[i];329hitCenter = (rectList[i].tl() + rectList[i].br())*(0.5); //center of rectangles330hits[i] = Point3d(hitCenter.x, hitCenter.y, std::log(scales[i]));331}332333rectList.clear();334foundWeights.clear();335336double logZ = std::log(1.3);337Point3d smothing(8, 16, logZ);338339MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);340341msGrouping.getModes(resultHits, resultWeights, 1);342343for (unsigned i=0; i < resultHits.size(); ++i)344{345346double scale = std::exp(resultHits[i].z);347hitCenter.x = resultHits[i].x;348hitCenter.y = resultHits[i].y;349Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );350Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),351int(s.width), int(s.height) );352353if (resultWeights[i] > detectThreshold)354{355rectList.push_back(resultRect);356foundWeights.push_back(resultWeights[i]);357}358}359}360361void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps)362{363CV_INSTRUMENT_REGION();364365groupRectangles(rectList, groupThreshold, eps, 0, 0);366}367368void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& weights, int groupThreshold, double eps)369{370CV_INSTRUMENT_REGION();371372groupRectangles(rectList, groupThreshold, eps, &weights, 0);373}374//used for cascade detection algorithm for ROC-curve calculating375void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels,376std::vector<double>& levelWeights, int groupThreshold, double eps)377{378CV_INSTRUMENT_REGION();379380groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);381}382//can be used for HOG detection algorithm only383void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,384std::vector<double>& foundScales, double detectThreshold, Size winDetSize)385{386CV_INSTRUMENT_REGION();387388groupRectangles_meanshift(rectList, detectThreshold, foundWeights, foundScales, winDetSize);389}390391392FeatureEvaluator::~FeatureEvaluator() {}393394bool FeatureEvaluator::read(const FileNode&, Size _origWinSize)395{396origWinSize = _origWinSize;397localSize = lbufSize = Size(0, 0);398if (scaleData.empty())399scaleData = makePtr<std::vector<ScaleData> >();400else401scaleData->clear();402return true;403}404405Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }406int FeatureEvaluator::getFeatureType() const {return -1;}407bool FeatureEvaluator::setWindow(Point, int) { return true; }408void FeatureEvaluator::getUMats(std::vector<UMat>& bufs)409{410if (!(sbufFlag & USBUF_VALID))411{412sbuf.copyTo(usbuf);413sbufFlag |= USBUF_VALID;414}415416bufs.clear();417bufs.push_back(uscaleData);418bufs.push_back(usbuf);419bufs.push_back(ufbuf);420}421422void FeatureEvaluator::getMats()423{424if (!(sbufFlag & SBUF_VALID))425{426usbuf.copyTo(sbuf);427sbufFlag |= SBUF_VALID;428}429}430431float FeatureEvaluator::calcOrd(int) const { return 0.; }432int FeatureEvaluator::calcCat(int) const { return 0; }433434bool FeatureEvaluator::updateScaleData( Size imgsz, const std::vector<float>& _scales )435{436if( scaleData.empty() )437scaleData = makePtr<std::vector<ScaleData> >();438439size_t i, nscales = _scales.size();440bool recalcOptFeatures = nscales != scaleData->size();441scaleData->resize(nscales);442443int layer_dy = 0;444Point layer_ofs(0,0);445Size prevBufSize = sbufSize;446sbufSize.width = std::max(sbufSize.width, (int)alignSize(cvRound(imgsz.width/_scales[0]) + 31, 32));447recalcOptFeatures = recalcOptFeatures || sbufSize.width != prevBufSize.width;448449for( i = 0; i < nscales; i++ )450{451FeatureEvaluator::ScaleData& s = scaleData->at(i);452if( !recalcOptFeatures && fabs(s.scale - _scales[i]) > FLT_EPSILON*100*_scales[i] )453recalcOptFeatures = true;454float sc = _scales[i];455Size sz;456sz.width = cvRound(imgsz.width/sc);457sz.height = cvRound(imgsz.height/sc);458s.ystep = sc >= 2 ? 1 : 2;459s.scale = sc;460s.szi = Size(sz.width+1, sz.height+1);461462if( i == 0 )463{464layer_dy = s.szi.height;465}466467if( layer_ofs.x + s.szi.width > sbufSize.width )468{469layer_ofs = Point(0, layer_ofs.y + layer_dy);470layer_dy = s.szi.height;471}472s.layer_ofs = layer_ofs.y*sbufSize.width + layer_ofs.x;473layer_ofs.x += s.szi.width;474}475476layer_ofs.y += layer_dy;477sbufSize.height = std::max(sbufSize.height, layer_ofs.y);478recalcOptFeatures = recalcOptFeatures || sbufSize.height != prevBufSize.height;479return recalcOptFeatures;480}481482483bool FeatureEvaluator::setImage( InputArray _image, const std::vector<float>& _scales )484{485CV_INSTRUMENT_REGION();486487Size imgsz = _image.size();488bool recalcOptFeatures = updateScaleData(imgsz, _scales);489490size_t i, nscales = scaleData->size();491if (nscales == 0)492{493return false;494}495Size sz0 = scaleData->at(0).szi;496sz0 = Size(std::max(rbuf.cols, (int)alignSize(sz0.width, 16)), std::max(rbuf.rows, sz0.height));497498if (recalcOptFeatures)499{500computeOptFeatures();501copyVectorToUMat(*scaleData, uscaleData);502}503504if (_image.isUMat() && !localSize.empty())505{506usbuf.create(sbufSize.height*nchannels, sbufSize.width, CV_32S);507urbuf.create(sz0, CV_8U);508509for (i = 0; i < nscales; i++)510{511const ScaleData& s = scaleData->at(i);512UMat dst(urbuf, Rect(0, 0, s.szi.width - 1, s.szi.height - 1));513resize(_image, dst, dst.size(), 1. / s.scale, 1. / s.scale, INTER_LINEAR_EXACT);514computeChannels((int)i, dst);515}516sbufFlag = USBUF_VALID;517}518else519{520Mat image = _image.getMat();521sbuf.create(sbufSize.height*nchannels, sbufSize.width, CV_32S);522rbuf.create(sz0, CV_8U);523524for (i = 0; i < nscales; i++)525{526const ScaleData& s = scaleData->at(i);527Mat dst(s.szi.height - 1, s.szi.width - 1, CV_8U, rbuf.ptr());528resize(image, dst, dst.size(), 1. / s.scale, 1. / s.scale, INTER_LINEAR_EXACT);529computeChannels((int)i, dst);530}531sbufFlag = SBUF_VALID;532}533534return true;535}536537//---------------------------------------------- HaarEvaluator ---------------------------------------538539bool HaarEvaluator::Feature :: read( const FileNode& node )540{541FileNode rnode = node[CC_RECTS];542FileNodeIterator it = rnode.begin(), it_end = rnode.end();543544int ri;545for( ri = 0; ri < RECT_NUM; ri++ )546{547rect[ri].r = Rect();548rect[ri].weight = 0.f;549}550551for(ri = 0; it != it_end; ++it, ri++)552{553FileNodeIterator it2 = (*it).begin();554it2 >> rect[ri].r.x >> rect[ri].r.y >>555rect[ri].r.width >> rect[ri].r.height >> rect[ri].weight;556}557558tilted = (int)node[CC_TILTED] != 0;559return true;560}561562HaarEvaluator::HaarEvaluator()563{564optfeaturesPtr = 0;565pwin = 0;566localSize = Size(4, 2);567lbufSize = Size(0, 0);568nchannels = 0;569tofs = 0;570sqofs = 0;571varianceNormFactor = 0;572hasTiltedFeatures = false;573}574575HaarEvaluator::~HaarEvaluator()576{577}578579bool HaarEvaluator::read(const FileNode& node, Size _origWinSize)580{581if (!FeatureEvaluator::read(node, _origWinSize))582return false;583size_t i, n = node.size();584CV_Assert(n > 0);585if(features.empty())586features = makePtr<std::vector<Feature> >();587if(optfeatures.empty())588optfeatures = makePtr<std::vector<OptFeature> >();589if (optfeatures_lbuf.empty())590optfeatures_lbuf = makePtr<std::vector<OptFeature> >();591features->resize(n);592FileNodeIterator it = node.begin();593hasTiltedFeatures = false;594std::vector<Feature>& ff = *features;595sbufSize = Size();596ufbuf.release();597598for(i = 0; i < n; i++, ++it)599{600if(!ff[i].read(*it))601return false;602if( ff[i].tilted )603hasTiltedFeatures = true;604}605nchannels = hasTiltedFeatures ? 3 : 2;606normrect = Rect(1, 1, origWinSize.width - 2, origWinSize.height - 2);607608localSize = lbufSize = Size(0, 0);609if (ocl::isOpenCLActivated())610{611if (ocl::Device::getDefault().isAMD() || ocl::Device::getDefault().isIntel() || ocl::Device::getDefault().isNVidia())612{613localSize = Size(8, 8);614lbufSize = Size(origWinSize.width + localSize.width,615origWinSize.height + localSize.height);616if (lbufSize.area() > 1024)617lbufSize = Size(0, 0);618}619}620621return true;622}623624Ptr<FeatureEvaluator> HaarEvaluator::clone() const625{626Ptr<HaarEvaluator> ret = makePtr<HaarEvaluator>();627*ret = *this;628return ret;629}630631632void HaarEvaluator::computeChannels(int scaleIdx, InputArray img)633{634CV_INSTRUMENT_REGION();635636const ScaleData& s = scaleData->at(scaleIdx);637sqofs = hasTiltedFeatures ? sbufSize.area() * 2 : sbufSize.area();638639if (img.isUMat())640{641int sx = s.layer_ofs % sbufSize.width;642int sy = s.layer_ofs / sbufSize.width;643int sqy = sy + (sqofs / sbufSize.width);644UMat sum(usbuf, Rect(sx, sy, s.szi.width, s.szi.height));645UMat sqsum(usbuf, Rect(sx, sqy, s.szi.width, s.szi.height));646sqsum.flags = (sqsum.flags & ~UMat::DEPTH_MASK) | CV_32S;647648if (hasTiltedFeatures)649{650int sty = sy + (tofs / sbufSize.width);651UMat tilted(usbuf, Rect(sx, sty, s.szi.width, s.szi.height));652integral(img, sum, sqsum, tilted, CV_32S, CV_32S);653}654else655{656UMatData* u = sqsum.u;657integral(img, sum, sqsum, noArray(), CV_32S, CV_32S);658CV_Assert(sqsum.u == u && sqsum.size() == s.szi && sqsum.type()==CV_32S);659}660}661else662{663Mat sum(s.szi, CV_32S, sbuf.ptr<int>() + s.layer_ofs, sbuf.step);664Mat sqsum(s.szi, CV_32S, sum.ptr<int>() + sqofs, sbuf.step);665666if (hasTiltedFeatures)667{668Mat tilted(s.szi, CV_32S, sum.ptr<int>() + tofs, sbuf.step);669integral(img, sum, sqsum, tilted, CV_32S, CV_32S);670}671else672integral(img, sum, sqsum, noArray(), CV_32S, CV_32S);673}674}675676void HaarEvaluator::computeOptFeatures()677{678CV_INSTRUMENT_REGION();679680if (hasTiltedFeatures)681tofs = sbufSize.area();682683int sstep = sbufSize.width;684CV_SUM_OFS( nofs[0], nofs[1], nofs[2], nofs[3], 0, normrect, sstep );685686size_t fi, nfeatures = features->size();687const std::vector<Feature>& ff = *features;688optfeatures->resize(nfeatures);689optfeaturesPtr = &(*optfeatures)[0];690for( fi = 0; fi < nfeatures; fi++ )691optfeaturesPtr[fi].setOffsets( ff[fi], sstep, tofs );692optfeatures_lbuf->resize(nfeatures);693694for( fi = 0; fi < nfeatures; fi++ )695optfeatures_lbuf->at(fi).setOffsets(ff[fi], lbufSize.width > 0 ? lbufSize.width : sstep, tofs);696697copyVectorToUMat(*optfeatures_lbuf, ufbuf);698}699700bool HaarEvaluator::setWindow( Point pt, int scaleIdx )701{702const ScaleData& s = getScaleData(scaleIdx);703704if( pt.x < 0 || pt.y < 0 ||705pt.x + origWinSize.width >= s.szi.width ||706pt.y + origWinSize.height >= s.szi.height )707return false;708709pwin = &sbuf.at<int>(pt) + s.layer_ofs;710const int* pq = (const int*)(pwin + sqofs);711int valsum = CALC_SUM_OFS(nofs, pwin);712unsigned valsqsum = (unsigned)(CALC_SUM_OFS(nofs, pq));713714double area = normrect.area();715double nf = area * valsqsum - (double)valsum * valsum;716if( nf > 0. )717{718nf = std::sqrt(nf);719varianceNormFactor = (float)(1./nf);720return area*varianceNormFactor < 1e-1;721}722else723{724varianceNormFactor = 1.f;725return false;726}727}728729730void HaarEvaluator::OptFeature::setOffsets( const Feature& _f, int step, int _tofs )731{732weight[0] = _f.rect[0].weight;733weight[1] = _f.rect[1].weight;734weight[2] = _f.rect[2].weight;735736if( _f.tilted )737{738CV_TILTED_OFS( ofs[0][0], ofs[0][1], ofs[0][2], ofs[0][3], _tofs, _f.rect[0].r, step );739CV_TILTED_OFS( ofs[1][0], ofs[1][1], ofs[1][2], ofs[1][3], _tofs, _f.rect[1].r, step );740CV_TILTED_OFS( ofs[2][0], ofs[2][1], ofs[2][2], ofs[2][3], _tofs, _f.rect[2].r, step );741}742else743{744CV_SUM_OFS( ofs[0][0], ofs[0][1], ofs[0][2], ofs[0][3], 0, _f.rect[0].r, step );745CV_SUM_OFS( ofs[1][0], ofs[1][1], ofs[1][2], ofs[1][3], 0, _f.rect[1].r, step );746CV_SUM_OFS( ofs[2][0], ofs[2][1], ofs[2][2], ofs[2][3], 0, _f.rect[2].r, step );747}748}749750Rect HaarEvaluator::getNormRect() const751{752return normrect;753}754755int HaarEvaluator::getSquaresOffset() const756{757return sqofs;758}759760//---------------------------------------------- LBPEvaluator -------------------------------------761bool LBPEvaluator::Feature :: read(const FileNode& node )762{763FileNode rnode = node[CC_RECT];764FileNodeIterator it = rnode.begin();765it >> rect.x >> rect.y >> rect.width >> rect.height;766return true;767}768769LBPEvaluator::LBPEvaluator()770{771features = makePtr<std::vector<Feature> >();772optfeatures = makePtr<std::vector<OptFeature> >();773scaleData = makePtr<std::vector<ScaleData> >();774optfeaturesPtr = 0;775pwin = 0;776}777778LBPEvaluator::~LBPEvaluator()779{780}781782bool LBPEvaluator::read( const FileNode& node, Size _origWinSize )783{784if (!FeatureEvaluator::read(node, _origWinSize))785return false;786if(features.empty())787features = makePtr<std::vector<Feature> >();788if(optfeatures.empty())789optfeatures = makePtr<std::vector<OptFeature> >();790if (optfeatures_lbuf.empty())791optfeatures_lbuf = makePtr<std::vector<OptFeature> >();792793features->resize(node.size());794optfeaturesPtr = 0;795FileNodeIterator it = node.begin(), it_end = node.end();796std::vector<Feature>& ff = *features;797for(int i = 0; it != it_end; ++it, i++)798{799if(!ff[i].read(*it))800return false;801}802nchannels = 1;803localSize = lbufSize = Size(0, 0);804if (ocl::isOpenCLActivated())805localSize = Size(8, 8);806807return true;808}809810Ptr<FeatureEvaluator> LBPEvaluator::clone() const811{812Ptr<LBPEvaluator> ret = makePtr<LBPEvaluator>();813*ret = *this;814return ret;815}816817void LBPEvaluator::computeChannels(int scaleIdx, InputArray _img)818{819const ScaleData& s = scaleData->at(scaleIdx);820821if (_img.isUMat())822{823int sx = s.layer_ofs % sbufSize.width;824int sy = s.layer_ofs / sbufSize.width;825UMat sum(usbuf, Rect(sx, sy, s.szi.width, s.szi.height));826integral(_img, sum, noArray(), noArray(), CV_32S);827}828else829{830Mat sum(s.szi, CV_32S, sbuf.ptr<int>() + s.layer_ofs, sbuf.step);831integral(_img, sum, noArray(), noArray(), CV_32S);832}833}834835void LBPEvaluator::computeOptFeatures()836{837int sstep = sbufSize.width;838839size_t fi, nfeatures = features->size();840const std::vector<Feature>& ff = *features;841optfeatures->resize(nfeatures);842optfeaturesPtr = &(*optfeatures)[0];843for( fi = 0; fi < nfeatures; fi++ )844optfeaturesPtr[fi].setOffsets( ff[fi], sstep );845copyVectorToUMat(*optfeatures, ufbuf);846}847848849void LBPEvaluator::OptFeature::setOffsets( const Feature& _f, int step )850{851Rect tr = _f.rect;852int w0 = tr.width;853int h0 = tr.height;854855CV_SUM_OFS( ofs[0], ofs[1], ofs[4], ofs[5], 0, tr, step );856tr.x += 2*w0;857CV_SUM_OFS( ofs[2], ofs[3], ofs[6], ofs[7], 0, tr, step );858tr.y += 2*h0;859CV_SUM_OFS( ofs[10], ofs[11], ofs[14], ofs[15], 0, tr, step );860tr.x -= 2*w0;861CV_SUM_OFS( ofs[8], ofs[9], ofs[12], ofs[13], 0, tr, step );862}863864865bool LBPEvaluator::setWindow( Point pt, int scaleIdx )866{867CV_Assert(0 <= scaleIdx && scaleIdx < (int)scaleData->size());868const ScaleData& s = scaleData->at(scaleIdx);869870if( pt.x < 0 || pt.y < 0 ||871pt.x + origWinSize.width >= s.szi.width ||872pt.y + origWinSize.height >= s.szi.height )873return false;874875pwin = &sbuf.at<int>(pt) + s.layer_ofs;876return true;877}878879880Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )881{882return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :883featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :884Ptr<FeatureEvaluator>();885}886887//---------------------------------------- Classifier Cascade --------------------------------------------888889CascadeClassifierImpl::CascadeClassifierImpl()890{891#ifdef HAVE_OPENCL892tryOpenCL = false;893#endif894}895896CascadeClassifierImpl::~CascadeClassifierImpl()897{898}899900bool CascadeClassifierImpl::empty() const901{902return !oldCascade && data.stages.empty();903}904905bool CascadeClassifierImpl::load(const String& filename)906{907oldCascade.release();908data = Data();909featureEvaluator.release();910911FileStorage fs(filename, FileStorage::READ);912if( !fs.isOpened() )913return false;914915if( read_(fs.getFirstTopLevelNode()) )916return true;917918fs.release();919920oldCascade.reset((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));921return !oldCascade.empty();922}923924void CascadeClassifierImpl::read(const FileNode& node)925{926read_(node);927}928929int CascadeClassifierImpl::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, int scaleIdx, double& weight )930{931CV_INSTRUMENT_REGION();932933assert( !oldCascade &&934(data.featureType == FeatureEvaluator::HAAR ||935data.featureType == FeatureEvaluator::LBP ||936data.featureType == FeatureEvaluator::HOG) );937938if( !evaluator->setWindow(pt, scaleIdx) )939return -1;940if( data.maxNodesPerTree == 1 )941{942if( data.featureType == FeatureEvaluator::HAAR )943return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );944else if( data.featureType == FeatureEvaluator::LBP )945return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );946else947return -2;948}949else950{951if( data.featureType == FeatureEvaluator::HAAR )952return predictOrdered<HaarEvaluator>( *this, evaluator, weight );953else if( data.featureType == FeatureEvaluator::LBP )954return predictCategorical<LBPEvaluator>( *this, evaluator, weight );955else956return -2;957}958}959960void CascadeClassifierImpl::setMaskGenerator(const Ptr<MaskGenerator>& _maskGenerator)961{962maskGenerator=_maskGenerator;963}964Ptr<CascadeClassifierImpl::MaskGenerator> CascadeClassifierImpl::getMaskGenerator()965{966return maskGenerator;967}968969Ptr<BaseCascadeClassifier::MaskGenerator> createFaceDetectionMaskGenerator()970{971return Ptr<BaseCascadeClassifier::MaskGenerator>();972}973974class CascadeClassifierInvoker : public ParallelLoopBody975{976public:977CascadeClassifierInvoker( CascadeClassifierImpl& _cc, int _nscales, int _nstripes,978const FeatureEvaluator::ScaleData* _scaleData,979const int* _stripeSizes, std::vector<Rect>& _vec,980std::vector<int>& _levels, std::vector<double>& _weights,981bool outputLevels, const Mat& _mask, Mutex* _mtx)982{983classifier = &_cc;984nscales = _nscales;985nstripes = _nstripes;986scaleData = _scaleData;987stripeSizes = _stripeSizes;988rectangles = &_vec;989rejectLevels = outputLevels ? &_levels : 0;990levelWeights = outputLevels ? &_weights : 0;991mask = _mask;992mtx = _mtx;993}994995void operator()(const Range& range) const CV_OVERRIDE996{997CV_INSTRUMENT_REGION();998999Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();1000double gypWeight = 0.;1001Size origWinSize = classifier->data.origWinSize;10021003for( int scaleIdx = 0; scaleIdx < nscales; scaleIdx++ )1004{1005const FeatureEvaluator::ScaleData& s = scaleData[scaleIdx];1006float scalingFactor = s.scale;1007int yStep = s.ystep;1008int stripeSize = stripeSizes[scaleIdx];1009int y0 = range.start*stripeSize;1010Size szw = s.getWorkingSize(origWinSize);1011int y1 = std::min(range.end*stripeSize, szw.height);1012Size winSize(cvRound(origWinSize.width * scalingFactor),1013cvRound(origWinSize.height * scalingFactor));10141015for( int y = y0; y < y1; y += yStep )1016{1017for( int x = 0; x < szw.width; x += yStep )1018{1019int result = classifier->runAt(evaluator, Point(x, y), scaleIdx, gypWeight);1020if( rejectLevels )1021{1022if( result == 1 )1023result = -(int)classifier->data.stages.size();1024if( classifier->data.stages.size() + result == 0 )1025{1026mtx->lock();1027rectangles->push_back(Rect(cvRound(x*scalingFactor),1028cvRound(y*scalingFactor),1029winSize.width, winSize.height));1030rejectLevels->push_back(-result);1031levelWeights->push_back(gypWeight);1032mtx->unlock();1033}1034}1035else if( result > 0 )1036{1037mtx->lock();1038rectangles->push_back(Rect(cvRound(x*scalingFactor),1039cvRound(y*scalingFactor),1040winSize.width, winSize.height));1041mtx->unlock();1042}1043if( result == 0 )1044x += yStep;1045}1046}1047}1048}10491050CascadeClassifierImpl* classifier;1051std::vector<Rect>* rectangles;1052int nscales, nstripes;1053const FeatureEvaluator::ScaleData* scaleData;1054const int* stripeSizes;1055std::vector<int> *rejectLevels;1056std::vector<double> *levelWeights;1057std::vector<float> scales;1058Mat mask;1059Mutex* mtx;1060};106110621063struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };1064struct getNeighbors { int operator ()(const CvAvgComp& e) const { return e.neighbors; } };10651066#ifdef HAVE_OPENCL1067bool CascadeClassifierImpl::ocl_detectMultiScaleNoGrouping( const std::vector<float>& scales,1068std::vector<Rect>& candidates )1069{1070int featureType = getFeatureType();1071std::vector<UMat> bufs;1072featureEvaluator->getUMats(bufs);1073Size localsz = featureEvaluator->getLocalSize();1074if( localsz.empty() )1075return false;1076Size lbufSize = featureEvaluator->getLocalBufSize();1077size_t localsize[] = { (size_t)localsz.width, (size_t)localsz.height };1078const int grp_per_CU = 12;1079size_t globalsize[] = { grp_per_CU*ocl::Device::getDefault().maxComputeUnits()*localsize[0], localsize[1] };1080bool ok = false;10811082ufacepos.create(1, MAX_FACES*3+1, CV_32S);1083UMat ufacepos_count(ufacepos, Rect(0, 0, 1, 1));1084ufacepos_count.setTo(Scalar::all(0));10851086if( ustages.empty() )1087{1088copyVectorToUMat(data.stages, ustages);1089if (!data.stumps.empty())1090copyVectorToUMat(data.stumps, unodes);1091else1092copyVectorToUMat(data.nodes, unodes);1093copyVectorToUMat(data.leaves, uleaves);1094if( !data.subsets.empty() )1095copyVectorToUMat(data.subsets, usubsets);1096}10971098int nstages = (int)data.stages.size();1099int splitstage_ocl = 1;11001101if( featureType == FeatureEvaluator::HAAR )1102{1103Ptr<HaarEvaluator> haar = featureEvaluator.dynamicCast<HaarEvaluator>();1104if( haar.empty() )1105return false;11061107if( haarKernel.empty() )1108{1109String opts;1110if ( !lbufSize.empty() )1111opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SUM_BUF_SIZE=%d -D SUM_BUF_STEP=%d -D NODE_COUNT=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D HAAR",1112localsz.width, localsz.height, lbufSize.area(), lbufSize.width, data.maxNodesPerTree, splitstage_ocl, nstages, MAX_FACES);1113else1114opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D NODE_COUNT=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D HAAR",1115localsz.width, localsz.height, data.maxNodesPerTree, splitstage_ocl, nstages, MAX_FACES);1116haarKernel.create("runHaarClassifier", ocl::objdetect::cascadedetect_oclsrc, opts);1117if( haarKernel.empty() )1118return false;1119}11201121Rect normrect = haar->getNormRect();1122int sqofs = haar->getSquaresOffset();11231124haarKernel.args((int)scales.size(),1125ocl::KernelArg::PtrReadOnly(bufs[0]), // scaleData1126ocl::KernelArg::ReadOnlyNoSize(bufs[1]), // sum1127ocl::KernelArg::PtrReadOnly(bufs[2]), // optfeatures11281129// cascade classifier1130ocl::KernelArg::PtrReadOnly(ustages),1131ocl::KernelArg::PtrReadOnly(unodes),1132ocl::KernelArg::PtrReadOnly(uleaves),11331134ocl::KernelArg::PtrWriteOnly(ufacepos), // positions1135normrect, sqofs, data.origWinSize);1136ok = haarKernel.run(2, globalsize, localsize, true);1137}1138else if( featureType == FeatureEvaluator::LBP )1139{1140if (data.maxNodesPerTree > 1)1141return false;11421143Ptr<LBPEvaluator> lbp = featureEvaluator.dynamicCast<LBPEvaluator>();1144if( lbp.empty() )1145return false;11461147if( lbpKernel.empty() )1148{1149String opts;1150if ( !lbufSize.empty() )1151opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SUM_BUF_SIZE=%d -D SUM_BUF_STEP=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D LBP",1152localsz.width, localsz.height, lbufSize.area(), lbufSize.width, splitstage_ocl, nstages, MAX_FACES);1153else1154opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D LBP",1155localsz.width, localsz.height, splitstage_ocl, nstages, MAX_FACES);1156lbpKernel.create("runLBPClassifierStumpSimple", ocl::objdetect::cascadedetect_oclsrc, opts);1157if( lbpKernel.empty() )1158return false;1159}11601161int subsetSize = (data.ncategories + 31)/32;1162lbpKernel.args((int)scales.size(),1163ocl::KernelArg::PtrReadOnly(bufs[0]), // scaleData1164ocl::KernelArg::ReadOnlyNoSize(bufs[1]), // sum1165ocl::KernelArg::PtrReadOnly(bufs[2]), // optfeatures11661167// cascade classifier1168ocl::KernelArg::PtrReadOnly(ustages),1169ocl::KernelArg::PtrReadOnly(unodes),1170ocl::KernelArg::PtrReadOnly(usubsets),1171subsetSize,11721173ocl::KernelArg::PtrWriteOnly(ufacepos), // positions1174data.origWinSize);11751176ok = lbpKernel.run(2, globalsize, localsize, true);1177}11781179if( ok )1180{1181Mat facepos = ufacepos.getMat(ACCESS_READ);1182const int* fptr = facepos.ptr<int>();1183int nfaces = fptr[0];1184nfaces = std::min(nfaces, (int)MAX_FACES);11851186for( int i = 0; i < nfaces; i++ )1187{1188const FeatureEvaluator::ScaleData& s = featureEvaluator->getScaleData(fptr[i*3 + 1]);1189candidates.push_back(Rect(cvRound(fptr[i*3 + 2]*s.scale),1190cvRound(fptr[i*3 + 3]*s.scale),1191cvRound(data.origWinSize.width*s.scale),1192cvRound(data.origWinSize.height*s.scale)));1193}1194}1195return ok;1196}1197#endif11981199bool CascadeClassifierImpl::isOldFormatCascade() const1200{1201return !oldCascade.empty();1202}12031204int CascadeClassifierImpl::getFeatureType() const1205{1206return featureEvaluator->getFeatureType();1207}12081209Size CascadeClassifierImpl::getOriginalWindowSize() const1210{1211return data.origWinSize;1212}12131214void* CascadeClassifierImpl::getOldCascade()1215{1216return oldCascade;1217}12181219static void detectMultiScaleOldFormat( const Mat& image, Ptr<CvHaarClassifierCascade> oldCascade,1220std::vector<Rect>& objects,1221std::vector<int>& rejectLevels,1222std::vector<double>& levelWeights,1223std::vector<CvAvgComp>& vecAvgComp,1224double scaleFactor, int minNeighbors,1225int flags, Size minObjectSize, Size maxObjectSize,1226bool outputRejectLevels = false )1227{1228MemStorage storage(cvCreateMemStorage(0));1229CvMat _image = cvMat(image);1230CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,1231minNeighbors, flags, cvSize(minObjectSize), cvSize(maxObjectSize), outputRejectLevels );1232Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);1233objects.resize(vecAvgComp.size());1234std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());1235}12361237void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::vector<Rect>& candidates,1238std::vector<int>& rejectLevels, std::vector<double>& levelWeights,1239double scaleFactor, Size minObjectSize, Size maxObjectSize,1240bool outputRejectLevels )1241{1242CV_INSTRUMENT_REGION();12431244Size imgsz = _image.size();1245Size originalWindowSize = getOriginalWindowSize();12461247if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )1248maxObjectSize = imgsz;12491250// If a too small image patch is entering the function, break early before any processing1251if( (imgsz.height < originalWindowSize.height) || (imgsz.width < originalWindowSize.width) )1252return;12531254std::vector<float> all_scales, scales;1255all_scales.reserve(1024);1256scales.reserve(1024);12571258// First calculate all possible scales for the given image and model, then remove undesired scales1259// This allows us to cope with single scale detections (minSize == maxSize) that do not fall on precalculated scale1260for( double factor = 1; ; factor *= scaleFactor )1261{1262Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );1263if( windowSize.width > imgsz.width || windowSize.height > imgsz.height )1264break;1265all_scales.push_back((float)factor);1266}12671268// This will capture allowed scales and a minSize==maxSize scale, if it is in the precalculated scales1269for( size_t index = 0; index < all_scales.size(); index++){1270Size windowSize( cvRound(originalWindowSize.width*all_scales[index]), cvRound(originalWindowSize.height*all_scales[index]) );1271if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height)1272break;1273if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )1274continue;1275scales.push_back(all_scales[index]);1276}12771278// If minSize and maxSize parameter are equal and scales is not filled yet, then the scale was not available in the precalculated scales1279// In that case we want to return the most fitting scale (closest corresponding scale using L2 distance)1280if( scales.empty() && !all_scales.empty() ){1281std::vector<double> distances;1282// Calculate distances1283for(size_t v = 0; v < all_scales.size(); v++){1284Size windowSize( cvRound(originalWindowSize.width*all_scales[v]), cvRound(originalWindowSize.height*all_scales[v]) );1285double d = (minObjectSize.width - windowSize.width) * (minObjectSize.width - windowSize.width)1286+ (minObjectSize.height - windowSize.height) * (minObjectSize.height - windowSize.height);1287distances.push_back(d);1288}1289// Take the index of lowest value1290// Use that index to push the correct scale parameter1291size_t iMin=0;1292for(size_t i = 0; i < distances.size(); ++i){1293if(distances[iMin] > distances[i])1294iMin=i;1295}1296scales.push_back(all_scales[iMin]);1297}12981299candidates.clear();1300rejectLevels.clear();1301levelWeights.clear();13021303#ifdef HAVE_OPENCL1304bool use_ocl = tryOpenCL && ocl::isOpenCLActivated() &&1305OCL_FORCE_CHECK(_image.isUMat()) &&1306!featureEvaluator->getLocalSize().empty() &&1307(data.minNodesPerTree == data.maxNodesPerTree) &&1308!isOldFormatCascade() &&1309maskGenerator.empty() &&1310!outputRejectLevels;1311#endif13121313Mat grayImage;1314_InputArray gray;13151316if (_image.channels() > 1)1317cvtColor(_image, grayImage, COLOR_BGR2GRAY);1318else if (_image.isMat())1319grayImage = _image.getMat();1320else1321_image.copyTo(grayImage);1322gray = grayImage;13231324if( !featureEvaluator->setImage(gray, scales) )1325return;13261327#ifdef HAVE_OPENCL1328// OpenCL code1329CV_OCL_RUN(use_ocl, ocl_detectMultiScaleNoGrouping( scales, candidates ))13301331if (use_ocl)1332tryOpenCL = false;1333#endif13341335// CPU code1336featureEvaluator->getMats();1337{1338Mat currentMask;1339if (maskGenerator)1340currentMask = maskGenerator->generateMask(gray.getMat());13411342size_t i, nscales = scales.size();1343cv::AutoBuffer<int> stripeSizeBuf(nscales);1344int* stripeSizes = stripeSizeBuf.data();1345const FeatureEvaluator::ScaleData* s = &featureEvaluator->getScaleData(0);1346Size szw = s->getWorkingSize(data.origWinSize);1347int nstripes = cvCeil(szw.width/32.);1348for( i = 0; i < nscales; i++ )1349{1350szw = s[i].getWorkingSize(data.origWinSize);1351stripeSizes[i] = std::max((szw.height/s[i].ystep + nstripes-1)/nstripes, 1)*s[i].ystep;1352}13531354CascadeClassifierInvoker invoker(*this, (int)nscales, nstripes, s, stripeSizes,1355candidates, rejectLevels, levelWeights,1356outputRejectLevels, currentMask, &mtx);1357parallel_for_(Range(0, nstripes), invoker);1358}1359}136013611362void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,1363std::vector<int>& rejectLevels,1364std::vector<double>& levelWeights,1365double scaleFactor, int minNeighbors,1366int flags, Size minObjectSize, Size maxObjectSize,1367bool outputRejectLevels )1368{1369CV_INSTRUMENT_REGION();13701371CV_Assert( scaleFactor > 1 && _image.depth() == CV_8U );13721373if( empty() )1374return;13751376if( isOldFormatCascade() )1377{1378Mat image = _image.getMat();1379std::vector<CvAvgComp> fakeVecAvgComp;1380detectMultiScaleOldFormat( image, oldCascade, objects, rejectLevels, levelWeights, fakeVecAvgComp, scaleFactor,1381minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );1382}1383else1384{1385detectMultiScaleNoGrouping( _image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize,1386outputRejectLevels );1387const double GROUP_EPS = 0.2;1388if( outputRejectLevels )1389{1390groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );1391}1392else1393{1394groupRectangles( objects, minNeighbors, GROUP_EPS );1395}1396}1397}13981399void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,1400double scaleFactor, int minNeighbors,1401int flags, Size minObjectSize, Size maxObjectSize)1402{1403CV_INSTRUMENT_REGION();14041405std::vector<int> fakeLevels;1406std::vector<double> fakeWeights;1407detectMultiScale( _image, objects, fakeLevels, fakeWeights, scaleFactor,1408minNeighbors, flags, minObjectSize, maxObjectSize );1409}14101411void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,1412std::vector<int>& numDetections, double scaleFactor,1413int minNeighbors, int flags, Size minObjectSize,1414Size maxObjectSize )1415{1416CV_INSTRUMENT_REGION();14171418Mat image = _image.getMat();1419CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );14201421if( empty() )1422return;14231424std::vector<int> fakeLevels;1425std::vector<double> fakeWeights;1426if( isOldFormatCascade() )1427{1428std::vector<CvAvgComp> vecAvgComp;1429detectMultiScaleOldFormat( image, oldCascade, objects, fakeLevels, fakeWeights, vecAvgComp, scaleFactor,1430minNeighbors, flags, minObjectSize, maxObjectSize );1431numDetections.resize(vecAvgComp.size());1432std::transform(vecAvgComp.begin(), vecAvgComp.end(), numDetections.begin(), getNeighbors());1433}1434else1435{1436detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize );1437const double GROUP_EPS = 0.2;1438groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS );1439}1440}144114421443CascadeClassifierImpl::Data::Data()1444{1445stageType = featureType = ncategories = maxNodesPerTree = minNodesPerTree = 0;1446}14471448bool CascadeClassifierImpl::Data::read(const FileNode &root)1449{1450static const float THRESHOLD_EPS = 1e-5f;14511452// load stage params1453String stageTypeStr = (String)root[CC_STAGE_TYPE];1454if( stageTypeStr == CC_BOOST )1455stageType = BOOST;1456else1457return false;14581459String featureTypeStr = (String)root[CC_FEATURE_TYPE];1460if( featureTypeStr == CC_HAAR )1461featureType = FeatureEvaluator::HAAR;1462else if( featureTypeStr == CC_LBP )1463featureType = FeatureEvaluator::LBP;1464else if( featureTypeStr == CC_HOG )1465{1466featureType = FeatureEvaluator::HOG;1467CV_Error(Error::StsNotImplemented, "HOG cascade is not supported in 3.0");1468}1469else1470return false;14711472origWinSize.width = (int)root[CC_WIDTH];1473origWinSize.height = (int)root[CC_HEIGHT];1474CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );14751476// load feature params1477FileNode fn = root[CC_FEATURE_PARAMS];1478if( fn.empty() )1479return false;14801481ncategories = fn[CC_MAX_CAT_COUNT];1482int subsetSize = (ncategories + 31)/32,1483nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );14841485// load stages1486fn = root[CC_STAGES];1487if( fn.empty() )1488return false;14891490stages.reserve(fn.size());1491classifiers.clear();1492nodes.clear();1493stumps.clear();14941495FileNodeIterator it = fn.begin(), it_end = fn.end();1496minNodesPerTree = INT_MAX;1497maxNodesPerTree = 0;14981499for( int si = 0; it != it_end; si++, ++it )1500{1501FileNode fns = *it;1502Stage stage;1503stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;1504fns = fns[CC_WEAK_CLASSIFIERS];1505if(fns.empty())1506return false;1507stage.ntrees = (int)fns.size();1508stage.first = (int)classifiers.size();1509stages.push_back(stage);1510classifiers.reserve(stages[si].first + stages[si].ntrees);15111512FileNodeIterator it1 = fns.begin(), it1_end = fns.end();1513for( ; it1 != it1_end; ++it1 ) // weak trees1514{1515FileNode fnw = *it1;1516FileNode internalNodes = fnw[CC_INTERNAL_NODES];1517FileNode leafValues = fnw[CC_LEAF_VALUES];1518if( internalNodes.empty() || leafValues.empty() )1519return false;15201521DTree tree;1522tree.nodeCount = (int)internalNodes.size()/nodeStep;1523minNodesPerTree = std::min(minNodesPerTree, tree.nodeCount);1524maxNodesPerTree = std::max(maxNodesPerTree, tree.nodeCount);15251526classifiers.push_back(tree);15271528nodes.reserve(nodes.size() + tree.nodeCount);1529leaves.reserve(leaves.size() + leafValues.size());1530if( subsetSize > 0 )1531subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);15321533FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();15341535for( ; internalNodesIter != internalNodesEnd; ) // nodes1536{1537DTreeNode node;1538node.left = (int)*internalNodesIter; ++internalNodesIter;1539node.right = (int)*internalNodesIter; ++internalNodesIter;1540node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;1541if( subsetSize > 0 )1542{1543for( int j = 0; j < subsetSize; j++, ++internalNodesIter )1544subsets.push_back((int)*internalNodesIter);1545node.threshold = 0.f;1546}1547else1548{1549node.threshold = (float)*internalNodesIter; ++internalNodesIter;1550}1551nodes.push_back(node);1552}15531554internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();15551556for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves1557leaves.push_back((float)*internalNodesIter);1558}1559}15601561if( maxNodesPerTree == 1 )1562{1563int nodeOfs = 0, leafOfs = 0;1564size_t nstages = stages.size();1565for( size_t stageIdx = 0; stageIdx < nstages; stageIdx++ )1566{1567const Stage& stage = stages[stageIdx];15681569int ntrees = stage.ntrees;1570for( int i = 0; i < ntrees; i++, nodeOfs++, leafOfs+= 2 )1571{1572const DTreeNode& node = nodes[nodeOfs];1573stumps.push_back(Stump(node.featureIdx, node.threshold,1574leaves[leafOfs], leaves[leafOfs+1]));1575}1576}1577}15781579return true;1580}158115821583bool CascadeClassifierImpl::read_(const FileNode& root)1584{1585#ifdef HAVE_OPENCL1586tryOpenCL = true;1587haarKernel = ocl::Kernel();1588lbpKernel = ocl::Kernel();1589#endif1590ustages.release();1591unodes.release();1592uleaves.release();1593if( !data.read(root) )1594return false;15951596// load features1597featureEvaluator = FeatureEvaluator::create(data.featureType);1598FileNode fn = root[CC_FEATURES];1599if( fn.empty() )1600return false;16011602return featureEvaluator->read(fn, data.origWinSize);1603}16041605void DefaultDeleter<CvHaarClassifierCascade>::operator ()(CvHaarClassifierCascade* obj) const { cvReleaseHaarClassifierCascade(&obj); }160616071608BaseCascadeClassifier::~BaseCascadeClassifier()1609{1610}16111612CascadeClassifier::CascadeClassifier() {}1613CascadeClassifier::CascadeClassifier(const String& filename)1614{1615load(filename);1616}16171618CascadeClassifier::~CascadeClassifier()1619{1620}16211622bool CascadeClassifier::empty() const1623{1624return cc.empty() || cc->empty();1625}16261627bool CascadeClassifier::load( const String& filename )1628{1629cc = makePtr<CascadeClassifierImpl>();1630if(!cc->load(filename))1631cc.release();1632return !empty();1633}16341635bool CascadeClassifier::read(const FileNode &root)1636{1637Ptr<CascadeClassifierImpl> ccimpl = makePtr<CascadeClassifierImpl>();1638bool ok = ccimpl->read_(root);1639if( ok )1640cc = ccimpl.staticCast<BaseCascadeClassifier>();1641else1642cc.release();1643return ok;1644}16451646void clipObjects(Size sz, std::vector<Rect>& objects,1647std::vector<int>* a, std::vector<double>* b)1648{1649size_t i, j = 0, n = objects.size();1650Rect win0 = Rect(0, 0, sz.width, sz.height);1651if(a)1652{1653CV_Assert(a->size() == n);1654}1655if(b)1656{1657CV_Assert(b->size() == n);1658}16591660for( i = 0; i < n; i++ )1661{1662Rect r = win0 & objects[i];1663if( !r.empty() )1664{1665objects[j] = r;1666if( i > j )1667{1668if(a) a->at(j) = a->at(i);1669if(b) b->at(j) = b->at(i);1670}1671j++;1672}1673}16741675if( j < n )1676{1677objects.resize(j);1678if(a) a->resize(j);1679if(b) b->resize(j);1680}1681}16821683void CascadeClassifier::detectMultiScale( InputArray image,1684CV_OUT std::vector<Rect>& objects,1685double scaleFactor,1686int minNeighbors, int flags,1687Size minSize,1688Size maxSize )1689{1690CV_INSTRUMENT_REGION();16911692CV_Assert(!empty());1693cc->detectMultiScale(image, objects, scaleFactor, minNeighbors, flags, minSize, maxSize);1694clipObjects(image.size(), objects, 0, 0);1695}16961697void CascadeClassifier::detectMultiScale( InputArray image,1698CV_OUT std::vector<Rect>& objects,1699CV_OUT std::vector<int>& numDetections,1700double scaleFactor,1701int minNeighbors, int flags,1702Size minSize, Size maxSize )1703{1704CV_INSTRUMENT_REGION();17051706CV_Assert(!empty());1707cc->detectMultiScale(image, objects, numDetections,1708scaleFactor, minNeighbors, flags, minSize, maxSize);1709clipObjects(image.size(), objects, &numDetections, 0);1710}17111712void CascadeClassifier::detectMultiScale( InputArray image,1713CV_OUT std::vector<Rect>& objects,1714CV_OUT std::vector<int>& rejectLevels,1715CV_OUT std::vector<double>& levelWeights,1716double scaleFactor,1717int minNeighbors, int flags,1718Size minSize, Size maxSize,1719bool outputRejectLevels )1720{1721CV_INSTRUMENT_REGION();17221723CV_Assert(!empty());1724cc->detectMultiScale(image, objects, rejectLevels, levelWeights,1725scaleFactor, minNeighbors, flags,1726minSize, maxSize, outputRejectLevels);1727clipObjects(image.size(), objects, &rejectLevels, &levelWeights);1728}17291730bool CascadeClassifier::isOldFormatCascade() const1731{1732CV_Assert(!empty());1733return cc->isOldFormatCascade();1734}17351736Size CascadeClassifier::getOriginalWindowSize() const1737{1738CV_Assert(!empty());1739return cc->getOriginalWindowSize();1740}17411742int CascadeClassifier::getFeatureType() const1743{1744CV_Assert(!empty());1745return cc->getFeatureType();1746}17471748void* CascadeClassifier::getOldCascade()1749{1750CV_Assert(!empty());1751return cc->getOldCascade();1752}17531754void CascadeClassifier::setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator)1755{1756CV_Assert(!empty());1757cc->setMaskGenerator(maskGenerator);1758}17591760Ptr<BaseCascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()1761{1762CV_Assert(!empty());1763return cc->getMaskGenerator();1764}17651766} // namespace cv176717681769