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

时间:2023-03-09 07:40:38
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>
#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* <= r1->x* + distance5x &&
r2->x* >= r1->x* - distance5x &&
r2->y* <= r1->y* + distance5x &&
r2->y* >= r1->y* - distance5x &&
r2->width* <= r1->width * &&
r2->width * >= r1->width*;
} //==================================================================
//函数名: ReadFaceCascade
//作者: qiurenbo
//日期: 2014-10-1
//功能: 根据候选区域的相似性(IsEqual函数)建立并查集
//输入参数:seq 候选目标区域序列
//返回值: 返回分类后的类别数
//修改记录:
//==================================================================
int SeqPartition( const Sequence* seq )
{
Sequence* result = ;
//CvMemStorage* temp_storage = 0;
int class_idx = ; memset(PTreeNodes, , MAXPTREENODES*sizeof(PTreeNode)); int i, j; //建立以seq中元素为根节点的森林
for( i = ; i < seq->total; i++ )
PTreeNodes[i].element = (char*)&seq->rectQueue[i]; //遍历所有根节点
for( i = ; i < seq->total; i++ )
{
PTreeNode* node = &PTreeNodes[i];
PTreeNode* root = node;
//确保node中元素指针不为空
if( !node->element )
continue; //找到元素在树中的根结点
while( root->parent )
root = root->parent; for( j = ; 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 = ; i < seq->total; i++ )
{
PTreeNode* node = &PTreeNodes[i];
int idx = -; if( node->element )
{
while( node->parent )
node = node->parent; //计算有几棵并查树,巧妙地利用取反避免重复计算
if( node->rank >= )
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[];
HaarStageClassifier *stages ;
stages = (HaarStageClassifier *)(StageClassifier_face+); //load classifier
int classifier_size = Classifier_face[];
HaarClassifier *cls ;
cls = (HaarClassifier*) (Classifier_face+); int class_info_size = class_info[];
int * cls_info ;
cls_info = (int*)(class_info+); //link cascade with stages
cascade->stage_classifier = stages;
//link stages,classifiers
int offset=;
int offset_t=(sizeof(HaarFeature)/sizeof(int));
int offset_l=offset_t+;
int offset_r=offset_t+;
int offset_a=offset_t+;
int offset_total=;
for(i=;i<stage_size;++i)
{
(stages+i)->classifier = (cls+offset);
offset +=(stages+i)->count;
} offset_total = + (sizeof(HaarFeature)/sizeof(int));
//link classifiers and haar_featrue;
for(i=;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 = ;
_int64 sq = ;
//移动指针到积分图的下一行,第一行全为0
sum += sumstep + ;
sqsum += sqsumstep + ; //y代表相对于输入检测矩阵起始第几行
for(int y = ; y < size.height; y++, src += srcstep,
sum += sumstep, sqsum += sqsumstep )
{
//sum和sqsum为(W+1)*(H+1)大小矩阵,故将第一列置为0
sum[-] = ;
sqsum[-] = ; for(int x = ; 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 - ) & ~(align-) );
}
//==================================================================
//函数名: 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 = ; //0.0001*(2^22)=419.4304 HidHaarClassifier* haar_classifier_ptr;
HidHaarTreeNode* haar_node_ptr;
int i, j, l; int total_classifiers = ;
int total_nodes = ; int has_tilted_features = ;
int max_count = ; /* 初始化HidCascade头 */
out->count = cascade->count;
out->stage_classifier = (HidHaarStageClassifier*)(out + );
//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 = ;
out->is_tree = ; // 用cascade初始化HidCascade
for( i = ; 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 = ;
haar_classifier_ptr += stage_classifier->count; //Stage构成一颗退化的二叉树(单分支),每个结点最多只有一个孩子
hid_stage_classifier->parent = (stage_classifier->parent == -)
? NULL : out->stage_classifier + stage_classifier->parent;
hid_stage_classifier->next = (stage_classifier->next == -)
? NULL : out->stage_classifier + stage_classifier->next;
hid_stage_classifier->child = (stage_classifier->child == -)
? NULL : out->stage_classifier + stage_classifier->child ; //判断该stage是否为树状结构(多分枝)
out->is_tree |= hid_stage_classifier->next != NULL; //赋值classifer属性
for( j = ; 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 = ; l < node_count; l++ )
{
HidHaarTreeNode* node = hid_classifier->node + l;
HaarFeature* feature = classifier->haar_feature + l;
memset( node, -, sizeof(*node) );
node->threshold = classifier->threshold[l];
node->left = classifier->left[l];
node->right = classifier->right[l]; //对特征数目进行判断,若是三特征,则至two_rects为0
if( (feature->rect[].weight) == ||
feature->rect[].r.width == ||
feature->rect[].r.height == )
memset( &(node->feature.rect[]), , sizeof(node->feature.rect[]) );
else
hid_stage_classifier->two_rects = ;
} //赋值alpha
memcpy( hid_classifier->alpha, classifier->alpha, (node_count+)*sizeof(hid_classifier->alpha[]));
haar_node_ptr = (HidHaarTreeNode*)(alpha_ptr+node_count + ); //判断cascade中的分类器是否是树桩分类器,只有根结点的决策树
out->is_stump_based &= node_count == ;
}
} //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 = , coi1 = ;
int i;
Rect equ_rect;
int weight_scale;
HaarFeature* feature;
HidHaarFeature* hidfeature;
int sum0 = , area0 = ;
Rect r[];
Rect tr;
int correction_ratio; //根据尺度获取窗口大小
_cascade->scale32x = scale32x;
_cascade->real_window_size.width = (_cascade->orig_window_size.width * scale32x + )>> ;
_cascade->real_window_size.height = (_cascade->orig_window_size.height * scale32x +) >> ; //设置隐式级联分类器的积分图
hidCascade = _cascade->hid_cascade;
hidCascade->sum = sum;
hidCascade->sqsum = sqsum; //根据尺度设置积分图起始矩阵的位置
equ_rect.x = equ_rect.y = (scale32x+)>>;
equ_rect.width = ((_cascade->orig_window_size.width-)*scale32x + ) >> ; //+0.5是为了四舍五入
equ_rect.height = ((_cascade->orig_window_size.height-)*scale32x + ) >> ;
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 = ; i < hidCascade->count; i++ )
{
int j, k, l;
for( j = ; j < hidCascade->stage_classifier[i].count; j++ )
{
for( l = ; 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 = ;
area0 = ; for( k = ; k < CV_HAAR_FEATURE_MAX; k++ )
{
if( !hidfeature->rect[k].p0 )
break; r[k] = feature->rect[k].r; //左上角坐标和矩阵长宽都按尺度放大
tr.x = (r[k].x * scale32x + ) >> ;
tr.width = (r[k].width * scale32x + ) >> ;
tr.y = ( r[k].y * scale32x + ) >> ;
tr.height = ( r[k].height * scale32x + ) >> ; 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 == )
area0 = tr.width * tr.height;
else
sum0 += hidfeature->rect[k].weight * tr.width * tr.height; }
//rect[0].weight ,权重和特征矩形面积成反比
hidfeature->rect[].weight = (int)(-sum0/area0); } /* l */
} /* j */
} }; uint64_t block1 = ;
//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 = -; 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 -; hidCascade = _cascade->hid_cascade;
if( !hidCascade )
return -; //确保矩形的有效性,并防止计算窗口出边界
if( pt.x < || pt.y < ||
pt.x + _cascade->real_window_size.width >= hidCascade->sum->cols- ||
pt.y + _cascade->real_window_size.height >= hidCascade->sum->rows- )
return -; //计算特征点在积分图中的偏移,相当于移动窗口
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-])>>))*windowArea[hidCascade->window_area-]>>;
//variance_norm_factor = int(sqrt(float(variance_factor))+0.5f);//qmath
variance_norm_factor = shortSqrtTable[variance_factor]; if( variance_norm_factor < )
variance_norm_factor = ; //计算每个classifier的用到的特征区域的特征值 for( i = start_stage; i < hidCascade->count; i++ )
//for( i = start_stage; i < hidCascade->count; i++ )
{
stage_sum = ; node = hidCascade->stage_classifier[i].classifier->node;
classifier = hidCascade->stage_classifier[i].classifier;
//if( hidCascade->stage_classifier[i].two_rects )
//{
for( j = ; 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 >> ;
//end_time = _itoll(TSCH, TSCL);
// block1 += end_time - start_time - overhead; //start_time = _itoll(TSCH, TSCL);
//计算Haar特征
sum = calc_sum(node->feature.rect[],p_offset) * node->feature.rect[].weight >> ;
sum += calc_sum(node->feature.rect[],p_offset) * node->feature.rect[].weight >> ; //两特征和三特征分开处理
if( node->feature.rect[].p0 )
sum += calc_sum(node->feature.rect[],p_offset) * node->feature.rect[].weight >> ; //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[] : classifier->alpha[];
// end_time = _itoll(TSCH, TSCL);
// block2 += end_time - start_time - overhead
node = (HidHaarTreeNode*)((char*)(node) + );
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 ;
} //==================================================================
//函数名: 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 = ; // 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 = ;
Rect r2 ;
int r2_neighbor;
int distance;//cvRound( r2.rect.width * 0.2 );
memset(&seq, , sizeof(Sequence));
memset(&comps, , sizeof(Sequence));
memset(&seq2, , sizeof(Sequence));
memset(&result_seq, , sizeof(result_seq)); int i; int factor32x;
int npass = ; if( !cascade )
return ; //获取积分图和平方和积分图的矩阵
GetMat(&sum , _img->rows + , _img->cols + , BITS32, );
GetMat(&sqsum, _img->rows + , _img->cols + , BITS64, );
GetMat(&tmp, _img->rows, _img->cols, BITS8, ); //若不存在隐式积分图(用于加速计算),则创建一个
if( !cascade->hid_cascade )
CreateHidHaarClassifierCascade(cascade); //计算积分图
Integral(_img, &sum, &sqsum); int count = ;
int count2 = ;
// 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 = ; factor32x*cascade->orig_window_size.width < (_img->cols - )<< &&
factor32x*cascade->orig_window_size.height < (_img->rows - )<<
&&factor32x<;
factor32x = (factor32x*scale_factor32x+)>> )
{ const int ystep32x = MAX(, factor32x); //调整搜索窗口尺度
Size win_size;
win_size.height = (cascade->orig_window_size.height * factor32x + )>>;
win_size.width = (cascade->orig_window_size.width * factor32x + )>>; //pass指扫描次数,stage_offset指第二次扫描时从第几个stage开始
int pass, stage_offset = ; //确保搜索窗口在尺度放大后仍然在图像中
int stop_height = ( ((_img->rows - win_size.height)<<)+ (ystep32x>>) ) / 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 = ; pass < npass; pass++ )
{ for( int _iy = ; _iy < stop_height; _iy++ )
{
//检测窗口纵坐标步长为2,保持不变
int iy = (_iy*ystep32x+)>>;
int _ix, _xstep = ; //stop_width是指_ix迭代的上限,_ix还要*ystep32x才是真正的窗口坐标
int stop_width =( ((_img->cols - win_size.width)<<) +ystep32x/) / ystep32x;
unsigned char* mask_row = tmp.imgPtr + tmp.cols* iy; for( _ix = ; _ix < stop_width; _ix += _xstep )
{ //检测窗口横坐标按步长为4开始移动,若没有检测到目标,则改变下一次步长为2
int ix = (_ix*ystep32x+)>>; // it really should be ystep //当前检测窗口左上角坐标
Point pt;
pt.x = ix;
pt.y = iy; //粗略检测
if( pass == )
{ int result = ;
_xstep = ; //start_time = _itoll(TSCH, TSCL);
result = RunHaarClassifierCascade( cascade, pt, );
//end_time = _itoll(TSCH, TSCL);
//cyclecountRun += end_time-start_time-overhead;
if( result > )
{
if( pass < npass - )
mask_row[ix] = ; }
//没有检测到改变步长为2(看ix的值)
if( result < )
_xstep = ;
}
//第二次检测先前粗定位的坐标
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 > )
{
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] = ; }
} } //因为前两个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 != )
{ //将候选目标按相似度构成并查集
//返回值代表并查集树的个数
int ncomp = SeqPartition(&seq); //对相邻候选区域进行累加,为计算平均边界做准备
for( i = ; 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 = ; 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* + n)/(*n);
rect->y = (comps.rectQueue[i].y* + n)/(*n);
rect->width = (comps.rectQueue[i].width* + n)/(*n);
rect->height = (comps.rectQueue[i].height* + n)/(*n);
seq2.neighbors[seq2.tail] = comps.neighbors[i];
seq2.tail++;
seq2.total++;
}
} //从候选矩形中得到最大的矩形
for( i = ; i < seq2.total; i++ )
{
r1 = seq2.rectQueue[i];
r1_neighbor = seq2.neighbors[i];
flag = ; for( j = ; j < seq2.total; j++ )
{
r2 = seq2.rectQueue[j];
r2_neighbor = seq2.neighbors[j];
distance = (r2.width *+)/;//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( , r1_neighbor ) || r1_neighbor < ) )
{
flag = ;
break;
}
} if( flag )
{
result_seq.rectQueue[result_seq.tail] = r1;
result_seq.tail++;
result_seq.total++; }
} } } void DownSample(Image* pImage, int factor)
{
int i = ;
int j = ;
int counti = ;
int countj = ; int step = pImage->cols / factor;
for (i =; i < pImage->rows; i+= factor)
{
countj++;
for (j =; j < pImage->cols; j += factor)
{
*(pImage->imgPtr + i*step/factor + j/factor) = *(pImage->imgPtr + i*pImage->cols + j);
counti++;
}
counti = ;
} pImage->cols /= factor;
pImage->rows /= factor;
}