c++圖像處理(二十)局部二進制模式 LBP特徵提取

LBP特徵提取

原理
(一)LBP特徵的描述
  原始的LBP算子定義爲在33的窗口內,以窗口中心像素爲閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,否則爲0。這樣,33鄰域內的8個點經比較可產生8位二進制數(通常轉換爲十進制數即LBP碼,共256種),即得到該窗口中心像素點的LBP值,並用這個值來反映該區域的紋理信息。如下圖所示:
在這裏插入圖片描述
(二)LBP的改進版本:
  原始的LBP提出後,研究人員不斷對其提出了各種改進和優化。

(1)圓形LBP算子:
  基本的 LBP 算子的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,這顯然不能滿足不同尺寸和頻率紋理的需要。爲了適應不同尺度的紋理特徵,並達到灰度和旋轉不變性的要求,Ojala 等對 LBP 算子進行了改進,將 3×3 鄰域擴展到任意鄰域,並用圓形鄰域代替了正方形鄰域,改進後的 LBP 算子允許在半徑爲 R 的圓形鄰域內有任意多個像素點。從而得到了諸如半徑爲R的圓形區域內含有P個採樣點的LBP算子;
  在這裏插入圖片描述
(2)LBP旋轉不變模式
  從 LBP 的定義可以看出,LBP 算子是灰度不變的,但卻不是旋轉不變的。圖像的旋轉就會得到不同的 LBP值。
  Maenpaa等人又將 LBP 算子進行了擴展,提出了具有旋轉不變性的 LBP 算子,即不斷旋轉圓形鄰域得到一系列初始定義的 LBP 值,取其最小值作爲該鄰域的 LBP 值。
  圖 2.5 給出了求取旋轉不變的 LBP 的過程示意圖,圖中算子下方的數字表示該算子對應的 LBP 值,圖中所示的 8 種 LBP模式,經過旋轉不變的處理,最終得到的具有旋轉不變性的 LBP 值爲 15。也就是說,圖中的 8 種 LBP 模式對應的旋轉不變的 LBP 模式都是00001111。
  在這裏插入圖片描述
(3)LBP等價模式
  一個LBP算子可以產生不同的二進制模式,對於半徑爲R的圓形區域內含有P個採樣點的LBP算子將會產生2P種模式。很顯然,隨着鄰域集內採樣點數的增加,二進制模式的種類是急劇增加的。例如:5×5鄰域內20個採樣點,有220=1,048,576種二進制模式。如此多的二值模式無論對於紋理的提取還是對於紋理的識別、分類及信息的存取都是不利的。同時,過多的模式種類對於紋理的表達是不利的。例如,將LBP算子用於紋理分類或人臉識別時,常採用LBP模式的統計直方圖來表達圖像的信息,而較多的模式種類將使得數據量過大,且直方圖過於稀疏。因此,需要對原始的LBP模式進行降維,使得數據量減少的情況下能最好的代表圖像的信息。
  爲了解決二進制模式過多的問題,提高統計性,Ojala提出了採用一種“等價模式”(Uniform Pattern)來對LBP算子的模式種類進行降維。Ojala等認爲,在實際圖像中,絕大多數LBP模式最多隻包含兩次從1到0或從0到1的跳變。因此,Ojala將“等價模式”定義爲:當某個LBP所對應的循環二進制數從0到1或從1到0最多有兩次跳變時,該LBP所對應的二進制就稱爲一個等價模式類。如00000000(0次跳變),00000111(只含一次從0到1的跳變),10001111(先由1跳到0,再由0跳到1,共兩次跳變)都是等價模式類。除等價模式類以外的模式都歸爲另一類,稱爲混合模式類,例如10010111(共四次跳變)(這是我的個人理解,不知道對不對)。
  通過這樣的改進,二進制模式的種類大大減少,而不會丟失任何信息。模式數量由原來的2P種減少爲 P ( P-1)+2種,其中P表示鄰域集內的採樣點數。對於3×3鄰域內8個採樣點來說,二進制模式由原始的256種減少爲58種,這使得特徵向量的維數更少,並且可以減少高頻噪聲帶來的影響。

代碼

原始的LBP算子

//原始的LBP算子
void Original_LBP(const QImage &image,QImage &otp)
{
    int width = image.width();
    int height = image.height();
    QImage* deal_image = new QImage((width-2),(height-2),QImage::Format_ARGB32);
    Init_Image(*deal_image,0);
    int  i = 0,j = 0;

    for(i = 1; i<height-1; i++)
    {
        for(j = 1; j<width-1; j++)
        {
            uchar center = (uchar)qGray(image.pixel(j,i));
            uchar code = 0;

            uchar f1 = (uchar)qGray(image.pixel(j - 1, i - 1));

            uchar f2 = (uchar)qGray(image.pixel(j - 1, i));

            uchar f3 = (uchar)qGray(image.pixel(j - 1, i + 1));

            uchar f4 = (uchar)qGray(image.pixel(j, i + 1));

            uchar f5 = (uchar)qGray(image.pixel(j+ 1, i + 1));

            uchar f6 = (uchar)qGray(image.pixel(j + 1, i));

            uchar f7 = (uchar)qGray(image.pixel(j + 1, i - 1));

            uchar f8 = (uchar)qGray(image.pixel(j, i - 1));



            code |= (f1 > center) << 7;

            code |= (f2 > center) << 6;

            code |= (f3 > center) << 5;

            code |= (f4 > center) << 4;

            code |= (f5 > center) << 3;

            code |= (f6 > center) << 2;

            code |= (f7 > center) << 1;

            code |= (f8 > center) << 0;


            deal_image->setPixel(j-1,i-1,qRgb(code,code,code));

        }
    }
    otp = *deal_image;

}

通常的理解的效率低LBP:

void usual_LBP(QImage &image,QImage &otp,int R,int P)
{
    int width = image.width();
    int height = image.height();
    QImage* out = new QImage(width-2*R,height-2*R,QImage::Format_ARGB32);
    Init_Image(*out,0);
    int i = 0, j = 0;
    double x=0,y=0;
    double All_p = (double)P;
    int center = 0;
    uchar lbp = 0;

    //計算領域值
    QVector<double> neighbors_x;
    QVector<double> neighbors_y;

    //計算LBP領域
    for(i = 0; i<P; i++)
    {
        x = R*cos(2.0*PI*i/All_p);
        y = -R*sin(2.0*PI*i/All_p);
        neighbors_x.push_back(x);
        neighbors_y.push_back(y);
    }

    //循環每個像素
    for(i = R; i<height-R-1; i++)
    {
        for(j = R; j<width-R-1; j++)
        {
            //中點像素值(目標)
            center = (uchar)qGray(image.pixel(j,i));

            lbp = 0;
            //LBP領域與中點的判斷
            for(int k = 0; k<neighbors_x.size(); k++)
            {
                //P個點的座標
                y = i + neighbors_y[k];
                x = j + neighbors_x[k];
                //調用雙線性插值
                uchar gray = (uchar)InterpBilinear(image,x,y);
                //按順序累加
                lbp |= (gray>center) <<k;
            }
            out->setPixel(j-R,i-R,qRgb(lbp,lbp,lbp));
        }
    }

    otp = *out;
}

效率高的LBP:

void LBP(QImage &image, QImage &otp, int R, int P)
{
    int width = image.width();
    int height = image.height();
    QImage* out = new QImage(width-2*R,height-2*R,QImage::Format_ARGB32);
    Init_Image(*out,0);
    int i = 0, j = 0;
    float x=0,y=0;
    float All_p = (float)P;
    int center = 0;
    int lbp = 0;

    for(int k = 0; k<P;k++)
    {
        //圓的參數方程
        x = R*cos(2.0*PI*k/All_p);
        y = (-R)*sin(2.0*PI*k/All_p);

        //對採樣點偏移量分別進行上下取整
        int fx = (int)x;
        int cx = fx+1;
        int fy = (int)y;
        int cy = fy + 1;
        //將座標偏移量映射到0-1之間
        float ty = y - fy;
        float tx = x - fx;
        //根據0-1之間的x,y的權重計算公式計算權重,權重與座標具體位置無關,與座標間的差值有關
        float w1 = (1-tx) * (1-ty);
        float w2 = tx * (1-ty);
        float w3 = (1-tx) * ty;
        float w4 = tx  * ty;

        //循環處理每個像素
        for(i = R; i<height-R-1;i++)
        {
            for(j = R; j<width-R-1;j++)
            {
                //獲得中心像素點的灰度值
                lbp = qGray(out->pixel(j-R,i-R));
                center = qGray(image.pixel(j,i));
                float f1 = qGray(image.pixel(j+fx,i+fy));
                float f2 = qGray(image.pixel(j+fx,i+cy));
                float f3 = qGray(image.pixel(j+cx,i+fy));
                float f4 = qGray(image.pixel(j+cx,i+cy));

                //根據雙線性插值公式計算第k個採樣點的灰度值
                int  neighbor = (f1 * w1) + (f3 * w2) + (f2 * w3) + (f4 * w4);
                //LBP特徵圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                lbp |= (neighbor>center) << k;
                //cout<<lbp<<endl;
                out->setPixel(j-R,i-R,qRgb(lbp,lbp,lbp));
            }
        }
    }

    otp = *out;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章