首页 > 代码库 > Opencv研读笔记:haartraining程序之cvCreateCARTClassifier函数详解(CART树状弱分类器创建)~

Opencv研读笔记:haartraining程序之cvCreateCARTClassifier函数详解(CART树状弱分类器创建)~

cvCreateCARTClassifier函数在haartraining程序中用于创建CART树状弱分类器,但一般只采用单一节点的CART分类器,即桩分类器,一个多节点的CART分类器训练耗时很多。根据自己的测试,要等差不多10分钟(2000正样本、2000负样本)才能训练完一个3节点的弱分类器,当然,总体的树状弱分类器的数目可能也会减少1/2。之所以将此函数拿出来说说,主要是因为在网上找不到针对这个函数的详细说明,同时,CART的应用十分广泛,自己也趁这个机会好好学学,把自己的一点理解分享给大家。

1. 先说说CART树的设计问题,也就是CvCARTClassifier这个结构体,结构体中变量的意义着实让我伤了一番脑筋。现添加其变量含义,如下:

typedef struct CvCARTClassifier
{
    CV_CLASSIFIER_FIELDS()
    /* number of internal nodes */
    int count;                      // 非叶子节点个数

    /* internal nodes (each array of <count> elements) */
    int* compidx;                   // 节点所采用的最优Haar特征序号
    float* threshold;               // 节点所采用的最优Haar特征阈值
    int* left;                      // 非叶子节点的左子节点序号(叶子节点为负数,非叶子节点为正数)
    int* right;                     // 非叶子节点的右子节点序号(叶子节点为负数,非叶子节点为正数)

    /* leaves (array of <count>+1 elements) */
    float* val;                     // 叶子节点输出置信度
} CvCARTClassifier;
其中,count就是main主函数中的参数nsplits,用于定义的是非叶子节点数,或者叫做中间节点数。个人认为,这样设计一棵树很科学,将非叶子节点与叶子节点分开表述,结构体十分简洁,只不过当时left的真实含义让我琢磨了挺长时间。

2. cvCreateCARTClassifier中节点的“分类属性”仍旧是Haar特征,“分类准则”是分类错误率的下降程度,在程序中表现为”父节点的左(右)分支error与当前节点基于最优Haar特征的error之和之间的差值(errdrop)的大小“。

3. CART树状分类器的形式多种多样,就3个非叶子节点来说,我调试之后,就遇到了如下两种弱分类器:

技术分享

4. 可能有童鞋会问,为什么要采用树状的弱分类器,我的理解是,一个树状的分类器在测试过程中,特征比较的次数相对串行的弱分类器要少很多,比如说,3个串行的Haar特征,比较次数是3次,但是如果是一颗3节点的CART树,比较次数可能只需要两次。并且, 一个树状弱分类器中,子节点针对的数据集更加具体,具有针对性,可能精度会更高。

以上就是自己对cvCreateCARTClassifier函数的理解,带有注释的源代码如下所示:

转载请注明:http://blog.csdn.net/wsj998689aa/article/details/43411809

CV_BOOST_IMPL
CvClassifier* cvCreateCARTClassifier( CvMat* trainData,                     // 训练样本特征值矩阵
                                     int flags,                             // 样本按行排列
                                     CvMat* trainClasses,                   // 训练样本类别向量
                                     CvMat* typeMask,           
                                     CvMat* missedMeasurementsMask,
                                     CvMat* compIdx,                        // 特征序列向量
                                     CvMat* sampleIdx,                      // 样本序列向量
                                     CvMat* weights,                        // 样本权值向量
                                     CvClassifierTrainParams* trainParams ) // 参数
{
    CvCARTClassifier* cart = NULL;          // CART树状弱分类器
    size_t datasize = 0;
    int count = 0;                          // CART中的节点数目
    int i = 0;
    int j = 0;

    CvCARTNode* intnode = NULL;             // CART节点
    CvCARTNode* list = NULL;                // CART节点列表
    int listcount = 0;                      // CART节点列表元素个数
    CvMat* lidx = NULL;                     // 当前节点左节点样本序列
    CvMat* ridx = NULL;                     // 当前节点右节点样本序列

    float maxerrdrop = 0.0F;
    int idx = 0;

    // 设置节点分裂函数指针
    void (*splitIdxCallback)( int compidx, float threshold,
        CvMat* idx, CvMat** left, CvMat** right,
        void* userdata );
    void* userdata;

    // 设置非叶子节点个数
    count = ((CvCARTTrainParams*) trainParams)->count;

    assert( count > 0 );

    datasize = sizeof( *cart ) + (sizeof( float ) + 3 * sizeof( int )) * count + 
        sizeof( float ) * (count + 1);

    cart = (CvCARTClassifier*) cvAlloc( datasize );
    memset( cart, 0, datasize );

    cart->count = count;

    // 输出当前样本的置信度 
    cart->eval = cvEvalCARTClassifier;
    
    cart->save = NULL;
    cart->release = cvReleaseCARTClassifier;

    cart->compidx = (int*) (cart + 1);                      // 当前非叶子节点的最优Haar特征序号
    cart->threshold = (float*) (cart->compidx + count);     // 当前非叶子节点的最优Haar特征阈值
    cart->left  = (int*) (cart->threshold + count);         // 当前节点的左子节点序号,包含叶子节点序号
    cart->right = (int*) (cart->left + count);              // 当前节点的右子节点序号,包含叶子节点序号
    cart->val = (float*) (cart->right + count);             // 叶子节点输出置信度

    datasize = sizeof( CvCARTNode ) * (count + count);
    intnode = (CvCARTNode*) cvAlloc( datasize );
    memset( intnode, 0, datasize );
    list = (CvCARTNode*) (intnode + count);

    // 节点分裂函数指针,一般为icvSplitIndicesCallback函数
    splitIdxCallback = ((CvCARTTrainParams*) trainParams)->splitIdx;
    userdata = http://www.mamicode.com/((CvCARTTrainParams*) trainParams)->userdata;>


这段程序有些细节我可能没有理解正确,比如说左右分支的error同时不为0时,我的解释是程序将右分支的优先级设置的更高些,就是一个可能出错的地方,还想与童鞋们一起探讨,谢谢!

Opencv研读笔记:haartraining程序之cvCreateCARTClassifier函数详解(CART树状弱分类器创建)~