当前位置:   article > 正文

OpenCV 2.4.9源码分析(一):detectMultiScale_detectmultiscale 多线程

detectmultiscale 多线程

OpenCV使用级联分类器做人脸识别的时候,调用了void CascadeClassifier::detectMultiScale方法,采用的滑窗机制,这里列出该函数的源码实现过程。

代码读起来不复杂,但是很有趣^_^。

  1. void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
  2. double scaleFactor, int minNeighbors,
  3. int flags, Size minObjectSize, Size maxObjectSize)
  4. {
  5. vector<int> fakeLevels; // 检测未通过层的级数
  6. vector<double> fakeWeights; // 未通过层的强分类器的输出, 不使用时outputRejectLevels = false;
  7. detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
  8. minNeighbors, flags, minObjectSize, maxObjectSize, false );
  9. }
  1. void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects,
  2. vector<int>& rejectLevels,
  3. vector<double>& levelWeights,
  4. double scaleFactor, int minNeighbors,
  5. int flags, Size minObjectSize, Size maxObjectSize,
  6. bool outputRejectLevels )
  7. {
  8. const double GROUP_EPS = 0.2;
  9. CV_Assert( scaleFactor > 1 && image.depth() == CV_8U ); // comment@2018.04.08 scaleFactor大于1且图像是8位的。
  10. if( empty() )
  11. return;
  12. if( isOldFormatCascade() )
  13. {
  14. MemStorage storage(cvCreateMemStorage(0));
  15. CvMat _image = image;
  16. CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
  17. minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
  18. vector<CvAvgComp> vecAvgComp;
  19. Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
  20. objects.resize(vecAvgComp.size());
  21. std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
  22. return;
  23. }
  24. objects.clear(); // 清空存放检测结果的容器
  25. if (!maskGenerator.empty()) { //
  26. maskGenerator->initializeMask(image);
  27. }
  28. if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
  29. maxObjectSize = image.size(); // 默认检测目标最大为图像本身大小
  30. Mat grayImage = image;
  31. if( grayImage.channels() > 1 ) // 通道数大于1,则转成灰度图
  32. {
  33. Mat temp;
  34. cvtColor(grayImage, temp, CV_BGR2GRAY);
  35. grayImage = temp;
  36. }
  37. Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U); // comment@2018.04.08 多加一行一列
  38. vector<Rect> candidates; // 存放候选 rect
  39. // 多尺度,每次放大scaleFactor倍
  40. for( double factor = 1; ; factor *= scaleFactor )
  41. {
  42. // 分类器训练的时候的图像大小
  43. Size originalWindowSize = getOriginalWindowSize(); // <height>20</height> <width>20</width> xml文件中的首两行即为训练的目标窗口大小
  44. // 每次放大sacleFactor倍
  45. Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
  46. // 原图一次缩小
  47. Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
  48. // 可供原始窗口移动的范围大小
  49. Size processingRectSize( scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height );
  50. // 检测窗口可以动区域小于0,表明图像比originalWindowSize还小,然后退出
  51. if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
  52. break;
  53. // 窗口大于最大检测目标时,退出;默认是输入图像大小
  54. if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
  55. break;
  56. // 窗口小于最小检测目标,跳过
  57. if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
  58. continue;
  59. // 拷贝数据
  60. Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
  61. resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
  62. // y步长
  63. int yStep;
  64. if( getFeatureType() == cv::FeatureEvaluator::HOG )
  65. {
  66. yStep = 4; // HOG特征,步长设为 4
  67. }
  68. else
  69. {
  70. yStep = factor > 2. ? 1 : 2; // 缩放因子大于2时,步长为1,否则为2
  71. }
  72. int stripCount, stripSize; // 并行计算个数及大小,分行并行计算
  73. const int PTS_PER_THREAD = 1000; // 预订时间标准系统
  74. stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
  75. stripCount = std::min(std::max(stripCount, 1), 100);
  76. stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
  77. // 调用但尺度检测函数
  78. if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
  79. rejectLevels, levelWeights, outputRejectLevels ) )
  80. break;
  81. }
  82. objects.resize(candidates.size());
  83. std::copy(candidates.begin(), candidates.end(), objects.begin());
  84. if( outputRejectLevels )
  85. {
  86. groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
  87. }
  88. else
  89. {
  90. groupRectangles( objects, minNeighbors, GROUP_EPS ); // 合并检测结果
  91. }
  92. }
  1. bool CascadeClassifier::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize,
  2. int stripSize, int yStep, double factor, vector<Rect>& candidates,
  3. vector<int>& levels, vector<double>& weights, bool outputRejectLevels )
  4. {
  5. /*
  6. getOriginalWindowSize --> data.origWinSize (width=20, height=20)
  7. */
  8. if( !featureEvaluator->setImage( image, data.origWinSize ) ) // 对图像做积分图
  9. return false;
  10. #if defined (LOG_CASCADE_STATISTIC)
  11. logger.setImage(image);
  12. #endif
  13. Mat currentMask;
  14. if (!maskGenerator.empty()) {
  15. currentMask=maskGenerator->generateMask(image);
  16. }
  17. vector<Rect> candidatesVector;
  18. vector<int> rejectLevels;
  19. vector<double> levelWeights;
  20. Mutex mtx;
  21. if( outputRejectLevels ) // outputRejectLevels = false
  22. {
  23. parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
  24. candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
  25. levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
  26. weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
  27. }
  28. else
  29. {
  30. // parallel_for_ 为了TBB加速时使用
  31. // 生成stripCount个平行线程(每个线程生成一个CascadeClassifierInvoker),
  32. // 在每个CascadeClassifierInvoker中分配当前缩放图像的N行做检测,这是TBB利用多线程做的加速计算
  33. parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
  34. candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
  35. }
  36. candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
  37. #if defined (LOG_CASCADE_STATISTIC)
  38. logger.write();
  39. #endif
  40. return true;
  41. }
  1. class CascadeClassifierInvoker : public ParallelLoopBody
  2. {
  3. public:
  4. CascadeClassifierInvoker( CascadeClassifier& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
  5. vector<Rect>& _vec, vector<int>& _levels, vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
  6. {
  7. classifier = &_cc;
  8. processingRectSize = _sz1;
  9. stripSize = _stripSize;
  10. yStep = _yStep;
  11. scalingFactor = _factor;
  12. rectangles = &_vec;
  13. rejectLevels = outputLevels ? &_levels : 0;
  14. levelWeights = outputLevels ? &_weights : 0;
  15. mask = _mask;
  16. mtx = _mtx;
  17. }
  18. void operator()(const Range& range) const
  19. {
  20. Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
  21. Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));
  22. int y1 = range.start * stripSize;
  23. int y2 = min(range.end * stripSize, processingRectSize.height);
  24. for( int y = y1; y < y2; y += yStep )
  25. {
  26. for( int x = 0; x < processingRectSize.width; x += yStep )
  27. {
  28. if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
  29. continue;
  30. }
  31. double gypWeight;
  32. // //result =1表示通过所有分类器,result<0表示失败的级数。
  33. int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
  34. #if defined (LOG_CASCADE_STATISTIC)
  35. logger.setPoint(Point(x, y), result);
  36. #endif
  37. if( rejectLevels )
  38. {
  39. if( result == 1 )
  40. result = -(int)classifier->data.stages.size();
  41. if( classifier->data.stages.size() + result < 4 )
  42. {
  43. mtx->lock();
  44. rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
  45. rejectLevels->push_back(-result);
  46. levelWeights->push_back(gypWeight);
  47. mtx->unlock();
  48. }
  49. }
  50. else if( result > 0 )
  51. {
  52. mtx->lock();
  53. rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
  54. winSize.width, winSize.height));
  55. mtx->unlock();
  56. }
  57. if( result == 0 )
  58. x += yStep;
  59. }
  60. }
  61. }
  62. CascadeClassifier* classifier;
  63. vector<Rect>* rectangles;
  64. Size processingRectSize;
  65. int stripSize, yStep;
  66. double scalingFactor;
  67. vector<int> *rejectLevels;
  68. vector<double> *levelWeights;
  69. Mat mask;
  70. Mutex* mtx;
  71. };
  1. int CascadeClassifier::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
  2. {
  3. CV_Assert( oldCascade.empty() );
  4. assert( data.featureType == FeatureEvaluator::HAAR ||
  5. data.featureType == FeatureEvaluator::LBP ||
  6. data.featureType == FeatureEvaluator::HOG );
  7. if( !evaluator->setWindow(pt) )
  8. return -1;
  9. if( data.isStumpBased )
  10. {
  11. if( data.featureType == FeatureEvaluator::HAAR )
  12. return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
  13. else if( data.featureType == FeatureEvaluator::LBP )
  14. return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
  15. else if( data.featureType == FeatureEvaluator::HOG )
  16. return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );
  17. else
  18. return -2;
  19. }
  20. else
  21. {
  22. if( data.featureType == FeatureEvaluator::HAAR )
  23. return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
  24. else if( data.featureType == FeatureEvaluator::LBP )
  25. return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
  26. else if( data.featureType == FeatureEvaluator::HOG )
  27. return predictOrdered<HOGEvaluator>( *this, evaluator, weight );
  28. else
  29. return -2;
  30. }
  31. }
合并候选框的函数 groupRectangles,在上一篇的文章里介绍了。

  1. void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights)
  2. {
  3. /*
  4. 当组合阈值groupThreshold小于等于0的时候,如果输出weights,
  5. 则weights中返回与rectList同样个数个1,函数直接返回,不进行合并操作
  6. */
  7. if( groupThreshold <= 0 || rectList.empty() )
  8. {
  9. if( weights )
  10. {
  11. size_t i, sz = rectList.size();
  12. weights->resize(sz);
  13. for( i = 0; i < sz; i++ )
  14. (*weights)[i] = 1;
  15. }
  16. return;
  17. }
  18. // 调用partition函数对rectList中的矩形进行分类
  19. // 其中nclasses表示组合类别,labels表示每个rect属于哪个类别的,相似度计算使用SimilarRects类
  20. vector<int> labels;
  21. int nclasses = partition(rectList, labels, SimilarRects(eps));
  22. vector<Rect> rrects(nclasses);
  23. vector<int> rweights(nclasses, 0);
  24. vector<int> rejectLevels(nclasses, 0);
  25. vector<double> rejectWeights(nclasses, DBL_MIN);
  26. int i, j, nlabels = (int)labels.size();
  27. /*
  28. * 组合分到同一类别的矩形并保存当前类别下通过stage的最大值以及最大的权重
  29. */
  30. for( i = 0; i < nlabels; i++ )
  31. {
  32. int cls = labels[i];
  33. rrects[cls].x += rectList[i].x;
  34. rrects[cls].y += rectList[i].y;
  35. rrects[cls].width += rectList[i].width;
  36. rrects[cls].height += rectList[i].height;
  37. rweights[cls]++;
  38. }
  39. if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
  40. {
  41. for( i = 0; i < nlabels; i++ )
  42. {
  43. int cls = labels[i];
  44. if( (*weights)[i] > rejectLevels[cls] )
  45. {
  46. rejectLevels[cls] = (*weights)[i];
  47. rejectWeights[cls] = (*levelWeights)[i];
  48. }
  49. else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
  50. rejectWeights[cls] = (*levelWeights)[i];
  51. }
  52. }
  53. for( i = 0; i < nclasses; i++ )
  54. {
  55. Rect r = rrects[i];
  56. float s = 1.f/rweights[i];
  57. rrects[i] = Rect(saturate_cast<int>(r.x*s),
  58. saturate_cast<int>(r.y*s),
  59. saturate_cast<int>(r.width*s),
  60. saturate_cast<int>(r.height*s));
  61. }
  62. rectList.clear();
  63. if( weights )
  64. weights->clear();
  65. if( levelWeights )
  66. levelWeights->clear();
  67. // 按照groupThreshold合并规则,以及是否存在包含关系输出合并后的矩形
  68. for( i = 0; i < nclasses; i++ )
  69. {
  70. Rect r1 = rrects[i];
  71. int n1 = levelWeights ? rejectLevels[i] : rweights[i];
  72. double w1 = rejectWeights[i];
  73. if( n1 <= groupThreshold )
  74. continue;
  75. // filter out small face rectangles inside large rectangles
  76. for( j = 0; j < nclasses; j++ )
  77. {
  78. int n2 = rweights[j];
  79. if( j == i || n2 <= groupThreshold )
  80. continue;
  81. Rect r2 = rrects[j];
  82. int dx = saturate_cast<int>( r2.width * eps );
  83. int dy = saturate_cast<int>( r2.height * eps );
  84. // 当r1在r2的内部时,跳出
  85. if( i != j &&
  86. r1.x >= r2.x - dx &&
  87. r1.y >= r2.y - dy &&
  88. r1.x + r1.width <= r2.x + r2.width + dx &&
  89. r1.y + r1.height <= r2.y + r2.height + dy &&
  90. (n2 > std::max(3, n1) || n1 < 3) )
  91. break;
  92. }
  93. if( j == nclasses )
  94. {
  95. rectList.push_back(r1);
  96. if( weights )
  97. weights->push_back(n1);
  98. if( levelWeights )
  99. levelWeights->push_back(w1);
  100. }
  101. }
  102. }













声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/733723
推荐阅读
相关标签
  

闽ICP备14008679号