cascade.xml参数解析
上图显示的参数,大多与opencv_traincascade.exe的输入参数已知。其中maxCatCount和featSize定义如下 maxCatCount:int maxCatCount; // 0 in case of numerical 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,//开始tilted都是false x+dx,dx,+2 ) ); } // haar_y2 if ( (x+dx <= winSize.width) && (y+dy*2 <= winSize.height) ) { features.push_back( Feature( offset,dy*2,y+dy,+2 ) ); } // haar_x3 if ( (x+dx*3 <= winSize.width) && (y+dy <= winSize.height) ) { features.push_back( Feature( offset,dx*3,x+dx,+3 ) ); } // haar_y3 if ( (x+dx <= winSize.width) && (y+dy*3 <= winSize.height) ) { features.push_back( Feature( offset,dy*3,+3 ) ); } if( mode != CvHaarFeatureParams::BASIC ) { // haar_x4 if ( (x+dx*4 <= winSize.width) && (y+dy <= winSize.height) ) { features.push_back( Feature( offset,dx*4,+2 ) ); } // haar_y4 if ( (x+dx <= winSize.width ) && (y+dy*4 <= winSize.height) ) { features.push_back( Feature( offset,dy*4,+2 ) ); } } // x2_y2 if ( (x+dx*2 <= winSize.width) && (y+dy*2 <= winSize.height) ) { features.push_back( Feature( offset,+2,+2 ) ); } if (mode != CvHaarFeatureParams::BASIC) { if ( (x+dx*3 <= winSize.width) && (y+dy*3 <= winSize.height) ) { features.push_back( Feature( offset,+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,+2 ) ); } // tilted haar_y2 if ( (x+dx <= winSize.width) && (y+dx+2*dy <= winSize.height) && (x-2*dy>= 0) ) { features.push_back( Feature( offset,2*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,y+dx,+3 ) ); } // tilted haar_y3 if ( (x+dx <= winSize.width) && (y+dx+3*dy <= winSize.height) && (x-3*dy>= 0) ) { features.push_back( Feature( offset,3*dy,x-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,+2 ) ); } // tilted haar_y4 if ( (x+dx <= winSize.width) && (y+dx+4*dy <= winSize.height) && (x-4*dy>= 0) ) { features.push_back( Feature( offset,4*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,offset ) } } } 另外,是不是觉得参数输入与输出不配,其实如下:(人家是有默认输入的) Feature( int offset,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,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,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,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,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,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,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,2*t,t)); } } } numFeatures = (int)features.size(); } CvHOGEvaluator::Feature::Feature( int offset,int x,int y,int cellW,int cellH ) { rect[0] = Rect(x,cellW,cellH); //cell0 rect[1] = Rect(x+cellW,cellH); //cell1 rect[2] = Rect(x,y+cellH,cellH); //cell2 rect[3] = Rect(x+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,h ) ); numFeatures = (int)features.size(); } CvLBPEvaluator::Feature::Feature( int offset,int _blockWidth,int _blockHeight ) { Rect tr = rect = cvRect(x,_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],offset ) tr.y +=2*rect.height; CV_SUM_OFFSETS( p[10],p[11],p[14],p[15],offset ) tr.x -= 2*rect.width; CV_SUM_OFFSETS( p[8],p[9],p[12],p[13],offset ) } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |