第四十三篇:haartrainning结果的含义

来源:互联网 发布:博客程序源码下载 编辑:程序博客网 时间:2024/05/29 21:28

opencv中cascadetrainning的结果如下:

下面来分析一下行结果的意思

<?xml version="1.0"?>

<opencv_storage>
<cascade>
  <stageType>BOOST</stageType>
  <featureType>HAAR</featureType>
  <height>24</height>                                                     //样本的高
  <width>24</width>                                                        //训练样本的宽
  <stageParams>                                                         
    <boostType>GAB</boostType>
    <minHitRate>9.4999998807907104e-001</minHitRate>                               
    <maxFalseAlarm>5.0000000000000000e-001</maxFalseAlarm>              //虚警
    <weightTrimRate>9.4999999999999996e-001</weightTrimRate>             //训练的识别正确率
    <maxDepth>1</maxDepth>
    <maxWeakCount>100</maxWeakCount></stageParams>
  <featureParams>
    <maxCatCount>0</maxCatCount>
    <featSize>1</featSize>
    <mode>ALL</mode></featureParams>
  <stageNum>15</stageNum>
  <stages>
    <!-- stage 0 -->                                                                                                 //分类器的级
    <_>
      <maxWeakCount>6</maxWeakCount>                                                           //弱分类器的个数   6个
      <stageThreshold>-9.3017762899398804e-001</stageThreshold>       //级联的强分类器的阈值
      <weakClassifiers>
        <_>
          <internalNodes>
            0 -1 53 -7.1651302278041840e-002</internalNodes>                          //0,-1代表左右的标志位     弱分类器的阈值
          <leafValues>
            4.8380860686302185e-001 -6.3721960783004761e-001</leafValues></_>           //左右的阈值
        <_>
          <internalNodes>
            0 -1 31 9.3044321984052658e-003</internalNodes>
          <leafValues>
            9.2744462192058563e-002 -7.5913965702056885e-001</leafValues></_>
        <_>
          <internalNodes>
            0 -1 90 -1.1703098565340042e-001</internalNodes>
          <leafValues>
            5.5687570571899414e-001 -2.3895284533500671e-001</leafValues></_>
        <_>
          <internalNodes>
            0 -1 161 -3.4744767472147942e-003</internalNodes>
          <leafValues>
            -6.7214423418045044e-001 1.8688599765300751e-001</leafValues></_>
        <_>
          <internalNodes>
            0 -1 27 1.8264122772961855e-003</internalNodes>
          <leafValues>
            1.5197283029556274e-001 -6.4832943677902222e-001</leafValues></_>
        <_>
          <internalNodes>
            0 -1 194 1.8098486179951578e-005</internalNodes>
          <leafValues>

            -3.2951089739799500e-001 3.1469380855560303e-001</leafValues></_></weakClassifiers></_>

    <_>
      <rects>
        <_>
          2 20 19 4 -1.</_>                      //矩阵。前四个数值是矩阵四个点的位置,最后一个数值是矩阵像素和的权值
        <_>
          2 21 19 2 2.</_></rects>        //矩阵。前四个数值是矩阵四个点的位置,最后一个是像素和的权值,这样两个矩阵就形成了一个Haar特征
      <tilted>0</tilted></_>                 //是否是倾斜的Haar特征



具体的树状分类器的意思可以从源程序读懂

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_IMPLCvClassifier* 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 = ((CvCARTTrainParams*) trainParams)->userdata;    // R代表样本按行排列,C代表样本按列排列    if( splitIdxCallback == NULL )    {        splitIdxCallback = ( CV_IS_ROW_SAMPLE( flags ) )            ? icvDefaultSplitIdx_R : icvDefaultSplitIdx_C;        userdata = trainData;    }    // 创建CART根节点    intnode[0].sampleIdx = sampleIdx;    intnode[0].stump = (CvStumpClassifier*)        ((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,        trainClasses, typeMask, missedMeasurementsMask, compIdx, sampleIdx, weights,        ((CvCARTTrainParams*) trainParams)->stumpTrainParams );    cart->left[0] = cart->right[0] = 0;    // 创建树状弱分类器,lerror或者rerror不为0代表着当前节点为非叶子节点    listcount = 0;    for( i = 1; i < count; i++ )    {        // 基于当前节点弱分类器阈值,对当前节点进行分裂        splitIdxCallback( intnode[i-1].stump->compidx, intnode[i-1].stump->threshold,            intnode[i-1].sampleIdx, &lidx, &ridx, userdata );        // 为分裂之后的非叶子节点计算最优特征        if( intnode[i-1].stump->lerror != 0.0F )        {            // 小于阈值的样本集合            list[listcount].sampleIdx = lidx;            // 基于新样本集合寻找最优特征            list[listcount].stump = (CvStumpClassifier*)                ((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,                trainClasses, typeMask, missedMeasurementsMask, compIdx,                list[listcount].sampleIdx,                weights, ((CvCARTTrainParams*) trainParams)->stumpTrainParams );            // 计算信息增益(这里是error的下降程度)            list[listcount].errdrop = intnode[i-1].stump->lerror                - (list[listcount].stump->lerror + list[listcount].stump->rerror);            list[listcount].leftflag = 1;            list[listcount].parent = i-1;            listcount++;        }        else        {            cvReleaseMat( &lidx );        }        // 同上,左分支换成右分支,偏向于右分支        if( intnode[i-1].stump->rerror != 0.0F )        {            list[listcount].sampleIdx = ridx;            list[listcount].stump = (CvStumpClassifier*)                ((CvCARTTrainParams*) trainParams)->stumpConstructor( trainData, flags,                trainClasses, typeMask, missedMeasurementsMask, compIdx,                list[listcount].sampleIdx,                weights, ((CvCARTTrainParams*) trainParams)->stumpTrainParams );            list[listcount].errdrop = intnode[i-1].stump->rerror                - (list[listcount].stump->lerror + list[listcount].stump->rerror);            list[listcount].leftflag = 0;       // 优先级更高的证据            list[listcount].parent = i-1;            listcount++;        }        else        {            cvReleaseMat( &ridx );        }        if( listcount == 0 ) break;        idx = 0;        maxerrdrop = list[idx].errdrop;        for( j = 1; j < listcount; j++ )        {            if( list[j].errdrop > maxerrdrop )            {                idx = j;                maxerrdrop = list[j].errdrop;            }        }        // 添加新节点        intnode[i] = list[idx];        // 确定当前节点的非叶子子节点的序号        if( list[idx].leftflag )        {            cart->left[list[idx].parent] = i;        }        else        {            cart->right[list[idx].parent] = i;        }        if( idx != (listcount - 1) )        {            list[idx] = list[listcount - 1];        }        listcount--;    }    // 这段代码用于确定树中节点最优特征序号、阈值与叶子节点序号和输出置信度    // left与right大于等于0,为0代表叶子节点    // 就算CART中只有一个节点,仍旧需要设置叶子节点    j = 0;    cart->count = 0;    for( i = 0; i < count && (intnode[i].stump != NULL); i++ )    {        cart->count++;        cart->compidx[i] = intnode[i].stump->compidx;        cart->threshold[i] = intnode[i].stump->threshold;        // 确定叶子序号与叶子的输出置信度        if( cart->left[i] <= 0 )        {            cart->left[i] = -j;            cart->val[j] = intnode[i].stump->left;            j++;        }        if( cart->right[i] <= 0 )        {            cart->right[i] = -j;            cart->val[j] = intnode[i].stump->right;            j++;        }    }    // 后续处理    for( i = 0; i < count && (intnode[i].stump != NULL); i++ )    {        intnode[i].stump->release( (CvClassifier**) &(intnode[i].stump) );        if( i != 0 )        {            cvReleaseMat( &(intnode[i].sampleIdx) );        }    }    for( i = 0; i < listcount; i++ )    {        list[i].stump->release( (CvClassifier**) &(list[i].stump) );        cvReleaseMat( &(list[i].sampleIdx) );    }    cvFree( &intnode );    return (CvClassifier*) cart;}





0 0
原创粉丝点击