1. 程式人生 > >機器學習 之 LBP特徵

機器學習 之 LBP特徵

綜述::
LBP特徵:Local Binary Pattern,區域性二值模式特徵,是一種用來描述影象區域性紋理特徵的運算元。LBP特徵運算元計算簡單、效果較好,資料量小,因此LBP特徵在計算機視覺的許多領域都得到了廣泛的應用,LBP特徵比較多用於目標檢測中。LBP計算出的特徵具有灰度不變性和旋轉不變性等顯著優點,例如對光照不敏感。

LBP的基本運算元

原始的LBP運算元定義為在3*3的視窗內,以視窗中心畫素為閾值,將相鄰的8個畫素的灰度值與其進行比較,若周圍畫素值大於中心畫素值,則該畫素 點的位置被標記為1,否則為0。這樣,3*3鄰域內的8個點經過比較可產生8個0或1,將這8個0或1作為二進位制數按照一定的次序排列形成一個二進位制數字,這個二進位制數字就是中心畫素的LBP值。LBP值共有

種可能,因此LBP值有256種。畫素之間的比較反映的是一種畫素之間的差異關係,能夠很好反映影象區域性紋理這種特徵。
這裡寫圖片描述

LBP特徵的改進

圓形LBP運算元:

所謂圓形LBP運算元:如果要計算某個畫素的點LBP特徵,以這個畫素點為中心,以一個任意大小半徑R畫一個圓,將落在圓內的畫素與中心點畫素比較得到LBP運算元。如下圖:
這裡寫圖片描述

這樣做的好處是:它能夠適應一定程式上紋理的尺度變化,並達到灰度和旋 轉不變性的要求。

LBP旋轉不變模式

首先講下為什麼LBP特徵會有旋轉可變這個情況,就是以上面3x3區域的畫素值為例:
這裡寫圖片描述
逆時針旋轉一格後得到的LBP值與原始不同。
那麼旋轉不變模式是什麼呢,就是旋轉圓形鄰域一週得到一系列初始定義的 LBP值,取其最小值作為該鄰域的 LBP 值,這樣無論怎樣旋轉,該點LBP值始終不變。如下圖所示:
這裡寫圖片描述

LBP等價模式

具體可見上面三個部落格裡的介紹,總的來說:
原來是LBP的每個位都作為該特徵的決定項,例如01100100與01101100被認為是兩個不同的特徵值,採用LBP的等價模式後,只認為0到1的跳變與1到0的跳變為一個特徵決定項,那麼這樣說的話,01100100中0~1有2個,1~0有2個,而01101100中0~1有2個,1~0也是有2個,我們認為它是同一個特徵,這樣LBP特徵的種類就大減少了。

opencv中LBP特徵實現

opencv中LBP特徵的實現原理:將一個影象視窗畫分成9個格子,統計每個格子的畫素和,然後把周圍8個畫素和與中間那個格子的畫素和比較,大於的取1,小於的取0,按順序組成一個8位的LBP特徵值。

opencv中主要是Lbpfeatures.cpp和Lbpfeatures.h兩個檔案來實現計算LBP特徵的原始碼。

Lbpfeatures.h原始碼註釋:

#ifndef _OPENCV_LBPFEATURES_H_
#define _OPENCV_LBPFEATURES_H_

#include "traincascade_features.h"

#define LBPF_NAME "lbpFeatureParams"
struct CvLBPFeatureParams : CvFeatureParams
{
    CvLBPFeatureParams();

};

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

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

        Rect rect;
        int p[16];
    };
    vector<Feature> features;

    Mat sum;
};

/*將9個視窗中周圍8個視窗的畫素和與中間視窗的畫素和比較得到LBP特徵值*/
inline uchar CvLBPEvaluator::Feature::calc(const Mat &_sum, size_t y) const
{
    const int* sum = _sum.ptr<int>((int)y);
    int cval = sum[p[5]] - sum[p[6]] - sum[p[9]] + sum[p[10]];

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

#endif

Lbpfeatures.cpp原始碼註釋:

#include "lbpfeatures.h"
#include "cascadeclassifier.h"

CvLBPFeatureParams::CvLBPFeatureParams()
{
    maxCatCount = 256;
    name = LBPF_NAME;
}

/*Lbp特徵的物件初始化*/
void CvLBPEvaluator::init(const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize)
{
    CV_Assert( _maxSampleCount > 0);
    /*分配積分圖記憶體*/
    sum.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1);
    /*呼叫父類初始化函式*/
    CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
}

/*設定影象寬高,並計算積分影象*/
void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
{
    CV_DbgAssert( !sum.empty() );
    CvFeatureEvaluator::setImage( img, clsLabel, idx );
    Mat innSum(winSize.height + 1, winSize.width + 1, sum.type(), sum.ptr<int>((int)idx));
    integral( img, innSum );
}

/*將特徵值寫到檔案中*/
void CvLBPEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
{
    _writeFeatures( features, fs, featureMap );
}

/*計算視窗內LBP特徵:將視窗內影象區域分成9個cell,
 計算中間那個cell與其他8個cell的畫素值和的大小關係,以實現LBP特徵計算*/
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();
}

/*初始化特徵的視窗座標:Feature類建構函式*/
CvLBPEvaluator::Feature::Feature()
{
    rect = cvRect(0, 0, 0, 0);
}

/*給特徵視窗點賦座標值:Feature類建構函式*/
CvLBPEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
{
    Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight);
    CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset )
    tr.x += 2*rect.width;
    CV_SUM_OFFSETS( p[2], p[3], p[6], p[7], tr, offset )
    tr.y +=2*rect.height;
    CV_SUM_OFFSETS( p[10], p[11], p[14], p[15], tr, offset )
    tr.x -= 2*rect.width;
    CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset )
}

/*將特徵座標寫到檔案*/
void CvLBPEvaluator::Feature::write(FileStorage &fs) const
{
    fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]";
}