train_cascade 源码阅读之LBP特征

本文深入探讨了在OpenCV中利用CvCascadeBoost进行训练时,如何通过LBP(局部二值模式)特征实现目标检测任务的数据初始化流程。详细介绍了从创建CvCascadeBoostTrainData对象到预计算特征值的过程,包括初始化特征评价器、设置样本数量、配置预计算值缓存大小等关键步骤。特别关注了如何在预计算阶段高效计算LBP特征,为后续的CvCascadeBoost训练奠定基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文以LBP特征为例,介绍了OpenCV中train_cascade数据初始化的过程。

1 在CvCascadeBoost中,创建了CvCascadeBoostTrainData对象。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool CvCascadeBoost::  
  2. train(  
  3.         const CvFeatureEvaluator* _featureEvaluator,  
  4.         int _numSamples,  
  5.         int _precalcValBufSize, int _precalcIdxBufSize,  
  6.         const CvCascadeBoostParams& _params )  
  7. {  
  8.     bool isTrained = false;  
  9.     CV_Assert( !data );  
  10.     clear();  
  11.     data = new <strong>CvCascadeBoostTrainData</strong>(  
  12.                 _featureEvaluator, _numSamples,  
  13.                 _precalcValBufSize, _precalcIdxBufSize, _params )  
  14.    ……  
  15. }  

2 在CvCascadeBoostTrainData中调用setData函数。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. CvCascadeBoostTrainData::CvCascadeBoostTrainData( const CvFeatureEvaluator* _featureEvaluator,  
  2.                                                   int _numSamples,  
  3.                                                   int _precalcValBufSize, int _precalcIdxBufSize,  
  4.                                                   const CvDTreeParams& _params )  
  5. {  
  6.    <strong> setData</strong>( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params );  
  7. }  

3 在setData函数中调用了预先计算特征,也就是参数中preCalcValBufSize, preClacIdxBufSize预留的内存初始化的地方。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvCascadeBoostTrainData::setData( const CvFeatureEvaluator* _featureEvaluator,  
  2.                                        int _numSamples,  
  3.                                        int _precalcValBufSize, int _precalcIdxBufSize,  
  4.                                        const CvDTreeParams& _params )  
  5. {  
  6.      
  7.  ……  
  8.   
  9.     // precalculate valCache and set indices in buf  
  10.    <strong> precalculate();</strong>  
  11.  ……  
  12.       
  13. }  


4 在precalculate函数中,有初始化时计算一次特征的函数。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvCascadeBoostTrainData::precalculate()  
  2. {  
  3.     int minNum = MIN( numPrecalcVal, numPrecalcIdx);  
  4.   
  5.     double proctime = -TIME( 0 );  
  6.     parallel_for_( Range(numPrecalcVal, numPrecalcIdx),  
  7.                    FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );  
  8.     parallel_for_( Range(0, minNum),  
  9.                    FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );  
  10.     parallel_for_( Range(minNum, numPrecalcVal),  
  11.                    <strong>FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );</strong>  
  12.     cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl;  
  13. }  


5 在该函数内,用函数指针调用了featureEvaluator的一个operator(),如果是LBP特征的话,则调用的是LBP子类的对应函数。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct FeatureValOnlyPrecalc : ParallelLoopBody  
  2. {  
  3.     FeatureValOnlyPrecalc( const CvFeatureEvaluator* _featureEvaluator, Mat* _valCache, int _sample_count )  
  4.     {  
  5.         featureEvaluator = _featureEvaluator;  
  6.         valCache = _valCache;  
  7.         sample_count = _sample_count;  
  8.     }  
  9.     void operator()( const Range& range ) const  
  10.     {  
  11.         for ( int fi = range.start; fi < range.end; fi++)  
  12.             forint si = 0; si < sample_count; si++ )  
  13.                 <strong>valCache->at<float>(fi,si) = (*featureEvaluator)( fi, si );</strong>  
  14.     }  
  15.     const CvFeatureEvaluator* featureEvaluator;  
  16.     Mat* valCache;  
  17.     int sample_count;  
  18. };  


6 在CvLBPEvaluator中定义了operator()虚函数。并且它调用了calc函数,返回计算得到的LBP特征。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class CvLBPEvaluator : public CvFeatureEvaluator  
  2. {  
  3. public:  
  4.     virtual ~CvLBPEvaluator() {}  
  5.     virtual void init(  
  6.             const CvFeatureParams   *_featureParams,  
  7.             int                     _maxSampleCount,  
  8.             cv::Size                _winSize );  
  9.     virtual void setImage(  
  10.             const cv::Mat   & img,  
  11.             uchar           clsLabel,  
  12.             int             idx);  
  13.   <strong>  virtual float operator()(  
  14.             int featureIdx,  
  15.             int sampleIdx) const  
  16.     { return (float)features[featureIdx].calc( sum, sampleIdx); }</strong>  
  17.     virtual void writeFeatures(  
  18.             cv::FileStorage &fs,  
  19.             const cv::Mat   &featureMap ) const;  
  20. protected:  
  21.     virtual void generateFeatures();  
  22.   
  23.     class Feature  
  24.     {  
  25.     public:  
  26.         Feature();  
  27.         Feature(  
  28.                 int offset,  
  29.                 int x,  
  30.                 int y,  
  31.                 int _block_w,  
  32.                 int _block_h  );  
  33.         <strong>uchar calc(  
  34.                 const cv::Mat& _sum,  
  35.                 size_t y ) const;</strong>  
  36.         void write( cv::FileStorage &fs ) const;  
  37.   
  38.         cv::Rect rect;  
  39.         <strong>int p[16];</strong>  
  40.     };  
  41.     <strong>std::vector<Feature> features;  
  42.   
  43.     cv::Mat sum;</strong>  
  44. };  

7 在CvLBPEvaluator初始化的时候,初始化了sum矩阵,它有样本和个数相同多的行。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::init(  
  2.         const CvFeatureParams *_featureParams,  
  3.         int _maxSampleCount, Size _winSize)  
  4. {  
  5.     CV_Assert( _maxSampleCount > 0);  
  6.     <strong>sum.create((int)_maxSampleCount,  
  7.                (_winSize.width + 1) * (_winSize.height + 1),  
  8.                CV_32SC1);</strong>  
  9.     CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );  
  10. }  


8 创建积分图矩阵,将矩阵的数据指向刚才创建好的sum的对应样本行。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)  
  2. {  
  3.     CV_DbgAssert( !sum.empty() );  
  4.     CvFeatureEvaluator::setImage( img, clsLabel, idx );  
  5.     Mat <strong>innSum</strong>(winSize.height + 1,  
  6.                winSize.width + 1,  
  7.                sum.type(),  
  8.                <strong>sum.ptr<int>((int)idx)</strong>);  
  9.     <strong>integral</strong>( img, innSum );  
  10. }  


9 调用积分图,返回LBP特征,从函数中可以看到,作者使用的是原始的LBP特征,并没有归一化或者合并等等操作。


        0    |      1    |    2    |    3    

   -------------------------------------

        4    |      5    |    6    |    7    

   -------------------------------------

        8    |      9    |   10 (cval)  |    11   

   -------------------------------------

       12   |     13   |  14   |    15   

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, size_t y) const  
  2. {  
  3.     const int* psum = _sum.ptr<int>((int)y);  
  4.     int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];  
  5.   
  6.     return (uchar)(  
  7.             (psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) |   // 0  
  8.             (psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) |    // 1  
  9.             (psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) |    // 2  
  10.             (psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) |  // 5  
  11.             (psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8  
  12.             (psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) |  // 7  
  13.             (psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) |   // 6  
  14.             (psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0));     // 3  
  15. }  


10 需要说明的是,上段代码中的p[?]保存的是图像像素在sum矩阵中的偏移量。这些地址是固定的,在CvFeatureEvaluator初始化的时候创建。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams,  
  2.                               int _maxSampleCount, Size _winSize )  
  3. {  
  4.     CV_Assert(_maxSampleCount > 0);  
  5.     featureParams = (CvFeatureParams *)_featureParams;  
  6.     winSize = _winSize;  
  7.     numFeatures = 0;  
  8.     cls.create( (int)_maxSampleCount, 1, CV_32FC1 );  
  9.     generateFeatures();  
  10. }  

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::generateFeatures()  
  2. {  
  3.     int offset = winSize.width + 1;  
  4.     forint x = 0; x < winSize.width; x++ )  
  5.         forint y = 0; y < winSize.height; y++ )  
  6.             forint w = 1; w <= winSize.width / 3; w++ )  
  7.                 forint h = 1; h <= winSize.height / 3; h++ )  
  8.                     if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )  
  9.                         features.push_back( Feature(offset, x, y, w, h ) );  
  10.     numFeatures = (int)features.size();  
  11. }  
从这里看,其实现还是和原始的LBP有些不同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值