opencv/modules/objdetect/src/cascadedetect.hpp

657 lines
22 KiB
C++

#pragma once
#include "opencv2/core/ocl.hpp"
namespace cv
{
void clipObjects(Size sz, std::vector<Rect>& objects,
std::vector<int>* a, std::vector<double>* b);
class FeatureEvaluator
{
public:
enum
{
HAAR = 0,
LBP = 1,
HOG = 2
};
struct ScaleData
{
ScaleData() { scale = 0.f; layer_ofs = ystep = 0; }
Size getWorkingSize(Size winSize) const
{
return Size(std::max(szi.width - winSize.width, 0),
std::max(szi.height - winSize.height, 0));
}
float scale;
Size szi;
int layer_ofs, ystep;
};
virtual ~FeatureEvaluator();
virtual bool read(const FileNode& node, Size origWinSize);
virtual Ptr<FeatureEvaluator> clone() const;
virtual int getFeatureType() const;
int getNumChannels() const { return nchannels; }
virtual bool setImage(InputArray img, const std::vector<float>& scales);
virtual bool setWindow(Point p, int scaleIdx);
const ScaleData& getScaleData(int scaleIdx) const
{
CV_Assert( 0 <= scaleIdx && scaleIdx < (int)scaleData->size());
return scaleData->at(scaleIdx);
}
virtual void getUMats(std::vector<UMat>& bufs);
virtual void getMats();
Size getLocalSize() const { return localSize; }
Size getLocalBufSize() const { return lbufSize; }
virtual float calcOrd(int featureIdx) const;
virtual int calcCat(int featureIdx) const;
static Ptr<FeatureEvaluator> create(int type);
protected:
enum { SBUF_VALID=1, USBUF_VALID=2 };
int sbufFlag;
bool updateScaleData( Size imgsz, const std::vector<float>& _scales );
virtual void computeChannels( int, InputArray ) {}
virtual void computeOptFeatures() {}
Size origWinSize, sbufSize, localSize, lbufSize;
int nchannels;
Mat sbuf, rbuf;
UMat urbuf, usbuf, ufbuf, uscaleData;
Ptr<std::vector<ScaleData> > scaleData;
};
class CascadeClassifierImpl CV_FINAL : public BaseCascadeClassifier
{
public:
CascadeClassifierImpl();
virtual ~CascadeClassifierImpl() CV_OVERRIDE;
bool empty() const CV_OVERRIDE;
bool load( const String& filename ) CV_OVERRIDE;
void read( const FileNode& node ) CV_OVERRIDE;
bool read_( const FileNode& node );
void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size(),
Size maxSize = Size() ) CV_OVERRIDE;
void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
CV_OUT std::vector<int>& numDetections,
double scaleFactor=1.1,
int minNeighbors=3, int flags=0,
Size minSize=Size(),
Size maxSize=Size() ) CV_OVERRIDE;
void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
CV_OUT std::vector<int>& rejectLevels,
CV_OUT std::vector<double>& levelWeights,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size(),
Size maxSize = Size(),
bool outputRejectLevels = false ) CV_OVERRIDE;
bool isOldFormatCascade() const CV_OVERRIDE;
Size getOriginalWindowSize() const CV_OVERRIDE;
int getFeatureType() const CV_OVERRIDE;
void* getOldCascade() CV_OVERRIDE;
void setMaskGenerator(const Ptr<MaskGenerator>& maskGenerator) CV_OVERRIDE;
Ptr<MaskGenerator> getMaskGenerator() CV_OVERRIDE;
protected:
enum { SUM_ALIGN = 64 };
bool detectSingleScale( InputArray image, Size processingRectSize,
int yStep, double factor, std::vector<Rect>& candidates,
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
Size sumSize0, bool outputRejectLevels = false );
#ifdef HAVE_OPENCL
bool ocl_detectMultiScaleNoGrouping( const std::vector<float>& scales,
std::vector<Rect>& candidates );
#endif
void detectMultiScaleNoGrouping( InputArray image, std::vector<Rect>& candidates,
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
double scaleFactor, Size minObjectSize, Size maxObjectSize,
bool outputRejectLevels = false );
enum { MAX_FACES = 10000 };
enum { BOOST = 0 };
enum { DO_CANNY_PRUNING = CASCADE_DO_CANNY_PRUNING,
SCALE_IMAGE = CASCADE_SCALE_IMAGE,
FIND_BIGGEST_OBJECT = CASCADE_FIND_BIGGEST_OBJECT,
DO_ROUGH_SEARCH = CASCADE_DO_ROUGH_SEARCH
};
friend class CascadeClassifierInvoker;
friend class SparseCascadeClassifierInvoker;
template<class FEval>
friend int predictOrdered( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
template<class FEval>
friend int predictCategorical( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
template<class FEval>
friend int predictOrderedStump( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
template<class FEval>
friend int predictCategoricalStump( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
int runAt( Ptr<FeatureEvaluator>& feval, Point pt, int scaleIdx, double& weight );
class Data
{
public:
struct DTreeNode
{
int featureIdx;
float threshold; // for ordered features only
int left;
int right;
};
struct DTree
{
int nodeCount;
};
struct Stage
{
int first;
int ntrees;
float threshold;
};
struct Stump
{
Stump() : featureIdx(0), threshold(0), left(0), right(0) { }
Stump(int _featureIdx, float _threshold, float _left, float _right)
: featureIdx(_featureIdx), threshold(_threshold), left(_left), right(_right) {}
int featureIdx;
float threshold;
float left;
float right;
};
Data();
bool read(const FileNode &node);
int stageType;
int featureType;
int ncategories;
int minNodesPerTree, maxNodesPerTree;
Size origWinSize;
std::vector<Stage> stages;
std::vector<DTree> classifiers;
std::vector<DTreeNode> nodes;
std::vector<float> leaves;
std::vector<int> subsets;
std::vector<Stump> stumps;
};
Data data;
Ptr<FeatureEvaluator> featureEvaluator;
Ptr<CvHaarClassifierCascade> oldCascade;
Ptr<MaskGenerator> maskGenerator;
UMat ugrayImage;
UMat ufacepos, ustages, unodes, uleaves, usubsets;
#ifdef HAVE_OPENCL
ocl::Kernel haarKernel, lbpKernel;
bool tryOpenCL;
#endif
Mutex mtx;
};
#define CC_CASCADE_PARAMS "cascadeParams"
#define CC_STAGE_TYPE "stageType"
#define CC_FEATURE_TYPE "featureType"
#define CC_HEIGHT "height"
#define CC_WIDTH "width"
#define CC_STAGE_NUM "stageNum"
#define CC_STAGES "stages"
#define CC_STAGE_PARAMS "stageParams"
#define CC_BOOST "BOOST"
#define CC_MAX_DEPTH "maxDepth"
#define CC_WEAK_COUNT "maxWeakCount"
#define CC_STAGE_THRESHOLD "stageThreshold"
#define CC_WEAK_CLASSIFIERS "weakClassifiers"
#define CC_INTERNAL_NODES "internalNodes"
#define CC_LEAF_VALUES "leafValues"
#define CC_FEATURES "features"
#define CC_FEATURE_PARAMS "featureParams"
#define CC_MAX_CAT_COUNT "maxCatCount"
#define CC_HAAR "HAAR"
#define CC_RECTS "rects"
#define CC_TILTED "tilted"
#define CC_LBP "LBP"
#define CC_RECT "rect"
#define CC_HOG "HOG"
#define CV_SUM_PTRS( p0, p1, p2, p3, sum, rect, step ) \
/* (x, y) */ \
(p0) = sum + (rect).x + (step) * (rect).y, \
/* (x + w, y) */ \
(p1) = sum + (rect).x + (rect).width + (step) * (rect).y, \
/* (x, y + h) */ \
(p2) = sum + (rect).x + (step) * ((rect).y + (rect).height), \
/* (x + w, y + h) */ \
(p3) = sum + (rect).x + (rect).width + (step) * ((rect).y + (rect).height)
#define CV_TILTED_PTRS( p0, p1, p2, p3, tilted, rect, step ) \
/* (x, y) */ \
(p0) = tilted + (rect).x + (step) * (rect).y, \
/* (x - h, y + h) */ \
(p1) = tilted + (rect).x - (rect).height + (step) * ((rect).y + (rect).height), \
/* (x + w, y + w) */ \
(p2) = tilted + (rect).x + (rect).width + (step) * ((rect).y + (rect).width), \
/* (x + w - h, y + w + h) */ \
(p3) = tilted + (rect).x + (rect).width - (rect).height \
+ (step) * ((rect).y + (rect).width + (rect).height)
#define CALC_SUM_(p0, p1, p2, p3, offset) \
((p0)[offset] - (p1)[offset] - (p2)[offset] + (p3)[offset])
#define CALC_SUM(rect,offset) CALC_SUM_((rect)[0], (rect)[1], (rect)[2], (rect)[3], offset)
#define CV_SUM_OFS( p0, p1, p2, p3, sum, rect, step ) \
/* (x, y) */ \
(p0) = sum + (rect).x + (step) * (rect).y, \
/* (x + w, y) */ \
(p1) = sum + (rect).x + (rect).width + (step) * (rect).y, \
/* (x, y + h) */ \
(p2) = sum + (rect).x + (step) * ((rect).y + (rect).height), \
/* (x + w, y + h) */ \
(p3) = sum + (rect).x + (rect).width + (step) * ((rect).y + (rect).height)
#define CV_TILTED_OFS( p0, p1, p2, p3, tilted, rect, step ) \
/* (x, y) */ \
(p0) = tilted + (rect).x + (step) * (rect).y, \
/* (x - h, y + h) */ \
(p1) = tilted + (rect).x - (rect).height + (step) * ((rect).y + (rect).height), \
/* (x + w, y + w) */ \
(p2) = tilted + (rect).x + (rect).width + (step) * ((rect).y + (rect).width), \
/* (x + w - h, y + w + h) */ \
(p3) = tilted + (rect).x + (rect).width - (rect).height \
+ (step) * ((rect).y + (rect).width + (rect).height)
#define CALC_SUM_OFS_(p0, p1, p2, p3, ptr) \
((ptr)[p0] - (ptr)[p1] - (ptr)[p2] + (ptr)[p3])
#define CALC_SUM_OFS(rect, ptr) CALC_SUM_OFS_((rect)[0], (rect)[1], (rect)[2], (rect)[3], ptr)
//---------------------------------------------- HaarEvaluator ---------------------------------------
class HaarEvaluator CV_FINAL : public FeatureEvaluator
{
public:
struct Feature
{
Feature();
bool read(const FileNode& node, const Size& origWinSize);
bool tilted;
enum { RECT_NUM = 3 };
struct RectWeigth
{
Rect r;
float weight;
} rect[RECT_NUM];
};
struct OptFeature
{
OptFeature();
enum { RECT_NUM = Feature::RECT_NUM };
float calc( const int* pwin ) const;
void setOffsets( const Feature& _f, int step, int tofs );
int ofs[RECT_NUM][4];
float weight[4];
};
HaarEvaluator();
virtual ~HaarEvaluator() CV_OVERRIDE;
virtual bool read( const FileNode& node, Size origWinSize) CV_OVERRIDE;
virtual Ptr<FeatureEvaluator> clone() const CV_OVERRIDE;
virtual int getFeatureType() const CV_OVERRIDE { return FeatureEvaluator::HAAR; }
virtual bool setWindow(Point p, int scaleIdx) CV_OVERRIDE;
Rect getNormRect() const;
int getSquaresOffset() const;
float operator()(int featureIdx) const
{ return optfeaturesPtr[featureIdx].calc(pwin) * varianceNormFactor; }
virtual float calcOrd(int featureIdx) const CV_OVERRIDE
{ return (*this)(featureIdx); }
protected:
virtual void computeChannels( int i, InputArray img ) CV_OVERRIDE;
virtual void computeOptFeatures() CV_OVERRIDE;
Ptr<std::vector<Feature> > features;
Ptr<std::vector<OptFeature> > optfeatures;
Ptr<std::vector<OptFeature> > optfeatures_lbuf;
bool hasTiltedFeatures;
int tofs, sqofs;
Vec4i nofs;
Rect normrect;
const int* pwin;
OptFeature* optfeaturesPtr; // optimization
float varianceNormFactor;
};
inline HaarEvaluator::Feature :: Feature()
{
tilted = false;
rect[0].r = rect[1].r = rect[2].r = Rect();
rect[0].weight = rect[1].weight = rect[2].weight = 0;
}
inline HaarEvaluator::OptFeature :: OptFeature()
{
weight[0] = weight[1] = weight[2] = 0.f;
ofs[0][0] = ofs[0][1] = ofs[0][2] = ofs[0][3] =
ofs[1][0] = ofs[1][1] = ofs[1][2] = ofs[1][3] =
ofs[2][0] = ofs[2][1] = ofs[2][2] = ofs[2][3] = 0;
}
inline float HaarEvaluator::OptFeature :: calc( const int* ptr ) const
{
float ret = weight[0] * CALC_SUM_OFS(ofs[0], ptr) +
weight[1] * CALC_SUM_OFS(ofs[1], ptr);
if( weight[2] != 0.0f )
ret += weight[2] * CALC_SUM_OFS(ofs[2], ptr);
return ret;
}
//---------------------------------------------- LBPEvaluator -------------------------------------
class LBPEvaluator CV_FINAL : public FeatureEvaluator
{
public:
struct Feature
{
Feature();
Feature( int x, int y, int _block_w, int _block_h ) :
rect(x, y, _block_w, _block_h) {}
bool read(const FileNode& node, const Size& origWinSize);
Rect rect; // weight and height for block
};
struct OptFeature
{
OptFeature();
int calc( const int* pwin ) const;
void setOffsets( const Feature& _f, int step );
int ofs[16];
};
LBPEvaluator();
virtual ~LBPEvaluator() CV_OVERRIDE;
virtual bool read( const FileNode& node, Size origWinSize ) CV_OVERRIDE;
virtual Ptr<FeatureEvaluator> clone() const CV_OVERRIDE;
virtual int getFeatureType() const CV_OVERRIDE { return FeatureEvaluator::LBP; }
virtual bool setWindow(Point p, int scaleIdx) CV_OVERRIDE;
int operator()(int featureIdx) const
{ return optfeaturesPtr[featureIdx].calc(pwin); }
virtual int calcCat(int featureIdx) const CV_OVERRIDE
{ return (*this)(featureIdx); }
protected:
virtual void computeChannels( int i, InputArray img ) CV_OVERRIDE;
virtual void computeOptFeatures() CV_OVERRIDE;
Ptr<std::vector<Feature> > features;
Ptr<std::vector<OptFeature> > optfeatures;
Ptr<std::vector<OptFeature> > optfeatures_lbuf;
OptFeature* optfeaturesPtr; // optimization
const int* pwin;
};
inline LBPEvaluator::Feature :: Feature()
{
rect = Rect();
}
inline LBPEvaluator::OptFeature :: OptFeature()
{
for( int i = 0; i < 16; i++ )
ofs[i] = 0;
}
inline int LBPEvaluator::OptFeature :: calc( const int* p ) const
{
int cval = CALC_SUM_OFS_( ofs[5], ofs[6], ofs[9], ofs[10], p );
return (CALC_SUM_OFS_( ofs[0], ofs[1], ofs[4], ofs[5], p ) >= cval ? 128 : 0) | // 0
(CALC_SUM_OFS_( ofs[1], ofs[2], ofs[5], ofs[6], p ) >= cval ? 64 : 0) | // 1
(CALC_SUM_OFS_( ofs[2], ofs[3], ofs[6], ofs[7], p ) >= cval ? 32 : 0) | // 2
(CALC_SUM_OFS_( ofs[6], ofs[7], ofs[10], ofs[11], p ) >= cval ? 16 : 0) | // 5
(CALC_SUM_OFS_( ofs[10], ofs[11], ofs[14], ofs[15], p ) >= cval ? 8 : 0)| // 8
(CALC_SUM_OFS_( ofs[9], ofs[10], ofs[13], ofs[14], p ) >= cval ? 4 : 0)| // 7
(CALC_SUM_OFS_( ofs[8], ofs[9], ofs[12], ofs[13], p ) >= cval ? 2 : 0)| // 6
(CALC_SUM_OFS_( ofs[4], ofs[5], ofs[8], ofs[9], p ) >= cval ? 1 : 0);
}
//---------------------------------------------- predictor functions -------------------------------------
template<class FEval>
inline int predictOrdered( CascadeClassifierImpl& cascade,
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
{
CV_INSTRUMENT_REGION();
int nstages = (int)cascade.data.stages.size();
int nodeOfs = 0, leafOfs = 0;
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
float* cascadeLeaves = &cascade.data.leaves[0];
CascadeClassifierImpl::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
CascadeClassifierImpl::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
for( int si = 0; si < nstages; si++ )
{
CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
int wi, ntrees = stage.ntrees;
sum = 0;
for( wi = 0; wi < ntrees; wi++ )
{
CascadeClassifierImpl::Data::DTree& weak = cascadeWeaks[stage.first + wi];
int idx = 0, root = nodeOfs;
do
{
CascadeClassifierImpl::Data::DTreeNode& node = cascadeNodes[root + idx];
double val = featureEvaluator(node.featureIdx);
idx = val < node.threshold ? node.left : node.right;
}
while( idx > 0 );
sum += cascadeLeaves[leafOfs - idx];
nodeOfs += weak.nodeCount;
leafOfs += weak.nodeCount + 1;
}
if( sum < stage.threshold )
return -si;
}
return 1;
}
template<class FEval>
inline int predictCategorical( CascadeClassifierImpl& cascade,
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
{
CV_INSTRUMENT_REGION();
int nstages = (int)cascade.data.stages.size();
int nodeOfs = 0, leafOfs = 0;
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
size_t subsetSize = (cascade.data.ncategories + 31)/32;
int* cascadeSubsets = &cascade.data.subsets[0];
float* cascadeLeaves = &cascade.data.leaves[0];
CascadeClassifierImpl::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
CascadeClassifierImpl::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
for(int si = 0; si < nstages; si++ )
{
CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
int wi, ntrees = stage.ntrees;
sum = 0;
for( wi = 0; wi < ntrees; wi++ )
{
CascadeClassifierImpl::Data::DTree& weak = cascadeWeaks[stage.first + wi];
int idx = 0, root = nodeOfs;
do
{
CascadeClassifierImpl::Data::DTreeNode& node = cascadeNodes[root + idx];
int c = featureEvaluator(node.featureIdx);
const int* subset = &cascadeSubsets[(root + idx)*subsetSize];
idx = (subset[c>>5] & (1 << (c & 31))) ? node.left : node.right;
}
while( idx > 0 );
sum += cascadeLeaves[leafOfs - idx];
nodeOfs += weak.nodeCount;
leafOfs += weak.nodeCount + 1;
}
if( sum < stage.threshold )
return -si;
}
return 1;
}
template<class FEval>
inline int predictOrderedStump( CascadeClassifierImpl& cascade,
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
{
CV_INSTRUMENT_REGION();
CV_Assert(!cascade.data.stumps.empty());
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
const CascadeClassifierImpl::Data::Stump* cascadeStumps = &cascade.data.stumps[0];
const CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
int nstages = (int)cascade.data.stages.size();
double tmp = 0;
for( int stageIdx = 0; stageIdx < nstages; stageIdx++ )
{
const CascadeClassifierImpl::Data::Stage& stage = cascadeStages[stageIdx];
tmp = 0;
int ntrees = stage.ntrees;
for( int i = 0; i < ntrees; i++ )
{
const CascadeClassifierImpl::Data::Stump& stump = cascadeStumps[i];
double value = featureEvaluator(stump.featureIdx);
tmp += value < stump.threshold ? stump.left : stump.right;
}
if( tmp < stage.threshold )
{
sum = (double)tmp;
return -stageIdx;
}
cascadeStumps += ntrees;
}
sum = (double)tmp;
return 1;
}
template<class FEval>
inline int predictCategoricalStump( CascadeClassifierImpl& cascade,
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
{
CV_INSTRUMENT_REGION();
CV_Assert(!cascade.data.stumps.empty());
int nstages = (int)cascade.data.stages.size();
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
size_t subsetSize = (cascade.data.ncategories + 31)/32;
const int* cascadeSubsets = &cascade.data.subsets[0];
const CascadeClassifierImpl::Data::Stump* cascadeStumps = &cascade.data.stumps[0];
const CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
double tmp = 0;
for( int si = 0; si < nstages; si++ )
{
const CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
int wi, ntrees = stage.ntrees;
tmp = 0;
for( wi = 0; wi < ntrees; wi++ )
{
const CascadeClassifierImpl::Data::Stump& stump = cascadeStumps[wi];
int c = featureEvaluator(stump.featureIdx);
const int* subset = &cascadeSubsets[wi*subsetSize];
tmp += (subset[c>>5] & (1 << (c & 31))) ? stump.left : stump.right;
}
if( tmp < stage.threshold )
{
sum = tmp;
return -si;
}
cascadeStumps += ntrees;
cascadeSubsets += ntrees*subsetSize;
}
sum = (double)tmp;
return 1;
}
namespace haar_cvt
{
bool convert(const FileNode& oldcascade_root, FileStorage& newfs);
}
}