cvHaarDetectObjects

来源:互联网 发布:微软windows官网进不去 编辑:程序博客网 时间:2024/05/21 10:22

cvHaarDetectObjects

 转自http://2000liuzhenxing.blog.163.com/blog/static/5167747520098195217494/

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;
 
   CvMat stub, *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;
   int npass = 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, “Invalidclassifier 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 aresupported” );
 
   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
           CvMat img1, 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见定义.
//#define CV_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);            //长宽+14通道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 beystep
                   
                       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 facerectangles               在大的面块中找出小的面块
       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到返回结果.
               
           }
       }
   }
 
   __END__;
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 在酒店换衣服忘记关窗帘了怎么办 淘宝店铺装修更改图片要收费怎么办 惠阳市教育考试考证号忘记怎么办 高考完被被骗去读自考以后怎么办 孩子学习遇到瓶颈期了老师该怎么办 微信家长群有不好的言论出现怎么办 铃木汽车后备箱电动锁没有电怎么办 坐飞机没有连号座位带孩子怎么办 白沙的衣服洗衣服时染上颜色怎么办 网购商家少发了货怎么办 我想成为安利的员工怎么办会员 安利皇后锅锅盖吸在桌子上怎么办 淘宝客服退款返佣金诈骗后怎么办 第一试用网的钱提现出现问题怎么办 一个手机号注册两个京东账号怎么办 白色衣服被洗衣粉泡白了怎么办 白色衣服染成一块块荧光色了怎么办 中脉远红镇痛护腰不会发热了怎么办 用完悦诗风吟脸变黑不均匀怎么办 护肤品开封后一年还没用完怎么办 兰蔻化妆品套装正品和假怎么办 月经期间卫生巾搞得屁股疼怎么办 大姨妈特别多用卫生巾老是漏怎么办 夏天用卫生巾不透气摩擦红了怎么办 在日本的洗手间用完的姨妈巾怎么办 想穿短裙但是膝盖怕凉怎么办 裤子被卫生巾粘住扯不下来怎么办 医生说来姨妈不可以用卫生巾怎么办 隆胸以后摸起来感觉假体会动怎么办 产后15个月说恶露没排干净怎么办 母猪产后两天肚子里还有小猪怎么办 背心式无痕运动文胸显得胸小怎么办 卫生巾过敏起疙瘩反复挠不好怎么办 去健身房办卡老板跑了怎么办 买货我已经拒收商家不退款怎么办 在京东买了东西拒收不退款怎么办 罗马仕充电宝进入休眠状态怎么办 广发信用卡寄到家没拿到快递怎么办 包邮商家要买家出物流费怎么办? 美团外卖下单转化率低怎么办 京东退款不小心点了取消退款怎么办