Added HOG features to the traincascade module
authorAlexey Kazakov <no@email>
Thu, 6 Oct 2011 16:46:03 +0000 (16:46 +0000)
committerAlexey Kazakov <no@email>
Thu, 6 Oct 2011 16:46:03 +0000 (16:46 +0000)
modules/traincascade/CMakeLists.txt
modules/traincascade/HOGfeatures.cpp [new file with mode: 0644]
modules/traincascade/HOGfeatures.h [new file with mode: 0644]
modules/traincascade/boost.cpp
modules/traincascade/cascadeclassifier.cpp
modules/traincascade/cascadeclassifier.h
modules/traincascade/features.cpp
modules/traincascade/traincascade.cpp
modules/traincascade/traincascade_features.h

index 21d968a..858f42f 100644 (file)
@@ -22,6 +22,7 @@ set(traincascade_files traincascade.cpp
     boost.cpp boost.h features.cpp traincascade_features.h
     haarfeatures.cpp haarfeatures.h
     lbpfeatures.cpp lbpfeatures.h
+       HOGfeatures.cpp HOGfeatures.h
     imagestorage.cpp imagestorage.h)
 
 set(the_target opencv_traincascade)    
diff --git a/modules/traincascade/HOGfeatures.cpp b/modules/traincascade/HOGfeatures.cpp
new file mode 100644 (file)
index 0000000..68c1943
--- /dev/null
@@ -0,0 +1,245 @@
+#include "HOGfeatures.h"\r
+#include "cascadeclassifier.h"\r
+\r
+\r
+CvHOGFeatureParams::CvHOGFeatureParams()\r
+{\r
+    maxCatCount = 0;\r
+    name = HOGF_NAME;\r
+    featSize = N_BINS * N_CELLS;\r
+}\r
+\r
+void CvHOGEvaluator::init(const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize)\r
+{\r
+    CV_Assert( _maxSampleCount > 0);\r
+    int cols = (_winSize.width + 1) * (_winSize.height + 1);\r
+    for (int bin = 0; bin < N_BINS; bin++)\r
+    {\r
+        hist.push_back(Mat(_maxSampleCount, cols, CV_32FC1));\r
+    }\r
+    normSum.create( (int)_maxSampleCount, cols, CV_32FC1 );\r
+    CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );\r
+}\r
+\r
+void CvHOGEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)\r
+{\r
+    CV_DbgAssert( !hist.empty());\r
+    CvFeatureEvaluator::setImage( img, clsLabel, idx );\r
+    vector<Mat> integralHist;\r
+    for (int bin = 0; bin < N_BINS; bin++)\r
+    {\r
+        integralHist.push_back( Mat(winSize.height + 1, winSize.width + 1, hist[bin].type(), hist[bin].ptr<float>((int)idx)) );\r
+    }\r
+    Mat integralNorm(winSize.height + 1, winSize.width + 1, normSum.type(), normSum.ptr<float>((int)idx));\r
+    integralHistogram(img, integralHist, integralNorm, (int)N_BINS);\r
+}\r
+\r
+//void CvHOGEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const\r
+//{\r
+//    _writeFeatures( features, fs, featureMap );\r
+//}\r
+\r
+void CvHOGEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const\r
+{\r
+    int featIdx;\r
+    int componentIdx;\r
+    const Mat_<int>& featureMap_ = (const Mat_<int>&)featureMap;\r
+    fs << FEATURES << "[";\r
+    for ( int fi = 0; fi < featureMap.cols; fi++ )\r
+        if ( featureMap_(0, fi) >= 0 )\r
+        {\r
+            fs << "{";\r
+            featIdx = fi / getFeatureSize();\r
+            componentIdx = fi % getFeatureSize();\r
+            features[featIdx].write( fs, componentIdx );\r
+            fs << "}";\r
+        }\r
+    fs << "]";  \r
+}\r
+\r
+void CvHOGEvaluator::generateFeatures()\r
+{\r
+    int offset = winSize.width + 1;\r
+    Size blockStep;\r
+    int x, y, t, w, h;\r
+\r
+    for (t = 8; t <= winSize.width/2; t+=8) //t = size of a cell. blocksize = 4*cellSize\r
+    {\r
+        blockStep = Size(4,4);\r
+        w = 2*t; //width of a block\r
+        h = 2*t; //height of a block\r
+        for (x = 0; x <= winSize.width - w; x += blockStep.width)\r
+        {\r
+            for (y = 0; y <= winSize.height - h; y += blockStep.height)\r
+            {\r
+                features.push_back(Feature(offset, x, y, t, t));\r
+            }\r
+        }\r
+        w = 2*t;\r
+        h = 4*t;\r
+        for (x = 0; x <= winSize.width - w; x += blockStep.width)\r
+        {\r
+            for (y = 0; y <= winSize.height - h; y += blockStep.height)\r
+            {\r
+                features.push_back(Feature(offset, x, y, t, 2*t));\r
+            }\r
+        }\r
+        w = 4*t;\r
+        h = 2*t; \r
+        for (x = 0; x <= winSize.width - w; x += blockStep.width)\r
+        {\r
+            for (y = 0; y <= winSize.height - h; y += blockStep.height)\r
+            {     \r
+                features.push_back(Feature(offset, x, y, 2*t, t));\r
+            }\r
+        }\r
+    }\r
+\r
+    numFeatures = (int)features.size();\r
+}\r
+\r
+CvHOGEvaluator::Feature::Feature()\r
+{\r
+    for (int i = 0; i < N_CELLS; i++)\r
+    {\r
+        rect[i] = Rect(0, 0, 0, 0);\r
+    }\r
+}\r
+\r
+CvHOGEvaluator::Feature::Feature( int offset, int x, int y, int cellW, int cellH )\r
+{\r
+    rect[0] = Rect(x, y, cellW, cellH); //cell0\r
+    rect[1] = Rect(x+cellW, y, cellW, cellH); //cell1\r
+    rect[2] = Rect(x, y+cellH, cellW, cellH); //cell2\r
+    rect[3] = Rect(x+cellW, y+cellH, cellW, cellH); //cell3\r
+\r
+    for (int i = 0; i < N_CELLS; i++)\r
+    {\r
+        CV_SUM_OFFSETS(fastRect[i].p0, fastRect[i].p1, fastRect[i].p2, fastRect[i].p3, rect[i], offset);\r
+    }\r
+}\r
+\r
+void CvHOGEvaluator::Feature::write(FileStorage &fs) const\r
+{\r
+    fs << CC_RECTS << "[";\r
+    for( int i = 0; i < N_CELLS; i++ )\r
+    {\r
+        fs << "[:" << rect[i].x << rect[i].y << rect[i].width << rect[i].height << "]";\r
+    }\r
+    fs << "]";\r
+}\r
+\r
+//cell and bin idx writing\r
+//void CvHOGEvaluator::Feature::write(FileStorage &fs, int varIdx) const\r
+//{\r
+//    int featComponent = varIdx % (N_CELLS * N_BINS);\r
+//    int cellIdx = featComponent / N_BINS;\r
+//    int binIdx = featComponent % N_BINS;\r
+//\r
+//    fs << CC_RECTS << "[:" << rect[cellIdx].x << rect[cellIdx].y << \r
+//        rect[cellIdx].width << rect[cellIdx].height << binIdx << "]";\r
+//}\r
+\r
+//cell[0] and featComponent idx writing. By cell[0] it's possible to recover all block\r
+//All block is nessesary for block normalization\r
+void CvHOGEvaluator::Feature::write(FileStorage &fs, int featComponentIdx) const\r
+{\r
+    fs << CC_RECT << "[:" << rect[0].x << rect[0].y << \r
+        rect[0].width << rect[0].height << featComponentIdx << "]";\r
+}\r
+\r
+\r
+void CvHOGEvaluator::integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const\r
+{\r
+    CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );\r
+    int x, y, binIdx;\r
+\r
+    Size gradSize(img.size());\r
+    Size histSize(histogram[0].size());\r
+    Mat grad(gradSize, CV_32F);\r
+    Mat qangle(gradSize, CV_8U);\r
+\r
+    AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);\r
+    int* xmap = (int*)mapbuf + 1;\r
+    int* ymap = xmap + gradSize.width + 2;\r
+\r
+    const int borderType = (int)BORDER_REPLICATE;\r
+\r
+    for( x = -1; x < gradSize.width + 1; x++ )\r
+        xmap[x] = borderInterpolate(x, gradSize.width, borderType);\r
+    for( y = -1; y < gradSize.height + 1; y++ )\r
+        ymap[y] = borderInterpolate(y, gradSize.height, borderType);\r
+\r
+    int width = gradSize.width;\r
+    AutoBuffer<float> _dbuf(width*4);\r
+    float* dbuf = _dbuf;\r
+    Mat Dx(1, width, CV_32F, dbuf);\r
+    Mat Dy(1, width, CV_32F, dbuf + width);\r
+    Mat Mag(1, width, CV_32F, dbuf + width*2);\r
+    Mat Angle(1, width, CV_32F, dbuf + width*3);\r
+\r
+    float angleScale = (float)(nbins/CV_PI);\r
+\r
+    for( y = 0; y < gradSize.height; y++ )\r
+    {\r
+        const uchar* currPtr = img.data + img.step*ymap[y];\r
+        const uchar* prevPtr = img.data + img.step*ymap[y-1];\r
+        const uchar* nextPtr = img.data + img.step*ymap[y+1];\r
+        float* gradPtr = (float*)grad.ptr(y);\r
+        uchar* qanglePtr = (uchar*)qangle.ptr(y);\r
+\r
+        for( x = 0; x < width; x++ )\r
+        {\r
+            dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);\r
+            dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);\r
+        }\r
+        cartToPolar( Dx, Dy, Mag, Angle, false );\r
+        for( x = 0; x < width; x++ )\r
+        {\r
+            float mag = dbuf[x+width*2];\r
+            float angle = dbuf[x+width*3];\r
+            angle = angle*angleScale - 0.5f;\r
+            int bidx = cvFloor(angle);\r
+            angle -= bidx;\r
+            if( bidx < 0 )\r
+                bidx += nbins;\r
+            else if( bidx >= nbins )\r
+                bidx -= nbins;\r
+\r
+            qanglePtr[x] = (uchar)bidx;\r
+            gradPtr[x] = mag;\r
+        }\r
+    }\r
+    integral(grad, norm, grad.depth());\r
+\r
+    float* histBuf;\r
+    const float* magBuf;\r
+    const uchar* binsBuf;\r
+\r
+    int binsStep = (int)( qangle.step / sizeof(uchar) );\r
+    int histStep = (int)( histogram[0].step / sizeof(float) );\r
+    int magStep = (int)( grad.step / sizeof(float) );\r
+    for( binIdx = 0; binIdx < nbins; binIdx++ )\r
+    {\r
+        histBuf = (float*)histogram[binIdx].data;\r
+        magBuf = (const float*)grad.data;\r
+        binsBuf = (const uchar*)qangle.data;\r
+\r
+        memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );\r
+        histBuf += histStep + 1;\r
+        for( y = 0; y < qangle.rows; y++ )\r
+        { \r
+            histBuf[-1] = 0.f;\r
+            float strSum = 0.f;\r
+            for( x = 0; x < qangle.cols; x++ )\r
+            {\r
+                if( binsBuf[x] == binIdx )\r
+                    strSum += magBuf[x];\r
+                histBuf[x] = histBuf[-histStep + x] + strSum;\r
+            }\r
+            histBuf += histStep;\r
+            binsBuf += binsStep;\r
+            magBuf += magStep;\r
+        }\r
+    }\r
+}\r
diff --git a/modules/traincascade/HOGfeatures.h b/modules/traincascade/HOGfeatures.h
new file mode 100644 (file)
index 0000000..d6e1b58
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef _OPENCV_HOGFEATURES_H_\r
+#define _OPENCV_HOGFEATURES_H_\r
+\r
+#include "traincascade_features.h"\r
+\r
+//#define TEST_INTHIST_BUILD\r
+//#define TEST_FEAT_CALC\r
+\r
+#define N_BINS 9\r
+#define N_CELLS 4\r
+\r
+#define HOGF_NAME "HOGFeatureParams"\r
+struct CvHOGFeatureParams : public CvFeatureParams\r
+{\r
+    CvHOGFeatureParams(); \r
+};\r
+\r
+class CvHOGEvaluator : public CvFeatureEvaluator\r
+{\r
+public:\r
+    virtual ~CvHOGEvaluator() {}\r
+    virtual void init(const CvFeatureParams *_featureParams,\r
+        int _maxSampleCount, Size _winSize );\r
+    virtual void setImage(const Mat& img, uchar clsLabel, int idx);    \r
+    virtual float operator()(int varIdx, int sampleIdx) const;\r
+    virtual void writeFeatures( FileStorage &fs, const Mat& featureMap ) const;\r
+protected:\r
+    virtual void generateFeatures();\r
+    virtual void integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const;\r
+    class Feature\r
+    {\r
+    public:\r
+        Feature();\r
+        Feature( int offset, int x, int y, int cellW, int cellH ); \r
+        float calc( const vector<Mat> &_hists, const Mat &_normSum, size_t y, int featComponent ) const; \r
+        void write( FileStorage &fs ) const;\r
+        void write( FileStorage &fs, int varIdx ) const;\r
+\r
+        Rect rect[N_CELLS]; //cells\r
+\r
+        struct\r
+        {\r
+            int p0, p1, p2, p3;\r
+        } fastRect[N_CELLS];\r
+    };\r
+    vector<Feature> features;\r
+\r
+    Mat normSum; //for nomalization calculation (L1 or L2)\r
+    vector<Mat> hist;\r
+};\r
+\r
+inline float CvHOGEvaluator::operator()(int varIdx, int sampleIdx) const\r
+{\r
+    int featureIdx = varIdx / (N_BINS * N_CELLS);\r
+    int componentIdx = varIdx % (N_BINS * N_CELLS);\r
+    //return features[featureIdx].calc( hist, sampleIdx, componentIdx); \r
+    return features[featureIdx].calc( hist, normSum, sampleIdx, componentIdx); \r
+}\r
+\r
+inline float CvHOGEvaluator::Feature::calc( const vector<Mat>& _hists, const Mat& _normSum, size_t y, int featComponent ) const\r
+{\r
+    float normFactor;\r
+    float res;\r
+\r
+    int binIdx = featComponent % N_BINS;\r
+    int cellIdx = featComponent / N_BINS;\r
+\r
+    const float *hist = _hists[binIdx].ptr<float>(y);\r
+    res = hist[fastRect[cellIdx].p0] - hist[fastRect[cellIdx].p1] - hist[fastRect[cellIdx].p2] + hist[fastRect[cellIdx].p3];\r
+\r
+    const float *normSum = _normSum.ptr<float>(y);\r
+    normFactor = (float)(normSum[fastRect[0].p0] - normSum[fastRect[1].p1] - normSum[fastRect[2].p2] + normSum[fastRect[3].p3]);\r
+    res = (res > 0.001f) ? ( res / (normFactor + 0.001f) ) : 0.f; //for cutting negative values, which apper due to floating precision\r
+\r
+    return res;\r
+}\r
+\r
+#endif // _OPENCV_HOGFEATURES_H_\r
index 7eb07b2..1ab93e6 100644 (file)
@@ -233,7 +233,7 @@ void CvCascadeBoostTrainData::setData( const CvFeatureEvaluator* _featureEvaluat
        if( _precalcValBufSize < 0 || _precalcIdxBufSize < 0)
         CV_Error( CV_StsOutOfRange, "_numPrecalcVal and _numPrecalcIdx must be positive or 0" );
 
-       var_count = var_all = featureEvaluator->getNumFeatures();
+       var_count = var_all = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
     sample_count = _numSamples;
     
     is_buf_16u = false;     
index ee5e8a2..d192dff 100644 (file)
@@ -4,7 +4,7 @@
 using namespace std;
 
 static const char* stageTypes[] = { CC_BOOST };
-static const char* featureTypes[] = { CC_HAAR, CC_LBP };
+static const char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };
 
 CvCascadeParams::CvCascadeParams() : stageType( defaultStageType ), 
     featureType( defaultFeatureType ), winSize( cvSize(24, 24) )
@@ -25,7 +25,9 @@ void CvCascadeParams::write( FileStorage &fs ) const
     CV_Assert( !stageTypeStr.empty() );
     fs << CC_STAGE_TYPE << stageTypeStr;
     String featureTypeStr = featureType == CvFeatureParams::HAAR ? CC_HAAR :
-                            featureType == CvFeatureParams::LBP ? CC_LBP : 0;
+                            featureType == CvFeatureParams::LBP ? CC_LBP : 
+                            featureType == CvFeatureParams::HOG ? CC_HOG :
+                            0;
     CV_Assert( !stageTypeStr.empty() );
     fs << CC_FEATURE_TYPE << featureTypeStr;
     fs << CC_HEIGHT << winSize.height;
@@ -49,7 +51,9 @@ bool CvCascadeParams::read( const FileNode &node )
         return false;
     rnode >> featureTypeStr;
     featureType = !featureTypeStr.compare( CC_HAAR ) ? CvFeatureParams::HAAR :
-                  !featureTypeStr.compare( CC_LBP ) ? CvFeatureParams::LBP : -1;
+                  !featureTypeStr.compare( CC_LBP ) ? CvFeatureParams::LBP : 
+                  !featureTypeStr.compare( CC_HOG ) ? CvFeatureParams::HOG :
+                  -1;
     if (featureType == -1)
         return false;
     node[CC_HEIGHT] >> winSize.height;
@@ -509,14 +513,15 @@ bool CvCascadeClassifier::load( const String cascadeDirName )
 
 void CvCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap )
 {
-    featureMap.create( 1, featureEvaluator->getNumFeatures(), CV_32SC1 );
+    int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
+    featureMap.create( 1, varCount, CV_32SC1 );
     featureMap.setTo(Scalar(-1));
     
     for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
         it != stageClassifiers.end(); it++ )
         ((CvCascadeBoost*)((Ptr<CvCascadeBoost>)(*it)))->markUsedFeaturesInMap( featureMap );
     
-    for( int fi = 0, idx = 0; fi < featureEvaluator->getNumFeatures(); fi++ )
+    for( int fi = 0, idx = 0; fi < varCount; fi++ )
         if ( featureMap.at<int>(0, fi) >= 0 )
             featureMap.ptr<int>(0)[fi] = idx++;
 }
index b59fdea..67de534 100644 (file)
@@ -5,6 +5,7 @@
 #include "traincascade_features.h"
 #include "haarfeatures.h"
 #include "lbpfeatures.h"
+#include "HOGfeatures.h" //new
 #include "boost.h"
 #include "cv.h"
 #include "cxcore.h"
@@ -41,6 +42,7 @@
 #define CC_FEATURES       FEATURES
 #define CC_FEATURE_PARAMS "featureParams"
 #define CC_MAX_CAT_COUNT  "maxCatCount"
+#define CC_FEATURE_SIZE   "featSize"
 
 #define CC_HAAR        "HAAR"
 #define CC_MODE        "mode"
@@ -53,6 +55,8 @@
 #define CC_LBP  "LBP"
 #define CC_RECT "rect"
 
+#define CC_HOG "HOG"
+
 #ifdef _WIN32
 #define TIME( arg ) (((double) clock()) / CLOCKS_PER_SEC)
 #else
index 1670ece..c117a99 100644 (file)
@@ -26,7 +26,7 @@ bool CvParams::scanAttr( const String prmName, const String val ) { return false
 
 //---------------------------- FeatureParams --------------------------------------
 
-CvFeatureParams::CvFeatureParams() : maxCatCount( 0 )
+CvFeatureParams::CvFeatureParams() : maxCatCount( 0 ), featSize( 1 )
 {
     name = CC_FEATURE_PARAMS; 
 }
@@ -34,11 +34,13 @@ CvFeatureParams::CvFeatureParams() : maxCatCount( 0 )
 void CvFeatureParams::init( const CvFeatureParams& fp )
 {
     maxCatCount = fp.maxCatCount;
+    featSize = fp.featSize;
 }
 
 void CvFeatureParams::write( FileStorage &fs ) const
 {
     fs << CC_MAX_CAT_COUNT << maxCatCount;
+    fs << CC_FEATURE_SIZE << featSize;
 }
 
 bool CvFeatureParams::read( const FileNode &node )
@@ -46,13 +48,16 @@ bool CvFeatureParams::read( const FileNode &node )
     if ( node.empty() )
         return false;
     maxCatCount = node[CC_MAX_CAT_COUNT];
-    return maxCatCount >= 0;
+    featSize = node[CC_FEATURE_SIZE];
+    return ( maxCatCount >= 0 && featSize >= 1 );
 }
 
 Ptr<CvFeatureParams> CvFeatureParams::create( int featureType )
 {
     return featureType == HAAR ? Ptr<CvFeatureParams>(new CvHaarFeatureParams) :
-        featureType == LBP ? Ptr<CvFeatureParams>(new CvLBPFeatureParams) : Ptr<CvFeatureParams>();
+        featureType == LBP ? Ptr<CvFeatureParams>(new CvLBPFeatureParams) : 
+        featureType == HOG ? Ptr<CvFeatureParams>(new CvHOGFeatureParams) :
+        Ptr<CvFeatureParams>();
 }
 
 //------------------------------------- FeatureEvaluator ---------------------------------------
@@ -79,5 +84,7 @@ void CvFeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
 Ptr<CvFeatureEvaluator> CvFeatureEvaluator::create(int type)
 {
     return type == CvFeatureParams::HAAR ? Ptr<CvFeatureEvaluator>(new CvHaarEvaluator) :
-        type == CvFeatureParams::LBP ? Ptr<CvFeatureEvaluator>(new CvLBPEvaluator) : Ptr<CvFeatureEvaluator>();
+        type == CvFeatureParams::LBP ? Ptr<CvFeatureEvaluator>(new CvLBPEvaluator) : 
+        type == CvFeatureParams::HOG ? Ptr<CvFeatureEvaluator>(new CvHOGEvaluator) :
+        Ptr<CvFeatureEvaluator>();
 }
index db1dd00..07dbe3e 100644 (file)
@@ -17,8 +17,9 @@ int main( int argc, char* argv[] )
     CvCascadeParams cascadeParams;
     CvCascadeBoostParams stageParams;
     Ptr<CvFeatureParams> featureParams[] = { Ptr<CvFeatureParams>(new CvHaarFeatureParams),
-                                             Ptr<CvFeatureParams>(new CvLBPFeatureParams)
-                                           }; 
+                                             Ptr<CvFeatureParams>(new CvLBPFeatureParams), 
+                                             Ptr<CvFeatureParams>(new CvHOGFeatureParams)
+                                           };
     int fc = sizeof(featureParams)/sizeof(featureParams[0]);
     if( argc == 1 )
     {
index 1e03a82..019a4b9 100644 (file)
@@ -65,13 +65,14 @@ public:
 class CvFeatureParams : public CvParams
 {
 public:
-    enum { HAAR = 0, LBP = 1 };
+    enum { HAAR = 0, LBP = 1, HOG = 2 };
     CvFeatureParams();
     virtual void init( const CvFeatureParams& fp );
     virtual void write( FileStorage &fs ) const;
     virtual bool read( const FileNode &node );
     static Ptr<CvFeatureParams> create( int featureType );
     int maxCatCount; // 0 in case of numerical features
+    int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
 };
 
 class CvFeatureEvaluator
@@ -87,6 +88,7 @@ public:
 
     int getNumFeatures() const { return numFeatures; }
     int getMaxCatCount() const { return featureParams->maxCatCount; }
+    int getFeatureSize() const { return featureParams->featSize; }
     const Mat& getCls() const { return cls; }
     float getCls(int si) const { return cls.at<float>(si, 0); }
 protected: