OpenCV Haar AdaBoost源码改进

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

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

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

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

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

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

Haar.h

#ifndef _HAAR_H_
#define _HAAR_H_
#include "Tables.h"

#define NODE_THRESHOLD_SHIFT 22

#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	(*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;
    short left;
    short right;
}HidHaarTreeNode;

typedef struct HidHaarClassifier
{
    short count;
    //CvHaarFeature* orig_feature;

    //HidHaarTreeNode* node;
    //int* alpha;
	HidHaarTreeNode node[MAXTREENODE];
    int alpha[MAXALPHA];
}HidHaarClassifier;

typedef struct HidHaarStageClassifier
{
    short  count;
    int threshold;
    //HidHaarClassifier* classifier;
	HidHaarClassifier classifier[MAXCLASSIFER];
    char 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

Haar.cpp

#include "Haar.h"
#include "loadCascade.h"
#include "Util.h"
#include "stdio.h"
#include "string.h"
#include <math.h>
#include "windows.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];

//分类器检测结果区域序列
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);

}

//==================================================================
//函数名:  CreateHidHaarClassifierCascade
//作者:    qiurenbo
//日期:    2014-09-28
//功能:    创建隐式积分图加快计算速度
//输入参数:cascade 级联分类器指针
//返回值:  static HidHaarClassifierCascade*   返回一个隐式级联分类器指针
//修改记录:
//==================================================================
static HidHaarClassifierCascade*
CreateHidHaarClassifierCascade(HaarClassifierCascade* cascade)
{

	cascade->hid_cascade = &hid_cascade;

    HidHaarClassifierCascade* out = cascade->hid_cascade;
	const int icv_stage_threshold_bias = 419; //0.0001*(2^22)=419.4304

    int i, j, l;

    int total_classifiers = 0;
    int total_nodes = 0;

    int has_tilted_features = 0;
    int max_count = 0;

    /* 初始化HidCascade头 */
    out->count = cascade->count;
    //out->stage_classifier = HidStages;

    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];
		//初始化为二特征,下面会根据真实的特征数至1或0(三特征)
        hid_stage_classifier->two_rects = 1;

		//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 = out->stage_classifier[i].classifier + j;
            int node_count = classifier->count;
			hid_classifier->count = node_count;
            //int* alpha_ptr = (int*)&Alpha[i][j];

           // hid_classifier->node = (HidHaarTreeNode*)&HidTreeNode[i][j];
           // hid_classifier->alpha = alpha_ptr;

			//赋值node属性
            for( l = 0; l < node_count; l++ )
            {
                HidHaarTreeNode* node =  out->stage_classifier[i].classifier[j].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]));

			//判断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;

	//根据尺度获取窗口大小
    _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++ )
            {
                HaarFeature* feature = &_cascade->stage_classifier[i].classifier[j].haar_feature[l];

                HidHaarFeature* hidfeature = &hidCascade->stage_classifier[i].classifier[j].node[l].feature;
				int sum0 = 0, area0 = 0;
                Rect r[3];

				Rect tr;
                int correction_ratio;
				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 */
    }

};

//==================================================================
//函数名:  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;
    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 + (hidCascade->window_area>>1))/hidCascade->window_area + (hidCascade->window_area>>1))/hidCascade->window_area;
    variance_norm_factor = int(sqrt(float(variance_factor))+0.5f);//qmath

    if( variance_norm_factor < 0 )
        variance_norm_factor = 1;
	//LARGE_INTEGER t1,t2,tc;
   	//QueryPerformanceFrequency(&tc);
	//QueryPerformanceCounter(&t1);

	//计算每个classifier的用到的特征区域的特征值
	for( i = start_stage; i < hidCascade->count; i++ )
	{
 		double stage_sum = 0;

		//if( hidCascade->stage_classifier[i].two_rects )
		//{
		for( j = 0; j < hidCascade->stage_classifier[i].count; j++ )
		{

			HidHaarTreeNode* node = hidCascade->stage_classifier[i].classifier[j].node;
			double sum, t = node->threshold*variance_norm_factor, a, b;

			//计算Haar特征
			sum = calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight;
			sum += calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight;

			//两特征和三特征分开处理
			if( node->feature.rect[2].p0 )
				sum += calc_sum(node->feature.rect[2],p_offset) * node->feature.rect[2].weight;

			a = hidCascade->stage_classifier[i].classifier[j].alpha[0];
			b = hidCascade->stage_classifier[i].classifier[j].alpha[1];
			stage_sum += sum < t ? a : b;
		}

		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数量
	int split_stage = 2;

   // ImgPtr stub, *img =  _img;
	Mat32		sum ;
	Mat64	    sqsum;
	Image	    tmp;

	//检测区域候选队列
	Sequence    seq;

	//结果候选队列
	Sequence    seq2;

	//并查集合并序列
	Sequence comps;

	memset(&seq, 0, sizeof(Sequence));
	memset(&comps, 0, sizeof(Sequence));
	memset(&seq2, 0, sizeof(Sequence));

    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);

	//将隐式级联分类器写入文件看是否正确
	//WriteCascadeToFile(cascade->hid_cascade);
	//LARGE_INTEGER t1,t2,tc;
	//QueryPerformanceFrequency(&tc);
	//QueryPerformanceCounter(&t1);
    //计算积分图
    Integral(_img, &sum, &sqsum);
	//QueryPerformanceCounter(&t2);
	//printf("SetImageFeatureRunTime:%fms\n",(t2.QuadPart - t1.QuadPart)*1000.0/tc.QuadPart);
	//WriteSumAndSqSumToFile(sum, sqsum);
	//LARGE_INTEGER t1,t2,tc;
   	//QueryPerformanceFrequency(&tc);
//	QueryPerformanceCounter(&t1);
	int count = 0;
	int count2 = 0;
	//不断调整窗口尺度,直到到达图像边缘(_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);
		//根据尺度设置隐式级联分类器中的特征和权重,并设置这些特征在积分图中的位置,以加速运算
        SetImagesForHaarClassifierCascade(cascade, &sum, &sqsum, factor32x );
		//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开始移动,若没有检测到目标,则改变下一次步长为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;
						//QueryPerformanceFrequency(&tc);
						//QueryPerformanceCounter(&t1);
						//Point引用传参,避免向形参的拷贝
						result = RunHaarClassifierCascade( cascade, pt, 0 );
						count++;
						//QueryPerformanceCounter(&t2);
						//printf("RunHaarRunTime:%fms\n",(t2.QuadPart - t1.QuadPart)*1000.0/tc.QuadPart);
						if( result > 0 )
						{
							if( pass < npass - 1 )
								mask_row[ix] = 1;

						}
						//没有检测到改变步长为2(看ix的值)
						if( result < 0 )
							_xstep = 1;
					}
					//第二次检测先前粗定位的坐标
					else if( mask_row[ix] )
					{
						//写Mask文件看是否正确
						//WriteMaskRowToFile(tmp);
						/*FILE* fp;

						fp = fopen("..//MaskRowResult//maskRow2.txt","a+");
						fprintf(fp, "%d %d\n ", ix, iy);
						fclose(fp);*/

						int result = RunHaarClassifierCascade(cascade, pt, stage_offset);
						count2++;
						//int result = 0;
						if( result > 0 )
						{

							/*Rect* rect = &seq.rectQueue[seq.tail++];
							rect->height = win_size.height;
							rect->width = win_size.width;
							rect->x = ix;
							rect->y = iy;
							seq.total++;*/

							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;

	}
 }

	//QueryPerformanceCounter(&t2);
	//printf("HaarRunTime:%fms\n",(t2.QuadPart - t1.QuadPart)*1000.0/tc.QuadPart);
	if( min_neighbors != 0 )
    {

        //将候选目标按相似度构成并查集
		//返回值代表并查集树的个数
        int ncomp = SeqPartition(&seq);

        //对相邻候选区域进行累加,为计算平均边界做准备
        for( i = 0; i < seq.total; i++ )
        {
            Rect r1 = seq.rectQueue[i];
			PTreeNode* 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++ )
        {
			Rect r1 = seq2.rectQueue[i];
            int r1_neighbor = seq2.neighbors[i];
            int j, flag = 1;

            for( j = 0; j < seq2.total; j++ )
            {
                Rect r2 = seq2.rectQueue[j];
				int r2_neighbor = seq2.neighbors[j];
                int 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++;

            }
        }

	}

}
时间: 2024-08-15 00:51:20

OpenCV Haar AdaBoost源码改进的相关文章

OpenCV Haar AdaBoost源码改进据说是比EMCV快6倍

<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>

OpenCV学习:OpenCV源码编译(vc9)

安装后的OpenCV程序下的build文件夹中,只找到了vc10.vc11和vc12三种编译版本的dll和lib文件,需要VS2010及以上的IDE版本,而没有我们常用的VS2008版本. 于是,需要的小伙伴们可以自己动手,丰衣足食! 1). 安装CMake cmake-2.8.8-win32-x86.exe (http://www.cmake.org/cmake/resources/software.html) 百度云盘:http://pan.baidu.com/s/1dEYbx77  密码:

全景视频拼接(二)--OpenCV源码解析

本文参考:http://blog.csdn.net/skeeee/article/details/19480693,做了一定修改和补充. 一.stitching_detail程序运行流程 1.命令行调用程序,输入源图像以及程序的参数 2.特征点检测,判断是使用surf还是orb,默认是surf. 3.对图像的特征点进行匹配,使用最近邻和次近邻方法,将两个最优的匹配的置信度保存下来. 4.对图像进行排序以及将置信度高的图像保存到同一个集合中,删除置信度比较低的图像间的匹配,得到能正确匹配的图像序列

OpenCV Canny 源码注释与分析

1986年,John F.Canny 完善了边缘检测理论,Canny算法以此命名. Canny 算法的步骤: 1. 使用滤波器卷积降噪 2. 使用Sobel导数计算梯度幅值和方向 3. 非极大值抑制 + 滞后阈值 在正式处理前,用高斯滤平滑波器对图像做滤波降噪的操作,避免噪声点的干扰,但在OpenCV的canny源码中,没有进行高斯滤波,需要使用者自行滤波:有些资料将非极大值抑制和滞后阈值视为两个步骤也是可行的,但是在源码中非极大值抑制 和滞后阈值是同时进行的. canny源码的位置:\open

OpenCV源码解析

OpenCV K-means源码解析 OpenCV 图片读取源码解析 OpenCV 视频播放源码解析 读懂OpenCV源码需要哪些基础? 通读过C++ Primer,目前C++属于入门级, 数字图像处理基础也有.目前看不懂OpenCV源码,请问还需要哪些基础, 从哪些方面入手源码比较好呢?谢谢 回答: 半年前实习的时候,在那个公司参与用OpenCV c++版本的开发公司自己的CV库.体会还比较深,现在回想起来,大概会有这么一些建议: 1. C++需要多了解一下,建议看看Bjarne大神的书 Op

SURF算法与源码分析、下

上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SURF特征点匹配的基础.总体来说算法思路和SIFT相似,只是每一步都做了不同程度的近似与简化,提高了效率. 1. SURF特征点方向分配 为了保证特征矢量具有旋转不变性,与SIFT特征一样,需要对每个特征点分配一个主方向.为些,我们需要以特征点为中心,以$6s$($s = 1.2 *L /9$为特征点

Opencv2.4.9源码分析——Gradient Boosted Trees

一.原理 梯度提升树(GBT,Gradient Boosted Trees,或称为梯度提升决策树)算法是由Friedman于1999年首次完整的提出,该算法可以实现回归.分类和排序.GBT的优点是特征属性无需进行归一化处理,预测速度快,可以应用不同的损失函数等. 从它的名字就可以看出,GBT包括三个机器学习的优化算法:决策树方法.提升方法和梯度下降法.前两种算法在我以前的文章中都有详细的介绍,在这里我只做简单描述. 决策树是一个由根节点.中间节点.叶节点和分支构成的树状模型,分支代表着数据的走向

【特征匹配】SURF原理与源码解析

SURF (Speed Up Robust Features)是SIFT改进版也是加速版,提高了检测特征点的速度,综合性能要优于SIFT. 下面先逐次介绍SURF的原理,最后解析opencv上SURF源码. 转载请注明出处:http://blog.csdn.net/luoshixian099/article/details/47778143 1.积分图像 SURF是对积分图像进行操作,从而实现了加速,采用盒子滤波器计算每个像素点的Hessian矩阵行列式时,只需要几次加减法运算,而且运算量与盒子

Android 关于 CountDownTimer onTick() 倒计时不准确问题源码分析

一.问题 CountDownTimer 使用比较简单,设置 5 秒的倒计时,间隔为 1 秒. final String TAG = "CountDownTimer"; new CountDownTimer(5 * 1000, 1000) { @Override public void onTick(long millisUntilFinished) { Log.i(TAG, "onTick → millisUntilFinished = " + millisUnti