train_cascade 源码阅读之Haar特征

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>









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值