LBP(Local Binary Pattern,局部二值模式)是一種用來描述圖像局部紋理特徵的算子;它具備旋轉不變性和灰度不變性等顯著的優勢。它是首先由T. Ojala, M.Pietikäinen,和 D. Harwood 在1994年提出,用於紋理特徵提取。並且,提取的特徵是圖像的局部的紋理特徵;html
一、LBP特徵的描述ios
原始的LBP算子定義爲在3*3的窗口內,以窗口中心像素爲閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,不然爲0。這樣,3*3鄰域內的8個點經比較可產生8位二進制數(一般轉換爲十進制數即LBP碼,共256種),即獲得該窗口中心像素點的LBP值,並用這個值來反映該區域的紋理信息。以下圖所示:算法
LBP的改進版本:機器學習
原始的LBP提出後,研究人員不斷對其提出了各類改進和優化。函數
(1)圓形LBP算子:學習
基本的 LBP算子的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,這顯然不能知足不一樣尺寸和頻率紋理的須要。爲了適應不一樣尺度的紋理特徵,並達到灰度和旋轉不變性的要求,Ojala等對 LBP算子進行了改進,將 3×3鄰域擴展到任意鄰域,並用圓形鄰域代替了正方形鄰域,改進後的 LBP算子容許在半徑爲 R 的圓形鄰域內有任意多個像素點。從而獲得了諸如半徑爲R的圓形區域內含有P個採樣點的LBP算子;優化
(2)LBP旋轉不變模式ui
從 LBP 的定義能夠看出,LBP 算子是灰度不變的,但卻不是旋轉不變的。圖像的旋轉就會獲得不一樣的 LBP值。編碼
Maenpaa等人又將 LBP算子進行了擴展,提出了具備旋轉不變性的 LBP算子,即不斷旋轉圓形鄰域獲得一系列初始定義的 LBP值,取其最小值做爲該鄰域的 LBP值。spa
圖 2.5 給出了求取旋轉不變的 LBP 的過程示意圖,圖中算子下方的數字表示該算子對應的 LBP值,圖中所示的 8種 LBP模式,通過旋轉不變的處理,最終獲得的具備旋轉不變性的 LBP值爲 15。也就是說,圖中的 8種 LBP 模式對應的旋轉不變的 LBP模式都是 00001111。
(3)LBP等價模式
一個LBP算子能夠產生不一樣的二進制模式,對於半徑爲R的圓形區域內含有P個採樣點的LBP算子將會產生P2種模式。很顯然,隨着鄰域集內採樣點數的增長,二進制模式的種類是急劇增長的。例如: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算子在每一個像素點均可以獲得一個LBP「編碼」,那麼,對一幅圖像(記錄的是每一個像素點的灰度值)提取其原始的LBP算子以後,獲得的原始LBP特徵依然是「一幅圖片」(記錄的是每一個像素點的LBP值)。
LBP的應用中,如紋理分類、人臉分析等,通常都不將LBP圖譜做爲特徵向量用於分類識別,而是採用LBP特徵譜的統計直方圖做爲特徵向量用於分類識別。
由於,從上面的分析咱們能夠看出,這個「特徵」跟位置信息是緊密相關的。直接對兩幅圖片提取這種「特徵」,並進行判別分析的話,會由於「位置沒有對準」而產生很大的偏差。後來,研究人員發現,能夠將一幅圖片劃分爲若干的子區域,對每一個子區域內的每一個像素點都提取LBP特徵,而後,在每一個子區域內創建LBP特徵的統計直方圖。如此一來,每一個子區域,就能夠用一個統計直方圖來進行描述;整個圖片就由若干個統計直方圖組成;
例如:一幅100*100像素大小的圖片,劃分爲10*10=100個子區域(能夠經過多種方式來劃分區域),每一個子區域的大小爲10*10像素;在每一個子區域內的每一個像素點,提取其LBP特徵,而後,創建統計直方圖;這樣,這幅圖片就有10*10個子區域,也就有了10*10個統計直方圖,利用這10*10個統計直方圖,就能夠描述這幅圖片了。以後,咱們利用各類類似性度量函數,就能夠判斷兩幅圖像之間的類似性了;
三、對LBP特徵向量進行提取的步驟
(1)首先將檢測窗口劃分爲16×16的小區域(cell);
(2)對於每一個cell中的一個像素,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,不然爲0。這樣,3*3鄰域內的8個點經比較可產生8位二進制數,即獲得該窗口中心像素點的LBP值;
(3)而後計算每一個cell的直方圖,即每一個數字(假定是十進制數LBP值)出現的頻率;而後對該直方圖進行歸一化處理。
(4)最後將獲得的每一個cell的統計直方圖進行鏈接成爲一個特徵向量,也就是整幅圖的LBP紋理特徵向量;
而後即可利用SVM或者其餘機器學習算法進行分類了。
===========================================================================
人臉識別之LBP (Local Binary Pattern)
1.算法簡介
LBP是一種簡單,有效的紋理分類的特徵提取算法。LBP算子是由Ojala等人於1996年提出的,主要的論文是"Multiresolution gray-scale and rotation invariant texture classification with local binary patterns", pami, vol 24, no.7, July 2002。LBP就是"local binary pattern"的縮寫。
關於論文的講解可參考連接 http://blog.sina.com.cn/s/blog_916b71bb0100w043.html
從紋理分析的角度來看,圖像上某個像素點的紋理特徵,大多數狀況下是指這個點和周圍像素點的關係,即這個點和它的鄰域內點的關係。從哪一個角度對這種關係提取特徵,就造成了不一樣種類的特徵。有了特徵,就能根據紋理進行分類。LBP構造了一種衡量一個像素點和它周圍像素點的關係。
對圖像中的每一個像素,經過計算以其爲中心的3*3鄰域內各像素和中心像素的大小關係,把像素的灰度值轉化爲一個八位二進制序列。具體計算過程以下圖所示,對於圖像的任意一點Ic,其LBP特徵計算爲,以Ic爲中心,取與Ic相鄰的8各點,按照順時針的方向記爲 I0,I1,...,I7;以Ic點的像素值爲閾值,若是 Ii 點的像素值小於Ic,則 Ii 被二值化爲0,不然爲1;將二值化獲得的0、1序列當作一個8位二進制數,將該二進制數轉化爲十進制就可獲得Ic點處的LBP算子的值。
基本的LBP算子只侷限在3*3的鄰域內,對於較大圖像大尺度的結構不能很好的提取須要的紋理特徵,所以研究者們對LBP算子進行了擴展。新的LBP算子LBP(P,R) 能夠計算不一樣半徑鄰域大小和不一樣像素點數的特徵值,其中P表示周圍像素點個數,R表示鄰域半徑,同時把原來的方形鄰域擴展到了圓形,下圖給出了四種擴展後的LBP例子,其中,R能夠是小數,對於沒有落到整數位置的點,根據軌道內離其最近的兩個整數位置像素灰度值,利用雙線性差值的方法能夠計算它的灰度值。
LBP(P,R)有2^p個值,也就是說圖像共有2^p種二進制模型,然而實際研究中發現,全部模式表達信息的重要程度是不一樣的,統計研究代表,一幅圖像中少數模式特別集中,達到總模式的百分之九十左右的比例,Ojala等人定義這種模式爲Uniform模式,若是一個二進制序列當作一個圈時,0-1以及1-0的變化出現的次數總和不超過兩次,那麼這個序列就是Uniform模式 ,好比,00000000、000111十、0010000一、11111111,在使用LBP表達圖像紋理時,一般只關心Uniform模式,而將全部其餘的模式歸到同一類中。
人臉圖像的各類LBP模式以下圖所示,由圖中能夠看出,變化後的圖像和原圖像相比,能更清晰的體現各典型區域的紋理,同時又淡化了對於研究價值不大的平滑區域的特徵,同時下降了特徵的維數。比較而言,Uniform模式表現的更逼真,在人臉識別和表情識別應用中,都是採用這種模式。
在表情識別中,最經常使用的是把LBP的統計柱狀圖做爲表情圖像的特徵向量。爲了考慮特徵的位置信息,把圖像分紅若干個小區域,在每一個小區域裏進行直方圖統計,即統計該區域內屬於某一模式的數量,最後再把全部區域的直方圖一次鏈接到一塊兒做爲特徵向量接受下一級的處理。
LBP算子利用了周圍點與該點的關係對該點進行量化。量化後能夠更有效地消除光照對圖像的影響。只要光照的變化不足以改變兩個點像素值之間的大小關係,那麼LBP算子的值不會發生變化,因此必定程度上,基於LBP的識別算法解決了光照變化的問題,可是當圖像光照變化不均勻時,各像素間的大小關係被破壞,對應的LBP模式也就發生了變化。
若是圖像旋轉了,那麼紋理特徵就旋轉了,這時獲得的2進制串也就旋轉了,LBP值會相應變化。爲了讓LBP具備旋轉不變性,將二進制串進行旋轉。假設一開始獲得的LBP特徵爲10010000,那麼將這個二進制特徵,按照順時針方向旋轉,能夠轉化爲00001001的形式,這樣獲得的LBP值是最小的。不管圖像怎麼旋轉,對點提取的二進制特徵的最小值是不變的,用最小值做爲提取的LBP特徵,這樣LBP就是旋轉不變的了。當P=8時,能產生的不一樣的二進制特徵數量是2^8個,通過上述表示,就變爲36個。(我覺得應當是2^8/8=32個)
代碼:
code1:原始(正方形)LBP代碼
1 lbpI=imread('D:/picture/lenagray.jpg') 2 I = imresize(lbpI,[256 256]); 3 [m,n,h] = size(I); 4 if h==3 5 I = rgb2gray(I); 6 end 7 lbpI = uint8(zeros([m n])); 8 for i = 2:m-1 9 for j = 2:n-1 10 neighbor = [I(i-1,j-1) I(i-1,j) I(i-1,j+1) I(i,j+1) I(i+1,j+1) I(i+1,j) I(i+1,j-1) I(i,j-1)] > I(i,j); 11 pixel = 0; 12 for k = 1:8 13 pixel = pixel + neighbor(1,k) * bitshift(1,8-k); 14 end 15 lbpI(i,j) = uint8(pixel); 16 end 17 end 18 imshow(lbpI)
code2:圓形(旋轉不變性)LBP代碼
1 %2017-05-12 學習LBP特徵 2 3 clc; 4 clear; 5 6 %讀圖操做 7 img=imread('D:/picture/lenagray.jpg'); 8 [m,n]=size(img); 9 subplot(121); 10 imshow(img,[]); 11 title('原圖'); 12 %% 13 %求旋轉不變LBP 14 img_LBP_ri=zeros(m,n); 15 for i=2:m-1 16 for j=2:n-1 17 18 code=zeros(1,8); %行向量,原始LBP特徵編碼 19 code(1)=img(i-1,j-1)>img(i,j); 20 code(2)=img(i-1,j)>img(i,j); 21 code(3)=img(i-1,j+1)>img(i,j); 22 code(4)=img(i,j+1)>img(i,j); 23 code(5)=img(i+1,j+1)>img(i,j); 24 code(6)=img(i+1,j)>img(i,j); 25 code(7)=img(i+1,j-1)>img(i,j); 26 code(8)=img(i,j-1)>img(i,j); 27 for p=1:8 28 img_LBP_ri(i,j)=img_LBP_ri(i,j)+code(p)*2^(8-p); %從左上角開始,順時針編碼 29 end 30 31 %循環左移,移動k位至關於把開頭的k個數放到最右邊 32 for k=1:7 33 code=[code(k+1:end) code(1:k)]; %移位以後的二進制編碼,右移表達式 code=[code(end-k+1:end) code(1:end-k)] 34 temp=0; 35 for p=1:8 36 temp=temp+code(p)*2^(8-p); 37 end 38 if temp<img_LBP_ri(i,j) %取旋轉以後的最小值 39 img_LBP_ri(i,j)=temp; 40 end 41 end 42 43 end 44 end 45 46 subplot(122) 47 imshow(img_LBP_ri,[]); 48 title('旋轉不變性LBP');
code3:正方形圓形對比LBP
1 #include "opencv2/core/core.hpp" 2 3 4 5 #include "opencv2/highgui/highgui.hpp" 6 7 8 9 #include <iostream> 10 11 #include <fstream> 12 13 #include <sstream> 14 15 16 17 using namespace cv; 18 19 using namespace std; 20 21 22 23 void elbp(Mat& src, Mat &dst, int radius, int neighbors) 24 25 { 26 27 28 29 for (int n = 0; n < neighbors; n++) 30 31 { 32 33 // 採樣點的計算 34 35 float x = static_cast<float>(-radius * sin(2.0*CV_PI*n / static_cast<float>(neighbors))); 36 37 float y = static_cast<float>(radius * cos(2.0*CV_PI*n / static_cast<float>(neighbors))); 38 39 // 上取整和下取整的值 40 41 int fx = static_cast<int>(floor(x)); 42 43 int fy = static_cast<int>(floor(y)); 44 45 int cx = static_cast<int>(ceil(x)); 46 47 int cy = static_cast<int>(ceil(y)); 48 49 // 小數部分 50 51 float ty = y - fy; 52 53 float tx = x - fx; 54 55 // 設置插值權重 56 57 float w1 = (1 - tx) * (1 - ty); 58 59 float w2 = tx * (1 - ty); 60 61 float w3 = (1 - tx) * ty; 62 63 float w4 = tx * ty; 64 65 // 循環處理圖像數據 66 67 for (int i = radius; i < src.rows - radius; i++) 68 69 { 70 71 for (int j = radius; j < src.cols - radius; j++) 72 73 { 74 75 // 計算插值 76 77 float t = static_cast<float>(w1*src.at<uchar>(i + fy, j + fx) + w2 * src.at<uchar>(i + fy, j + cx) + w3 * src.at<uchar>(i + cy, j + fx) + w4 * src.at<uchar>(i + cy, j + cx)); 78 79 // 進行編碼 80 81 dst.at<uchar>(i - radius, j - radius) += ((t > src.at<uchar>(i, j)) || (std::abs(t - src.at<uchar>(i, j)) < std::numeric_limits<float>::epsilon())) << n; 82 83 } 84 85 } 86 87 } 88 89 } 90 91 92 93 void elbp1(Mat& src, Mat &dst) 94 95 { 96 97 98 99 // 循環處理圖像數據 100 101 for (int i = 1; i < src.rows - 1; i++) 102 103 { 104 105 for (int j = 1; j < src.cols - 1; j++) 106 107 { 108 109 uchar tt = 0; 110 111 int tt1 = 0; 112 113 uchar u = src.at<uchar>(i, j); 114 115 if (src.at<uchar>(i - 1, j - 1) > u) { tt += 1 << tt1; } 116 117 tt1++; 118 119 if (src.at<uchar>(i - 1, j) > u) { tt += 1 << tt1; } 120 121 tt1++; 122 123 if (src.at<uchar>(i - 1, j + 1) > u) { tt += 1 << tt1; } 124 125 tt1++; 126 127 if (src.at<uchar>(i, j + 1) > u) { tt += 1 << tt1; } 128 129 tt1++; 130 131 if (src.at<uchar>(i + 1, j + 1) > u) { tt += 1 << tt1; } 132 133 tt1++; 134 135 if (src.at<uchar>(i + 1, j) > u) { tt += 1 << tt1; } 136 137 tt1++; 138 139 if (src.at<uchar>(i + 1, j - 1) > u) { tt += 1 << tt1; } 140 141 tt1++; 142 143 if (src.at<uchar>(i - 1, j) > u) { tt += 1 << tt1; } 144 145 tt1++; 146 147 148 149 dst.at<uchar>(i - 1, j - 1) = tt;// 更正,以前是dst.at<uchar>(i,j)=tt; 150 151 } 152 153 } 154 155 } 156 157 158 159 int main() 160 161 { 162 163 Mat img = cv::imread("D:/picture/lenagray.jpg", 0); 164 165 namedWindow("image"); 166 167 imshow("image", img); 168 169 170 171 int radius, neighbors; 172 173 radius = 1; 174 175 neighbors = 8; 176 177 178 179 //建立一個LBP圖譜 180 181 Mat dst = Mat(img.rows - 2 * radius, img.cols - 2 * radius, CV_8UC1, Scalar(0)); 182 183 elbp1(img, dst); 184 185 namedWindow("normal"); 186 187 imshow("normal", dst); 188 189 190 191 Mat dst1 = Mat(img.rows - 2 * radius, img.cols - 2 * radius, CV_8UC1, Scalar(0)); 192 193 elbp(img, dst1, 1, 8); 194 195 namedWindow("circle"); 196 197 imshow("circle", dst1); 198 199 cv::waitKey(0); 200 201 }