OpenCV Haar分类器人脸检测部分代码注释

转自:http://www.haogongju.net/art/1441498

上一篇文章《浅析人脸检测之Haar分类器方法》我大致讲了下Haar分类器训练及检测的原理,原本想写一篇单独解析OpenCV Haar分类器代码的文章,但是手头有了新的任务,这个计划暂时要拖后了,我简单把之前给检测部分代码写的注释贴过来。只是为了记录一下,注释的不够细致,无甚价值。

1. cvHaarDetectObjectsForROC

  1 CvSeq*
  2 cvHaarDetectObjectsForROC(  const CvArr* _img, 
  3                      CvHaarClassif ierCascade* cascade, CvMemStorage* storage,
  4                      std::vector< int>& rejectLevels, std::vector< double>& levelWeights,
  5                       double scaleFactor,  int minNeighbors,  int flags, 
  6                      CvSize minSize, CvSize maxSize,  bool outputRejectLevels )
  7 {
  8      const  double GROUP_EPS =  0.2;
  9     CvMat stub, *img = (CvMat*)_img;
 10     cv::Ptr<CvMat> temp, sum, tilted, sqsum, normImg, sumcanny, imgSmall;
 11     CvSeq* result_seq =  0;
 12     cv::Ptr<CvMemStorage> temp_storage;
 13 
 14     cv::ConcurrentRectVector allCandidates;
 15     std::vector<cv::Rect> rectList;
 16     std::vector< int> rweights;
 17      double factor;
 18      int coi;
 19      bool doCanny Pruning = (flags & CV_HAAR_DO_CANNY_PRUNING) !=  0; // 平滑区域过滤标识
 20       bool findBiggestObject = (flags & CV_HAAR_FIND_BIGGEST_OBJECT) !=  0; // 返回最大目标标识
 21       bool roughSearch = (flags & CV_HAAR_DO_ROUGH_SEARCH) !=  0; // 只返回第一个目标标识
 22 
 23       // 错误
 24       if( !CV_IS_HAAR_CLASSIFIER(cascade) )
 25         CV_Error( !cascade ? CV_StsNullPtr : CV_StsBadArg,  " Invalid classifier cascade " );
 26 
 27      if( !storage )
 28         CV_Error( CV_StsNullPtr,  " Null storage pointer " );
 29 
 30     img = cvGetMat( img, &stub, &coi );
 31      if( coi )
 32         CV_Error( CV_BadCOI,  " COI is not supported " );
 33 
 34      if( CV_MAT_DEPTH(img->type) != CV_8U )
 35         CV_Error( CV_StsUnsupportedFormat,  " Only 8-bit images are supported " );
 36     
 37      if( scaleFactor <=  1 )
 38         CV_Error( CV_StsOutOfRange,  " scale factor must be > 1 " );
 39 
 40      // 如果存在查找最大目标属性则去除SCALE_IMAGE属性
 41       if( findBiggestObject )
 42         flags &= ~CV_HAAR_SCALE_IMAGE;
 43     
 44      if( maxSize.height ==  0 || maxSize.width ==  0 )
 45     {
 46         maxSize.height = img->rows;
 47         maxSize.width = img->cols;
 48     }
 49 
 50     temp = cvCreateMat( img->rows, img->cols, CV_8 UC1 );
 51      // 注意这里rows+1.cols+1是为了建立一个0值的缓存区以提高计算效率
 52      sum = cvCreateMat( img->rows +  1, img->cols +  1, CV_32SC1 ); // 积分图求和的结果
 53      sqsum = cvCreateMat( img->rows +  1, img->cols +  1, CV_64FC1 ); // 积分图求和的平方的结果
 54 
 55      if( !cascade->hid_cascade )
 56         icvCreateHidHaarClassifierCascade(cascade);; // 创建级联分类器
 57 
 58      if( cascade->hid_cascade->has_tilted_features )
 59         tilted = cvCreateMat( img->rows +  1, img->cols +  1, CV_32SC1 ); // 积分图求和并倾斜45度的结果
 60 
 61     result_seq = cvCreateSeq(  0sizeof(CvSeq),  sizeof(CvAvg Comp), storage ); // 创建用于存放检测结果的数组
 62 
 63      if( CV_MAT_CN(img->type) >  1 )
 64     {
 65         cvCvtColor( img, temp, CV_BGR2GRAY ); // 灰度处理
 66          img = temp;
 67     }
 68      // 如果存在查找最大目标属性则去除SCALE_IMAGE和平滑过滤属性
 69       if( findBiggestObject )
 70         flags &= ~(CV_HAAR_SCALE_IMAGE|CV_HAAR_DO_CANNY_PRUNING);
 71 
 72      // 缩放图片而不改变检测窗口大小
 73  /*      if( flags & CV_HAAR_SCALE_IMAGE )
 74      {
 75          CvSize winSize0 = cascade->orig_window_size;
 76  #ifdef HAVE_IPP
 77          int use_ipp = cascade->hid_cascade->ipp_stages != 0;
 78 
 79          if( use_ipp )
 80              normImg = cvCreateMat( img->rows, img->cols, CV_32FC1 );
 81  #endif
 82          imgSmall = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1 );
 83 
 84          for( factor = 1; ; factor *= scaleFactor )
 85          {
 86              CvSize winSize = { cvRound(winSize0.width*factor),
 87                                  cvRound(winSize0.height*factor) };
 88              CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) };
 89              CvSize sz1 = { sz.width - winSize0.width + 1, sz.height - winSize0.height + 1 };
 90 
 91              CvRect equRect = { icv_object_win_border, icv_object_win_border,
 92                  winSize0.width - icv_object_win_border*2,
 93                  winSize0.height - icv_object_win_border*2 };
 94 
 95              CvMat img1, sum1, sqsum1, norm1, tilted1, mask1;
 96              CvMat* _tilted = 0;
 97 
 98              if( sz1.width <= 0 || sz1.height <= 0 )
 99                  break;
100              if( winSize.width > maxSize.width || winSize.height > maxSize.height )
101                  break;
102              if( winSize.width < minSize.width || winSize.height < minSize.height )
103                  continue;
104 
105              img1 = cvMat( sz.height, sz.width, CV_8UC1, imgSmall->data.ptr );
106              sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr );
107              sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr );
108              if( tilted )
109              {
110                  tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr );
111                  _tilted = &tilted1;
112              }
113              norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, normImg ? normImg->data.ptr : 0 );
114              mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr );
115 
116              cvResize( img, &img1, CV_INTER_LINEAR );
117              cvIntegral( &img1, &sum1, &sqsum1, _tilted );
118 
119              int ystep = factor > 2 ? 1 : 2;
120          #ifdef HAVE_TBB
121              const int LOCS_PER_THREAD = 1000;
122              int stripCount = ((sz1.width/ystep)*(sz1.height + ystep-1)/ystep + LOCS_PER_THREAD/2)/LOCS_PER_THREAD;
123              stripCount = std::min(std::max(stripCount, 1), 100);
124          #else
125              const int stripCount = 1;
126          #endif
127              
128  #ifdef HAVE_IPP
129              if( use_ipp )
130              {
131                  cv::Mat fsum(sum1.rows, sum1.cols, CV_32F, sum1.data.ptr, sum1.step);
132                  cv::Mat(&sum1).convertTo(fsum, CV_32F, 1, -(1<<24));
133              }
134              else
135  #endif
136                  cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, _tilted, 1. );            
137              
138              cv::Mat _norm1(&norm1), _mask1(&mask1);
139              cv::parallel_for(cv::BlockedRange(0, stripCount),
140                           cv::HaarDetectObjects_ScaleImage_Invoker(cascade,
141                                  (((sz1.height + stripCount - 1)/stripCount + ystep-1)/ystep)*ystep,
142                                  factor, cv::Mat(&sum1), cv::Mat(&sqsum1), &_norm1, &_mask1,
143                                  cv::Rect(equRect), allCandidates, rejectLevels, levelWeights, outputRejectLevels));
144          }
145      }
146      else  */
147     {
148          int n_factors =  0; // 代表检测窗口有多少种尺寸
149          cv::Rect scanROI; // 感兴趣区域
150 
151         cvIntegral( img, sum, sqsum, tilted ); // 积分图计算
152 
153           // 存在平滑过滤属性
154           if( doCannyPruning )
155         {
156             sumcanny = cvCreateMat( img->rows +  1, img->cols +  1, CV_32SC1 );
157             cvCanny( img, temp,  0503 );
158             cvIntegral( temp, sumcanny );
159         }
160 
161          // 得到检测窗口有多少种尺寸,即n_factors值
162           for( n_factors =  0, factor =  1;
163              factor*cascade->orig_window_size.width < img->cols -  10 &&
164              factor*cascade->orig_window_size.height < img->rows -  10;
165              n_factors++, factor *= scaleFactor )
166             ;
167          // 初始化检测窗口的比例
168           if( findBiggestObject )
169         {
170             scaleFactor =  1./scaleFactor;
171             factor *= scaleFactor;
172         }
173          else
174             factor =  1;
175 
176          for( ; n_factors-- >  0; factor *= scaleFactor )
177         {
178              const  double ystep = std::max(  2., factor );
179              // 得到新的检测窗口size
180              CvSize winSize = { cvRound( cascade->orig_window_size.width * factor ),
181                                 cvRound( cascade->orig_window_size.height * factor )};
182             CvRect equRect = {  0000 };
183              int *p[ 4] = { 0, 0, 0, 0};
184              int *pq[ 4] = { 0, 0, 0, 0};
185              int startX =  0, startY =  0;
186              int endX = cvRound((img->cols - winSize.width) / ystep);
187              int endY = cvRound((img->rows - winSize.height) / ystep);
188             
189              // 忽略小于最小窗口限制的检测窗口size
190               if( winSize.width < minSize.width || winSize.height < minSize.height )
191             {
192                  if( findBiggestObject )
193                      break;
194                  continue;
195             }
196             
197              // 为分类器设置积分图及检测窗口比例
198              cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );
199             cvZero( temp );
200 
201              if( doCannyPruning )
202             {
203                 equRect.x = cvRound(winSize.width* 0.15);
204                 equRect.y = cvRound(winSize.height* 0.15);
205                 equRect.width = cvRound(winSize.width* 0.7);
206                 equRect.height = cvRound(winSize.height* 0.7);
207 
208                 p[ 0] = ( int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) + equRect.x;
209                 p[ 1] = ( int*)(sumcanny->data.ptr + equRect.y*sumcanny->step)
210                             + equRect.x + equRect.width;
211                 p[ 2] = ( int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) + equRect.x;
212                 p[ 3] = ( int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step)
213                             + equRect.x + equRect.width;
214 
215                 pq[ 0] = ( int*)(sum->data.ptr + equRect.y*sum->step) + equRect.x;
216                 pq[ 1] = ( int*)(sum->data.ptr + equRect.y*sum->step)
217                             + equRect.x + equRect.width;
218                 pq[ 2] = ( int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) + equRect.x;
219                 pq[ 3] = ( int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step)
220                             + equRect.x + equRect.width;
221             }
222             
223              // 根据ROI重置
224               if( scanROI.area() >  0 )
225             {
226                  // adjust start_height and stop_height
227                  startY = cvRound(scanROI.y / ystep);
228                 endY = cvRound((scanROI.y + scanROI.height - winSize.height) / ystep);
229 
230                 startX = cvRound(scanROI.x / ystep);
231                 endX = cvRound((scanROI.x + scanROI.width - winSize.width) / ystep);
232             }
233              // parallel_for?并行运算?
234               // 在当前尺寸下对每一个位置进行分类器的检测
235              cv::parallel_for(cv::BlockedRange(startY, endY),
236                 cv::HaarDetectObjects_ScaleCascade_Invoker(cascade, winSize, cv::Range(startX, endX),
237                                                            ystep, sum->step, ( const  int**)p,
238                                                            ( const  int**)pq, allCandidates ));
239 
240              if( findBiggestObject && !allCandidates.empty() && scanROI.area() ==  0 )
241             {
242                 rectList.resize(allCandidates.size());
243                 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
244                 
245                 groupRectangles(rectList, std::max(minNeighbors,  1), GROUP_EPS);
246                 
247                  if( !rectList.empty() )
248                 {
249                     size_t i, sz = rectList.size();
250                     cv::Rect maxRect;
251                     
252                      for( i =  0; i < sz; i++ )
253                     {
254                          if( rectList[i].area() > maxRect.area() )
255                             maxRect = rectList[i];
256                     }
257                     
258                     allCandidates.push_back(maxRect);
259                     
260                     scanROI = maxRect;
261                      int dx = cvRound(maxRect.width*GROUP_EPS);
262                      int dy = cvRound(maxRect.height*GROUP_EPS);
263                     scanROI.x = std::max(scanROI.x - dx,  0);
264                     scanROI.y = std::max(scanROI.y - dy,  0);
265                     scanROI.width = std::min(scanROI.width + dx* 2, img->cols- 1-scanROI.x);
266                     scanROI.height = std::min(scanROI.height + dy* 2, img->rows- 1-scanROI.y);
267                 
268                      double minScale = roughSearch ?  0.6 :  0.4;
269                     minSize.width = cvRound(maxRect.width*minScale);
270                     minSize.height = cvRound(maxRect.height*minScale);
271                 }
272             }
273         }
274     }
275 
276     rectList.resize(allCandidates.size());
277      if(!allCandidates.empty())
278         std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
279     
280      // 相邻重复检测区域进行组合
281       if( minNeighbors !=  0 || findBiggestObject )
282     {
283          if( outputRejectLevels )
284         {
285             groupRectangles(rectList, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
286         }
287          else
288         {
289             groupRectangles(rectList, rweights, std::max(minNeighbors,  1), GROUP_EPS);
290         }
291     }
292      else
293         rweights.resize(rectList.size(), 0);
294        
295      // 获得检测结果
296       if( findBiggestObject && rectList.size() )
297     {
298         CvAvgComp result_comp = {{ 0, 0, 0, 0}, 0};
299         
300          for( size_t i =  0; i < rectList.size(); i++ )
301         {
302             cv::Rect r = rectList[i];
303              if( r.area() > cv::Rect(result_comp.rect).area() )
304             {
305                 result_comp.rect = r;
306                 result_comp.neighbors = rweights[i];
307             }
308         }
309         cvSeqPush( result_seq, &result_comp );
310     }
311      else
312     {
313          for( size_t i =  0; i < rectList.size(); i++ )
314         {
315             CvAvgComp c;
316             c.rect = rectList[i];
317             c.neighbors = !rweights.empty() ? rweights[i] :  0;
318             cvSeqPush( result_seq, &c );
319         }
320     }
321 
322      return result_seq;

323 } 

你可能感兴趣的:(OpenCV Haar分类器人脸检测部分代码注释)