您的位置:首页 > 大数据 > 人工智能

train_cascade 源码阅读之LBP特征

2015-01-19 18:14 393 查看
本文以LBP特征为例,介绍了OpenCV中train_cascade数据初始化的过程。

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

bool CvCascadeBoost::
train(
const CvFeatureEvaluator* _featureEvaluator,
int _numSamples,
int _precalcValBufSize, int _precalcIdxBufSize,
const CvCascadeBoostParams& _params )
{
bool isTrained = false;
CV_Assert( !data );
clear();
data = new <strong>CvCascadeBoostTrainData</strong>(
_featureEvaluator, _numSamples,
_precalcValBufSize, _precalcIdxBufSize, _params )
……
}


2 在CvCascadeBoostTrainData中调用setData函数。

CvCascadeBoostTrainData::CvCascadeBoostTrainData( const CvFeatureEvaluator* _featureEvaluator,
int _numSamples,
int _precalcValBufSize, int _precalcIdxBufSize,
const CvDTreeParams& _params )
{
<strong> setData</strong>( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params );
}


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

void CvCascadeBoostTrainData::setData( const CvFeatureEvaluator* _featureEvaluator,
int _numSamples,
int _precalcValBufSize, int _precalcIdxBufSize,
const CvDTreeParams& _params )
{

……

// precalculate valCache and set indices in buf
<strong> precalculate();</strong>
……

}


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

void CvCascadeBoostTrainData::precalculate()
{
int minNum = MIN( numPrecalcVal, numPrecalcIdx);

double proctime = -TIME( 0 );
parallel_for_( Range(numPrecalcVal, numPrecalcIdx),
FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );
parallel_for_( Range(0, minNum),
FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );
parallel_for_( Range(minNum, numPrecalcVal),
<strong>FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );</strong>
cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl;
}


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

struct FeatureValOnlyPrecalc : ParallelLoopBody
{
FeatureValOnlyPrecalc( const CvFeatureEvaluator* _featureEvaluator, Mat* _valCache, int _sample_count )
{
featureEvaluator = _featureEvaluator;
valCache = _valCache;
sample_count = _sample_count;
}
void operator()( const Range& range ) const
{
for ( int fi = range.start; fi < range.end; fi++)
for( int si = 0; si < sample_count; si++ )
<strong>valCache->at<float>(fi,si) = (*featureEvaluator)( fi, si );</strong>
}
const CvFeatureEvaluator* featureEvaluator;
Mat* valCache;
int sample_count;
};


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

class CvLBPEvaluator : public CvFeatureEvaluator
{
public:
virtual ~CvLBPEvaluator() {}
virtual void init(
const CvFeatureParams   *_featureParams,
int                     _maxSampleCount,
cv::Size                _winSize );
virtual void setImage(
const cv::Mat   & img,
uchar           clsLabel,
int             idx);
<strong>  virtual float operator()(
int featureIdx,
int sampleIdx) const
{ return (float)features[featureIdx].calc( sum, sampleIdx); }</strong>
virtual void writeFeatures(
cv::FileStorage &fs,
const cv::Mat   &featureMap ) const;
protected:
virtual void generateFeatures();

class Feature
{
public:
Feature();
Feature(
int offset,
int x,
int y,
int _block_w,
int _block_h  );
<strong>uchar calc(
const cv::Mat& _sum,
size_t y ) const;</strong>
void write( cv::FileStorage &fs ) const;

cv::Rect rect;
<strong>int p[16];</strong>
};
<strong>std::vector<Feature> features;

cv::Mat sum;</strong>
};


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

void CvLBPEvaluator::init(
const CvFeatureParams *_featureParams,
int _maxSampleCount, Size _winSize)
{
CV_Assert( _maxSampleCount > 0);
<strong>sum.create((int)_maxSampleCount,
(_winSize.width + 1) * (_winSize.height + 1),
CV_32SC1);</strong>
CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
}


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

void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
{
CV_DbgAssert( !sum.empty() );
CvFeatureEvaluator::setImage( img, clsLabel, idx );
Mat <strong>innSum</strong>(winSize.height + 1,
winSize.width + 1,
sum.type(),
<strong>sum.ptr<int>((int)idx)</strong>);
<strong>integral</strong>( img, innSum );
}


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

0 | 1 | 2 | 3

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

4 | 5 | 6 | 7

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

8 | 9 | 10 (cval) | 11

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

12 | 13 | 14 | 15

inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, size_t y) const
{
const int* psum = _sum.ptr<int>((int)y);
int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];

return (uchar)(
(psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) |   // 0
(psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) |    // 1
(psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) |    // 2
(psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) |  // 5
(psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8
(psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) |  // 7
(psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) |   // 6
(psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0));     // 3
}


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

void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams,
int _maxSampleCount, Size _winSize )
{
CV_Assert(_maxSampleCount > 0);
featureParams = (CvFeatureParams *)_featureParams;
winSize = _winSize;
numFeatures = 0;
cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
generateFeatures();
}


void CvLBPEvaluator::generateFeatures()
{
int offset = winSize.width + 1;
for( int x = 0; x < winSize.width; x++ )
for( int y = 0; y < winSize.height; y++ )
for( int w = 1; w <= winSize.width / 3; w++ )
for( int h = 1; h <= winSize.height / 3; h++ )
if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )
features.push_back( Feature(offset, x, y, w, h ) );
numFeatures = (int)features.size();
}
从这里看,其实现还是和原始的LBP有些不同的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: