OpenCV Haar AdaBoost原始碼改進(比EMCV快6倍)

NO IMAGE

這幾天研究了OpenCV原始碼 Haar AdaBoost演算法,作了一下改進

1.去掉了所有動態分配記憶體的操作,對嵌入式系統有一定的速度提升

2.註釋覆蓋了大量關鍵程式碼

3.減少了程式碼一半的體積,並且減少了部分健壯性的程式碼,速度比OpenCV原始碼提升16%

4.修改了大量資料結構,不依賴CV原始碼直接編譯

5.去掉了double型,改成Int

6.開方改成查表

7.除法改成乘法加位移

 

注:使用時請注意,現僅支援單分支的Stages和單結點的Classifier訓練好的結果集

        在720MHZ的DSP板子上對一幅352*288的灰度影象進行人臉檢測只需300ms,比EMCV快6倍

完整PC版工程連結 (VC6.0能直接編譯,但沒有5,6,7步的優化)點選開啟連結

完整DSP版工程連結 (CCS3.0能直接編譯,包含所有優化)點選開啟連結

DSP優化的關鍵程式碼例項如下(這個版本在CCS下編譯,若想用VC6.0直接編譯,還要修改一定的資料結構)

Haar.cpp

 

<pre name="code" class="cpp">#include "Haar.h"
#include "loadCascade.h"
#include "Util.h"
#include "stdio.h"
#include "string.h"
#include <math.h>
#include <stdint.h>
#include <c6x.h>
/*******************Global************************************/
HaarClassifierCascade *cascade ;
//HidHaarClassifierCascade hid_cascade;
//32bits cell 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\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
//第二次分類用到的起始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\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;
//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\n", cyclecountSet);
//	printf("The RunImage section took: %lld CPU cycles\n", cyclecountRun);
//	printf("The Block1 section took: %lld CPU cycles\n", block1);
//	printf("The Block2 section took: %lld CPU cycles\n", block2);
if( min_neighbors != 0 )
{
//將候選目標按相似度構成並查集
//返回值代表並查集樹的個數
int ncomp = SeqPartition(&seq);
//對相鄰候選區域進行累加,為計算平均邊界做準備
for( i = 0; i < seq.total; i   )
{
r1 = seq.rectQueue[i];
node = &PTreeNodes[i];
while(node->parent)
node = node->parent;
int idx = (node - PTreeNodes);
comps.neighbors[idx]  ;
comps.rectQueue[idx].x  = r1.x;
comps.rectQueue[idx].y  = r1.y;
comps.rectQueue[idx].width  = r1.width;
comps.rectQueue[idx].height  = r1.height;
}
// 計算平均目標邊界
for( i = 0; i < seq.total; i   )
{
int n = comps.neighbors[i];
//只有滿足最小臨接的結果才是最終結果
if( n >= min_neighbors )
{
Rect* rect = &seq2.rectQueue[seq2.tail];
rect->x = (comps.rectQueue[i].x*2   n)/(2*n);
rect->y = (comps.rectQueue[i].y*2   n)/(2*n);
rect->width = (comps.rectQueue[i].width*2   n)/(2*n);
rect->height = (comps.rectQueue[i].height*2   n)/(2*n);
seq2.neighbors[seq2.tail] = comps.neighbors[i];
seq2.tail  ;
seq2.total  ;
}
}
//從候選矩形中得到最大的矩形
for( i = 0; i < seq2.total; i   )
{
r1 = seq2.rectQueue[i];
r1_neighbor = seq2.neighbors[i];
flag = 1;
for( j = 0; j < seq2.total; j   )
{
r2 = seq2.rectQueue[j];
r2_neighbor = seq2.neighbors[j];
distance = (r2.width *2 5)/10;//cvRound( r2.rect.width * 0.2 );
if( i != j &&
r1.x >= r2.x - distance &&
r1.y >= r2.y - distance &&
r1.x   r1.width <= r2.x   r2.width   distance &&
r1.y   r1.height <= r2.y   r2.height   distance &&
(r2_neighbor > MAX( 3, r1_neighbor ) || r1_neighbor < 3) )
{
flag = 0;
break;
}
}
if( flag )
{
result_seq.rectQueue[result_seq.tail] = r1;
result_seq.tail  ;
result_seq.total  ;
}
}
}
}
void DownSample(Image* pImage, int factor)
{
int i = 0;
int j = 0;
int counti = 0;
int countj = 0;
int step = pImage->cols / factor;
for (i =0; i < pImage->rows; i = factor)
{
countj  ;
for (j =0; j < pImage->cols; j  = factor)
{
*(pImage->imgPtr   i*step/factor   j/factor) = *(pImage->imgPtr   i*pImage->cols   j);
counti  ;
}
counti = 0;
}
pImage->cols /= factor;
pImage->rows /= factor;
}

 

 

 

 

 

 

Haar.h

 

 

#ifndef _HAAR_H_
#define _HAAR_H_
#include "Tables.h"
#define NODE_THRESHOLD_SHIFT 22
#define MAXHIDCASCADE 200000  //隱式級聯分類器所佔空間(位元組)
#define MAXROWS   400   
#define MAXCOLS   400	
#define MAXSTAGES  22   
#define MAXCLASSIFER  213
#define MAXTREENODE 2
#define MAXALPHA   2
#define MAXSEQS    25
#define MaxMatNum  2
#define RGBCHANNEL 3
#define BITS8         0x00000001
#define BITS32        0x00000010
#define BITS64        0x00000100
#define CV_8TO16U_SQR(x)  my8x16uSqrTab[(x) 128]
#define CLR_RESULT_QUEUE() 	result_seq.tail = 0;\
result_seq.total = 0; 
typedef unsigned char BYTE;
typedef long long _int64;
typedef unsigned char	(*ImgPtr);
typedef unsigned char	(*Mat8Ptr);
typedef int				(*Mat32Ptr);
typedef _int64			(*Mat64Ptr);
/*****************並查集資料結構*******************************/
#define MAXPTREENODES 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