cascade.xml参数解析
2015-08-29 17:31
337 查看
上图显示的参数,大多与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 << "]"; }关于weakClassifiers的internalNodes和leafValues参数,如下:
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 << "}"; }
即:internalNodes中四个变量代表一个node,分别为node中的left/right标记、特征池中的ID和threshold。leafValues中两个变量代表一个node,分别为left leaf的值和right leaf的值。
补充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 << "]"; }其中的rects:
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型 }说明:opencv learning书中有提到(550页),我们使用1个特征(一个只有一个分裂的树),最多3个。
类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(); }其中的Feature构造如下:
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 );
其中的CV_SUM_OFFSETS和CV_TILTED_OFFSETS如下:
#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); } }LBP计算:
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 ) }
相关文章推荐
- 【ssh】Ibatis代码自动生成工具(项目)——Abator安装与应用实例(图解)
- MFC Radio Button 使用 分组 RadioButton 用法
- Hession
- 0009.Scala内部类
- Android屏幕适配全攻略(最权威的官方适配指导)
- 1236:排名
- PHP定时执行任务/Cron Job
- 循环-02. 计算圆周率(15)
- 红色警戒2修改器原理百科(二)
- MYSQL字段
- ThinkPHP多个分页的实现
- [leetcode] 141.Linked List Cycle
- UI 19 数据库
- ICPC 7096 A Rational Sequence
- 记录一些PHP7RCC1编译问题
- 子元素使用float后使父元素有高度的方法
- 代码格式化工具 sourceInsight editplus ultraedit keil
- 44_02 nginx
- 求二叉树中两个节点的最低公共祖先
- Scala 深入浅出实战经典 第68讲:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析