OpenCV Haar AdaBoost源码改进(比EMCV快6倍)

来源:互联网 发布:2016美国经济非农数据 编辑:程序博客网 时间:2024/06/05 06:56

这几天研究了OpenCV源码 Haar AdaBoost算法,作了一下改进

1.去掉了所有动态分配内存的操作,对嵌入式系统有一定的速度提升

2.注释覆盖了大量关键代码

3.减少了代码一半的体积,并且减少了部分健壮性的代码,速度比OpenCV源码提升16%

4.修改了大量数据结构,不依赖CV源码直接编译

5.去掉了double型,改成Int

6.开方改成查表

7.除法改成乘法加位移


注:使用时请注意,现仅支持单分支的Stages和单结点的Classifier训练好的结果集

        在720MHZ的DSP板子上对一幅352*288的灰度图像进行人脸检测只需300ms,比EMCV快6倍

完整PC版工程链接 (VC6.0能直接编译,但没有5,6,7步的优化)点击打开链接

完整DSP版工程链接 (CCS3.0能直接编译,包含所有优化)点击打开链接

DSP优化的关键代码实例如下(这个版本在CCS下编译,若想用VC6.0直接编译,还要修改一定的数据结构)

Haar.cpp

<pre name="code" class="cpp">#include "Haar.h"#include "loadCascade.h"#include "Util.h"#include "stdio.h"#include "string.h"#include <math.h>#include <stdint.h>#include <c6x.h>/*******************Global************************************/HaarClassifierCascade *cascade ;//HidHaarClassifierCascade hid_cascade;//32bits cell MatintMatPool32[MaxMatNum][MAXROWS][MAXCOLS];//8bits cell unsigned char  MatPool8[MaxMatNum][MAXROWS][MAXCOLS];//8bits*3 cell unsigned char  ImgRGBPool8[MaxMatNum][RGBCHANNEL][MAXROWS][MAXCOLS];//64bits  cell _int64 MatPool64[MaxMatNum][MAXROWS][MAXCOLS];//候选区域坐标节点并查集PTreeNode PTreeNodes[MAXPTREENODES];char HidCascade[MAXHIDCASCADE];//分类器检测结果区域序列Sequence result_seq;//==================================================================//函数名:  IsEqual//作者:    qiurenbo//日期:    2014-10-1//功能:    判断两个矩形是否邻接//输入参数:_r1  _r2 候选区域矩形      //返回值:  返回相似性(是否是邻接的矩形)//修改记录://==================================================================int IsEqual( const void* _r1, const void* _r2){    const Rect* r1 = (const Rect*)_r1;    const Rect* r2 = (const Rect*)_r2;int distance5x = r1->width ;//int distance = cvRound(r1->width*0.2);    return r2->x*5 <= r1->x*5 + distance5x &&r2->x*5 >= r1->x*5 - distance5x &&r2->y*5 <= r1->y*5 + distance5x &&r2->y*5 >= r1->y*5 - distance5x &&r2->width*5 <= r1->width * 6 &&r2->width * 6 >= r1->width*5;}//==================================================================//函数名:  ReadFaceCascade//作者:    qiurenbo//日期:    2014-10-1//功能:    根据候选区域的相似性(IsEqual函数)建立并查集//输入参数:seq  候选目标区域序列      //返回值:  返回分类后的类别数 //修改记录://==================================================================int SeqPartition( const Sequence* seq ){Sequence* result = 0;    //CvMemStorage* temp_storage = 0;    int class_idx = 0;    memset(PTreeNodes, 0, MAXPTREENODES*sizeof(PTreeNode));        int i, j;       //建立以seq中元素为根节点的森林    for( i = 0; i < seq->total; i++ )PTreeNodes[i].element = (char*)&seq->rectQueue[i];//遍历所有根节点for( i = 0; i < seq->total; i++ )    {        PTreeNode* node = &PTreeNodes[i];PTreeNode* root = node;//确保node中元素指针不为空        if( !node->element )            continue;        //找到元素在树中的根结点        while( root->parent )            root = root->parent;        for( j = 0; j < seq->total; j++ )        {            PTreeNode* node2 = &PTreeNodes[j];            //确保1.node中元素指针不为空//    2.且不是同一个node结点//    3.且是相似区域// 若是相似区域,则合并元素            if( node2->element && node2 != node &&                IsEqual( node->element, node2->element))            {                PTreeNode* root2 = node2;                               //找到元素在树中的根结点                while( root2->parent )                    root2 = root2->parent;//合并的前提是不在一颗树中                if( root2 != root )                {//秩小的树归入秩大的树中                    if( root->rank > root2->rank )                        root2->parent = root;//秩相等的时候才改变树的秩                    else                    {                        root->parent = root2;                        root2->rank += root->rank == root2->rank;                        root = root2;                    }                    //assert( root->parent == 0 );                    // 路径压缩,子节点node2直接指向根节点                    while( node2->parent )                    {                        PTreeNode* temp = node2;                        node2 = node2->parent;                        temp->parent = root;                    }                    // 路径压缩,子节点node直接指向根节点                    node2 = node;                    while( node2->parent )                    {                        PTreeNode* temp = node2;                        node2 = node2->parent;                        temp->parent = root;                    }                }            }        }    }for( i = 0; i < seq->total; i++ )    {        PTreeNode* node = &PTreeNodes[i];        int idx = -1;                if( node->element )        {            while( node->parent )                node = node->parent;//计算有几棵并查树,巧妙地利用取反避免重复计算            if( node->rank >= 0 )                node->rank = ~class_idx++;            idx = ~node->rank;        }           }    return class_idx;}//==================================================================//函数名:  ReadFaceCascade//作者:    qiurenbo//日期:    2014-09-30//功能:    读取Cascade文件//输入参数:void      //返回值:  void  //修改记录://==================================================================void ReadFaceCascade(){int i;//load cascadecascade = (HaarClassifierCascade*)HaarClassifierCascade_face;//load stagesint stage_size = StageClassifier_face[0];HaarStageClassifier *stages ;stages = (HaarStageClassifier *)(StageClassifier_face+1);//load classifierint classifier_size = Classifier_face[0];HaarClassifier *cls ;cls = (HaarClassifier*) (Classifier_face+1);int class_info_size = class_info[0];int * cls_info ;cls_info = (int*)(class_info+1);//link cascade with stagescascade->stage_classifier = stages;//link stages,classifiersint offset=0;int offset_t=(sizeof(HaarFeature)/sizeof(int));int offset_l=offset_t+1;int offset_r=offset_t+2;int offset_a=offset_t+3;int offset_total=0;for(i=0;i<stage_size;++i){(stages+i)->classifier = (cls+offset);offset +=(stages+i)->count;}offset_total = 5+ (sizeof(HaarFeature)/sizeof(int));//link classifiers and haar_featrue;for(i=0;i<classifier_size;++i){HaarClassifier *cs= cls+i;cs->haar_feature = (HaarFeature*)(cls_info+i*offset_total);cs->threshold = (int*)(cls_info+i*offset_total+offset_t);cs->left =(int*)(cls_info+i*offset_total+offset_l);cs->right=(int*)(cls_info+i*offset_total+offset_r);cs->alpha=(int*)(cls_info+i*offset_total+offset_a);}}//==================================================================//函数名:  IntegralImage//作者:    qiurenbo//日期:    2014-09-26//功能:    从矩阵池中获取rows * cols的矩阵 //输入参数:mat矩阵结构体地址//rows待分配的行数//cols待分配的列数//type    待分配的矩阵类型 //matIndex 从矩阵池中分配的矩阵序列(手动指定..)      //返回值:  void  //修改记录://==================================================================void GetMat(void* mat, int rows, int cols, int type, int matIndex){switch(type){case BITS8: ((Mat8*)mat)->rows = rows;((Mat8*)mat)->cols = cols;((Mat8*)mat)->mat8Ptr =  (Mat8Ptr)&MatPool8[matIndex];break;case BITS32: ((Mat32*)mat)->rows = rows;((Mat32*)mat)->cols = cols;((Mat32*)mat)->mat32Ptr =  (Mat32Ptr)&MatPool32[matIndex];break;case BITS64:((Mat64*)mat)->rows = rows;((Mat64*)mat)->cols = cols;((Mat64*)mat)->mat64Ptr =  (Mat64Ptr)&MatPool64[matIndex];break;}}//==================================================================//函数名:  IntegralImage//作者:    qiurenbo//日期:    2014-09-26//功能:    计算目标检测区域的积分图//输入参数:src待检测目标所在矩阵起始//srcstep 待检测区域列数//sum积分图矩阵(W+1)*(H+1)//sumstep 积分图矩阵列数    //sqsum平方和图矩阵 (W+1)*(H+1)//sqsumstep 平方和图矩阵列数//size   待检测区域大小 W*H//          //          //返回值:  void  //修改记录://==================================================================void IntegralImage(ImgPtr src, int srcstep,   Mat32Ptr sum, int sumstep,        Mat64Ptr sqsum, int sqsumstep,   Size size){int s = 0;_int64 sq = 0;//移动指针到积分图的下一行,第一行全为0sum += sumstep + 1;     sqsum += sqsumstep + 1; //y代表相对于输入检测矩阵起始第几行for(int y = 0; y < size.height; y++, src += srcstep,       sum += sumstep, sqsum += sqsumstep )    {   //sum和sqsum为(W+1)*(H+1)大小矩阵,故将第一列置为0sum[-1] = 0;                                        sqsum[-1] = 0;                                      for(int x = 0 ; x < size.width; x++ )    {                                                   int it = src[x];                           int t = (it);   //查表计算平方_int64tq =  CV_8TO16U_SQR(it);              //s代表行上的累加和s += t;  //sq代表行上的累加和sq += tq;                                       t = sum[x - sumstep] + s;                       tq = sqsum[x - sqsumstep] + sq;                 sum[x] = t;                                     sqsum[x] = (_int64)tq;                                  }                                                       }                        }//==================================================================//函数名:  Integral//作者:    qiurenbo//日期:    2014-09-26//功能:    计算目标检测区域的积分图//输入参数:image 图像//sumImage 积分图指针//sumSqImage 平方和图指针                 //返回值:  void  //修改记录://==================================================================void Integral(Image* image, Mat32* sumImage, Mat64* sumSqImage){//取保地址空间已经分配,从数组中if (image == NULL || sumImage == NULL || sumSqImage == NULL)return;    Image*src    =(Image*)image;    Mat32 *sum =(Mat32*)sumImage;    Mat64 *sqsum =(Mat64*)sumSqImage;   Size size;size.height = src->rows;size.width =  src->cols;    IntegralImage(src->imgPtr, src->cols,sum->mat32Ptr, sum->cols,     sqsum->mat64Ptr, sqsum->cols,size);    }//==================================================================//函数名:  AlignPtr//作者:    qiurenbo//日期:    2014-10-03//功能:按algin字节对齐//输入参数:ptr 要对齐的指针   //align 对齐的字节数           //返回值:  void*   //修改记录://==================================================================void* AlignPtr( const void* ptr, int align){return (void*)( ((unsigned int)ptr + align - 1) & ~(align-1) );} //==================================================================//函数名:  CreateHidHaarClassifierCascade//作者:    qiurenbo//日期:    2014-09-28//功能:    创建隐式积分图加快计算速度//输入参数:cascade 级联分类器指针              //返回值:  static HidHaarClassifierCascade*   返回一个隐式级联分类器指针//修改记录://==================================================================static HidHaarClassifierCascade*CreateHidHaarClassifierCascade(HaarClassifierCascade* cascade){cascade->hid_cascade = (struct HidHaarClassifierCascade *)HidCascade;//分配栈空间    HidHaarClassifierCascade* out = (struct HidHaarClassifierCascade *)HidCascade;const int icv_stage_threshold_bias = 419; //0.0001*(2^22)=419.4304HidHaarClassifier* haar_classifier_ptr;    HidHaarTreeNode* haar_node_ptr;    int i, j, l;       int total_classifiers = 2135;    int total_nodes = 0;          int has_tilted_features = 0;    int max_count = 0;    /* 初始化HidCascade头 */    out->count = cascade->count;    out->stage_classifier = (HidHaarStageClassifier*)(out + 1);//out->stage_classifier = (HidHaarStageClassifier*)AlignPtr(out + 1, 4);//classifier起始地址haar_classifier_ptr = (HidHaarClassifier*)(out->stage_classifier + cascade->count);//haar_classifier_ptr = (HidHaarClassifier*)AlignPtr(out->stage_classifier + cascade->count, 4);//node起始地址    //haar_node_ptr = (HidHaarTreeNode*)AlignPtr(haar_classifier_ptr + total_classifiers, 4);haar_node_ptr = (HidHaarTreeNode*)(haar_classifier_ptr + total_classifiers);    out->is_stump_based = 1;    out->is_tree = 0;    // 用cascade初始化HidCascade    for( i = 0; i < cascade->count; i++ )    {//用cascades Stage初始化HidCascade的Stage        HaarStageClassifier* stage_classifier = cascade->stage_classifier + i;        HidHaarStageClassifier* hid_stage_classifier = out->stage_classifier + i;        hid_stage_classifier->count = stage_classifier->count;        hid_stage_classifier->threshold = stage_classifier->threshold - icv_stage_threshold_bias;        //hid_stage_classifier->classifier = (struct HidHaarClassifier *)&HidClassifiers[i]; hid_stage_classifier->classifier = haar_classifier_ptr;//初始化为二特征,下面会根据真实的特征数至1或0(三特征)        hid_stage_classifier->two_rects = 1;haar_classifier_ptr += stage_classifier->count;//Stage构成一颗退化的二叉树(单分支),每个结点最多只有一个孩子        hid_stage_classifier->parent = (stage_classifier->parent == -1)            ? NULL : out->stage_classifier + stage_classifier->parent;        hid_stage_classifier->next = (stage_classifier->next == -1)            ? NULL :  out->stage_classifier + stage_classifier->next;        hid_stage_classifier->child = (stage_classifier->child == -1)            ? NULL : out->stage_classifier + stage_classifier->child ;        //判断该stage是否为树状结构(多分枝)        out->is_tree |= hid_stage_classifier->next != NULL;//赋值classifer属性        for( j = 0; j < stage_classifier->count; j++ )        {            HaarClassifier* classifier = stage_classifier->classifier + j;            HidHaarClassifier* hid_classifier = hid_stage_classifier->classifier + j;            int node_count = classifier->count;            int* alpha_ptr = (int*)(haar_node_ptr + node_count);hid_classifier->count = node_count;    hid_classifier->node = haar_node_ptr;            hid_classifier->alpha = alpha_ptr;           //赋值node属性            for( l = 0; l < node_count; l++ )            {                HidHaarTreeNode* node =  hid_classifier->node + l;                HaarFeature* feature = classifier->haar_feature + l;                memset( node, -1, sizeof(*node) );                node->threshold = classifier->threshold[l];                node->left = classifier->left[l];                node->right = classifier->right[l];//对特征数目进行判断,若是三特征,则至two_rects为0                if( (feature->rect[2].weight) == 0 ||                    feature->rect[2].r.width == 0 ||                    feature->rect[2].r.height == 0 )                    memset( &(node->feature.rect[2]), 0, sizeof(node->feature.rect[2]) );                else                    hid_stage_classifier->two_rects = 0;            }//赋值alpha            memcpy( hid_classifier->alpha, classifier->alpha, (node_count+1)*sizeof(hid_classifier->alpha[0]));haar_node_ptr = (HidHaarTreeNode*)(alpha_ptr+node_count + 1);                //判断cascade中的分类器是否是树桩分类器,只有根结点的决策树            out->is_stump_based &= node_count == 1;        }    }    //cascade->hid_cascade = out;    //assert( (char*)haar_node_ptr - (char*)out <= datasize );       return out;}//==================================================================//函数名:  SetImagesForHaarClassifierCascade//作者:    qiurenbo//日期:    2014-09-29//功能:    根据尺度调整Haar特征的大小和权重//输入参数:cascade 级联分类器指针 //sum     积分图//sqsum   平方和积分图//scale32x 尺度             //返回值:  无//修改记录://==================================================================void SetImagesForHaarClassifierCascade(HaarClassifierCascade* _cascade, Mat32* sum, Mat64* sqsum, int scale32x){      HidHaarClassifierCascade* hidCascade;    int coi0 = 0, coi1 = 0;    int i;    Rect equ_rect;    int weight_scale;HaarFeature* feature;    HidHaarFeature* hidfeature;int sum0 = 0, area0 = 0;    Rect r[3];Rect tr;    int correction_ratio;//根据尺度获取窗口大小    _cascade->scale32x = scale32x;    _cascade->real_window_size.width = (_cascade->orig_window_size.width * scale32x + 16)>>5 ;    _cascade->real_window_size.height = (_cascade->orig_window_size.height * scale32x +16) >> 5;//设置隐式级联分类器的积分图hidCascade = _cascade->hid_cascade;    hidCascade->sum = sum;    hidCascade->sqsum = sqsum;//根据尺度设置积分图起始矩阵的位置equ_rect.x = equ_rect.y = (scale32x+16)>>5;        equ_rect.width = ((_cascade->orig_window_size.width-2)*scale32x + 16 ) >> 5;   //+0.5是为了四舍五入    equ_rect.height = ((_cascade->orig_window_size.height-2)*scale32x + 16 ) >> 5;    weight_scale = equ_rect.width*equ_rect.height;    hidCascade->window_area = weight_scale; //矩形面积//获取积分图上起始矩阵四个像素的坐标    hidCascade->p0 = sum->mat32Ptr + (equ_rect.y) * sum->cols+ equ_rect.x;    hidCascade->p1 = sum->mat32Ptr + (equ_rect.y) * sum->cols + equ_rect.x + equ_rect.width;    hidCascade->p2 = sum->mat32Ptr + (equ_rect.y + equ_rect.height) * sum->cols + equ_rect.x;    hidCascade->p3 = sum->mat32Ptr + (equ_rect.y + equ_rect.height) * sum->cols + equ_rect.x + equ_rect.width;//获取平方和积分图上起始矩阵四个像素的坐标hidCascade->pq0 = sqsum->mat64Ptr + (equ_rect.y) * sqsum->cols+ equ_rect.x;    hidCascade->pq1 = sqsum->mat64Ptr + (equ_rect.y) * sqsum->cols+ equ_rect.x + equ_rect.width;    hidCascade->pq2 = sqsum->mat64Ptr + (equ_rect.y + equ_rect.height) * sqsum->cols+ equ_rect.x;    hidCascade->pq3 = sqsum->mat64Ptr + (equ_rect.y + equ_rect.height) * sqsum->cols+ equ_rect.x + equ_rect.width;//遍历每个Classifer所使用的特征,对它们进行尺度放大,并将改变的值赋给HidCascade,隐式级联分类器for( i = 0; i < hidCascade->count; i++ )    {        int j, k, l;        for( j = 0; j < hidCascade->stage_classifier[i].count; j++ )        {            for( l = 0; l < hidCascade->stage_classifier[i].classifier[j].count; l++ )            {                feature = &_cascade->stage_classifier[i].classifier[j].haar_feature[l];               hidfeature = &hidCascade->stage_classifier[i].classifier[j].node[l].feature;sum0 = 0;area0 = 0;                         for( k = 0; k < CV_HAAR_FEATURE_MAX; k++ )                {                    if( !hidfeature->rect[k].p0 )                        break;                    r[k] = feature->rect[k].r;//左上角坐标和矩阵长宽都按尺度放大tr.x = (r[k].x * scale32x + 16) >> 5;                    tr.width = (r[k].width * scale32x + 16) >> 5;                    tr.y = ( r[k].y * scale32x + 16 ) >> 5;                    tr.height = ( r[k].height * scale32x +16 ) >> 5;                    correction_ratio = weight_scale;//设置矩阵四个顶点在积分图中的位置(为了计算特征方便)hidfeature->rect[k].p0 = sum->mat32Ptr + tr.y * sum->cols +  tr.x;hidfeature->rect[k].p1 = sum->mat32Ptr + tr.y * sum->cols +  tr.x + tr.width; hidfeature->rect[k].p2 = sum->mat32Ptr + (tr.y + tr.height) *sum->cols +  tr.x;                     hidfeature->rect[k].p3 = sum->mat32Ptr + (tr.y + tr.height) *sum->cols +  tr.x + tr.width;//rect[1] = weight/area, 左移22位是为了避免浮点计算,将权值/检测窗口面积(不断扩大),降低权值                    hidfeature->rect[k].weight = ((feature->rect[k].weight)<< NODE_THRESHOLD_SHIFT)/(correction_ratio);                    if( k == 0 )                        area0 = tr.width * tr.height;                   else                        sum0 += hidfeature->rect[k].weight * tr.width * tr.height;                      } //rect[0].weight ,权重和特征矩形面积成反比 hidfeature->rect[0].weight = (int)(-sum0/area0);                                } /* l */        } /* j */    }    };uint64_t block1 = 0;//uint64_t block2 = 0;//==================================================================//函数名:  RunHaarClassifierCascade//作者:    qiurenbo//日期:    2014-09-30//功能:    在指定窗口范围计算特征//输入参数:_cascade级联分类器指针 //pt检测窗口左上角坐标//start_stage 起始stage下标   //返回值:  <=0未检测到目标或参数有问题//1成功检测到目标//修改记录://====================================================================int RunHaarClassifierCascade( HaarClassifierCascade* _cascade, Point& pt, int start_stage ){                               int result = -1;    int p_offset, pq_offset;    int i, j;    _int64 rectsum, variance_factor;    int variance_norm_factor;HidHaarClassifier* classifier;HidHaarTreeNode* node;int sum, t, a, b;int stage_sum;/*uint64_t start_time, end_time, overhead, cyclecountSet=0, cyclecountRun=0; //In the initialization portion of the code:TSCL = 0; //enable TSCstart_time = _itoll(TSCH, TSCL);end_time = _itoll(TSCH, TSCL);overhead = end_time-start_time; //Calculating the overhead of the method.*/    HidHaarClassifierCascade* hidCascade;if (_cascade == NULL)return -1;      hidCascade = _cascade->hid_cascade;    if( !hidCascade )return -1;//确保矩形的有效性,并防止计算窗口出边界if( pt.x < 0 || pt.y < 0 ||        pt.x + _cascade->real_window_size.width >= hidCascade->sum->cols-2 ||        pt.y + _cascade->real_window_size.height >= hidCascade->sum->rows-2 )        return -1;//计算特征点在积分图中的偏移,相当于移动窗口    p_offset = pt.y * (hidCascade->sum->cols) + pt.x;    pq_offset = pt.y * (hidCascade->sqsum->cols) + pt.x;//计算移动后整个窗口的特征值    rectsum = calc_sum(*hidCascade,p_offset);//*cascade->inv_window_area;    variance_factor = hidCascade->pq0[pq_offset] - hidCascade->pq1[pq_offset] -                           hidCascade->pq2[pq_offset] + hidCascade->pq3[pq_offset];     variance_factor = (variance_factor - ((rectsum*rectsum*windowArea[hidCascade->window_area-324])>>16))*windowArea[hidCascade->window_area-324]>>16;//variance_norm_factor = int(sqrt(float(variance_factor))+0.5f);//qmathvariance_norm_factor = shortSqrtTable[variance_factor];    if( variance_norm_factor < 0 )        variance_norm_factor = 1;//计算每个classifier的用到的特征区域的特征值for( i = start_stage; i < hidCascade->count; i++ )//for( i = start_stage; i < hidCascade->count; i++ ){ stage_sum = 0;node = hidCascade->stage_classifier[i].classifier->node;classifier = hidCascade->stage_classifier[i].classifier;//if( hidCascade->stage_classifier[i].two_rects )//{for( j = 0; j < hidCascade->stage_classifier[i].count; j++ ){//start_time = _itoll(TSCH, TSCL);//classifier = hidCascade->stage_classifier[i].classifier + j;//start_time = _itoll(TSCH, TSCL);t = node->threshold*variance_norm_factor >> 10;//end_time = _itoll(TSCH, TSCL);//block1 += end_time - start_time - overhead;//start_time = _itoll(TSCH, TSCL);//计算Haar特征sum = calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight >> 10;sum += calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight >> 10;//两特征和三特征分开处理if( node->feature.rect[2].p0 )sum += calc_sum(node->feature.rect[2],p_offset) * node->feature.rect[2].weight >> 10;//end_time = _itoll(TSCH, TSCL);//block1 += end_time - start_time - overhead;////a = classifier->alpha[0];//b = classifier->alpha[1];//start_time = _itoll(TSCH, TSCL);stage_sum += sum < t ? classifier->alpha[0] : classifier->alpha[1];//end_time = _itoll(TSCH, TSCL);//block2 += end_time - start_time - overheadnode = (HidHaarTreeNode*)((char*)(node) + 80);classifier++;}if( stage_sum < hidCascade->stage_classifier[i].threshold ){return -i;}}       //QueryPerformanceCounter(&t2);   //printf("FeatureDetectTime:%fms\n",(t2.QuadPart - t1.QuadPart)*1000.0/tc.QuadPart);      return 1;}//==================================================================//函数名:  HaarDetectObjects//作者:    qiurenbo//日期:    2014-09-30//功能:    在指定图片中查找目标//输入参数: _img图片指针    //cascade级联分类器指针 //start_stage起始stage下标 //scale_factor32x窗口变化尺度倍数 /32//min_neighbors最小临界目标(min_neighbors个以上的候选目标的区域才是最后的目标区域)//minSize目标最小的大小//返回值:  <=0未检测到目标或参数有问题//1成功检测到目标//修改记录://====================================================================void HaarDetectObjects(Image* _img,HaarClassifierCascade* cascade,   //训练好的级联分类器char* storage, int scale_factor32x,                    int min_neighbors, int flags, Size minSize){//第一次分类用到的最大stage//第二次分类用到的起始stageint split_stage = 2;   // ImgPtr stub, *img =  _img;Mat32sum ;Mat64    sqsum;Image    tmp;//检测区域候选队列Sequence    seq;//结果候选恿?Sequence    seq2;//并查集合并序列Sequence comps;Rect r1;PTreeNode* node;    int r1_neighbor;    int j, flag = 1;Rect r2 ;int r2_neighbor;    int distance;//cvRound( r2.rect.width * 0.2 );memset(&seq, 0, sizeof(Sequence));memset(&comps, 0, sizeof(Sequence));memset(&seq2, 0, sizeof(Sequence));memset(&result_seq, 0, sizeof(result_seq));    int i;        int factor32x;    int npass = 2;    if( !cascade )       return ;//获取积分图和平方和积分图的矩阵GetMat(&sum , _img->rows + 1, _img->cols + 1, BITS32, 0);GetMat(&sqsum, _img->rows + 1, _img->cols + 1, BITS64, 0);GetMat(&tmp, _img->rows, _img->cols, BITS8, 1);//若不存在隐式积分图(用于加速计算),则创建一个 if( !cascade->hid_cascade )CreateHidHaarClassifierCascade(cascade);    //计算积分图    Integral(_img, &sum, &sqsum);int count = 0;int count2 = 0;// In the variable declaration portion of the code:/*uint64_t start_time, end_time, overhead, cyclecountSet=0, cyclecountRun=0;// In the initialization portion of the code:TSCL = 0; //enable TSCstart_time = _itoll(TSCH, TSCL);end_time = _itoll(TSCH, TSCL);overhead = end_time-start_time; //Calculating the overhead of the method.*///不断调整窗口尺度,直到到达图像边缘(_img->cols-10) ||(_img->rows - 10)//并且确保尺度小于3倍(96)    for( factor32x = 32; factor32x*cascade->orig_window_size.width < (_img->cols - 10)<<5 &&factor32x*cascade->orig_window_size.height < (_img->rows - 10)<<5&&factor32x<96;factor32x = (factor32x*scale_factor32x+16)>>5 )    {        const int ystep32x = MAX(64, factor32x);//调整搜索窗口尺度        Size win_size;win_size.height = (cascade->orig_window_size.height * factor32x + 16)>>5;win_size.width = (cascade->orig_window_size.width * factor32x + 16 )>>5;       //pass指扫描次数,stage_offset指第二次扫描时从第几个stage开始        int pass, stage_offset = 0;//确保搜索窗口在尺度放大后仍然在图像中        int stop_height =  ( ((_img->rows - win_size.height)<<5)+ (ystep32x>>1) ) / ystep32x;//确保搜索窗口大于目标的最小尺寸        if( win_size.width < minSize.width || win_size.height < minSize.height )            continue;//QueryPerformanceFrequency(&tc);//QueryPerformanceCounter(&t1);//根据尺度设置隐式级联分类器中的特征和权重,并设置这些特征在积分图中的位置,以加速运算// Code to be profiled//start_time = _itoll(TSCH, TSCL);   SetImagesForHaarClassifierCascade(cascade, &sum, &sqsum, factor32x );//end_time = _itoll(TSCH, TSCL);//cyclecountSet = end_time-start_time-overhead;//QueryPerformanceCounter(&t2);//printf("SetImageFeatureRunTime:%fms\n",(t2.QuadPart - t1.QuadPart)*1000.0/tc.QuadPart);//设置粗检测所使用的起始分类器cascade->hid_cascade->count = split_stage;//用检测窗口扫描两遍图像://第一遍通过级联两个stage粗略定位目标大致区域,对候选区域进行标定(利用tmp矩阵)//第二遍对标定的候选区域进行完整筛选,将候选区域放置到队列中for( pass = 0; pass < npass; pass++ ){for( int _iy = 0; _iy < stop_height; _iy++ ){//检测窗口纵坐标步长为2,保持不变int iy = (_iy*ystep32x+16)>>5;int _ix, _xstep = 1;//stop_width是指_ix迭代的上限,_ix还要*ystep32x才是真正的窗口坐标int stop_width =( ((_img->cols - win_size.width)<<5) +ystep32x/2) / ystep32x;unsigned char* mask_row = tmp.imgPtr + tmp.cols* iy;for( _ix = 0; _ix < stop_width; _ix += _xstep ){//检测窗口横坐标按步长为4开始移动,若没有检测到目标,则改变下一次步长为2int ix = (_ix*ystep32x+16)>>5; // it really should be ystep//当前检测窗口左上角坐标Point pt;pt.x = ix;pt.y = iy;//粗略检测if( pass == 0 ){int result = 0;_xstep = 2;//start_time = _itoll(TSCH, TSCL);result = RunHaarClassifierCascade( cascade, pt, 0 );//end_time = _itoll(TSCH, TSCL);//cyclecountRun += end_time-start_time-overhead;if( result > 0 ){if( pass < npass - 1 )mask_row[ix] = 1;}//没有检测到改变步长为2(看ix的值)if( result < 0 )_xstep = 1;}//第二次检测先前粗定位的坐标else if( mask_row[ix] ){//start_time = _itoll(TSCH, TSCL);int result = RunHaarClassifierCascade(cascade, pt, stage_offset);//end_time = _itoll(TSCH, TSCL);//cyclecountRun += end_time-start_time-overhead;//count2++;//int result = 0;if( result > 0 ){seq.rectQueue[seq.tail].height = win_size.height;seq.rectQueue[seq.tail].width = win_size.width;seq.rectQueue[seq.tail].x = ix;seq.rectQueue[seq.tail].y = iy;seq.total++;seq.tail++;}elsemask_row[ix] = 0;                        }}        }//因为前两个stage在第一次检测的时候已经用过;//第二次检测的时候,从第3个stage开始进行完整的检测stage_offset = cascade->hid_cascade->count;cascade->hid_cascade->count = cascade->count;//cascade->hid_cascade->count = 15;} }//printf("The SetImage section took: %lld CPU cycles\n", cyclecountSet);//printf("The RunImage section took: %lld CPU cycles\n", cyclecountRun);//printf("The Block1 section took: %lld CPU cycles\n", block1);//printf("The Block2 section took: %lld CPU cycles\n", block2);if( min_neighbors != 0 )    {        //将候选目标按相似度构成并查集//返回值代表并查集树的个数        int ncomp = SeqPartition(&seq);        //对相邻候选区域进行累加,为计算平均边界做准备        for( i = 0; i < seq.total; i++ )        {            r1 = seq.rectQueue[i];node = &PTreeNodes[i];while(node->parent)node = node->parent;            int idx = (node - PTreeNodes);                       comps.neighbors[idx]++;            comps.rectQueue[idx].x += r1.x;            comps.rectQueue[idx].y += r1.y;            comps.rectQueue[idx].width += r1.width;            comps.rectQueue[idx].height += r1.height;        }// 计算平均目标边界        for( i = 0; i < seq.total; i++ )        {            int n = comps.neighbors[i];//只有满足最小临接的结果才是最终结果            if( n >= min_neighbors )            {                Rect* rect = &seq2.rectQueue[seq2.tail];                rect->x = (comps.rectQueue[i].x*2 + n)/(2*n);                rect->y = (comps.rectQueue[i].y*2 + n)/(2*n);rect->width = (comps.rectQueue[i].width*2 + n)/(2*n);                rect->height = (comps.rectQueue[i].height*2 + n)/(2*n);                seq2.neighbors[seq2.tail] = comps.neighbors[i];seq2.tail++;                seq2.total++;            }        }//从候选矩形中得到最大的矩形        for( i = 0; i < seq2.total; i++ )        {r1 = seq2.rectQueue[i];            r1_neighbor = seq2.neighbors[i];            flag = 1;            for( j = 0; j < seq2.total; j++ )            {                r2 = seq2.rectQueue[j];r2_neighbor = seq2.neighbors[j];                distance = (r2.width *2+5)/10;//cvRound( r2.rect.width * 0.2 );                if( i != j &&                    r1.x >= r2.x - distance &&                    r1.y >= r2.y - distance &&                    r1.x + r1.width <= r2.x + r2.width + distance &&                    r1.y + r1.height <= r2.y + r2.height + distance &&                    (r2_neighbor > MAX( 3, r1_neighbor ) || r1_neighbor < 3) )                {                    flag = 0;                    break;                }            }            if( flag )            {result_seq.rectQueue[result_seq.tail] = r1;result_seq.tail++;result_seq.total++;                            }        }}}void DownSample(Image* pImage, int factor){int i = 0;int j = 0;int counti = 0;int countj = 0;int step = pImage->cols / factor;for (i =0; i < pImage->rows; i+= factor){countj++;for (j =0; j < pImage->cols; j += factor){*(pImage->imgPtr + i*step/factor + j/factor) = *(pImage->imgPtr + i*pImage->cols + j);counti++;}counti = 0;}pImage->cols /= factor;pImage->rows /= factor;}



Haar.h

#ifndef _HAAR_H_#define _HAAR_H_#include "Tables.h"#define NODE_THRESHOLD_SHIFT 22#define MAXHIDCASCADE 200000  //隐式级联分类器所占空间(字节)#define MAXROWS   400   #define MAXCOLS   400#define MAXSTAGES  22   #define MAXCLASSIFER  213#define MAXTREENODE 2#define MAXALPHA   2#define MAXSEQS    25#define MaxMatNum  2#define RGBCHANNEL 3#define BITS8         0x00000001#define BITS32        0x00000010#define BITS64        0x00000100#define CV_8TO16U_SQR(x)  my8x16uSqrTab[(x)+128]#define CLR_RESULT_QUEUE() result_seq.tail = 0;\result_seq.total = 0; typedef unsigned char BYTE;typedef long long _int64;typedef unsigned char(*ImgPtr);typedef unsigned char(*Mat8Ptr);typedef int(*Mat32Ptr);typedef _int64(*Mat64Ptr);/*****************并查集数据结构*******************************/#define MAXPTREENODES 100typedef struct PTreeNode{    struct PTreeNode* parent;    char* element;    int rank;}PTreeNode;/************************积分图变量***************************/typedef int sumtype;typedef _int64 sqsumtype;/************************************************************/typedef struct Rect{   int x;   int y;   int width;   int height;}Rect;typedef struct{    int width;    int height;}Size;typedef struct Image{ ImgPtr  imgPtr;int rows;int cols;}Image;typedef struct Mat8{Mat8Ptr  mat8Ptr;int rows;int cols;}Mat8;typedef struct Mat32{Mat32Ptr  mat32Ptr;int rows;int cols;}Mat32;typedef struct Mat64{Mat64Ptr  mat64Ptr;int rows;int cols;}Mat64;typedef struct Sequence{int       total; Rect  rectQueue[MAXSEQS];int  neighbors[MAXSEQS];int  tail;}Sequence;//Haar特征的数量#define CV_HAAR_FEATURE_MAX  3    /*************HidHaar to Caculation Feature***********************************/typedef struct HidHaarFeature{    struct    {        sumtype *p0, *p1, *p2, *p3;        int weight;    }    rect[CV_HAAR_FEATURE_MAX];}HidHaarFeature;typedef struct HidHaarTreeNode{    HidHaarFeature feature;    int threshold;    int left;    int right;}HidHaarTreeNode;typedef struct HidHaarClassifier{    int count;    //CvHaarFeature* orig_feature;    HidHaarTreeNode* node;    int* alpha;//HidHaarTreeNode node[MAXTREENODE];    //int alpha[MAXALPHA];}HidHaarClassifier;typedef struct HidHaarStageClassifier{    int  count;    int threshold;    HidHaarClassifier* classifier;//HidHaarClassifier classifier[MAXCLASSIFER];    int two_rects;        struct HidHaarStageClassifier* next;    struct HidHaarStageClassifier* child;    struct HidHaarStageClassifier* parent;}HidHaarStageClassifier;typedef struct HidHaarClassifierCascade{    int  count;    int  is_stump_based;    int  has_tilted_features;    int  is_tree;    int window_area;    Mat32* sum;Mat64* sqsum;    HidHaarStageClassifier* stage_classifier;//HidHaarStageClassifier stage_classifier[MAXSTAGES];    sqsumtype *pq0, *pq1, *pq2, *pq3;    sumtype *p0, *p1, *p2, *p3;    void** ipp_stages;}HidHaarClassifierCascade;/******************Haar Cascade*****************************************/typedef struct HaarFeature{    int  tilted;    struct    {        Rect r;        int weight;    } rect[CV_HAAR_FEATURE_MAX];}HaarFeature;typedef struct HaarClassifier{    int count;    HaarFeature* haar_feature;    int* threshold;    int* left;    int* right;    int* alpha;}HaarClassifier;typedef struct HaarStageClassifier{    int  count;    int threshold;    HaarClassifier* classifier;    int next;    int child;    int parent;}HaarStageClassifier;typedef struct HaarClassifierCascade{    int  flags;    int  count;    Size orig_window_size;    Size real_window_size;    int scale32x;    HaarStageClassifier* stage_classifier;    HidHaarClassifierCascade* hid_cascade;}HaarClassifierCascade;typedef struct CvAvgComp{    Rect rect;    int neighbors;}CvAvgComp;typedef struct Point{int x;int y;}Point;/******************全局变量****************************************///cascadeextern HaarClassifierCascade *cascade ;//extern HidHaarClassifierCascade hid_cascade;//32bits cell Matextern int  MatPool32[MaxMatNum][MAXROWS][MAXCOLS];//8bits cell extern unsigned char  MatPool8[MaxMatNum][MAXROWS][MAXCOLS];//8bits*3 cell extern unsigned char  ImgRGBPool8[MaxMatNum][RGBCHANNEL][MAXROWS][MAXCOLS];//64bits float cell extern _int64 MatPool64[MaxMatNum][MAXROWS][MAXCOLS];//分类器检测结果区域序列extern Sequence result_seq;/********************全局函数******************************************/extern void ReadFaceCascade();extern void HaarDetectObjects(Image* _img,HaarClassifierCascade* cascade,     char* storage, int scale_factor32x,  int min_neighbors, int flags, Size minSize);#endif



4 0