zoukankan      html  css  js  c++  java
  • OpenCV Haar AdaBoost源代码改进(比EMCV快6倍)

    这几天研究了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版project链接 (VC6.0能直接编译。但没有5。6,7步的优化)点击打开链接

    完整DSP版project链接 (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 Mat
    int		MatPool32[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 cascade
    	cascade = (HaarClassifierCascade*)HaarClassifierCascade_face;
    
    	//load stages
    	int stage_size = StageClassifier_face[0];
    	HaarStageClassifier *stages ;
    	stages = (HaarStageClassifier *)(StageClassifier_face+1);
    	
    	//load classifier
    	int 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 stages
    	cascade->stage_classifier = stages;
    	//link stages。classifiers
    	int 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;
    	//移动指针到积分图的下一行,第一行全为0
    	sum += 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)大小矩阵,故将第一列置为0
    		sum[-1] = 0;                                        
    		sqsum[-1] = 0;                                      
    		
    		for(int x = 0 ; x < size.width; x++ )    
    		{                                                   
    			int it = src[x];                           
    			int t = (it);   
    			
    			//查表计算平方
    			_int64	tq =  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.4304
    
    	HidHaarClassifier* 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 TSC start_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);//qmath variance_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 - overhead node = (HidHaarTreeNode*)((char*)(node) + 80); classifier++; } if( stage_sum < hidCascade->stage_classifier[i].threshold ) { return -i; } } //QueryPerformanceCounter(&t2); //printf("FeatureDetectTime:%fms ",(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 //第二次分类用到的起始stage int split_stage = 2; // ImgPtr stub, *img = _img; Mat32 sum ; 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 TSC start_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 ",(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開始移动,若没有检測到目标,则改变下一次步长为2 int 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++; } else mask_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 ", cyclecountSet); // printf("The RunImage section took: %lld CPU cycles ", cyclecountRun); // printf("The Block1 section took: %lld CPU cycles ", block1); // printf("The Block2 section took: %lld CPU cycles ", 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 100
    typedef 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;
    
    
    
    
    
    
    /******************全局变量****************************************/
    //cascade
    extern HaarClassifierCascade *cascade ;
    //extern HidHaarClassifierCascade hid_cascade;
    
    
    //32bits cell Mat
    extern 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



  • 相关阅读:
    mysql优化之索引优化
    mysqld --debug-sync
    mysql.cnf 配制文件详解
    my.cnf 中字符集设置
    tcp_tw_reuse 与 net.ipv4.tcp_tw_recycle
    mysql init_connect 参数的其他用处
    监控mysql索引使用效率的脚本
    mysql 源代码学习 博客 [lock..]
    mysqld with valgrind
    思维导图软件
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5137710.html
Copyright © 2011-2022 走看看