关于opencv中人脸识别主函数的部分注释详解。

     近段时间在搞opencv的视频人脸识别,无奈自带的分类器的准确度,实在是不怎么样,但又能怎样呢?自己又研究不清楚各大类检测算法。

     正所谓,功能是由函数完成的,于是自己便看cvHaarDetectObjects 这个识别主函数的源代码,尝试了解并进行改造它,以提高精确度。

     可惜实力有限啊,里面的结构非常复杂,参杂着更多的函数体,有一些是网上找不到用法的,导致最终无法整体了解,只搞了一般,这里分享

下我自己总结的注释。

  1 CvSeq* cvHaarDetectObjects( const CvArr* _img,//传入图像
  2                      CvHaarClassifierCascade* cascade, //传入xml路径
  3                      CvMemStorage* storage,//传入内存容器
  4                      double scaleFactor,//传入缩放值
  5                      int minNeighbors, 
  6                      int flags,
  7                      CvSize minSize, 
  8                      CvSize maxSize ){
  9 
 10     std::vector<int> fakeLevels;//int 类型的容器
 11     std::vector<double> fakeWeights;//double
 12     return cvHaarDetectObjectsForROC( _img, cascade, storage, fakeLevels, fakeWeights,
 13                                 scaleFactor, minNeighbors, flags, minSize, maxSize, false );//进入这个参数
 14     //执行目标检测,这个函数
 15 }
 16 
 17 CvSeq* cvHaarDetectObjectsForROC(const CvArr* _img,
 18      CvHaarClassifierCascade* cascade,
 19      CvMemStorage* storage,
 20      std::vector<int>& rejectLevels, 
 21      std::vector<double>& levelWeights,
 22      double scaleFactor,
 23      int minNeighbors, 
 24      int flags,
 25      CvSize minSize, 
 26      CvSize maxSize,
 27      bool outputRejectLevels ){
 28 
 29     const double GROUP_EPS = 0.2;//定义一个double常数据
 30     CvMat stub, *img = (CvMat*)_img;//定义一个矩阵stub和把传入的图片转化为矩阵
 31     cv::Ptr<CvMat> temp, sum, tilted, sqsum, normImg, sumcanny, imgSmall;//定义矩阵类
 32     CvSeq* result_seq = 0;//定义最终返回的指针数据变量
 33     cv::Ptr<CvMemStorage> temp_storage;//内存类的定义
 34 
 35     std::vector<cv::Rect> allCandidates;//矩形类
 36     std::vector<cv::Rect> rectList;//矩形类
 37     std::vector<int> rweights;//int 容器
 38     double factor;
 39     int coi;
 40     bool doCannyPruning = (flags & CV_HAAR_DO_CANNY_PRUNING) != 0;//这三个都是判断传入的flags是什么类型,这个是做canny边缘处理
 41     bool findBiggestObject = (flags & CV_HAAR_FIND_BIGGEST_OBJECT) != 0;
 42     bool roughSearch = (flags & CV_HAAR_DO_ROUGH_SEARCH) != 0;
 43     //CV_HAAR_DO_CANNY_PRUNING利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域
 44     //CV_HAAR_SCALE_IMAGE  按比例正常检测
 45     //CV_HAAR_FIND_BIGGEST_OBJECT只检测最大的物体
 46     //CV_HAAR_DO_ROUGH_SEARCH只做初略检测
 47 
 48     cv::Mutex mtx;//定义互斥锁,确保线程唯一
 49 
 50     if( !CV_IS_HAAR_CLASSIFIER(cascade) )
 51         CV_Error( !cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier cascade" );//无效的级联分类器,输出
 52 
 53     if( !storage )
 54         CV_Error( CV_StsNullPtr, "Null storage pointer" );//内存为空
 55 
 56     img = cvGetMat( img, &stub, &coi );//IplImage 到cvMat 的转换
 57     if( coi )
 58         CV_Error( CV_BadCOI, "COI is not supported" );
 59 
 60     if( CV_MAT_DEPTH(img->type) != CV_8U )//对图像的深度判断
 61         CV_Error( CV_StsUnsupportedFormat, "Only 8-bit images are supported" );
 62 
 63     if( scaleFactor <= 1 )//对缩放值的判断
 64         CV_Error( CV_StsOutOfRange, "scale factor must be > 1" );
 65 
 66     if( findBiggestObject )
 67         flags &= ~CV_HAAR_SCALE_IMAGE;
 68 
 69     if( maxSize.height == 0 || maxSize.width == 0 )//判断,如果传进来的检测窗口的尺寸,如果有一个为0,下面赋值为矩阵的行数和列数
 70     {
 71         maxSize.height = img->rows;
 72         maxSize.width = img->cols;
 73     }
 74 
 75     temp = cvCreateMat( img->rows, img->cols, CV_8UC1 );//中间值矩阵模板初始化
 76     sum = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );//积分图求和的结果矩阵模板
 77     sqsum = cvCreateMat( img->rows + 1, img->cols + 1, CV_64FC1 );////积分图求和的平方的结果
 78 
 79     if( !cascade->hid_cascade )
 80         icvCreateHidHaarClassifierCascade(cascade);//创建分类器,填写 casecade 中相关的头信息,如有多少个 stage,  每个 stage 下有多少个 tree ,每个 tree 下有多少个 node ,以及相关的阈值等信息
 81 
 82     if( cascade->hid_cascade->has_tilted_features )
 83         tilted = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );//创建用于存放积分图求和并倾斜45度的检测结果矩阵
 84 
 85     result_seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), storage );//初始化最总返回结果变量
 86 
 87     if( CV_MAT_CN(img->type) > 1 )//如果由传入的图片转化为的矩阵的数据类型是比32位浮点高为真,进入if语句
 88     {
 89         cvCvtColor( img, temp, CV_BGR2GRAY );//灰度转化,此时temp指针式灰度数据的
 90         img = temp;//把值给会img,temp只起到一个中间保存的作用
 91     }
 92 
 93     if( findBiggestObject )//是否只检测最大的物体,是,则进入if语句
 94         flags &= ~(CV_HAAR_SCALE_IMAGE|CV_HAAR_DO_CANNY_PRUNING);
 95 
 96     if( flags & CV_HAAR_SCALE_IMAGE )//按比例正常检测,&是位运算 1|1=1,
 97     {
 98         CvSize winSize0 = cascade->orig_window_size;//获取检测窗口的大小,由分类器返回
 99 
100         //下面是定义块,如果有定义HAVE_IPP,那么进入下面的数据赋值
101         //但是在CvHaarClassifierCascade结构体里面的CvHidHaarClassifierCascade是空的
102 #ifdef HAVE_IPP    
103         int use_ipp = cascade->hid_cascade->ipp_stages != 0;
104         if( use_ipp )
105             normImg = cvCreateMat( img->rows, img->cols, CV_32FC1 );
106 #endif
107 
108         imgSmall = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1 );//创建新矩阵
109 
110         for( factor = 1; ; factor *= scaleFactor )//无循环条件的死循环
111         {
112             //定义3个矩形 大小
113             //经输出测试过,矩阵的width和cols是一样大
114             //我们假设上面的 winSize0 的 width,height都是10,factor循环到4,那么winSize的width和height都是40
115             //我们再假设img的width和height都是10,sz的就变为2.5
116             //sz1的就变为负的了,下面直接跳出循环,所以一般图片的w和h都比检测的窗口size要大得多
117             //重新假设他们都是100,那么sz就是25,sz1就是16
118             //此时改factor为5,sz为20,sz1为20-10+1=11
119             //由此可知,随着factor的增大,sz1的双值减小,由于factor *= scaleFactor的,且scaleFactor比1大,所以
120             //sz1必递减
121             //综上述,检测窗口win会越来越大,sz类窗口会越来越小
122             CvSize winSize = { cvRound(winSize0.width*factor), cvRound(winSize0.height*factor) };
123             CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) };
124             CvSize sz1 = { sz.width - winSize0.width + 1, sz.height - winSize0.height + 1 };
125 
126             //定义矩形框,icv_object_win_border,这个东西,找遍没找到
127 
128             CvRect equRect = { icv_object_win_border, icv_object_win_border,
129                 winSize0.width - icv_object_win_border*2,
130                 winSize0.height - icv_object_win_border*2 };
131 
132             CvMat img1, sum1, sqsum1, norm1, tilted1, mask1;
133             CvMat* _tilted = 0;
134 
135             if( sz1.width <= 0 || sz1.height <= 0 )//当sz1窗口大小为负的时候,循环结束。
136                 break;
137             if( winSize.width > maxSize.width || winSize.height > maxSize.height )//当检测窗口过大,也跳出循环
138                 break;
139             if( winSize.width < minSize.width || winSize.height < minSize.height )//过小,也跳出,不过它是继续循环
140                 continue;
141 
142             //在还没跳出循环的情况下,下面分别以sz的宽和高创建矩阵
143             img1 = cvMat( sz.height, sz.width, CV_8UC1, imgSmall->data.ptr );
144             sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr );
145             sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr );
146             if( tilted )//这个是矩阵类
147             {
148                 tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr );//一样是初始化
149                 _tilted = &tilted1;
150             }
151 
152             //这下面的是以sz1为基础初始化的矩阵
153             norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, normImg ? normImg->data.ptr : 0 );
154             mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr );
155 
156             cvResize( img, &img1, CV_INTER_LINEAR );//双线性插值,重新调整img的大小,相关数据存入img1
157             cvIntegral( &img1, &sum1, &sqsum1, _tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted
158 
159             int ystep = factor > 2 ? 1 : 2;//这里判断了下factor的大小,大于2,ystep就是1
160             const int LOCS_PER_THREAD = 1000;
161             //接着上面的假设,factor是4,那么此时的yster是1
162             //stripCount就是(11/1 * 11/1+1000/2)/1000 < 1
163             int stripCount = ((sz1.width/ystep)*(sz1.height + ystep-1)/ystep + LOCS_PER_THREAD/2)/LOCS_PER_THREAD;
164             stripCount = std::min(std::max(stripCount, 1), 100);
165             //然后和1对比,找出最大值,再和100比较,找出最小
166 
167 #ifdef HAVE_IPP
168             if( use_ipp )
169             {
170                 cv::Mat fsum(sum1.rows, sum1.cols, CV_32F, sum1.data.ptr, sum1.step);
171                 cv::Mat(&sum1).convertTo(fsum, CV_32F, 1, -(1<<24));
172             }
173             else
174 #endif
175             cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, _tilted, 1. );
176             //上面这个函数是为隐藏的cascade(hidden cascade)指定图像积分图像、平方和图像与倾斜和图像、特征矩形,然后让它检测
177             //sum1是上面生成的32bt积分图像,sqsum 单通道64比特图像的平方和图像 
178             //tilted 单通道32比特整数格式的图像的倾斜和
179             //1是窗口比例,如果 scale=1, 就只用原始窗口尺寸检测 (只检测同样尺寸大小的目标物体) 
180             //- 原始窗口尺寸在函数cvLoadHaarClassifierCascade中定义 (在 "<default_face_cascade>"中缺省为24x24), 
181             //如果scale=2, 使用的窗口是上面的两倍 (在face cascade中缺省值是48x48 )。
182             //这样尽管可以将检测速度提高四倍,但同时尺寸小于48x48的人脸将不能被检测到
183             cv::Mat _norm1(&norm1), _mask1(&mask1);
184 
185             //HaarDetectObjects_ScaleImage_Invoker进行并行运算(可以返回rejectLevels和levelWeights)
186             cv::parallel_for_(cv::Range(0, stripCount),
187                          cv::HaarDetectObjects_ScaleImage_Invoker(cascade,
188                                 (((sz1.height + stripCount - 1)/stripCount + ystep-1)/ystep)*ystep,
189                                 factor, cv::Mat(&sum1), cv::Mat(&sqsum1), &_norm1, &_mask1,
190                                 cv::Rect(equRect), allCandidates, rejectLevels, levelWeights, outputRejectLevels, &mtx));
191         }
192     }
193     else
194     {
195         int n_factors = 0;
196         cv::Rect scanROI;
197 
198         cvIntegral( img, sum, sqsum, tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted
199 
200         if( doCannyPruning )//边缘处理
201         {
202             sumcanny = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );
203             cvCanny( img, temp, 0, 50, 3 );//得到边缘图像
204             cvIntegral( temp, sumcanny );//再次积分
205         }
206 
207         for( n_factors = 0, factor = 1;
208              factor*cascade->orig_window_size.width < img->cols - 10 &&
209              factor*cascade->orig_window_size.height < img->rows - 10;
210              n_factors++, factor *= scaleFactor )
211             ;
212 
213         if( findBiggestObject )
214         {
215             scaleFactor = 1./scaleFactor;
216             factor *= scaleFactor;
217         }
218         else
219             factor = 1;
220 
221         for( ; n_factors-- > 0; factor *= scaleFactor )
222         {
223             const double ystep = std::max( 2., factor );
224             CvSize winSize = { cvRound( cascade->orig_window_size.width * factor ),
225                                 cvRound( cascade->orig_window_size.height * factor )};
226             CvRect equRect = { 0, 0, 0, 0 };
227             int *p[4] = {0,0,0,0};
228             int *pq[4] = {0,0,0,0};
229             int startX = 0, startY = 0;
230             int endX = cvRound((img->cols - winSize.width) / ystep);
231             int endY = cvRound((img->rows - winSize.height) / ystep);
232 
233             if( winSize.width < minSize.width || winSize.height < minSize.height )
234             {
235                 if( findBiggestObject )
236                     break;
237                 continue;
238             }
239 
240             if ( winSize.width > maxSize.width || winSize.height > maxSize.height )
241             {
242                 if( !findBiggestObject )
243                     break;
244                 continue;
245             }
246 
247             cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );
248             cvZero( temp );
249 
250             if( doCannyPruning )
251             {
252                 equRect.x = cvRound(winSize.width*0.15);
253                 equRect.y = cvRound(winSize.height*0.15);
254                 equRect.width = cvRound(winSize.width*0.7);
255                 equRect.height = cvRound(winSize.height*0.7);
256 
257                 p[0] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) + equRect.x;
258                 p[1] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step)
259                             + equRect.x + equRect.width;
260                 p[2] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) + equRect.x;
261                 p[3] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step)
262                             + equRect.x + equRect.width;
263 
264                 pq[0] = (int*)(sum->data.ptr + equRect.y*sum->step) + equRect.x;
265                 pq[1] = (int*)(sum->data.ptr + equRect.y*sum->step)
266                             + equRect.x + equRect.width;
267                 pq[2] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) + equRect.x;
268                 pq[3] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step)
269                             + equRect.x + equRect.width;
270             }
271 
272             if( scanROI.area() > 0 )
273             {
274                 //adjust start_height and stop_height
275                 startY = cvRound(scanROI.y / ystep);
276                 endY = cvRound((scanROI.y + scanROI.height - winSize.height) / ystep);
277 
278                 startX = cvRound(scanROI.x / ystep);
279                 endX = cvRound((scanROI.x + scanROI.width - winSize.width) / ystep);
280             }
281 
282             cv::parallel_for_(cv::Range(startY, endY),
283                 cv::HaarDetectObjects_ScaleCascade_Invoker(cascade, winSize, cv::Range(startX, endX),
284                                                            ystep, sum->step, (const int**)p,
285                                                            (const int**)pq, allCandidates, &mtx ));
286 
287             if( findBiggestObject && !allCandidates.empty() && scanROI.area() == 0 )
288             {
289                 rectList.resize(allCandidates.size());
290                 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
291 
292                 groupRectangles(rectList, std::max(minNeighbors, 1), GROUP_EPS);
293 
294                 if( !rectList.empty() )
295                 {
296                     size_t i, sz = rectList.size();
297                     cv::Rect maxRect;
298 
299                     for( i = 0; i < sz; i++ )
300                     {
301                         if( rectList[i].area() > maxRect.area() )
302                             maxRect = rectList[i];
303                     }
304 
305                     allCandidates.push_back(maxRect);
306 
307                     scanROI = maxRect;
308                     int dx = cvRound(maxRect.width*GROUP_EPS);
309                     int dy = cvRound(maxRect.height*GROUP_EPS);
310                     scanROI.x = std::max(scanROI.x - dx, 0);
311                     scanROI.y = std::max(scanROI.y - dy, 0);
312                     scanROI.width = std::min(scanROI.width + dx*2, img->cols-1-scanROI.x);
313                     scanROI.height = std::min(scanROI.height + dy*2, img->rows-1-scanROI.y);
314 
315                     double minScale = roughSearch ? 0.6 : 0.4;
316                     minSize.width = cvRound(maxRect.width*minScale);
317                     minSize.height = cvRound(maxRect.height*minScale);
318                 }
319             }
320         }
321     }
322 
323     //上面的循环结束后,进入到这里
324     rectList.resize(allCandidates.size());
325     if(!allCandidates.empty())
326         std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
327 
328     if( minNeighbors != 0 || findBiggestObject )
329     {
330         if( outputRejectLevels )
331         {
332             groupRectangles(rectList, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
333         }
334         else
335         {
336             groupRectangles(rectList, rweights, std::max(minNeighbors, 1), GROUP_EPS);
337         }
338     }
339     else
340         rweights.resize(rectList.size(),0);
341 
342     if( findBiggestObject && rectList.size() )
343     {
344         CvAvgComp result_comp = {{0,0,0,0},0};
345 
346         for( size_t i = 0; i < rectList.size(); i++ )
347         {
348             cv::Rect r = rectList[i];
349             if( r.area() > cv::Rect(result_comp.rect).area() )
350             {
351                 result_comp.rect = r;
352                 result_comp.neighbors = rweights[i];
353             }
354         }
355         cvSeqPush( result_seq, &result_comp );
356     }
357     else
358     {
359         for( size_t i = 0; i < rectList.size(); i++ )
360         {
361             CvAvgComp c;
362             c.rect = rectList[i];
363             c.neighbors = !rweights.empty() ? rweights[i] : 0;
364             cvSeqPush( result_seq, &c );
365         }
366     }
367 
368     return result_seq;
369 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏fangyangcoder

基于交通灯数据集的端到端分类

抓住11月的尾巴,这里写上昨天做的一个DL的作业吧,作业很简单,基于交通灯的图像分类,但这确是让你从0构建深度学习系统的好例子,很多已有的数据集都封装好了,直接...

1261
来自专栏慎独

Python科学计算和绘图入门

2704
来自专栏数据结构与算法

洛谷P3707 [SDOI2017]相关分析(线段树)

题目描述 Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度、颜色等等,进而估算出星星的距离,半径等等。 Frank不仅喜欢观测...

2855
来自专栏机器之心

教程 | 如何将模型部署到安卓移动端,这里有一份简单教程

截至 2018 年,全球活跃的安卓设备已经超过了 20 亿部。安卓手机的迅速普及在很大程度上得益于各种各样的智能应用,从地图到图片编辑器无所不有。随着深度学习技...

1451
来自专栏自动化测试实战

flask第35篇——模板项目实战(一)

上面代码综合了之前学过的知识,包括:flask for 循环、set模板赋值其余都是前端的内容,这里不做过多的阐述。

584
来自专栏数据结构与算法

BZOJ1004: [HNOI2008]Cards(Burnside引理 背包dp)

  小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有 多少种染色方案,Sun很快就给出了答案.进一步...

723
来自专栏贾志刚-OpenCV学堂

OpenCV实现图像连通组件标记与分析

连接组件标记算法(connected component labeling algorithm)是图像分析中最常用的算法之一,算法的实质是扫描一幅图像的每个像素...

1152
来自专栏WOLFRAM

Mathematica 11 在数学教学中的新功能部分示例

1747
来自专栏蓝天

boost库thread.hpp编译警告honored已修复

请浏览:https://svn.boost.org/trac/boost/ticket/7874

892
来自专栏AI研习社

如何使用 TensorFlow mobile 将 PyTorch 和 Keras 模型部署到移动设备

截止到今年,已经有超过 20 亿活跃的安卓设备。安卓手机的迅速普及很大程度上是因为各式各样的智能 app,从地图到图片编辑器应有尽有。随着深度学习的出现,我们的...

2233

扫码关注云+社区