图像特征提取之LBP特征
原文站點:https://senitco.github.io/2017/06/12/image-feature-lbp/
??局部二值模式(Local Binary Patter, LBP)是一種用來描述圖像局部紋理特征的算子,LBP特征具有灰度不變性和旋轉不變性等顯著優點,它將圖像中的各個像素與其鄰域像素值進行比較,將結果保存為二進制數,并將得到的二進制比特串作為中心像素的編碼值,也就是LBP特征值。LBP提供了一種衡量像素間鄰域關系的特征模式,因此可以有效地提取圖像的局部特征,而且由于其計算簡單,可用于基于紋理分類的實時應用場景,例如目標檢測、人臉識別等。
原始LBP特征
??原始的LBP算子定義于圖像中3×3的鄰域窗口,取窗口內中心像素的灰度值作為閾值,將8鄰域像素的灰度值與其進行比較,若鄰域像素值大于中心像素值,則比較結果取值為1,否則為0。這樣鄰域內的8個像素點經過比較后可得到8位二進制數,將其按順序依次排列即可得到中心像素的LBP值。LBP特征值反映了中心像素和其鄰域的紋理信息。LBP的取值一共有28=256種,和一幅普通的灰度圖像類似,因此可將LBP特征以灰度圖的形式表達出來。由于LBP特征考慮的是紋理信息,而不包含顏色信息,因此彩色圖需轉換為灰度圖。原始LBP特征的提取過程如下圖所示:
公式定義如下:
其中 (xc,yc)代表鄰域窗口內的中心像素,其像素值為 ic, ip為鄰域內其他像素值,s(x)是符號函數。
原始LBP特征的實現代碼(OpenCV)如下: 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特征(Circular LBP or Extended LBP)
??原始LBP特征考慮的是固定半徑范圍內的鄰域像素,不能滿足不同尺寸和頻率紋理的需求,當圖像的尺寸發生變化時,LBP特征將不能正確編碼局部鄰域的紋理信息。為了適應不同尺寸的紋理特征,Ojala等人對LBP算子
進行了改進,將3×3鄰域窗口擴展到任意鄰域,并用圓形鄰域代替了正方形鄰域,改進后的LBP算子允許在半徑為R的鄰域內有任意多個像素點,從而得到在半徑為R的區域內含有P個采樣點的LBP算子。
采樣點的坐標可通過以下公式計算:
yp=yc+Rsin(2πp/P)
其中 (xc,yc)為中心像素點, (xp,yp),p∈P為鄰域內某個采樣點,通過上次可以計算任意個采樣點的坐標,但是得到的坐標值未必為整數,因此可通過雙線性插值的方法來得到該采樣點的像素值:
f(x,y)=[1?xx][f(0,0)?f(1,0)f(0,1)f(1,1)][1?y?y]
圓形LBP特征的實現代碼如下:
//圓形LBP特征計算,這種方法適于理解,但在效率上存在問題,聲明時默認neighbors=8 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*sinfloat 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特征的效率優化版本:
//圓形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,ryfloat 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特征(Rotation Invariant LBP)
??無論是原始LBP算子還是圓形LBP算子,都只是灰度不變的,而不是旋轉不變的,旋轉圖像會得到不同的LBP特征值。相關研究人員又提出了一種具有旋轉不變性的LBP算子,即不斷旋轉圓形鄰域的采樣點,或者以不同的鄰域像素作為起始點,順時針遍歷所有采樣點,得到一系列編碼值(P個),取其中最小的作為該鄰域中心像素的LBP值。旋轉不變LBP算子的示意圖如下:
旋轉不變LBP特征的實現代碼如下:
//旋轉不變圓形LBP特征計算,聲明時默認neighbors=8 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,ryfloat 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;}} }LBP等價模式(Uniform LBP)
??對于一個半徑為R的圓形區域,包含有P個鄰域采樣點,則LBP算子可能產生2P種模式。隨著鄰域內采樣點數的增加,LBP值的取值數量呈指數級增長。例如5×5鄰域內20個采樣點,則對應有220中模式,過多的二進制模式不利于紋理信息的提取、分類、識別。例如,將LBP特征用于紋理分類或人臉識別時,一般采用LBP特征的統計直方圖來表達圖像的信息,而較多的模式種類將使得數據量過大,且直方圖過于稀疏。因此,需要對原始的LBP特征進行降維,使得數據量減少的情況下能最好地表達圖像的信息。
??為了解決二進制模式過多的問題,提高統計性,Ojala提出了一種“等價模式”(Uniform Pattern)來對LBP特征的模式種類進行降維。Ojala認為,在實際圖像中,絕大數LBP模式最多只包含兩次從0到1或者從1到0的跳變,“等價模式”定義為:當某個LBP所對應的循環二進制數從0到1或者從1到0最多有兩次跳變時,該LBP所對應的二進制就是一個等價模式類。如00000000(0次跳變),11000011(2次跳變)都是等價模式類。除等價模式類以外的模式都歸為另一類,稱為混合模式類,例如10010111(共4次跳變)。通過改進,二進制模式的種類大大減少,由原來的2P中降為P(P?1)+2+1種,其中P(P?1)為2次跳變的模式數,2為0次跳變(全”0”或全”1”)的模式數,1為混合模式的數量,由于是循環二進制數,因此’0’、’1’跳變次數不可能為奇數次。對于3×3鄰域內8個采樣點來說,二進制模式由原始的256種變為59種。這使得特征向量的維數大大減少,并且可以減少高頻噪聲帶來的影響。實驗表明,一般情況下,等價模式的數目占全部模式的90%以上,可以有效對數據進行降維。下圖為58種等價模式類:
在具體實現中,等價模式類按值遞增從1開始編碼,混合模式類編碼為0,因此得到的LBP特征圖整體偏暗。LBP等價模式的實現代碼如下:
//等價模式LBP特征計算 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,ryfloat 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; }此外,旋轉不變的Uniform LBP算子的等價模式類的數目為P+1個,對于8個采樣點,基于等價模式的旋轉不變LBP模式只有9個輸出,該模式對于上圖的Uniform LBP,每一行都是旋轉不變的,對應同一個編碼值。
多尺度LBP(Multiscale Block LBP)
??基本LBP算子獲取的是單個像素和其鄰域像素間的紋理信息,屬于微觀特征。中科院的研究人員針對此提出了一種多尺度的LBP算子,將圖像分為一個個塊(block),再將每個塊分為一個個的小連通區域(cell),類似于HOG特征,cell內的灰度平均值或者和值作為當前cell的灰度閾值,與鄰域cell進行比較得到LBP值,生成的特征即為MB-LBP,block大小為3×3,cell大小為1,就是原始的LBP特征。下圖所示block為9×9,cell為3×3。
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); }多尺度模式下同樣用到了降維,論文中是直接采樣統計的方法對不同尺度的LBP算子的模式進行統計,選取占比例較高的模式,而不是利用跳變規則。具體來說,就是將得到的MB-LBP特征計算統計直方圖,通過對bin中的數值進行排序以及權衡,將排序在前N(63)位的特征值看作是等價模式類,其余的為混合模式類,總共為N+1類,論文中稱之為(SEMB-LBP, Statistically Effective MB-LBP)。
SEMB-LBP的實現代碼如下:
圖像的LBP特征向量(Local Binary Patterns Histograms)
??對圖像中的每個像素求取LBP特征值可得到圖像的LBP特征圖譜,但一般不直接將LBP圖譜作為特征向量用于分類識別,而是類似于HOG特征,采用LBP特征的統計直方圖作為特征向量。將LBP特征圖譜劃分為若干個子連通區域,并提取每個局部塊的直方圖,然后將這些直方圖一次連接在一起形成LBP特征的統計直方圖(LBPH),即可用于分類識別的LBP特征向量。
LBP特征向量的具體計算過程如下:
- 按照上述算法計算圖像的LBP特征圖譜
- 將LBP特征圖譜分塊,例如分成8×8=64個區域
- 計算每個子區域中LBP特征值的統計直方圖,并進行歸一化,直方圖大小為1×numPatterns
- 將所有區域的統計直方圖按空間順序依次連接,得到整幅圖像的LBP特征向量,大小為1×(numPatterns×64)
- 從足夠數量的樣本中提取LBP特征,并利用機器學習的方法進行訓練得到模型,用于分類和識別等領域。
??對于LBP特征向量的維度,鄰域采樣點為8個,如果是原始的LBP特征,其模式數量為256,特征維數為64×256=16384;如果是Uniform LBP特征,其模式數量為59,特征維數為64×59=3776,使用等價模式特征,可以有效進行數據降維,而對模型性能卻無較大影響。
LBP特征向量的實現代碼如下:
??除了以上幾種比較經典的LBP特征外,還有諸多變種,如TLBP(中心像素與周圍所有像素比較,而不是根據采樣點的數目),DLBP(編碼4鄰域的灰度變化,每個方向上用兩個比特編碼),MLBP(將中心像素值替換為采樣點像素的平均值),VLBP,RGB-LBP等。
LBP特征的應用
目標檢測
??人臉檢測中比較典型的模型是Haar特征 + AdaBoost分類器,目前OpenCV也支持LBP + AdaBoost和HOG + AdaBoost的方法進行目標檢測,而且LBP特征的訓練速度較快,適用于實時檢測場景。
人臉識別
??人臉識別中LBP特征向量主要是用于直方圖的比較,通過距離度量的方式(例如方差)找到訓練數據中與輸入圖像距離最小的特征向量,將其對應的類別作為識別結果輸出。
reference
- Paper: Gray Scale and Rotation Invariant Texture Classification with Local Binary Patterns
- Paper: Multiresolution Gray Scale and Rotation Invariant Texture Classification with Local Binary Patterns
- Paper: Face Recognition with Local Binary Patterns
- Paper: Learning Multi-scale Block Local Binary Patterns for Face Recognition
- http://www.voidcn.com/blog/quincuntial/article/p-4988349.html
- http://blog.csdn.net/zouxy09/article/details/7929531
- http://blog.jasonding.top/2014/11/04/Machine%20Learning/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89%E3%80%91LBP%E7%BA%B9%E7%90%86%E7%89%B9%E5%BE%81/
- http://blog.csdn.net/liulina603/article/details/8291105
總結
以上是生活随笔為你收集整理的图像特征提取之LBP特征的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网最新装逼手册
- 下一篇: 集成海康威视Sadp SDK实现获取设备