源代码在opencv库里面的cvboost.cpp文件里面,宏ICV_DEF_FIND_STUMP_THRESHOLD( suffix, type, error )的总体思路是通过定义带参数的宏,把参数传递给宏内部的函数,然后进行一系列的计算,并最终根据一定的算法找到合适的阈值。具体解析如下:
#define ICV_DEF_FIND_STUMP_THRESHOLD( suffix, type, error ) \
static int icvFindStumpThreshold_##suffix( \
uchar* data, size_t datastep, //训练样本的 特征-样本数据 \
uchar* wdata, size_t wstep, //wdata是样本的权重矩阵 \
uchar* ydata, size_t ystep, //ydata是样本的类别矩阵 \
uchar* idxdata, size_t idxstep, int num, //idxdata是样本根据特征值大小排序后的索引数据,样本按照特征值的大小升序排列 \
float* lerror, //左边不纯度 \
float* rerror, //右边不纯度 \
float* threshold, float* left, float* right, \
float* sumw, float* sumwy, float* sumwyy ) \
{ \
int found = 0; \
float wyl = 0.0F; \
float wl = 0.0F; \
float wyyl = 0.0F; \
float wyr = 0.0F; \
float wr = 0.0F; \
\
float curleft = 0.0F; \
float curright = 0.0F; \
float* prevval = NULL; \
float* curval = NULL; \
float curlerror = 0.0F; \
float currerror = 0.0F; \
\
int i = 0; \
int idx = 0; \
\
if( *sumw == FLT_MAX ) \
{ \
/* calculate sums */ \
float *y = NULL; \
float *w = NULL; \
float wy = 0.0F; \
\
*sumw = 0.0F; \
*sumwy = 0.0F; \
*sumwyy = 0.0F; \
for( i = 0; i < num; i++ ) \
{ \
idx = (int) ( *((type*) (idxdata + i*idxstep)) );//根据样本的数据和step确定其样本索引序号idx \
w = (float*) (wdata + idx * wstep); \
*sumw += *w; //计算权重和 \
y = (float*) (ydata + idx * ystep); \
wy = (*w) * (*y); //计算权重w和类别y的乘积 \
*sumwy += wy; \
*sumwyy += wy * (*y); \
} \
} \
\
for( i = 0; i < num; i++ ) \
{ \
idx = (int) ( *((type*) (idxdata + i*idxstep)) ); \
curval = (float*) (data + idx * datastep); \
/* for debug purpose */ \
if( i > 0 ) assert( (*prevval) <= (*curval) ); \
\
wyr = *sumwy - wyl;//计算在当前特征值以前的去除重复的特征值后的权重和类别乘积和\
wr = *sumw - wl; //计算在当前特征值以前的去除重复的特征值后的权重和 \
\
if( wl > 0.0 ) curleft = wyl / wl; //在剔除出有相同特征值的样本中,计算正样本比重(因为负样本的类别为0)\
else curleft = 0.0F; \
\
if( wr > 0.0 ) curright = wyr / wr; //在未剔除出的样本中,计算正样本的权重和比重 \
else curright = 0.0F; \
\
error \
\
if( curlerror + currerror < (*lerror) + (*rerror) )//如果当前的不纯度和小于原来的不纯度和\
{ \
(*lerror) = curlerror; \
(*rerror) = currerror; \
*threshold = *curval; \
if( i > 0 ) {
/************************树桩分类器的阈值:0.5*(当前特征值值与前一个特征值的和)*************************/
*threshold = 0.5F * (*threshold + *prevval); //阈值的大小为0.5*(当前特征值值与前一个特征值的和) \
} \
*left = curleft;//树桩分类器的左边的返回值 \
*right = curright;//树桩分类器右边的返回值 \
found = 1; \
} \
\
do \
{ //在计算参数时,也就是计算与当前特征值重复的特征值的相关参数 \
wl += *((float*) (wdata + idx * wstep)); \
wyl += (*((float*) (wdata + idx * wstep))) \
* (*((float*) (ydata + idx * ystep))); \
wyyl += *((float*) (wdata + idx * wstep)) \
* (*((float*) (ydata + idx * ystep))) \
* (*((float*) (ydata + idx * ystep))); \
} \
while( (++i) < num && \
( *((float*) (data + (idx = \
(int) ( *((type*) (idxdata + i*idxstep))) ) * datastep)) \
== *curval ) );
/*
*do-while作用:计算与当前重复特征值的wl,wyl,wyyl。
*while括号里的含义是当i<num时,并且idxdata中的第++i个特征值与当前的特征值相等时,就执行
*do-while循环里的内容。对于同一个特征值,如果做阈值的话,对于分类结果只能是同一种情况
*,所以要跳过对同一个相等的特征值来确定阈值的情况,另一个作用是这样也无形中加快了训练
*过程。
*/
\
--i; \
prevval = curval; //把当前特征值变成前一个,依次遍历所有的特征值,并最终选择一个最优的阈值 \
} /* for each value */ \
\
return found; \
}