zoukankan      html  css  js  c++  java
  • 关于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 }

          正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我。

  • 相关阅读:
    Postgresql HStore 插件试用小结
    postgres-xl 安装与部署 【异常处理】ERROR: could not open file (null)/STDIN_***_0 for write, No such file or directory
    GPDB 5.x PSQL Quick Reference
    postgresql 数据库schema 复制
    hive 打印日志
    gp与 pg 查询进程
    jquery table 发送两次请求 解惑
    python 字符串拼接效率打脸帖
    postgresql 日期类型处理实践
    IBM Rational Rose软件下载以及全破解方法
  • 原文地址:https://www.cnblogs.com/linguanh/p/4267432.html
Copyright © 2011-2022 走看看