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

train_cascade 源码阅读之Haar特征

2015-01-19 21:43 399 查看
train_cascade中对Haar特征的处理与LBP特征类似,调用方法也相近,都是作为CvFeatureEvaluator的子类,框架性的东西都是一致的,具体调用过程可以参考
train_cascade
源码阅读之LBP feature ,下面主要讨论的是Haar特征的实现。

下面片段是生成用于在积分图中的矩形块的坐标,Feature类中存的是在积分图矩阵中的初始偏移量,矩形的左上角坐标和宽高,以及是否旋转。不同类型的Haar特征已经在代码中体现的很明确了,故不赘述。

<span style="font-size:14px;">void CvHaarEvaluator::generateFeatures()
{
int mode = ((const CvHaarFeatureParams*)((CvFeatureParams*)featureParams))->mode;
int offset = winSize.width + 1;
for( int x = 0; x < winSize.width; x++ )
{
for( int y = 0; y < winSize.height; y++ )
{
for( int dx = 1; dx <= winSize.width; dx++ )
{
for( int dy = 1; dy <= winSize.height; dy++ )
{
// haar_x2
if ( (x+dx*2 <= winSize.width) && (y+dy <= winSize.height) )
{
features.push_back( Feature( offset, false,
x,    y, dx*2, dy, -1,
x+dx, y, dx  , dy, +2 ) );
}
// haar_y2
if ( (x+dx <= winSize.width) && (y+dy*2 <= winSize.height) )
{
features.push_back( Feature( offset, false,
x,    y, dx, dy*2, -1,
x, y+dy, dx, dy,   +2 ) );
}
// haar_x3
if ( (x+dx*3 <= winSize.width) && (y+dy <= winSize.height) )
{
features.push_back( Feature( offset, false,
x,    y, dx*3, dy, -1,
x+dx, y, dx  , dy, +3 ) );
}
// haar_y3
if ( (x+dx <= winSize.width) && (y+dy*3 <= winSize.height) )
{
features.push_back( Feature( offset, false,
x, y,    dx, dy*3, -1,
x, y+dy, dx, dy,   +3 ) );
}
if( mode != CvHaarFeatureParams::BASIC )
{
// haar_x4
if ( (x+dx*4 <= winSize.width) && (y+dy <= winSize.height) )
{
features.push_back( Feature( offset, false,
x,    y, dx*4, dy, -1,
x+dx, y, dx*2, dy, +2 ) );
}
// haar_y4
if ( (x+dx <= winSize.width ) && (y+dy*4 <= winSize.height) )
{
features.push_back( Feature( offset, false,
x, y,    dx, dy*4, -1,
x, y+dy, dx, dy*2, +2 ) );
}
}
// x2_y2
if ( (x+dx*2 <= winSize.width) && (y+dy*2 <= winSize.height) )
{
features.push_back( Feature( offset, false,
x,    y,    dx*2, dy*2, -1,
x,    y,    dx,   dy,   +2,
x+dx, y+dy, dx,   dy,   +2 ) );
}
if (mode != CvHaarFeatureParams::BASIC)
{
if ( (x+dx*3 <= winSize.width) && (y+dy*3 <= winSize.height) )
{
features.push_back( Feature( offset, false,
x   , y   , dx*3, dy*3, -1,
x+dx, y+dy, dx  , dy  , +9) );
}
}
if (mode == CvHaarFeatureParams::ALL)
{
// tilted haar_x2
if ( (x+2*dx <= winSize.width) && (y+2*dx+dy <= winSize.height) && (x-dy>= 0) )
{
features.push_back( Feature( offset, true,
x, y, dx*2, dy, -1,
x, y, dx,   dy, +2 ) );
}
// tilted haar_y2
if ( (x+dx <= winSize.width) && (y+dx+2*dy <= winSize.height) && (x-2*dy>= 0) )
{
features.push_back( Feature( offset, true,
x, y, dx, 2*dy, -1,
x, y, dx, dy,   +2 ) );
}
// tilted haar_x3
if ( (x+3*dx <= winSize.width) && (y+3*dx+dy <= winSize.height) && (x-dy>= 0) )
{
features.push_back( Feature( offset, true,
x,    y,    dx*3, dy, -1,
x+dx, y+dx, dx,   dy, +3 ) );
}
// tilted haar_y3
if ( (x+dx <= winSize.width) && (y+dx+3*dy <= winSize.height) && (x-3*dy>= 0) )
{
features.push_back( Feature( offset, true,
x,    y,    dx, 3*dy, -1,
x-dy, y+dy, dx, dy,   +3 ) );
}
// tilted haar_x4
if ( (x+4*dx <= winSize.width) && (y+4*dx+dy <= winSize.height) && (x-dy>= 0) )
{
features.push_back( Feature( offset, true,
x,    y,    dx*4, dy, -1,
x+dx, y+dx, dx*2, dy, +2 ) );
}
// tilted haar_y4
if ( (x+dx <= winSize.width) && (y+dx+4*dy <= winSize.height) && (x-4*dy>= 0) )
{
features.push_back( Feature( offset, true,
x,    y,    dx, 4*dy, -1,
x-dy, y+dy, dx, 2*dy, +2 ) );
}
}
}
}
}
}
numFeatures = (int)features.size();
}</span>
接着,在 CvHaarEvaluator::Feature构造函数中,对刚刚求得的坐标做了偏移量上的转换。

<span style="font-size:14px;">CvHaarEvaluator::Feature::Feature( int offset, bool _tilted,
int x0, int y0, int w0, int h0, float wt0,
int x1, int y1, int w1, int h1, float wt1,
int x2, int y2, int w2, int h2, float wt2 )
{
tilted = _tilted;

rect[0].r.x = x0;
rect[0].r.y = y0;
rect[0].r.width  = w0;
rect[0].r.height = h0;
rect[0].weight   = wt0;

rect[1].r.x = x1;
rect[1].r.y = y1;
rect[1].r.width  = w1;
rect[1].r.height = h1;
rect[1].weight   = wt1;

rect[2].r.x = x2;
rect[2].r.y = y2;
rect[2].r.width  = w2;
rect[2].r.height = h2;
rect[2].weight   = wt2;

if( !tilted )
{
for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
{
if( rect[j].weight == 0.0F )
break;
CV_SUM_OFFSETS( fastRect[j].p0, fastRect[j].p1,
fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
}
}
else
{
for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
{
if( rect[j].weight == 0.0F )
break;
CV_TILTED_OFFSETS( fastRect[j].p0, fastRect[j].p1,
fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
}
}
}
</span>
CV_SUM_OFFSET和CV_TILTED_OFFSET是计算偏移量的宏,它们将左上角点和宽高转换成在单行sum或者tilted矩阵中的位置。sum矩阵的求法和LBP中是一样的,也是利用了OpenCV自带的cv::integral函数,而斜45度的矩阵也没有用到旋转图像之类的操作,而是…嗯,还是integral函数,自带重载功能,实现了45度倾斜操作。

<span style="font-size:14px;">void CvHaarEvaluator::setImage(const Mat& img, uchar clsLabel, int idx)
{
CV_DbgAssert( !sum.empty() && !tilted.empty() && !normfactor.empty() );
CvFeatureEvaluator::setImage( img, clsLabel, idx);
Mat innSum(winSize.height + 1, winSize.width + 1, sum.type(), sum.ptr<int>((int)idx));
Mat innTilted(winSize.height + 1, winSize.width + 1, tilted.type(), tilted.ptr<int>((int)idx));
Mat innSqSum;
integral(img, innSum, innSqSum, innTilted);
normfactor.ptr<float>(0)[idx] = calcNormFactor( innSum, innSqSum );
}</span>
归一化因子计算如下:

<span style="font-size:14px;">float calcNormFactor( const Mat& sum, const Mat& sqSum )
{
CV_DbgAssert( sum.cols > 3 && sqSum.rows > 3 );
Rect normrect( 1, 1, sum.cols - 3, sum.rows - 3 );
size_t p0, p1, p2, p3;
CV_SUM_OFFSETS( p0, p1, p2, p3, normrect, sum.step1() )
double area = normrect.width * normrect.height;
const int *sp = (const int*)sum.data;
int valSum = sp[p0] - sp[p1] - sp[p2] + sp[p3];
const double *sqp = (const double *)sqSum.data;
double valSqSum = sqp[p0] - sqp[p1] - sqp[p2] + sqp[p3];
return (float) sqrt( (double) (area * valSqSum - (double)valSum * valSum) );
}</span>


SqSum是平方积分图。

最后,不同小块乘上权重系数,作为Haar特征值。

<span style="font-size:14px;">inline float CvHaarEvaluator::Feature::calc(
const cv::Mat &_sum,
const cv::Mat &_tilted,
size_t y) const
{
const int* img = tilted ?
_tilted.ptr<int>((int)y) : _sum.ptr<int>((int)y);
float ret = rect[0].weight * (
img[fastRect[0].p0]
- img[fastRect[0].p1]
- img[fastRect[0].p2]
+ img[fastRect[0].p3] ) +
rect[1].weight * (
img[fastRect[1].p0]
- img[fastRect[1].p1]
- img[fastRect[1].p2]
+ img[fastRect[1].p3] );
if( rect[2].weight != 0.0f )
ret += rect[2].weight * (
img[fastRect[2].p0]
- img[fastRect[2].p1]
- img[fastRect[2].p2]
+ img[fastRect[2].p3] );
return ret;
}</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: