void cvCreateTreeCascadeClassifier( const char* dirname,//存放n个stage的文本数据的位置,例如:c:\data
const char* vecfilename,//存放vec样本文件的位置
const char* bgfilename, //存放bg负样本文件的位置
int npos, int nneg, int nstages,
int numprecalculated,
int numsplits,
float minhitrate, float maxfalsealarm,
float weightfraction,
int mode, int symmetric,
int equalweights,
int winwidth, int winheight,
int boosttype, int stumperror,
int maxtreesplits, int minpos, bool bg_vecfile )//maxtreesplits – maximum number of nodes in tree. If maxtreesplits < nsplits, tree will not be built
{
CvTreeCascadeClassifier* tcc = NULL;
CvIntHaarFeatures* haar_features = NULL;
CvHaarTrainingData* training_data = NULL;
CvMat* vals = NULL;
CvMat* cluster_idx = NULL;
CvMat* idx = NULL;
CvMat* features_idx = NULL;
CV_FUNCNAME( "cvCreateTreeCascadeClassifier" );
__BEGIN__;
int i, k;
CvTreeCascadeNode* leaves;
int best_num, cur_num;
CvSize winsize;
char stage_name[PATH_MAX];
char buf[PATH_MAX];
char* suffix;
int total_splits;
int poscount;
int negcount;
int consumed;
double false_alarm;
double proctime;
int nleaves;
double required_leaf_fa_rate;
float neg_ratio;
int max_clusters;
max_clusters = CV_MAX_CLUSTERS;
neg_ratio = (float) nneg / npos;
nleaves = 1 + MAX( 0, maxtreesplits );
required_leaf_fa_rate = pow( (double) maxfalsealarm, (double) nstages ) / nleaves;//最大虚警率的nstages次方,再除以叶子总数即为叶子虚警率
printf( "Required leaf false alarm rate: %g\n", required_leaf_fa_rate );
total_splits = 0;
winsize = cvSize( winwidth, winheight );
CV_CALL( cluster_idx = cvCreateMat( 1, npos + nneg, CV_32SC1 ) );
CV_CALL( idx = cvCreateMat( 1, npos + nneg, CV_32SC1 ) );
CV_CALL( tcc = (CvTreeCascadeClassifier*)
icvLoadTreeCascadeClassifier( dirname, winwidth + 1, &total_splits ) );
CV_CALL( leaves = icvFindDeepestLeaves( tcc ) );//最后一个stage作为最深的叶子leaf
CV_CALL( icvPrintTreeCascade( tcc->root ) );
haar_features = icvCreateIntHaarFeatures( winsize, mode, symmetric );//这个是计算haar特征的数目以及相关参数(计算每个特征的计算公式),
typedef struct CvTHaarFeature
{
char desc[CV_HAAR_FEATURE_DESC_MAX];
int tilted;
struct
{
CvRect r;
float weight;
} rect[CV_HAAR_FEATURE_MAX];
} CvTHaarFeature;
typedef struct CvFastHaarFeature
{
int tilted;
struct
{
int p0, p1, p2, p3;
float weight;
} rect[CV_HAAR_FEATURE_MAX];
} CvFastHaarFeature;
typedef struct CvIntHaarFeatures
{
CvSize winsize;
int count;
CvTHaarFeature* feature;
CvFastHaarFeature* fastfeature;
} CvIntHaarFeatures;
其中CvTHaarFeature和CvFastHaarFeature的区别在于:CvTHaarFeature是标示特征覆盖的窗口的坐标(Cvrect r),CvFastHaarFeature是将特征覆盖的窗口区域拉直,然后计算cvEvalFastHaarFeature.
CV_INLINE float cvEvalFastHaarFeature( const CvFastHaarFeature* feature,
const sum_type* sum, const sum_type* tilted )
{
const sum_type* img = feature->tilted ? tilted : sum;//此处img是判断是否是旋转后的。如果不是,那么这个是已经计算了每个位置的像素积分和的。
float ret = feature->rect[0].weight*
(img[feature->rect[0].p0] - img[feature->rect[0].p1] -
img[feature->rect[0].p2] + img[feature->rect[0].p3]) +
feature->rect[1].weight*
(img[feature->rect[1].p0] - img[feature->rect[1].p1] -
img[feature->rect[1].p2] + img[feature->rect[1].p3]);
if( feature->rect[2].weight != 0.0f )
ret += feature->rect[2].weight *
( img[feature->rect[2].p0] - img[feature->rect[2].p1] -
img[feature->rect[2].p2] + img[feature->rect[2].p3] );
return ret;
}
printf( "Number of features used : %d\n", haar_features->count );//输出32*32的子窗口所有特征的数目
training_data = icvCreateHaarTrainingData( winsize, npos + nneg );
sprintf( stage_name, "%s/", dirname );
suffix = stage_name + strlen( stage_name );
if (! bg_vecfile)
if( !icvInitBackgroundReaders( bgfilename, winsize ) && nstages > 0 )//icvInitBackgroundReaders创建背景读取指针,并返回1
CV_ERROR( CV_StsError, "Unable to read negative images" );
if( nstages > 0 )
{
do
{
CvSplit* first_split;
CvSplit* last_split;
CvSplit* cur_split;
CvTreeCascadeNode* parent;
CvTreeCascadeNode* cur_node;
CvTreeCascadeNode* last_node;
first_split = last_split = cur_split = NULL;
parent = leaves;
leaves = NULL;
do
{
int best_clusters;
float posweight, negweight;
double leaf_fa_rate;
if( parent ) sprintf( buf, "%d", parent->idx );
else sprintf( buf, "NULL" );
printf( "\nParent node: %s\n\n", buf );
printf( "*** 1 cluster ***\n" );
tcc->eval = icvEvalTreeCascadeClassifierFilter;//此处设定是为了后面样本的过滤作用,用来过滤出能够通过前面各个stage强分类器的正负样本。
icvSetLeafNode( tcc, parent );//设置从根节点到叶子节点的路径
consumed = 0;
poscount = icvGetHaarTrainingDataFromVec( training_data, 0, npos,
(CvIntHaarClassifier*) tcc, vecfilename, &consumed );//从vec正样本文件中获取计算过积分图的正样本到training_data中。
printf( "POS: %d %d %f\n", poscount, consumed, ((double) poscount)/consumed );
if( poscount <= 0 )
CV_ERROR( CV_StsError, "Unable to obtain positive samples" );
fflush( stdout );
proctime = -TIME( 0 );
nneg = (int) (neg_ratio * poscount);
negcount = icvGetHaarTrainingDataFromBG( training_data, poscount, nneg,
(CvIntHaarClassifier*) tcc, &false_alarm, bg_vecfile ? bgfilename : NULL );
printf( "NEG: %d %g\n", negcount, false_alarm );
printf( "BACKGROUND PROCESSING TIME: %.2f\n", (proctime + TIME( 0 )) );
if( negcount <= 0 )
CV_ERROR( CV_StsError, "Unable to obtain negative samples" );
leaf_fa_rate = false_alarm;
if( leaf_fa_rate <= required_leaf_fa_rate )//达到最低要求的叶子误警率即结束。
{
printf( "Required leaf false alarm rate achieved. "
"Branch training terminated.\n" );
}
else if( nleaves == 1 && tcc->next_idx == nstages )
{
printf( "Required number of stages achieved. "
"Branch training terminated.\n" );//达到设定的stages也结束。
}
else
{
CvTreeCascadeNode* single_cluster;
CvTreeCascadeNode* multiple_clusters;
CvSplit* cur_split;
int single_num;
icvSetNumSamples( training_data, poscount + negcount );
posweight = (equalweights) ? 1.0F / (poscount + negcount) : (0.5F/poscount);
negweight = (equalweights) ? 1.0F / (poscount + negcount) : (0.5F/negcount);
icvSetWeightsAndClasses( training_data,
poscount, posweight, 1.0F, negcount, negweight, 0.0F );//为每个样本设置权值和类别。
fflush( stdout );
proctime = -TIME( 0 );
icvPrecalculate( training_data, haar_features, numprecalculated );//负责计算所有取出的正负样本的前 numprecalculated个 Haar特征值(由 icvGetTrainingDataCallback 实现),
for( i = 0; i < num_samples; i++ )
{
for( j = 0; j < num; j++ )//num=numprecalculated
{
val = cvEvalFastHaarFeature(
( haar_features->fastfeature
+ first + j ),
(sum_type*) (training_data->sum.data.ptr
+ i * training_data->sum.step),
(sum_type*) (training_data->tilted.data.ptr
+ i * training_data->tilted.step) );
normfactor = training_data->normfactor.data.fl[i];
val = ( normfactor == 0.0F ) ? 0.0F : (val / normfactor);
#ifdef CV_COL_ARRANGEMENT
CV_MAT_ELEM( *mat, float, j, i ) = val;
#else
CV_MAT_ELEM( *mat, float, i, j ) = val;//将计算好的特征值放在mat指针指定的内存中。
#endif
}
}
并且对每种特征,将所有样本标号按其特征值升序排序(由 cvGetSortedIndices实现,每种特征分别排序)。
printf( "Precalculation time: %.2f\n", (proctime + TIME( 0 )) );
CV_CALL( single_cluster = icvCreateTreeCascadeNode() );
fflush( stdout );
proctime = -TIME( 0 );
single_cluster->stage =
(CvStageHaarClassifier*) icvCreateCARTStageClassifier(
training_data, NULL, haar_features,
minhitrate, maxfalsealarm, symmetric,
weightfraction, numsplits, (CvBoostType) boosttype,
(CvStumpError) stumperror, 0 );//这地方开始创建cart的stage强分类器,其中numsplits表示树的最小节点数
printf( "Stage training time: %.2f\n", (proctime + TIME( 0 )) );
single_num = icvNumSplits( single_cluster->stage );//表示强分类器
是由几个弱分类器构建的
best_num = single_num;
best_clusters = 1;
multiple_clusters = NULL;
printf( "Number of used features: %d\n", single_num );
if( maxtreesplits >= 0 )
{
max_clusters = MIN( max_clusters, maxtreesplits - total_splits + 1 );
}
vals = NULL;
for( k = 2; k <= max_clusters; k++ )
{
int cluster;
int stop_clustering;
printf( "*** %d clusters ***\n", k );
stop_clustering = ( k * minpos > poscount );
if( !stop_clustering )
{
int num[CV_MAX_CLUSTERS];
if( k == 2 )
{
proctime = -TIME( 0 );
CV_CALL( vals = icvGetUsedValues( training_data, 0, poscount,
haar_features, single_cluster->stage ) );
printf( "Getting values for clustering time: %.2f\n", (proctime + TIME(0)) );
printf( "Value matirx size: %d x %d\n", vals->rows, vals->cols );
fflush( stdout );
cluster_idx->cols = vals->rows;
for( i = 0; i < negcount; i++ ) idx->data.i[i] = poscount + i;
}
proctime = -TIME( 0 );
CV_CALL( cvKMeans2( vals, k, cluster_idx, CV_TERM_CRITERIA() ) );
printf( "Clustering time: %.2f\n", (proctime + TIME( 0 )) );
for( cluster = 0; cluster < k; cluster++ ) num[cluster] = 0;
for( i = 0; i < cluster_idx->cols; i++ )
num[cluster_idx->data.i[i]]++;
for( cluster = 0; cluster < k; cluster++ )
{
if( num[cluster] < minpos )
{
stop_clustering = 1;
break;
}
}
}
if( stop_clustering )
{
printf( "Clusters are too small. Clustering aborted.\n" );
break;
}
cur_num = 0;
cur_node = last_node = NULL;
for( cluster = 0; (cluster < k) && (cur_num < best_num); cluster++ )
{
CvTreeCascadeNode* new_node;
int num_splits;
int last_pos;
int total_pos;
printf( "Cluster: %d\n", cluster );
last_pos = negcount;
for( i = 0; i < cluster_idx->cols; i++ )
{
if( cluster_idx->data.i[i] == cluster )
{
idx->data.i[last_pos++] = i;
}
}
idx->cols = last_pos;
total_pos = idx->cols - negcount;
printf( "# pos: %d of %d. (%d%%)\n", total_pos, poscount,
100 * total_pos / poscount );
CV_CALL( new_node = icvCreateTreeCascadeNode() );
if( last_node ) last_node->next = new_node;
else cur_node = new_node;
last_node = new_node;
posweight = (equalweights)
? 1.0F / (total_pos + negcount) : (0.5F / total_pos);
negweight = (equalweights)
? 1.0F / (total_pos + negcount) : (0.5F / negcount);
icvSetWeightsAndClasses( training_data,
poscount, posweight, 1.0F, negcount, negweight, 0.0F );
fflush( stdout );
proctime = -TIME( 0 );
new_node->stage = (CvStageHaarClassifier*)
icvCreateCARTStageClassifier( training_data, idx, haar_features,
minhitrate, maxfalsealarm, symmetric,
weightfraction, numsplits, (CvBoostType) boosttype,
(CvStumpError) stumperror, best_num - cur_num );
printf( "Stage training time: %.2f\n", (proctime + TIME( 0 )) );
if( !(new_node->stage) )
{
printf( "Stage training aborted.\n" );
cur_num = best_num + 1;
}
else
{
num_splits = icvNumSplits( new_node->stage );
cur_num += num_splits;
printf( "Number of used features: %d\n", num_splits );
}
}
if( cur_num < best_num )
{
icvReleaseTreeCascadeNodes( &multiple_clusters );
best_num = cur_num;
best_clusters = k;
multiple_clusters = cur_node;
}
else
{
icvReleaseTreeCascadeNodes( &cur_node );
}
}
cvReleaseMat( &vals );
CV_CALL( cur_split = (CvSplit*) cvAlloc( sizeof( *cur_split ) ) );
CV_ZERO_OBJ( cur_split );
if( last_split ) last_split->next = cur_split;
else first_split = cur_split;
last_split = cur_split;
cur_split->single_cluster = single_cluster;
cur_split->multiple_clusters = multiple_clusters;
cur_split->num_clusters = best_clusters;
cur_split->parent = parent;
cur_split->single_multiple_ratio = (float) single_num / best_num;
}
if( parent ) parent = parent->next_same_level;
} while( parent );
do
{
float max_single_multiple_ratio;
cur_split = NULL;
max_single_multiple_ratio = 0.0F;
last_split = first_split;
while( last_split )
{
if( last_split->single_cluster && last_split->multiple_clusters &&
last_split->single_multiple_ratio > max_single_multiple_ratio )
{
max_single_multiple_ratio = last_split->single_multiple_ratio;
cur_split = last_split;
}
last_split = last_split->next;
}
if( cur_split )
{
if( maxtreesplits < 0 ||
cur_split->num_clusters <= maxtreesplits - total_splits + 1 )
{
cur_split->single_cluster = NULL;
total_splits += cur_split->num_clusters - 1;
}
else
{
icvReleaseTreeCascadeNodes( &(cur_split->multiple_clusters) );
cur_split->multiple_clusters = NULL;
}
}
} while( cur_split );
leaves = last_node = NULL;
last_split = first_split;
while( last_split )
{
cur_node = (last_split->multiple_clusters)
? last_split->multiple_clusters : last_split->single_cluster;
parent = last_split->parent;
if( parent ) parent->child = cur_node;
for( ; cur_node; cur_node = cur_node->next )
{
FILE* file;
if( last_node ) last_node->next_same_level = cur_node;
else leaves = cur_node;
last_node = cur_node;
cur_node->parent = parent;
cur_node->idx = tcc->next_idx;
tcc->next_idx++;
sprintf( suffix, "%d/%s", cur_node->idx, CV_STAGE_CART_FILE_NAME );
file = NULL;
if( icvMkDir( stage_name ) && (file = fopen( stage_name, "w" )) != 0 )
{
cur_node->stage->save( (CvIntHaarClassifier*) cur_node->stage, file );
fprintf( file, "\n%d\n%d\n",
((parent) ? parent->idx : -1),
((cur_node->next) ? tcc->next_idx : -1) );
}
else
{
printf( "Failed to save classifier into %s\n", stage_name );
}
if( file ) fclose( file );
}
if( parent ) sprintf( buf, "%d", parent->idx );
else sprintf( buf, "NULL" );
printf( "\nParent node: %s\n", buf );
printf( "Chosen number of splits: %d\n\n", (last_split->multiple_clusters)
? (last_split->num_clusters - 1) : 0 );
cur_split = last_split;
last_split = last_split->next;
cvFree( &cur_split );
}
printf( "Total number of splits: %d\n", total_splits );
if( !(tcc->root) ) tcc->root = leaves;
CV_CALL( icvPrintTreeCascade( tcc->root ) );
} while( leaves );
{
char xml_path[1024];
int len = (int)strlen(dirname);
CvHaarClassifierCascade* cascade = 0;
strcpy( xml_path, dirname );
if( xml_path[len-1] == '\\' || xml_path[len-1] == '/' )
len--;
strcpy( xml_path + len, ".xml" );
cascade = cvLoadHaarClassifierCascade( dirname, cvSize(winwidth,winheight) );
if( cascade )
cvSave( xml_path, cascade );
cvReleaseHaarClassifierCascade( &cascade );
}
}
printf( "\nCascade performance\n" );
tcc->eval = icvEvalTreeCascadeClassifier;
consumed = 0;
poscount = icvGetHaarTrainingDataFromVec( training_data, 0, npos,
(CvIntHaarClassifier*) tcc, vecfilename, &consumed );
printf( "POS: %d %d %f\n", poscount, consumed,
(consumed > 0) ? (((float) poscount)/consumed) : 0 );
if( poscount <= 0 )
fprintf( stderr, "Warning: unable to obtain positive samples\n" );
proctime = -TIME( 0 );
negcount = icvGetHaarTrainingDataFromBG( training_data, poscount, nneg,
(CvIntHaarClassifier*) tcc, &false_alarm, bg_vecfile ? bgfilename : NULL );
printf( "NEG: %d %g\n", negcount, false_alarm );
printf( "BACKGROUND PROCESSING TIME: %.2f\n", (proctime + TIME( 0 )) );
if( negcount <= 0 )
fprintf( stderr, "Warning: unable to obtain negative samples\n" );
__END__;
if (! bg_vecfile)
icvDestroyBackgroundReaders();
if( tcc ) tcc->release( (CvIntHaarClassifier**) &tcc );
icvReleaseIntHaarFeatures( &haar_features );
icvReleaseHaarTrainingData( &training_data );
cvReleaseMat( &cluster_idx );
cvReleaseMat( &idx );
cvReleaseMat( &vals );
cvReleaseMat( &features_idx );
}
一、树状分类器
1、构造一棵决策树CvCARTClassifier,树状分类器
//层次关系:CvCARTClassifier CvCARTNode CvStumpClassifier
//CvCARTClassifier由count个CvCARTNode组成,每个CvCARTNode有一个CvStumpClassifier,
CvClassifier* cvCreateCARTClassifier( CvMat* trainData,//所有样本的所有特征值
int flags, //标识矩阵按行或列组织
CvMat* trainClasses,
CvMat* typeMask,
CvMat* missedMeasurementsMask,
CvMat* compIdx,
CvMat* sampleIdx,//选择部分样本时的样本号
CvMat* weights,
CvClassifierTrainParams* trainParams )
#define CV_CLASSIFIER_FIELDS() \
int flags; \
float(*eval)( struct CvClassifier*, CvMat* ); \
void (*tune)( struct CvClassifier*, CvMat*, int flags, CvMat*, CvMat*, CvMat*, \
CvMat*, CvMat* ); \
int (*save)( struct CvClassifier*, const char* file_name ); \
void (*release)( struct CvClassifier** );
typedef struct CvClassifier
{
CV_CLASSIFIER_FIELDS()
} CvClassifier;
typedef struct CvCARTNode
{
CvMat* sampleIdx;
CvStumpClassifier* stump;
int parent; //父节点索引号
int leftflag; //1:left节点时;0:right节点
float errdrop;//剩余的误差
} CvCARTNode;
//一个弱分类器,所用特征的索引、阈值及哪侧为正样本
typedef struct CvStumpClassifier
{
CV_CLASSIFIER_FIELDS()
int compidx; //对应特征的索引
float lerror;
float rerror;
float threshold; //该特征阈值
float left; //均值或左侧正样本比例,left=p(y=1)/(p(y=1)+p(y=-1)),对分类器若left为正样本则为1,反之为0
float right;
} CvStumpClassifier;
typedef struct CvCARTClassifier
{
CV_CLASSIFIER_FIELDS()
int count;
int* compidx;
float* threshold;
int* left;//当前节点的左子节点为叶节点时,存叶节点序号的负值(从0开始);非叶节点,存该节点序号
int* right;//当前节点的右子节点为叶节点时,存叶节点序号的负值(从0开始);非叶节点,存该节点序号
float* val;//存叶节点的stump->left或stump->right,值为正样本比例p(y=1)/(p(y=1)+p(y=-1))
} CvCARTClassifier;
typedef struct CvCARTTrainParams
{
CV_CLASSIFIER_TRAIN_PARAM_FIELDS()
int count;//节点数
CvClassifierTrainParams* stumpTrainParams;
CvClassifierConstructor stumpConstructor;
//定义了函数指针变量,变量名为splitIdx,将样本按第compidx个特征的threshold分为left和right
void (*splitIdx)( int compidx, float threshold,
CvMat* idx, CvMat** left, CvMat** right,
void* userdata );
void* userdata;
} CvCARTTrainParams;
2、用树状分类器进行检测
//sample只是一个样本,判断该样本在CvCARTClassifier树中的那个叶节点上
//返回该样本所在叶节点的正样本比例p(y=1)/(p(y=1)+p(y=-1))
float cvEvalCARTClassifier( CvClassifier* classifier, CvMat* sample )
//根据树状分类器判断样本在树上的位置,即在哪个叶节点上。返回叶节点序号
float cvEvalCARTClassifierIdx( CvClassifier* classifier, CvMat* sample )
二、boost
1、boost过程流程,将各种类型归为一个函数cvBoostStartTraining/cvBoostNextWeakClassifier,
通过参数区分不同类型的boost。都是在最优弱分类器已知,各样本对该分类器估计值已计算存入weakEvalVals
typedef struct CvBoostTrainer
{
CvBoostType type;
int count;
int* idx;//要么null,要么存样本索引号
float* F;//存logiBoost的F
} CvBoostTrainer;
调用顺序:cvBoostStartTraining ———> startTraining[type] ———> icvBoostStartTraining等
//定义函数cvBoostStartTraining
CvBoostTrainer* cvBoostStartTraining( ...,CvBoostType type )
{
return startTraining[type]( trainClasses, weakTrainVals, weights, sampleIdx, type );
}
//定义函数指针类型的数组变量startTraining[4]
CvBoostStartTraining startTraining[4] = {
icvBoostStartTraining,
icvBoostStartTraining,
icvBoostStartTrainingLB,
icvBoostStartTraining
};
//定义函数指针类型CvBoostStartTraining
typedef CvBoostTrainer* (*CvBoostStartTraining)( CvMat* trainClasses,
CvMat* weakTrainVals,
CvMat* weights,
CvMat* sampleIdx,
CvBoostType type );
调用顺序:cvBoostNextWeakClassifier———> nextWeakClassifier[trainer->type]———> icvBoostNextWeakClassifierLB等
//定义函数cvBoostNextWeakClassifier
float cvBoostNextWeakClassifier( ..., CvBoostTrainer* trainer )
{
return nextWeakClassifier[trainer->type]( weakEvalVals, trainClasses,weakTrainVals, weights, trainer);
}
//定义函数指针类型的数组变量nextWeakClassifier[4]
CvBoostNextWeakClassifier nextWeakClassifier[4] = {
icvBoostNextWeakClassifierDAB,
icvBoostNextWeakClassifierRAB,
icvBoostNextWeakClassifierLB,
icvBoostNextWeakClassifierGAB
};
//定义函数指针类型CvBoostNextWeakClassifier
typedef float (*CvBoostNextWeakClassifier)( CvMat* weakEvalVals,
CvMat* trainClasses,
CvMat* weakTrainVals,
CvMat* weights,
CvBoostTrainer* data );
2、具体的startTraining和NextWeakClassifier
//y*=2y-1,类别标签由{0,1}变为{-1,1},并将它填入weakTrainVals
//返回CvBoostTrainer,其中F = NULL;
CvBoostTrainer* icvBoostStartTraining( CvMat* trainClasses,//类别标签{0,1},
CvMat* weakTrainVals,//类别标签{-1,1},
CvMat* weights,
CvMat* sampleIdx,//要么null,要么存样本索引号
CvBoostType type )
//更新权重,特征的响应函数值weakEvalVals已知,即分类器已确定,分类结果在weakEvalVals
float icvBoostNextWeakClassifierDAB( CvMat* weakEvalVals,//响应函数值{1,-1}
CvMat* trainClasses,//类别标签{0,1},
CvMat* weakTrainVals,//没使用,应该是{1,-1}
CvMat* weights, //将被更新
CvBoostTrainer* trainer )//用于确定要被更新权重的样本
//更新Real AdaBoost权重,特征的响应函数值weakEvalVals已知,即分类器已确定
float icvBoostNextWeakClassifierRAB( CvMat* weakEvalVals,//响应函数值,应该是测对率=(测对数/总数)
CvMat* trainClasses,//类别标签{0,1},
CvMat* weakTrainVals,//没使用
CvMat* weights, //被更新,w=w*[exp(-1/2*log(evaldata/1-evaldata))]
CvBoostTrainer* trainer )//用于确定要被更新的样本
//样本数,权重,类别标签,响应函数值F,z值,样本索引
//由F计算LogitBoost的w和z,z返回到traindata
void icvResponsesAndWeightsLB( int num, uchar* wdata, int wstep,
uchar* ydata, int ystep, //类别标签
uchar* fdata, int fstep, //响应函数值F
uchar* traindata, int trainstep,//用于存z
int* indices ) //样本索引
//初始F=0;得p=1/2,计算w、z
CvBoostTrainer* icvBoostStartTrainingLB( CvMat* trainClasses,//类别标签{0,1},
CvMat* weakTrainVals, //存Z值
CvMat* weights,
CvMat* sampleIdx,//要么null,要么存样本索引号
CvBoostType type )
//已知f,先算F=F+f,再算p=1/(1+exp(-F)),再算z,w
float icvBoostNextWeakClassifierLB( CvMat* weakEvalVals,//f,是对z的回归
CvMat* trainClasses,//类别标签{0,1}
CvMat* weakTrainVals,//存Z值
CvMat* weights,
CvBoostTrainer* trainer )
//Gentle AdaBoost,已知f,算w=w*exp(-yf)
CV_BOOST_IMPL
float icvBoostNextWeakClassifierGAB( CvMat* weakEvalVals,//f=p(y=1|x)-p(y=-1|x)
CvMat* trainClasses,//类别标签{0,1}
CvMat* weakTrainVals,//没使用
CvMat* weights,
CvBoostTrainer* trainer )
typedef struct CvTreeCascadeNode
{
CvStageHaarClassifier* stage; //与节点对应的分类器
struct CvTreeCascadeNode* next;
struct CvTreeCascadeNode* child;
struct CvTreeCascadeNode* parent;
struct CvTreeCascadeNode* next_same_level;
struct CvTreeCascadeNode* child_eval;
int idx;
int leaf;
} CvTreeCascadeNode;