转载自: http://blog.csdn.net/wuxiaoyao12/article/details/48088999
上图显示的参数,大多与opencv_traincascade.exe的输入参数已知。其中maxCatCount和featSize定义如下
maxCatCount:int maxCatCount; // 0 in case of numerical features
featSize:int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
feature结构对于上两值默认的是:
CvFeatureParams::CvFeatureParams() : maxCatCount( 0 ), featSize( 1 ) {...};
其中
LBP:maxCatCount = 256;
HOG:featSize = N_BINS * N_CELLS;
其他情况均用默认值。
关于maxWeakCount、stageThreshold和weakClassifiers,如下:
- void CvCascadeBoost::write( FileStorage &fs, const Mat& featureMap ) const
- {
- CvCascadeBoostTree* weakTree;
- fs << CC_WEAK_COUNT << weak->total; //弱分类器总数
- fs << CC_STAGE_THRESHOLD << threshold; //见后续补充
- fs << CC_WEAK_CLASSIFIERS << "[";
- for( int wi = 0; wi < weak->total; wi++)
- {
- weakTree = *((CvCascadeBoostTree**) cvGetSeqElem( weak, wi ));
- weakTree->write( fs, featureMap );
- }
- fs << "]";
- }
- void CvCascadeBoostTree::write( FileStorage &fs, const Mat& featureMap )
- {
- int maxCatCount = ((CvCascadeBoostTrainData*)data)->featureEvaluator->getMaxCatCount();
- int subsetN = (maxCatCount + 31)/32;
- queue<CvDTreeNode*> internalNodesQueue;
- int size = (int)pow( 2.f, (float)ensemble->get_params().max_depth);
- Ptr<float> leafVals = new float[size];
- int leafValIdx = 0;
- int internalNodeIdx = 1;
- CvDTreeNode* tempNode;
- CV_DbgAssert( root );
- internalNodesQueue.push( root );
- fs << "{";
- fs << CC_INTERNAL_NODES << "[:";
- while (!internalNodesQueue.empty())
- {
- tempNode = internalNodesQueue.front();
- CV_Assert( tempNode->left ); //左分支存在
- if ( !tempNode->left->left && !tempNode->left->right) // left node is leaf 左分支是叶子节点
- {
- leafVals[-leafValIdx] = (float)tempNode->left->value;
- fs << leafValIdx-- ; //0 -1 -2...
- }
- else //左分支不是叶子节点
- {
- internalNodesQueue.push( tempNode->left );
- fs << internalNodeIdx++; //1 2 3...
- }
- CV_Assert( tempNode->right ); //右分支存在
- if ( !tempNode->right->left && !tempNode->right->right) // right node is leaf 右分支是叶子节点
- {
- leafVals[-leafValIdx] = (float)tempNode->right->value;
- fs << leafValIdx--; //0,-1,-2...
- }
- else//右分支不是叶子节点
- {
- internalNodesQueue.push( tempNode->right );
- fs << internalNodeIdx++; //1 2 3...
- }
- int fidx = tempNode->split->var_idx; //var_idx:分裂中所用到的变量的索引
- fidx = featureMap.empty() ? fidx : featureMap.at<int>(0, fidx);
- fs << fidx;
- if ( !maxCatCount )
- fs << tempNode->split->ord.c; //c:用在数值变量的分裂上的阈值。规则如下:如果var_value<c,那么next_node<-left,否则next_node<-right。
- else
- for( int i = 0; i < subsetN; i++ )
- fs << tempNode->split->subset[i];//subset:二值集合,用在在类别向量的分裂上。规则如下:如果var_value在subset里,那么next_node<-left,否则next_node<-right。
- internalNodesQueue.pop();
- }
- fs << "]"; // CC_INTERNAL_NODES
- fs << CC_LEAF_VALUES << "[:";
- for (int ni = 0; ni < -leafValIdx; ni++)
- fs << leafVals[ni]; //即从上面得到的节点value
- fs << "]"; // CC_LEAF_VALUES
- fs << "}";
- }
补充threshold:
- int i, count = data->sample_count, nz_count = 0;
- double sum, threshold;
- if( params.weight_trim_rate <= 0. || params.weight_trim_rate >= 1. )
- EXIT;
- // use weak_eval as temporary buffer for sorted weights
- cvCopy( weights, weak_eval );
- icvSort_64f( weak_eval->data.db, count, 0 );
- // as weight trimming(调整) occurs immediately after updating the weights,
- // where they are renormalized, we assume that the weight sum = 1.
- sum = 1. - params.weight_trim_rate;
- for( i = 0; i < count; i++ )
- {
- double w = weak_eval->data.db[i];
- if( sum <= 0 )
- break;
- sum -= w;
- }
- threshold = i < count ? weak_eval->data.db[i] : DBL_MAX;
关于features:
- void _writeFeatures( const std::vector<Feature> features, cv::FileStorage &fs, const cv::Mat& featureMap )
- {
- fs << FEATURES << "[";
- const cv::Mat_<int>& featureMap_ = (const cv::Mat_<int>&)featureMap;
- for ( int fi = 0; fi < featureMap.cols; fi++ ) //个数即featureMap.cols
- if ( featureMap_(0, fi) >= 0 )
- {
- fs << "{";
- features[fi].write( fs );
- fs << "}";
- }
- fs << "]";
- }
- void CvHaarEvaluator::Feature::write( FileStorage &fs ) const
- {
- fs << CC_RECTS << "[";
- for( int ri = 0; ri < CV_HAAR_FEATURE_MAX && rect[ri].r.width != 0; ++ri ) //CV_HAAR_FEATURE_MAX=3,上图就表示了我们只用了一个特征
- {
- fs << "[:" << rect[ri].r.x << rect[ri].r.y <<
- rect[ri].r.width << rect[ri].r.height << rect[ri].weight << "]";
- }
- fs << "]" << CC_TILTED << tilted; //bool型
- }
类haar特征的tilted取法如下(包括特征计算)
- void CvHaarEvaluator::generateFeatures()
- {
- int mode = ((const CvHaarFeatureParams*)((CvFeatureParams*)featureParams))->mode;
- int offset = winSize.width + 1;
- for( int x = 0; x < winSize.width; x++ )
- {
- for( int y = 0; y < winSize.height; y++ )
- {
- for( int dx = 1; dx <= winSize.width; dx++ )
- {
- for( int dy = 1; dy <= winSize.height; dy++ )
- {
- // haar_x2
- if ( (x+dx*2 <= winSize.width) && (y+dy <= winSize.height) )
- {
- features.push_back( Feature( offset, false,x, y, dx*2, dy, -1,//开始tilted都是false
- x+dx, y, dx , dy, +2 ) );
- }
- // haar_y2
- if ( (x+dx <= winSize.width) && (y+dy*2 <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx, dy*2, -1,
- x, y+dy, dx, dy, +2 ) );
- }
- // haar_x3
- if ( (x+dx*3 <= winSize.width) && (y+dy <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx*3, dy, -1,
- x+dx, y, dx , dy, +3 ) );
- }
- // haar_y3
- if ( (x+dx <= winSize.width) && (y+dy*3 <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx, dy*3, -1,
- x, y+dy, dx, dy, +3 ) );
- }
- if( mode != CvHaarFeatureParams::BASIC )
- {
- // haar_x4
- if ( (x+dx*4 <= winSize.width) && (y+dy <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx*4, dy, -1,
- x+dx, y, dx*2, dy, +2 ) );
- }
- // haar_y4
- if ( (x+dx <= winSize.width ) && (y+dy*4 <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx, dy*4, -1,
- x, y+dy, dx, dy*2, +2 ) );
- }
- }
- // x2_y2
- if ( (x+dx*2 <= winSize.width) && (y+dy*2 <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x, y, dx*2, dy*2, -1,
- x, y, dx, dy, +2,
- x+dx, y+dy, dx, dy, +2 ) );
- }
- if (mode != CvHaarFeatureParams::BASIC)
- {
- if ( (x+dx*3 <= winSize.width) && (y+dy*3 <= winSize.height) )
- {
- features.push_back( Feature( offset, false,
- x , y , dx*3, dy*3, -1,
- x+dx, y+dy, dx , dy , +9) );
- }
- }
- if (mode == CvHaarFeatureParams::ALL)
- {
- // tilted haar_x2
- if ( (x+2*dx <= winSize.width) && (y+2*dx+dy <= winSize.height) && (x-dy>= 0) )
- {
- features.push_back( Feature( offset, true, //这里开始tilted是true
- x, y, dx*2, dy, -1,
- x, y, dx, dy, +2 ) );
- }
- // tilted haar_y2
- if ( (x+dx <= winSize.width) && (y+dx+2*dy <= winSize.height) && (x-2*dy>= 0) )
- {
- features.push_back( Feature( offset, true,
- x, y, dx, 2*dy, -1,
- x, y, dx, dy, +2 ) );
- }
- // tilted haar_x3
- if ( (x+3*dx <= winSize.width) && (y+3*dx+dy <= winSize.height) && (x-dy>= 0) )
- {
- features.push_back( Feature( offset, true,
- x, y, dx*3, dy, -1,
- x+dx, y+dx, dx, dy, +3 ) );
- }
- // tilted haar_y3
- if ( (x+dx <= winSize.width) && (y+dx+3*dy <= winSize.height) && (x-3*dy>= 0) )
- {
- features.push_back( Feature( offset, true,
- x, y, dx, 3*dy, -1,
- x-dy, y+dy, dx, dy, +3 ) );
- }
- // tilted haar_x4
- if ( (x+4*dx <= winSize.width) && (y+4*dx+dy <= winSize.height) && (x-dy>= 0) )
- {
- features.push_back( Feature( offset, true,
- x, y, dx*4, dy, -1,
- x+dx, y+dx, dx*2, dy, +2 ) );
- }
- // tilted haar_y4
- if ( (x+dx <= winSize.width) && (y+dx+4*dy <= winSize.height) && (x-4*dy>= 0) )
- {
- features.push_back( Feature( offset, true,
- x, y, dx, 4*dy, -1,
- x-dy, y+dy, dx, 2*dy, +2 ) );
- }
- }
- }
- }
- }
- }
- numFeatures = (int)features.size();
- }
- CvHaarEvaluator::Feature::Feature( int offset, bool _tilted,
- int x0, int y0, int w0, int h0, float wt0,
- int x1, int y1, int w1, int h1, float wt1,
- int x2, int y2, int w2, int h2, float wt2 )
- {
- tilted = _tilted;
- rect[0].r.x = x0;
- rect[0].r.y = y0;
- rect[0].r.width = w0;
- rect[0].r.height = h0;
- rect[0].weight = wt0;
- rect[1].r.x = x1;
- rect[1].r.y = y1;
- rect[1].r.width = w1;
- rect[1].r.height = h1;
- rect[1].weight = wt1;
- rect[2].r.x = x2;
- rect[2].r.y = y2;
- rect[2].r.width = w2;
- rect[2].r.height = h2;
- rect[2].weight = wt2;
- if( !tilted )
- {
- for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
- {
- if( rect[j].weight == 0.0F )
- break;
- CV_SUM_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
- }
- }
- else
- {
- for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
- {
- if( rect[j].weight == 0.0F )
- break;
- CV_TILTED_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
- }
- }
- }
另外,是不是觉得参数输入与输出不配,其实如下:(人家是有默认输入的)
- Feature( int offset, bool _tilted,
- int x0, int y0, int w0, int h0, float wt0,
- int x1, int y1, int w1, int h1, float wt1,
- int x2 = 0, int y2 = 0, int w2 = 0, int h2 = 0, float wt2 = 0.0F );
- #define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
- /* (x, y) */ \
- (p0) = (rect).x + (step) * (rect).y; \
- /* (x + w, y) */ \
- (p1) = (rect).x + (rect).width + (step) * (rect).y; \
- /* (x, y + h) */ \
- (p2) = (rect).x + (step) * ((rect).y + (rect).height); \
- /* (x + w, y + h) */ \
- (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
- #define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step ) \
- /* (x, y) */ \
- (p0) = (rect).x + (step) * (rect).y; \
- /* (x - h, y + h) */ \
- (p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\
- /* (x + w, y + w) */ \
- (p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width); \
- /* (x + w - h, y + w + h) */ \
- (p3) = (rect).x + (rect).width - (rect).height \
- + (step) * ((rect).y + (rect).width + (rect).height);
Feature类组成如下:
- class Feature
- {
- public:
- Feature();
- Feature( int offset, bool _tilted,
- int x0, int y0, int w0, int h0, float wt0,
- int x1, int y1, int w1, int h1, float wt1,
- int x2 = 0, int y2 = 0, int w2 = 0, int h2 = 0, float wt2 = 0.0F );
- float calc( const cv::Mat &sum, const cv::Mat &tilted, size_t y) const;
- void write( cv::FileStorage &fs ) const;
- bool tilted;
- struct
- {
- cv::Rect r;
- float weight;
- } rect[CV_HAAR_FEATURE_MAX];
- struct
- {
- int p0, p1, p2, p3;
- } fastRect[CV_HAAR_FEATURE_MAX];
- };
- inline float CvHaarEvaluator::operator()(int featureIdx, int sampleIdx) const
- {
- float nf = normfactor.at<float>(0, sampleIdx);
- return !nf ? 0.0f : (features[featureIdx].calc( sum, tilted, sampleIdx)/nf);
- }
- inline float CvHaarEvaluator::Feature::calc( const cv::Mat &_sum, const cv::Mat &_tilted, size_t y) const
- {
- const int* img = tilted ? _tilted.ptr<int>((int)y) : _sum.ptr<int>((int)y);
- float ret = rect[0].weight * (img[fastRect[0].p0] - img[fastRect[0].p1] - img[fastRect[0].p2] + img[fastRect[0].p3] ) +
- rect[1].weight * (img[fastRect[1].p0] - img[fastRect[1].p1] - img[fastRect[1].p2] + img[fastRect[1].p3] );
- if( rect[2].weight != 0.0f )
- ret += rect[2].weight * (img[fastRect[2].p0] - img[fastRect[2].p1] - img[fastRect[2].p2] + img[fastRect[2].p3] );
- return ret;
- }
补充
HOG计算:
- void CvHOGEvaluator::generateFeatures()
- {
- int offset = winSize.width + 1;
- Size blockStep;
- int x, y, t, w, h;
- for (t = 8; t <= winSize.width/2; t+=8) //t = size of a cell. blocksize = 4*cellSize
- {
- blockStep = Size(4,4);
- w = 2*t; //width of a block
- h = 2*t; //height of a block
- for (x = 0; x <= winSize.width - w; x += blockStep.width)
- {
- for (y = 0; y <= winSize.height - h; y += blockStep.height)
- {
- features.push_back(Feature(offset, x, y, t, t));
- }
- }
- w = 2*t;
- h = 4*t;
- for (x = 0; x <= winSize.width - w; x += blockStep.width)
- {
- for (y = 0; y <= winSize.height - h; y += blockStep.height)
- {
- features.push_back(Feature(offset, x, y, t, 2*t));
- }
- }
- w = 4*t;
- h = 2*t;
- for (x = 0; x <= winSize.width - w; x += blockStep.width)
- {
- for (y = 0; y <= winSize.height - h; y += blockStep.height)
- {
- features.push_back(Feature(offset, x, y, 2*t, t));
- }
- }
- }
- numFeatures = (int)features.size();
- }
- CvHOGEvaluator::Feature::Feature( int offset, int x, int y, int cellW, int cellH )
- {
- rect[0] = Rect(x, y, cellW, cellH); //cell0
- rect[1] = Rect(x+cellW, y, cellW, cellH); //cell1
- rect[2] = Rect(x, y+cellH, cellW, cellH); //cell2
- rect[3] = Rect(x+cellW, y+cellH, cellW, cellH); //cell3
- for (int i = 0; i < N_CELLS; i++)
- {
- CV_SUM_OFFSETS(fastRect[i].p0, fastRect[i].p1, fastRect[i].p2, fastRect[i].p3, rect[i], offset);
- }
- }
- void CvLBPEvaluator::generateFeatures()
- {
- int offset = winSize.width + 1;
- for( int x = 0; x < winSize.width; x++ )
- for( int y = 0; y < winSize.height; y++ )
- for( int w = 1; w <= winSize.width / 3; w++ )
- for( int h = 1; h <= winSize.height / 3; h++ )
- if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )
- features.push_back( Feature(offset, x, y, w, h ) );
- numFeatures = (int)features.size();
- }
- CvLBPEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
- {
- Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight);
- CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset )
- tr.x += 2*rect.width;
- CV_SUM_OFFSETS( p[2], p[3], p[6], p[7], tr, offset )
- tr.y +=2*rect.height;
- CV_SUM_OFFSETS( p[10], p[11], p[14], p[15], tr, offset )
- tr.x -= 2*rect.width;
- CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset )
- }