LBP特征提取原理及代碼實現


 

1 背景

      LBP(Local Binary Pattern,局部二值模式)是一種用來描述圖像局部紋理特征的算子,具有旋轉不變形和灰度值不變形等顯著優點。主要用於紋理特征提取,在人臉識別部分有較好的效果。

2     LBP特征原理

2.1概述

    從94年T. Ojala, M.Pietikäinen, 和D. Harwood提出至今,LBP大致經歷了三個版本。下面按照時間順序進行介紹。(下面的First-LBP、Second-LBP..均為自己定義,只是為寫技術文檔方便,LBP作者並未定義。)

2.2First-LBP原理

    最初的LBP算子通過定義一個3x3的窗口,以窗口內中心點的像素值為標准,對比窗口內另8個點像素值的大小,大於為1,小於為0。8個點形成一個二進制數字(通常轉換為十進制表示)即為中心點的LBP特征值。詳細計算如下圖:

                

 

  

          通過上面得到的LBP算子具有很多缺點,后研究人員在LBP基礎上進行不斷改進。

2.3 Second-LBP原理

           原始LBP算子計算區域為像素點的周圍8個點,在圖像尺寸發生改變時會出現很大的偏差,不能正確反映像素點周圍的紋理信息。為適應不同尺寸紋理特征,LBP原作者將圓形鄰域代替正方形鄰域。同時增加了旋轉不變的特性,在對LBP特征值的存儲部分,也進行了改進。詳細如下文。

2.3.1圓形LBP算子

          圓形LBP算子計算中,以像素點為圓心,R為半徑,提取半徑上P個采樣點,根據2.2中像素值比較方法,進行像素值大小的比較,得到該點的LBP特征值。其中提取采樣點的方法如下:

                                 xt = xd + Rcos(2πp/P)

                    yt = yd  - Rsin(2πp/P)

  (xt,yt)為某個采樣點,(xd,yd)為鄰域中心點,p為第P個采樣點,P為采樣點的個數。得到采樣點的坐標可能為小數,改進后的LBP采用雙線性插值法進行計算該點的像素值:

     

 

 

            幾種不同半徑不同采樣點數量的LBP算子:

 

 

 

2.3.2旋轉不變LBP特征:

 

       上面通過采取圓形鄰域的計算,一定程度上削弱了尺度改變的影響。研究人員在上面的基礎上進一步擴展,使具備旋轉不變的特征。

 

      首先,在確定半徑大小和采樣點數目后,不斷旋轉圓形鄰域內采樣點的位置,得到一系列的LBP特征值,從這些LBP特征值中選擇最小的值作為LBP中心像素點的LBP特征值,具體如下圖:

          

 

 

      通過不斷旋轉,取最小值,使具備旋轉不變特性。

2.3.3 Uniform Pattern LBP特征:

         Uniform Pattern LBP特征也稱為等價模式或均勻模式。對LBP特征值的存儲方式上,進行了優化。詳細如下。

          假設對於半徑為R的圓形鄰域內提取P個采樣點,會產生2種二進制表達方法,隨着鄰域內采樣點數目的增加,二進制模式的種類以指數形式增加,不利於LBP特征值的存儲、提取、分類和識別。LBP原作者提出一種“等價模式”對LBP算子進行降維。詳細如下。

          在實際圖像中,絕大多數LBP模式只包括從0到1或從0到1的轉變,LBP原作者將“等價模式”定義為當某個LBP特征值所對應的二進制數從0到1或從1到0的轉變最多有兩次時,該LBP所對應的二進制就稱為一個等價模式。如00000000(0次跳變)、00000011(1次跳變)、10001111

(2次跳變)均為等價模式類。除等價模式類外均歸為混合模式類。上述算法,使得模式數量由原來的2p種減少為P(P-1)+2+1種(P代表采樣點的數量)。

     實例介紹:

          如采樣點數為8,即256種LBP特征值,根據等價模式可分為59類:跳變0次——2個,跳變1次——0個,跳變2次——56個,跳變3….8次——1個。(跳變1次為0個是因為LBP作者把LBP二進制數字看做一個圓性的序列,故跳變1次為0個)

2.3.4 MB-LBP特征:

       MB-LBP特征,全稱為Multiscale Block LBP,由中科院的研究人員研究發表,原理與HOG特征提取有相似之處,接掃MB-LBP僅用於了解,下面是原理介紹。

       首先將圖像分為分為多個塊,再將每個小塊分成多個區域,每個區域的灰度值為該區域內灰度值的平均值。在一個塊內,將中心區域的灰度值大小與周圍區域的灰度值大小進行比較形成LBP特征值。如下圖:

          

 

 

          作者對得到的MB-LBP特征值同樣進行均值編碼。首先,對得到的特征值采用直方圖進行表示,計算每一種特征值的數量,進行排序,將排序在前63為的特征值看作是等價模式類,其他的為混合模式類,共64類。

 

2.4         Third-LBP原理

LBP的最后一步改進為LBPH即LBP特征統計直方圖的使用,可用於機器學習特征的提取。這種表示方法由Ahonen等人提出,將LBP特征圖像分成m個局部塊,提取每個局部塊的直方圖,並依次連接在一起形成LBP特征的統計直方圖。具體過程如下:

(1)         計算圖像中每一像素點的LBP特征值。

(2)         圖像進行分成多塊。(Opencv中默認將LBP特征

圖像分為8行8列64塊區域。

(3)      計算每塊區域的LBP特征值的直方圖,並將直方圖進行歸一化。(橫坐標為LBP特征值的表示方式,縱坐標為數量)

(4)   將上面計算的每塊區域特征圖像的直方圖按順序依次排列成一行,形成LBP特征向量。

(5)   用機器學習方法對LBP特征向量進行訓練。

舉例說明LBPH的維度:
    采樣點為8個,如果用的是原始的LBP或Extended LBP特征,其LBP特征值的模式為256種,則一幅圖像的LBP特征向量維度為:64*256=16384維, 而如果使用的UniformPatternLBP特征,其LBP值的模式為59種,其特征向量維度為:64*59=3776維,可以看出,使用等價模式特征,其特征向量的維度大大減少, 這意味着使用機器學習方法進行學習的時間將大大減少,而性能上沒有受到很大影響。 

代碼環節:

  目前,Opencv內,暫無LBP簡便的接口函數,如若使用,可觀看下面源代碼或使用下面代碼。

  LBP在人臉識別部分的應用:

 
         
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include"opencv2/face.hpp"

using namespace std;
using namespace cv;

int main(int argc,char* argv[])
{
vector<Mat> images;
vector<int> labels;
char tdt[10];
for(int i=1;i<8;i++)
{
sprintf(tdt,"0%d.jpg",i);
Mat image = imread(tdt);
Mat grayImage;
cvtColor(image,grayImage,COLOR_BGR2GRAY);
images.push_back(grayImage);
labels.push_back(1);
}
for(int i=8;i<12;i++)
{
sprintf(tdt,"0%d.jpg",i);
Mat image = imread(tdt);
Mat grayImage;
cvtColor(image,grayImage,COLOR_BGR2GRAY);
images.push_back(grayImage);
labels.push_back(2);
}
Ptr<cv::face::FaceRecognizer> p ;
p->train(images,labels);
Mat test= imread("12.jpg");
Mat grayImage;
cvtColor(test,grayImage,COLOR_BGR2GRAY);
int result = p->predict(grayImage);
cout<<result<<endl;
system("pause");
return 0;
}
 

 

 

 

  原始LBP特征計算代碼:

template <typename _tp>
void getOriginLBPFeature(InputArray _src,OutputArray _dst)
{
    Mat src = _src.getMat();
    _dst.create(src.rows-2,src.cols-2,CV_8UC1);
    Mat dst = _dst.getMat();
    dst.setTo(0);
    for(int i=1;i<src.rows-1;i++)
    {
        for(int j=1;j<src.cols-1;j++)
        {
            _tp center = src.at<_tp>(i,j);
            unsigned char lbpCode = 0;
            lbpCode |= (src.at<_tp>(i-1,j-1) > center) << 7;
            lbpCode |= (src.at<_tp>(i-1,j  ) > center) << 6;
            lbpCode |= (src.at<_tp>(i-1,j+1) > center) << 5;
            lbpCode |= (src.at<_tp>(i  ,j+1) > center) << 4;
            lbpCode |= (src.at<_tp>(i+1,j+1) > center) << 3;
            lbpCode |= (src.at<_tp>(i+1,j  ) > center) << 2;
            lbpCode |= (src.at<_tp>(i+1,j-1) > center) << 1;
            lbpCode |= (src.at<_tp>(i  ,j-1) > center) << 0;
            dst.at<uchar>(i-1,j-1) = lbpCode;
        }
    }
}

    圓形LBP特征計算:

template <typename _tp>
void getCircularLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors)
{
    Mat src = _src.getMat();
    //LBP特征圖像的行數和列數的計算要准確
    _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);
    Mat dst = _dst.getMat();
    dst.setTo(0);
    //循環處理每個像素
    for(int i=radius;i<src.rows-radius;i++)
    {
        for(int j=radius;j<src.cols-radius;j++)
        {
            //獲得中心像素點的灰度值
            _tp center = src.at<_tp>(i,j);
            unsigned char lbpCode = 0;
            for(int k=0;k<neighbors;k++)
            {
                //根據公式計算第k個采樣點的坐標,這個地方可以優化,不必每次都進行計算radius*cos,radius*sin
                float x = i + static_cast<float>(radius *                     cos(2.0 * CV_PI * k / neighbors));
                float y = j - static_cast<float>(radius *                     sin(2.0 * CV_PI * k / neighbors));
                //根據取整結果進行雙線性插值,得到第k個采樣點的灰度值

                //1.分別對x,y進行上下取整
                int x1 = static_cast<int>(floor(x));
                int x2 = static_cast<int>(ceil(x));
                int y1 = static_cast<int>(floor(y));
                int y2 = static_cast<int>(ceil(y));

                //2.計算四個點(x1,y1),(x1,y2),(x2,y1),(x2,y2)的權重
                //下面的權重計算方式有個問題,如果四個點都相等,則權重全為0,計算出來的插值為0
                //float w1 = (x2-x)*(y2-y); //(x1,y1)
                //float w2 = (x2-x)*(y-y1); //(x1,y2)
                //float w3 = (x-x1)*(y2-y); //(x2,y1)
                //float w4 = (x-x1)*(y-y1); //(x2,y2)

                //將坐標映射到0-1之間
                float tx = x - x1;
                float ty = y - y1;
                //根據0-1之間的x,y的權重計算公式計算權重
                float w1 = (1-tx) * (1-ty);
                float w2 =    tx  * (1-ty);
                float w3 = (1-tx) *    ty;
                float w4 =    tx  *    ty;
                //3.根據雙線性插值公式計算第k個采樣點的灰度值
                float neighbor = src.at<_tp>(x1,y1) * w1 + src.at<_tp>(x1,y2) *w2                     + src.at<_tp>(x2,y1) * w3 +src.at<_tp>(x2,y2) *w4;
                //通過比較獲得LBP值,並按順序排列起來
                lbpCode |= (neighbor>center) <<(neighbors-k-1);
            }
            dst.at<uchar>(i-radius,j-radius) = lbpCode;
        }
    }
}
//圓形LBP特征計算,效率優化版本,聲明時默認neighbors=8
template <typename _tp>
void getCircularLBPFeatureOptimization(InputArray _src,OutputArray _dst,int radius,int neighbors)
{
    Mat src = _src.getMat();
    //LBP特征圖像的行數和列數的計算要准確
    _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);
    Mat dst = _dst.getMat();
    dst.setTo(0);
    for(int k=0;k<neighbors;k++)
    {
        //計算采樣點對於中心點坐標的偏移量rx,ry
        float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
        float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));
        //為雙線性插值做准備
        //對采樣點偏移量分別進行上下取整
        int x1 = static_cast<int>(floor(rx));
        int x2 = static_cast<int>(ceil(rx));
        int y1 = static_cast<int>(floor(ry));
        int y2 = static_cast<int>(ceil(ry));
        //將坐標偏移量映射到0-1之間
        float tx = rx - x1;
        float ty = ry - y1;
        //根據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(int i=radius;i<src.rows-radius;i++)
        {
            for(int j=radius;j<src.cols-radius;j++)
            {
                //獲得中心像素點的灰度值
                _tp center = src.at<_tp>(i,j);
                //根據雙線性插值公式計算第k個采樣點的灰度值
                float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2                     + src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;
                //LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);
            }
        }
    }
}

    旋轉不變圓形LBP特征計算:

template <typename _tp>
void getRotationInvariantLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors)
{
    Mat src = _src.getMat();
    //LBP特征圖像的行數和列數的計算要准確
    _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);
    Mat dst = _dst.getMat();
    dst.setTo(0);
    for(int k=0;k<neighbors;k++)
    {
        //計算采樣點對於中心點坐標的偏移量rx,ry
        float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
        float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));
        //為雙線性插值做准備
        //對采樣點偏移量分別進行上下取整
        int x1 = static_cast<int>(floor(rx));
        int x2 = static_cast<int>(ceil(rx));
        int y1 = static_cast<int>(floor(ry));
        int y2 = static_cast<int>(ceil(ry));
        //將坐標偏移量映射到0-1之間
        float tx = rx - x1;
        float ty = ry - y1;
        //根據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(int i=radius;i<src.rows-radius;i++)
        {
            for(int j=radius;j<src.cols-radius;j++)
            {
                //獲得中心像素點的灰度值
                _tp center = src.at<_tp>(i,j);
                //根據雙線性插值公式計算第k個采樣點的灰度值
                float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2                     + src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;
                //LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);
            }
        }
    }
    //進行旋轉不變處理
    for(int i=0;i<dst.rows;i++)
    {
        for(int j=0;j<dst.cols;j++)
        {
            unsigned char currentValue = dst.at<uchar>(i,j);
            unsigned char minValue = currentValue;
            for(int k=1;k<neighbors;k++)
            {
    //循環左移
                unsigned char temp = (currentValue>>(neighbors-k)) | (currentValue<<k);
                if(temp < minValue)
                {
                    minValue = temp;
                }
            }
            dst.at<uchar>(i,j) = minValue;
        }
    }
}

    均勻(等價)模式:

template <typename _tp>
void getUniformPatternLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors)
{
    Mat src = _src.getMat();
    //LBP特征圖像的行數和列數的計算要准確
    _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);
    Mat dst = _dst.getMat();
    dst.setTo(0);
    //LBP特征值對應圖像灰度編碼表,直接默認采樣點為8位
    uchar temp = 1;
    uchar table[256] = {0};
    for(int i=0;i<256;i++)
    {
        if(getHopTimes(i)<3)
        {
            table[i] = temp;
            temp++;
        }
    }
    //是否進行UniformPattern編碼的標志
    bool flag = false;
    //計算LBP特征圖
    for(int k=0;k<neighbors;k++)
    {
        if(k==neighbors-1)
        {
            flag = true;
        }
        //計算采樣點對於中心點坐標的偏移量rx,ry
        float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));
        float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));
        //為雙線性插值做准備
        //對采樣點偏移量分別進行上下取整
        int x1 = static_cast<int>(floor(rx));
        int x2 = static_cast<int>(ceil(rx));
        int y1 = static_cast<int>(floor(ry));
        int y2 = static_cast<int>(ceil(ry));
        //將坐標偏移量映射到0-1之間
        float tx = rx - x1;
        float ty = ry - y1;
        //根據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(int i=radius;i<src.rows-radius;i++)
        {
            for(int j=radius;j<src.cols-radius;j++)
            {
                //獲得中心像素點的灰度值
                _tp center = src.at<_tp>(i,j);
                //根據雙線性插值公式計算第k個采樣點的灰度值
                float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2                     + src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;
                //LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得
                dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);
                //進行LBP特征的UniformPattern編碼
                if(flag)
                {
                    dst.at<uchar>(i-radius,j-radius) = table[dst.at<uchar>(i-radius,j-radius)];
                }
            }
        }
    }
}
//計算跳變次數
int getHopTimes(int n)
{
    int count = 0;
    bitset<8> binaryCode = n;
    for(int i=0;i<8;i++)
    {
        if(binaryCode[i] != binaryCode[(i+1)%8])
        {
            count++;
        }
    }
    return count;
}

 MB-LBP代碼:

//MB-LBP特征的計算
void getMultiScaleBlockLBPFeature(InputArray _src,OutputArray _dst,int scale)
{
    Mat src = _src.getMat();
    Mat dst = _dst.getMat();
    //定義並計算積分圖像
    int cellSize = scale / 3;
    int offset = cellSize / 2;
    Mat cellImage(src.rows-2*offset,src.cols-2*offset,CV_8UC1);
    for(int i=offset;i<src.rows-offset;i++)
    {
        for(int j=offset;j<src.cols-offset;j++)
        {
            int temp = 0;
            for(int m=-offset;m<offset+1;m++)
            {
                for(int n=-offset;n<offset+1;n++)
                {
                    temp += src.at<uchar>(i+n,j+m);
                }
            }
            temp /= (cellSize*cellSize);
            cellImage.at<uchar>(i-cellSize/2,j-cellSize/2) = uchar(temp); 
        }
    }
    getOriginLBPFeature<uchar>(cellImage,dst);
}

MB-LBP編碼的計算:

//求SEMB-LBP
void SEMB_LBPFeature(InputArray _src,OutputArray _dst,int scale)
{
    Mat dst=_dst.getMat();
    Mat MB_LBPImage;
    getMultiScaleBlockLBPFeature(_src,MB_LBPImage,scale);
    //imshow("dst",dst);
    Mat histMat;
    int histSize = 256;
    float range[] = {float(0),float(255)};
    const float* ranges = {range};
    //計算LBP特征值0-255的直方圖
    calcHist(&MB_LBPImage,1,0,Mat(),histMat,1,&histSize,&ranges,true,false);
    histMat.reshape(1,1);
    vector<float> histVector(histMat.rows*histMat.cols);
    uchar table[256];
    memset(table,64,256);
    if(histMat.isContinuous())
    {
        //histVector = (int *)(histMat.data);
        //將直方圖histMat變為vector向量histVector
        histVector.assign((float*)histMat.datastart,(float*)histMat.dataend);
        vector<float> histVectorCopy(histVector);
        //對histVector進行排序,即對LBP特征值的數量進行排序,降序排列
        sort(histVector.begin(),histVector.end(),greater<float>());
        for(int i=0;i<63;i++)
        {
            for(int j=0;j<histVectorCopy.size();j++)
            {
                if(histVectorCopy[j]==histVector[i])
                {
                    //得到類似於Uniform的編碼表
                    table[j]=i;
                }
            }
        }
    }
    dst = MB_LBPImage;
    //根據編碼表得到SEMB-LBP
    for(int i=0;i<dst.rows;i++)
    {
        for(int j=0;j<dst.cols;j++)
        {
            dst.at<uchar>(i,j) = table[dst.at<uchar>(i,j)];
        }
    }
}

LBPH特征圖像的計算:

//計算LBP特征圖像的直方圖LBPH
Mat getLBPH(InputArray _src,int numPatterns,int grid_x,int grid_y,bool normed)
{
    Mat src = _src.getMat();
    int width = src.cols / grid_x;
    int height = src.rows / grid_y;
    //定義LBPH的行和列,grid_x*grid_y表示將圖像分割成這么些塊,numPatterns表示LBP值的模式種類
    Mat result = Mat::zeros(grid_x * grid_y,numPatterns,CV_32FC1);
    if(src.empty())
    {
        return result.reshape(1,1);
    }
    int resultRowIndex = 0;
    //對圖像進行分割,分割成grid_x*grid_y塊,grid_x,grid_y默認為8
    for(int i=0;i<grid_x;i++)
    {
        for(int j=0;j<grid_y;j++)
        {
            //圖像分塊
            Mat src_cell = Mat(src,Range(i*height,(i+1)*height),Range(j*width,(j+1)*width));
            //計算直方圖
            Mat hist_cell = getLocalRegionLBPH(src_cell,0,(numPattern-1),true);
            //將直方圖放到result中
            Mat rowResult = result.row(resultRowIndex);
            hist_cell.reshape(1,1).convertTo(rowResult,CV_32FC1);
            resultRowIndex++;
        }
    }
    return result.reshape(1,1);
}
//計算一個LBP特征圖像塊的直方圖
Mat getLocalRegionLBPH(const Mat& src,int minValue,int maxValue,bool normed)
{
    //定義存儲直方圖的矩陣
    Mat result;
    //計算得到直方圖bin的數目,直方圖數組的大小
    int histSize = maxValue - minValue + 1;
    //定義直方圖每一維的bin的變化范圍
    float range[] = { static_cast<float>(minValue),static_cast<float>(maxValue + 1) };
    //定義直方圖所有bin的變化范圍
    const float* ranges = { range };
    //計算直方圖,src是要計算直方圖的圖像,1是要計算直方圖的圖像數目,0是計算直方圖所用的圖像的通道序號,從0索引
    //Mat()是要用的掩模,result為輸出的直方圖,1為輸出的直方圖的維度,histSize直方圖在每一維的變化范圍
    //ranges,所有直方圖的變化范圍(起點和終點)
    calcHist(&src,1,0,Mat(),result,1,&histSize,&ranges,true,false);
    //歸一化
    if(normed)
    {
        result /= (int)src.total();
    }
    //結果表示成只有1行的矩陣
    return result.reshape(1,1);
}

 

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM