EasyPR源碼剖析(6):車牌判斷之LBP特征
一、LBP特征
LBP指局部二值模式,英文全稱:Local Binary Pattern,是一種用來描述圖像局部特征的算子,LBP特征具有灰度不變性和旋轉不變性等顯著優點。
原始的LBP算子定義在像素3*3的鄰域內,以鄰域中心像素為閾值,相鄰的8個像素的灰度值與鄰域中心的像素值進行比較,若周圍像素大於中心像素值,則該像素點的位置被標記為1,否則為0。這樣,3*3鄰域內的8個點經過比較可產生8位二進制數,將這8位二進制數依次排列形成一個二進制數字,這個二進制數字就是中心像素的LBP值,LBP值共有2^8種可能,因此LBP值有256種。中心像素的LBP值反映了該像素周圍區域的紋理信息。
備註:計算LBP特征的圖像必須是灰度圖,如果是彩色圖,需要先轉換成灰度圖。
上述過程用圖像表示為:
原始LBP特征的具體的代碼如下:
1 //原始LBP特征計算 2 template <typename _tp> 3 void getOriginLBPFeature(InputArray _src,OutputArray _dst) 4 { 5 Mat src = _src.getMat(); 6 _dst.create(src.rows-2,src.cols-2,CV_8UC1); 7 Mat dst = _dst.getMat(); 8 dst.setTo(0); 9 for(int i=1;i<src.rows-1View Code;i++) 10 { 11 for(int j=1;j<src.cols-1;j++) 12 { 13 _tp center = src.at<_tp>(i,j); 14 unsigned char lbpCode = 0; 15 lbpCode |= (src.at<_tp>(i-1,j-1) > center) << 7; 16 lbpCode |= (src.at<_tp>(i-1,j ) > center) << 6; 17 lbpCode |= (src.at<_tp>(i-1,j+1) > center) << 5; 18 lbpCode |= (src.at<_tp>(i ,j+1) > center) << 4; 19 lbpCode |= (src.at<_tp>(i+1,j+1) > center) << 3; 20 lbpCode |= (src.at<_tp>(i+1,j ) > center) << 2; 21 lbpCode |= (src.at<_tp>(i+1,j-1) > center) << 1; 22 lbpCode |= (src.at<_tp>(i ,j-1) > center) << 0; 23 dst.at<uchar>(i-1,j-1) = lbpCode; 24 } 25 } 26 }
在原始的LBP特征提出以後,研究人員對LBP特征進行了很多的改進,因此產生了許多LBP的改進版本。
圓形LBP特征(Circular LBP or Extended LBP)
由於原始LBP特征使用的是固定鄰域內的灰度值,因此當圖像的尺度發生變化時,LBP特征的編碼將會發生錯誤,LBP特征將不能正確的反映像素點周圍的紋理信息,因此研究人員對其進行了改進。基本的 LBP 算子的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,這顯然不能滿足不同尺寸和頻率紋理的需要。為了適應不同尺度的紋理特征,並達到灰度和旋轉不變性的要求, 對 LBP 算子進行改進,將 3×3 鄰域擴展到任意鄰域,並用圓形鄰域代替了正方形鄰域,改進後的 LBP 算子允許在半徑為 R 的圓形鄰域內有任意多個像素點。從而得到了諸如半徑為R的圓形區域內含有P個采樣點的LBP算子:
旋轉不變LBP特征
從上面可以看出,上面的LBP特征具有灰度不變性,但還不具備旋轉不變性,因此研究人員又在上面的基礎上進行了擴展,提出了具有旋轉不變性的LBP特征。首先不斷的旋轉圓形鄰域內的LBP特征,根據選擇得到一系列的LBP特征值,從這些LBP特征值選擇LBP特征值最小的作為中心像素點的LBP特征。具體做法如下圖所示:
如圖,通過對得到的LBP特征進行旋轉,得到一系列的LBP特征值,最終將特征值最小的一個特征模式作為中心像素點的LBP特征。
1 //旋轉不變圓形LBP特征計算,聲明時默認neighbors=8 2 template <typename _tp> 3 void getRotationInvariantLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors) 4 { 5 Mat src = _src.getMat(); 6 //LBP特征圖像的行數和列數的計算要準確 7 _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1); 8 Mat dst = _dst.getMat(); 9 dst.setTo(0); 10 for(int k=0;k<neighbors;k++) 11 { 12 //計算采樣點對於中心點坐標的偏移量rx,ry 13 float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors)); 14 float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors)); 15 //為雙線性插值做準備 16 //對采樣點偏移量分別進行上下取整 17 int x1 = static_cast<int>(floor(rx)); 18 int x2 = static_cast<int>(ceil(rx)); 19 int y1 = static_cast<int>(floor(ry)); 20 int y2 = static_cast<int>(ceil(ry)); 21 //將坐標偏移量映射到0-1之間 22 float tx = rx - x1; 23 float ty = ry - y1; 24 //根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關 25 float w1 = (1-tx) * (1-ty); 26 float w2 = tx * (1-ty); 27 float w3 = (1-tx) * ty; 28 float w4 = tx * ty; 29 //循環處理每個像素 30 for(int i=radius;i<src.rows-radius;i++) 31 { 32 for(int j=radius;j<src.cols-radius;j++) 33 { 34 //獲得中心像素點的灰度值 35 _tp center = src.at<_tp>(i,j); 36 //根據雙線性插值公式計算第k個采樣點的灰度值 37 float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2 38 + src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4; 39 //LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得 40 dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1); 41 } 42 } 43 } 44 //進行旋轉不變處理 45 for(int i=0;i<dst.rows;i++) 46 { 47 for(int j=0;j<dst.cols;j++) 48 { 49 unsigned char currentValue = dst.at<uchar>(i,j); 50 unsigned char minValue = currentValue; 51 for(int k=1;k<neighbors;k++) 52 { 53 //循環左移 54 unsigned char temp = (currentValue>>(neighbors-k)) | (currentValue<<k); 55 if(temp < minValue) 56 { 57 minValue = temp; 58 } 59 } 60 dst.at<uchar>(i,j) = minValue; 61 } 62 } 63 }View Code
Uniform Pattern 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(共四次跳變)。通過這樣的改進,二進制模式的種類大大減少,而不會丟失任何信息。模式數量由原來的2^P減少為 P ( P-1)+2種,其中P表示鄰域集內的采樣點數。對於3×3鄰域內8個采樣點來說,二進制模式由原始的256種減少為58種,即它把值分為59類,58個uniform pattern為一類,其它的所有值為第59類。這樣直方圖從原來的256維變成59維。這使得特征向量的維數更少,並且可以減少高頻噪聲帶來的影響。
1 //等價模式LBP特征計算 2 template <typename _tp> 3 void getUniformPatternLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors) 4 { 5 Mat src = _src.getMat(); 6 //LBP特征圖像的行數和列數的計算要準確 7 _dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1); 8 Mat dst = _dst.getMat(); 9 dst.setTo(0); 10 //LBP特征值對應圖像灰度編碼表,直接默認采樣點為8位 11 uchar temp = 1; 12 uchar table[256] = {0}; 13 for(int i=0;i<256;i++) 14 { 15 if(getHopTimes(i)<3) 16 { 17 table[i] = temp; 18 temp++; 19 } 20 } 21 //是否進行UniformPattern編碼的標誌 22 bool flag = false; 23 //計算LBP特征圖 24 for(int k=0;k<neighbors;k++) 25 { 26 if(k==neighbors-1) 27 { 28 flag = true; 29 } 30 //計算采樣點對於中心點坐標的偏移量rx,ry 31 float rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors)); 32 float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors)); 33 //為雙線性插值做準備 34 //對采樣點偏移量分別進行上下取整 35 int x1 = static_cast<int>(floor(rx)); 36 int x2 = static_cast<int>(ceil(rx)); 37 int y1 = static_cast<int>(floor(ry)); 38 int y2 = static_cast<int>(ceil(ry)); 39 //將坐標偏移量映射到0-1之間 40 float tx = rx - x1; 41 float ty = ry - y1; 42 //根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關 43 float w1 = (1-tx) * (1-ty); 44 float w2 = tx * (1-ty); 45 float w3 = (1-tx) * ty; 46 float w4 = tx * ty; 47 //循環處理每個像素 48 for(int i=radius;i<src.rows-radius;i++) 49 { 50 for(int j=radius;j<src.cols-radius;j++) 51 { 52 //獲得中心像素點的灰度值 53 _tp center = src.at<_tp>(i,j); 54 //根據雙線性插值公式計算第k個采樣點的灰度值 55 float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2 56 + src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4; 57 //LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得 58 dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1); 59 //進行LBP特征的UniformPattern編碼 60 if(flag) 61 { 62 dst.at<uchar>(i-radius,j-radius) = table[dst.at<uchar>(i-radius,j-radius)]; 63 } 64 } 65 } 66 } 67 } 68 //計算跳變次數 69 int getHopTimes(int n) 70 { 71 int count = 0; 72 bitset<8> binaryCode = n; 73 for(int i=0;i<8;i++) 74 { 75 if(binaryCode[i] != binaryCode[(i+1)%8]) 76 { 77 count++; 78 } 79 } 80 return count; 81 }View Code
二、LBPH——圖像的LBP特征向量
LBPH,Local Binary Patterns Histograms,即LBP特征的統計直方圖,LBPH將LBP特征與圖像的空間信息結合在一起。這種表示方法由Ahonen等人在論文[3]中提出,他們將LBP特征圖像分成m個局部塊,並提取每個局部塊的直方圖,然後將這些直方圖依次連接在一起形成LBP特征的統計直方圖,即LBPH。
一幅圖像具體的計算LBPH的過程(以Opencv中的人臉識別為例):
1. 計算圖像的LBP特征圖像;
2. 將LBP特征圖像進行分塊,Opencv中默認將LBP特征圖像分成8行8列64塊區域;
3. 計算每塊區域特征圖像的直方圖cell_LBPH,將直方圖進行歸一化,直方圖大小為1*numPatterns;
4. 將上面計算的每塊區域特征圖像的直方圖按分塊的空間順序依次排列成一行,形成LBP特征向量,大小為1*(numPatterns*64);
5. 用機器學習的方法對LBP特征向量進行訓練,用來檢測和識別目標。
舉例說明LBPH的維度:
采樣點為8個,如果用的是原始的LBP或Extended LBP特征,其LBP特征值的模式為256種,則一幅圖像的LBP特征向量維度為:64*256=16384維,
而如果使用的UniformPatternLBP特征,其LBP值的模式為59種,其特征向量維度為:64*59=3776維,可以看出,使用等價模式特征,其特征向量的維度大大減少,
這意味著使用機器學習方法進行學習的時間將大大減少,而性能上沒有受到很大影響。
1 //計算LBP特征圖像的直方圖LBPH 2 Mat getLBPH(InputArray _src,int numPatterns,int grid_x,int grid_y,bool normed) 3 { 4 Mat src = _src.getMat(); 5 int width = src.cols / grid_x; 6 int height = src.rows / grid_y; 7 //定義LBPH的行和列,grid_x*grid_y表示將圖像分割成這麽些塊,numPatterns表示LBP值的模式種類 8 Mat result = Mat::zeros(grid_x * grid_y,numPatterns,CV_32FC1); 9 if(src.empty()) 10 { 11 return result.reshape(1,1); 12 } 13 int resultRowIndex = 0; 14 //對圖像進行分割,分割成grid_x*grid_y塊,grid_x,grid_y默認為8 15 for(int i=0;i<grid_x;i++) 16 { 17 for(int j=0;j<grid_y;j++) 18 { 19 //圖像分塊 20 Mat src_cell = Mat(src,Range(i*height,(i+1)*height),Range(j*width,(j+1)*width)); 21 //計算直方圖 22 Mat hist_cell = getLocalRegionLBPH(src_cell,0,(numPattern-1),true); 23 //將直方圖放到result中 24 Mat rowResult = result.row(resultRowIndex); 25 hist_cell.reshape(1,1).convertTo(rowResult,CV_32FC1); 26 resultRowIndex++; 27 } 28 } 29 return result.reshape(1,1); 30 } 31 //計算一個LBP特征圖像塊的直方圖 32 Mat getLocalRegionLBPH(const Mat& src,int minValue,int maxValue,bool normed) 33 { 34 //定義存儲直方圖的矩陣 35 Mat result; 36 //計算得到直方圖bin的數目,直方圖數組的大小 37 int histSize = maxValue - minValue + 1; 38 //定義直方圖每一維的bin的變化範圍 39 float range[] = { static_cast<float>(minValue),static_cast<float>(maxValue + 1) }; 40 //定義直方圖所有bin的變化範圍 41 const float* ranges = { range }; 42 //計算直方圖,src是要計算直方圖的圖像,1是要計算直方圖的圖像數目,0是計算直方圖所用的圖像的通道序號,從0索引 43 //Mat()是要用的掩模,result為輸出的直方圖,1為輸出的直方圖的維度,histSize直方圖在每一維的變化範圍 44 //ranges,所有直方圖的變化範圍(起點和終點) 45 calcHist(&src,1,0,Mat(),result,1,&histSize,&ranges,true,false); 46 //歸一化 47 if(normed) 48 { 49 result /= (int)src.total(); 50 } 51 //結果表示成只有1行的矩陣 52 return result.reshape(1,1); 53 }View Code
上面的LBP特征都是較經典的LBP特征,除此之外,LBP特征還有大量的變種,如TLBP,DLBP,MLBP,MB-LBP,RGB-LBP等。
EasyPR源碼剖析(6):車牌判斷之LBP特征