浅谈OPENCV人脸识别(转)

浅谈OPENCV人脸识别(转)

OpenCV的人脸检测主要是调用训练好的cascade(Haar分类器)来进行模式匹配。

cvHaarDetectObjects,先将图像灰度化,根据传入参数判断是否进行canny边缘处理(默认不使用),再进行匹配。匹配后收集找出的匹配块,过滤噪声,计算相邻个数如果超过了规定值(传入的min_neighbors)就当成输出结果,否则删去。

匹配循环:将匹配分类器放大scale(传入值)倍,同时原图缩小scale倍,进行匹配,直到匹配分类器的大小大于原图,则返回匹配结果。匹配的时候调用cvRunHaarClassifierCascade来进行匹配,将所有结果存入CvSeq* Seq (可动态增长元素序列),将结果传给cvHaarDetectObjects。

cvRunHaarClassifierCascade函数整体是根据传入的图像和cascade来进行匹配。并且可以根据传入的cascade类型不同(树型、stump(不完整的树)或其他的),进行不同的匹配方式。

函数 cvRunHaarClassifierCascade 用于对单幅图片的检测。在函数调用前首先利用 cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数 (=> 窗口尺寸)。当分析的矩形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。

为了了解OpenCV人脸检测中寻找匹配图像的详细过程,就把cvHaarDetectObjects和cvRunHaarClassifierCascade的源文件详细看了一遍,并打上了注释。方便大家阅读。

附cvHaarDetectObjects代码:

CV_IMPL CvSeq*

cvHaarDetectObjects(const CvArr* _img, 

                    CvHaarClassifierCascade* cascade,

                    CvMemStorage* storage, double scale_factor,

                    int min_neighbors, int flags, CvSize min_size )

{

   int split_stage = 2;

 

  CvMatstub, *img = (CvMat*)_img;   //CvMat多通道矩阵  *img=_img指针代换传入图

   CvMat *temp = 0, *sum = 0, *tilted = 0, *sqsum = 0, *norm_img = 0, *sumcanny =0, *img_small = 0;

   CvSeq* seq = 0;

   CvSeq*seq2 =0;                //CvSeq可动态增长元素序列

   CvSeq* idx_seq = 0;

   CvSeq* result_seq = 0;

   CvMemStorage* temp_storage = 0;

   CvAvgComp* comps = 0;

   int i;

   #ifdef_OPENMP

   CvSeq* seq_thread[CV_MAX_THREADS] = {0};

   int max_threads = 0;

#endif

   

   CV_FUNCNAME( “cvHaarDetectObjects” );

 

   __BEGIN__;

 

   double factor;

   intnpass = 2,coi;                                                                                                                //npass=2

   int do_canny_pruning = flags &CV_HAAR_DO_CANNY_PRUNING;                //true做canny边缘处理

 

   if( !CV_IS_HAAR_CLASSIFIER(cascade) )

       CV_ERROR( !cascade ? CV_StsNullPtr : CV_StsBadArg, “Invalid classifier cascade”);

 

   if( !storage )

       CV_ERROR( CV_StsNullPtr, “Null storage pointer” );

 

   CV_CALL( img = cvGetMat( img, &stub, &coi ));

   if( coi )

       CV_ERROR( CV_BadCOI, “COI is not supported” );  //一些出错代码

 

   if( CV_MAT_DEPTH(img->type) != CV_8U )

       CV_ERROR( CV_StsUnsupportedFormat, “Only 8-bit images are supported” );

 

   CV_CALL( temp = cvCreateMat( img->rows, img->cols, CV_8UC1 ));

   CV_CALL( sum = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 ));

   CV_CALL( sqsum = cvCreateMat( img->rows + 1, img->cols + 1, CV_64FC1 ));

   CV_CALL( temp_storage = cvCreateChildMemStorage( storage ));

 

#ifdef _OPENMP

   max_threads = cvGetNumThreads();

   for( i = 0; i < max_threads; i++ )

   {

       CvMemStorage* temp_storage_thread;

       CV_CALL( temp_storage_thread =cvCreateMemStorage(0));                //CV_CALL就是运行,假如出错就报错。

       CV_CALL( seq_thread[i] = cvCreateSeq( 0,sizeof(CvSeq),               //CvSeq可动态增长元素序列

 

    sizeof(CvRect),temp_storage_thread ));

   }

#endif

 

   if( !cascade->hid_cascade )

       CV_CALL( icvCreateHidHaarClassifierCascade(cascade) );

 

   if( cascade->hid_cascade->has_tilted_features )

       tilted = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1);         //多通道矩阵图像长宽+1 4通道

 

   seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvRect), temp_storage);        //创建序列seq  矩形

   seq2 = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), temp_storage);    //创建序列seq2  矩形和邻近

   result_seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), storage);   //创建序列result_seq  矩形和邻近

 

   if( min_neighbors == 0 )

       seq = result_seq;

 

   if( CV_MAT_CN(img->type) > 1 )

   {

       cvCvtColor( img, temp, CV_BGR2GRAY );   //img转为灰度

       img =temp;                                                                                                                                

   }

   

   if( flags & CV_HAAR_SCALE_IMAGE)                                                                                        //flag&& 匹配图

   {

       CvSize win_size0 = cascade->orig_window_size;  //CvSize win_size0为分类器的原始大小

       int use_ipp = cascade->hid_cascade->ipp_stages != 0&&              

      icvApplyHaarClassifier_32s32f_C1R_p!= 0;  //IPP相关函数

 

       if( use_ipp )

           CV_CALL( norm_img = cvCreateMat( img->rows, img->cols, CV_32FC1));           //图像的矩阵化 4通道.

       CV_CALL( img_small = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1));       //小图矩阵化单通道长宽+1

 

for( factor = 1; ;factor *= scale_factor )  //成scale_factor倍数匹配

       {

           int positive = 0;

           int x, y;

           CvSize win_size = { cvRound(win_size0.width*factor),

                               cvRound(win_size0.height*factor) };                      //winsize         分类器行列(扩大factor倍)        

           CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor )};  //sz 图像行列(缩小factor倍)三个Cvsize

           CvSize sz1 = { sz.width – win_size0.width, sz.height – win_size0.height};    //sz1 图像减分类器行列

           CvRect rect1 = { icv_object_win_border, icv_object_win_border,

win_size0.width – icv_object_win_border*2,   //icv_object_win_border(int)初始值=1

win_size0.height –icv_object_win_border*2 };  //矩形框rect1

     CvMatimg1, sum1, sqsum1, norm1, tilted1, mask1;      //多通道矩阵

           CvMat* _tilted = 0;

 

           if( sz1.width <= 0 || sz1.height <= 0 )                                      //图片宽或高小于分类器–>跳出

               break;

           if( win_size.width < min_size.width || win_size.height < min_size.height)    //分类器高或宽小于给定的mini_size的高或宽–>继续

               continue;

//CV_8UC1见定义.

//#defineCV_MAKETYPE(depth,cn) ((depth) + (((cn)-1) <<CV_CN_SHIFT))   

//深度+(cn-1)左移3位   depth,depth+8,depth+16,depth+24.

           img1 = cvMat( sz.height, sz.width, CV_8UC1, img_small->data.ptr);            //小图的矩阵化 img1 单通道    

           sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr);             //长宽+1 4通道8位           多通道矩阵

           sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr);         //长宽+1 4通道16位

           if( tilted )

           {

               tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr);   //长宽+1 4通道8位

               _tilted =&tilted1;                                                                 //长宽+1 4通道8位

           }

           norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, norm_img ?norm_img->data.ptr : 0 );  //norm1 图像减分类器行列 4通道

           mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr);                     //mask1 灰度图

            cvResize( img, &img1, CV_INTER_LINEAR);                //img双线性插值输出到img1

   cvIntegral( &img1, &sum1, &sqsum1, _tilted);     //计算积分图像

 

           if( use_ipp && icvRectStdDev_32s32f_C1R_p( sum1.data.i, sum1.step,

               sqsum1.data.db, sqsum1.step, norm1.data.fl, norm1.step, sz1, rect1 ) < 0 )

               use_ipp = 0;

 

           if( use_ipp)                                                                                //如果ipp=true   (intel视频处理加速等的函数库)

           {

               positive =mask1.cols*mask1.rows;                                                                                 //mask1长乘宽–>positive

               cvSet( &mask1, cvScalarAll(255) );    //mask1赋值为255

               for( i = 0; i < cascade->count; i++ )

               {

                   if( icvApplyHaarClassifier_32s32f_C1R_p(sum1.data.i, sum1.step,

                       norm1.data.fl, norm1.step, mask1.data.ptr, mask1.step,

                       sz1, &positive, cascade->hid_cascade->stage_classifier[i].threshold,

                       cascade->hid_cascade->ipp_stages[i]) < 0 )

                   {

                       use_ipp=0;                 //ipp=false;

                       break;

                   }

                   if( positive <= 0 )

                       break;

               }

           }

           

           if( !use_ipp)                                                                                        //如果ipp=false

           {

               cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, 0, 1. );

               for( y = 0, positive = 0; y < sz1.height; y++ )

                   for( x = 0; x < sz1.width; x++ )

                   {

                       mask1.data.ptr[mask1.step*y + x] =

                           cvRunHaarClassifierCascade( cascade, cvPoint(x,y), 0 ) > 0;   //匹配图像.

                       positive += mask1.data.ptr[mask1.step*y + x];

                   }

           }

 

           if( positive > 0 )

           {

               for( y = 0; y < sz1.height; y++ )

                   for( x = 0; x < sz1.width; x++ )

                       if( mask1.data.ptr[mask1.step*y + x] != 0 )

                       {

                           CvRect obj_rect = { cvRound(y*factor),cvRound(x*factor),    

                                win_size.width,win_size.height };

                           cvSeqPush( seq, &obj_rect );   //将匹配块放到seq中

                       }

           }

       }

   }

   else                                                                                                        //!(flag&& 匹配图)

   {

       cvIntegral( img, sum, sqsum, tilted );

          if( do_canny_pruning )

       {

           sumcanny = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1);                 //如果做canny边缘检测

           cvCanny( img, temp, 0, 50, 3 );

           cvIntegral( temp, sumcanny );

       }

   

       if( (unsigned)split_stage >= (unsigned)cascade->count ||

           cascade->hid_cascade->is_tree)                                                         {

           split_stage = cascade->count;

           npass = 1;

       }

 

       for( factor = 1; factor*cascade->orig_window_size.width < img->cols –10&&                                //匹配

                        factor*cascade->orig_window_size.height < img->rows – 10;

            factor *= scale_factor )

       {

           const double ystep = MAX( 2, factor );

           CvSize win_size = { cvRound( cascade->orig_window_size.width * factor ),

                               cvRound( cascade->orig_window_size.height * factor )};

           CvRect equ_rect = { 0, 0, 0, 0 };

           int *p0 = 0, *p1 = 0, *p2 = 0, *p3 = 0;

           int *pq0 = 0, *pq1 = 0, *pq2 = 0, *pq3 = 0;

           int pass, stage_offset = 0;

           int stop_height = cvRound((img->rows – win_size.height) / ystep);

 

           if( win_size.width < min_size.width || win_size.height < min_size.height)                        //超边跳出

               continue;

 

           cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor);                        //匹配

           cvZero( temp);                                                                                                //清空temp数组

 

           if( do_canny_pruning)                                                                                        //canny边缘检测

           {

               equ_rect.x = cvRound(win_size.width*0.15);

               equ_rect.y = cvRound(win_size.height*0.15);

               equ_rect.width = cvRound(win_size.width*0.7);

               equ_rect.height = cvRound(win_size.height*0.7);

 

               p0 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step) + equ_rect.x;

               p1 = (int*)(sumcanny->data.ptr + equ_rect.y*sumcanny->step)

                           + equ_rect.x + equ_rect.width;

               p2 = (int*)(sumcanny->data.ptr + (equ_rect.y +equ_rect.height)*sumcanny->step) + equ_rect.x;

               p3 = (int*)(sumcanny->data.ptr + (equ_rect.y +equ_rect.height)*sumcanny->step)

                           + equ_rect.x + equ_rect.width;

 

               pq0 = (int*)(sum->data.ptr + equ_rect.y*sum->step) + equ_rect.x;

               pq1 = (int*)(sum->data.ptr + equ_rect.y*sum->step)

                           + equ_rect.x + equ_rect.width;

               pq2 = (int*)(sum->data.ptr + (equ_rect.y + equ_rect.height)*sum->step) +equ_rect.x;

               pq3 = (int*)(sum->data.ptr + (equ_rect.y + equ_rect.height)*sum->step)

                           + equ_rect.x + equ_rect.width;

           }

 

           cascade->hid_cascade->count =split_stage;                                                //分裂级

 

           for( pass = 0; pass < npass; pass++ )

           {

#ifdef _OPENMP

   #pragma omp parallel for num_threads(max_threads), schedule(dynamic)

#endif

               for( int _iy = 0; _iy < stop_height; _iy++ )

               {

                   int iy = cvRound(_iy*ystep);

                   int _ix, _xstep = 1;

                   int stop_width = cvRound((img->cols – win_size.width) / ystep);

                   uchar* mask_row = temp->data.ptr + temp->step * iy;

 

                   for( _ix = 0; _ix < stop_width; _ix += _xstep )

                   {

                       int ix = cvRound(_ix*ystep); // it really should be ystep

                   

                       if( pass == 0 )          //第一次循环做

                       {

                           int result;

                           _xstep = 2;

 

                           if( do_canny_pruning )      //canny边缘检测

                           {

                               int offset;

                               int s, sq;

                       

                               offset = iy*(sum->step/sizeof(p0[0])) + ix;

                 s= p0[offset] – p1[offset] – p2[offset] + p3[offset];

               sq= pq0[offset] – pq1[offset] – pq2[offset] + pq3[offset];

                               if( s < 100 || sq < 20 )

                                   continue;

                           }

 

                           result = cvRunHaarClassifierCascade( cascade, cvPoint(ix,iy), 0);                //匹配结果存到result里

                           if( result > 0 )

                           {

                               if( pass < npass – 1 )

                                   mask_row[ix] = 1;

                               else

                               {

                                   CvRect rect = cvRect(ix,iy,win_size.width,win_size.height);

#ifndef_OPENMP                                                                                                        //如果用OpenMP

                                   cvSeqPush( seq, &rect );                                                        //result放到seq中

#else                                                                                                                        //如果不用OpenMP

                                   cvSeqPush( seq_thread[omp_get_thread_num()], &rect);                        //result放到seq_thread里

#endif

                               }

                           }

                           if( result < 0 )

                               _xstep = 1;

                       }

                       else if( mask_row[ix])             //不是第一次

                       {

                           int result = cvRunHaarClassifierCascade( cascade,cvPoint(ix,iy),      stage_offset );

                           if( result > 0 )

                           {

                               if( pass == npass – 1 )         //如果是最后一次

                               {

                                   CvRect rect = cvRect(ix,iy,win_size.width,win_size.height);

#ifndef _OPENMP

                                   cvSeqPush( seq, &rect );

#else

                                   cvSeqPush( seq_thread[omp_get_thread_num()], &rect );

#endif

                               }

                           }

                           else

                               mask_row[ix] = 0;

                       }

                   }

               }

               stage_offset = cascade->hid_cascade->count;

               cascade->hid_cascade->count = cascade->count;

           }

       }

   }

 

#ifdef _OPENMP

// gather theresults                                              //收集结果

for( i = 0; i <max_threads; i++ )

{

CvSeq* s =seq_thread[i];

       int j, total = s->total;

       CvSeqBlock* b = s->first;

       for( j = 0; j < total; j += b->count, b = b->next )

           cvSeqPushMulti( seq, b->data, b->count);                 //结果输出到seq

}

#endif

 

   if( min_neighbors != 0 )

   {// group retrieved rectangles in order to filter outnoise         收集找出的匹配块,过滤噪声

       int ncomp = cvSeqPartition( seq, 0, &idx_seq, is_equal, 0 );

       CV_CALL( comps = (CvAvgComp*)cvAlloc( (ncomp+1)*sizeof(comps[0])));

       memset( comps, 0, (ncomp+1)*sizeof(comps[0]));

 

       // count number ofneighbors                                  计算相邻个数

       for( i = 0; i < seq->total; i++ )

       {

           CvRect r1 = *(CvRect*)cvGetSeqElem( seq, i );

           int idx = *(int*)cvGetSeqElem( idx_seq, i );

           assert( (unsigned)idx < (unsigned)ncomp );

 

           comps[idx].neighbors++;

            

           comps[idx].rect.x += r1.x;

           comps[idx].rect.y += r1.y;

           comps[idx].rect.width += r1.width;

           comps[idx].rect.height += r1.height;

       }

       // calculate average boundingbox                                   计算重心

       for( i = 0; i < ncomp; i++ )

       {

           int n = comps[i].neighbors;

           if( n >= min_neighbors )

           {

               CvAvgComp comp;

               comp.rect.x = (comps[i].rect.x*2 + n)/(2*n);

               comp.rect.y = (comps[i].rect.y*2 + n)/(2*n);

               comp.rect.width = (comps[i].rect.width*2 + n)/(2*n);

               comp.rect.height = (comps[i].rect.height*2 + n)/(2*n);

               comp.neighbors = comps[i].neighbors;

 

               cvSeqPush( seq2, &comp );  //结果输入到seq2

           }

       }

 

       // filter out small face rectangles inside large face rectangles               在大的面块中找出小的面块

       for( i = 0; i < seq2->total; i++)                                                                                //在seq2中寻找

       {

           CvAvgComp r1 = *(CvAvgComp*)cvGetSeqElem( seq2, i);               //r1指向结果

           int j, flag = 1;

 

           for( j = 0; j < seq2->total; j++ )

           {

               CvAvgComp r2 = *(CvAvgComp*)cvGetSeqElem( seq2, j );

               int distance = cvRound( r2.rect.width * 0.2 );

           

               if( i != j &&

                   r1.rect.x >= r2.rect.x – distance &&

                   r1.rect.y >= r2.rect.y – distance &&

                   r1.rect.x + r1.rect.width <= r2.rect.x + r2.rect.width + distance &&

                   r1.rect.y + r1.rect.height <= r2.rect.y + r2.rect.height + distance&&

                   (r2.neighbors > MAX( 3, r1.neighbors ) || r1.neighbors < 3) )

               {

                   flag = 0;

                   break;

               }

           }

 

           if( flag )

           {

               cvSeqPush( result_seq, &r1 );      //添加r1到返回结果.

               /* cvSeqPush( result_seq, &r1.rect ); */

           }

       }

   }

 

   __END__;

 

#ifdef _OPENMP

for( i = 0; i <max_threads; i++ )

{

if( seq_thread[i] )

           cvReleaseMemStorage( &seq_thread[i]->storage);                //如果使用了OpenMP就释放使用的seq_thread

}

#endif

 

   cvReleaseMemStorage( &temp_storage );

   cvReleaseMat( &sum );

   cvReleaseMat( &sqsum );

   cvReleaseMat( &tilted );       //释放使用的空间

   cvReleaseMat( &temp );

   cvReleaseMat( &sumcanny );

   cvReleaseMat( &norm_img );

   cvReleaseMat( &img_small );

   cvFree( &comps );

 

   returnresult_seq;           //返回结果

}

 

下面是cvRunHaarClassifierCascade的:

CV_IMPL int
cvRunHaarClassifierCascade( CvHaarClassifierCascade* _cascade,
                           CvPoint pt, int start_stage )
{
    int result = -1;
    CV_FUNCNAME(”cvRunHaarClassifierCascade”);

   __BEGIN__;

   int p_offset, pq_offset;
    int i, j;
    double mean, variance_norm_factor;
    CvHidHaarClassifierCascade* cascade;

   if( !CV_IS_HAAR_CLASSIFIER(_cascade) )
        CV_ERROR( !_cascade ? CV_StsNullPtr: CV_StsBadArg, “Invalid cascade pointer” );

   cascade = _cascade->hid_cascade;
    if( !cascade )
        CV_ERROR( CV_StsNullPtr, “Hiddencascade has not been created.\n”
            “UsecvSetImagesForHaarClassifierCascade” );

   if( pt.x < 0 || pt.y < 0 ||
        pt.x +_cascade->real_window_size.width >= cascade->sum.width-2 ||
        pt.y + _cascade->real_window_size.height>= cascade->sum.height-2)                  //超边退出
        EXIT;

   p_offset = pt.y * (cascade->sum.step/sizeof(sumtype)) + pt.x;
    pq_offset = pt.y *(cascade->sqsum.step/sizeof(sqsumtype)) + pt.x;
    mean = calc_sum(*cascade,p_offset)*cascade->inv_window_area;
    variance_norm_factor = cascade->pq0[pq_offset] –cascade->pq1[pq_offset]-               //左上+右下-右上-左下
                          cascade->pq2[pq_offset] + cascade->pq3[pq_offset];
    variance_norm_factor = variance_norm_factor*cascade->inv_window_area– mean*mean;
    if( variance_norm_factor >= 0. )
        variance_norm_factor =sqrt(variance_norm_factor);
    else
        variance_norm_factor = 1.;

   if( cascade->is_tree)           //是树形的分类器,就按照层来匹配.
    {
        CvHidHaarStageClassifier* ptr;
        assert( start_stage == 0);                     //start_stage==0继续

       result = 1;
        ptr = cascade->stage_classifier;

       while( ptr )
        {   

double stage_sum =0;

           for( j = 0; j < ptr->count; j++ )
           {               stage_sum += icvEvalHidHaarClassifier( ptr->classifier +j,     //层判断
                   variance_norm_factor, p_offset );
            }

           if( stage_sum >= ptr->threshold )
            {
               ptr =ptr->child;               //层判断通过,到下一层.
            }
            else
            {
               while( ptr && ptr->next == NULL ) ptr =ptr->parent;             //未通过,且当前子分类器没有同层分类器,没有返回上层
               if( ptr == NULL)              //如果刚才已经是最顶层了.
               {
                   result =0;                 //返回0,退出.
                   EXIT;
               }
               ptr =ptr->next;               //指向下一个分类器.
            }
        }
    }
    else if( cascade->is_stump_based)              //如果是stump类的分类器
    {
        for( i = start_stage; i <cascade->count; i++ )
        {
            doublestage_sum = 0;

           if( cascade->stage_classifier[i].two_rects )
            {
               for( j = 0; j < cascade->stage_classifier[i].count; j++ )
               {
                   CvHidHaarClassifier* classifier = cascade->stage_classifier[i].classifier +j;
                   CvHidHaarTreeNode* node = classifier->node;
                   double sum, t = node->threshold*variance_norm_factor, a, b;

                   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;

                   a = classifier->alpha[0];
                   b = classifier->alpha[1];
                   stage_sum += sum < t ? a : b;
               }
            }
            else
            {
               for( j = 0; j < cascade->stage_classifier[i].count; j++ )
               {
                   CvHidHaarClassifier* classifier = cascade->stage_classifier[i].classifier +j;
                   CvHidHaarTreeNode* node = classifier->node;
                   double sum, t = node->threshold*variance_norm_factor, a, b;

                   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 = classifier->alpha[0];
                   b = classifier->alpha[1];
                   stage_sum += sum < t ? a : b;
               }
            }

           if( stage_sum < cascade->stage_classifier[i].threshold )
           {                   //没通过.则返回负的没通过的分类器数.
               result = -i;
               EXIT;
            }
        }
    }
   else                    //如果不是那两种强分类器
    {
        for( i = start_stage; i <cascade->count; i++ )
       {           

double stage_sum =0;

           for( j = 0; j < cascade->stage_classifier[i].count; j++ )
            {    

stage_sum +=icvEvalHidHaarClassifier(
                   cascade->stage_classifier[i].classifier + j,
                   variance_norm_factor, p_offset );
            }

           if( stage_sum < cascade->stage_classifier[i].threshold )
            {  

result = -i;
               EXIT;
            }
        }
    }

   result = 1;

   __END__;

   return result;     //返回结果
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值