opencv中實現LBP
阿新 • • 發佈:2019-01-08
LBP的改進版本:
原始的LBP提出後,研究人員不斷對其提出了各種改進和優化。
(1)圓形LBP運算元:
基本的 LBP運算元的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,這顯然不能滿足不同尺寸和頻率紋理的需要。為了適應不同尺度的紋理特徵,並達到灰度和旋轉不變性的要求,Ojala等對 LBP 運算元進行了改進,將 3×3鄰域擴充套件到任意鄰域,並用圓形鄰域代替了正方形鄰域,改進後的 LBP 運算元允許在半徑為 R 的圓形鄰域內有任意多個畫素點。從而得到了諸如半徑為R的圓形區域內含有P個取樣點的LBP運算元;
LBP的基本思想是以影象中某個畫素為中心,對相鄰畫素進行閾值比較。如果中心畫素的亮度大於等於它的相鄰畫素,把相鄰畫素標記為1,否則標記為0。我們可以用二進位制數字來表示LBP圖中的每個畫素的LBP編碼,比如下圖中的中心畫素,它的LBP編碼為:00010011,其十進位制值為19。
用公式表示就是:
其中(xc,yc)是中心畫素,ic是灰度值,in是相鄰畫素的灰度值,s是一個符號函式:
在OpenCV的LBP演算法中,使用圓形的LBP運算元:
其中R是半徑,p是樣本點的個數。
如果就算的結果不在畫素座標上,我們則使用雙線性插值進行近似處理。
並且為適應選擇不變性,將得到的八位二進位制進行位迴圈處理,將最小值作為LBP的值。
下面的程式碼中,我們分別實現了通常LBP圖和圓形運算元LBP圖。
程式碼如下:
// LBP_opencv.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <Opencv245.h> #include <bitset> #include <math.h> using namespace std; using namespace cv; #define neighbors 8 //void elbp(Mat& src, Mat& dst, int radius, int neighbors) //{ // for(int n=0; n<neighbors; n++) // { // // 取樣點的計算 // float y = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors))); // float x = static_cast<float>(radius * cos(2.0*CV_PI*n/static_cast<float>(neighbors))); // // 上取整和下取整的值 // int fx = static_cast<int>(floor(x)); // int fy = static_cast<int>(floor(y)); // int cx = static_cast<int>(ceil(x)); // int cy = static_cast<int>(ceil(y)); // // 小數部分 // float ty = y - fy; // float tx = x - fx; // // 設定插值權重 // 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++) // { // // 計算插值 // 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)); // // 進行編碼 // 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; // } // } // } // //} int bToDeci(bitset<neighbors>& bit ) { int result = 0; for (size_t i = 0; i < bit.size(); i++) { result += pow(2,i)*bit[i]; } return result; } void elbp(Mat& src, Mat& dst, int radius) { bitset<neighbors> bits(0); int m; for(int i=radius; i < src.rows-radius;i++) { for(int j=radius;j < src.cols-radius;j++) { for (int n = 0; n < neighbors; n++) { float Xb = static_cast<float>(radius * cos(2.0*CV_PI*n/static_cast<float>(neighbors))); float Yb = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors))); float Xp = i + Xb; float Yp = j + Yb; //上取整和下取整的值,則其周圍左上,右上,左下,右下四點分別為:(cXp, cYp),(fXp, cYp),(cXp, fYp),(fXp, fYp) int fXp = static_cast<int>(floor(Xp)); int cXp = static_cast<int>(ceil(Xp)); int fYp = static_cast<int>(floor(Yp)); int cYp = static_cast<int>(ceil(Yp)); //得到近鄰點(Xp,Yp)的值,進行插值 float t = static_cast<float>((src.at<uchar>(fXp,cYp)-src.at<uchar>(cXp,cYp))*Xp + (src.at<uchar>(cXp,fYp)-src.at<uchar>(cXp,cYp))*Yp + (src.at<uchar>(fXp,fYp)+src.at<uchar>(cXp,cYp)-src.at<uchar>(fXp,cYp)-src.at<uchar>(cXp,fYp))*Xp*fYp + +src.at<uchar>(cXp,cYp)); if ( (t > src.at<uchar>(i, j))||(std::abs(t-src.at<uchar>(i,j)) < std::numeric_limits<float>::epsilon()) ) { bits.set(n); } } m = bToDeci(bits); //進行迴圈位移,求出最小的lbp值 for (int n = 1; n < neighbors; n++) { bits = (bits >> (neighbors - n) | (bits << n)); if (bToDeci(bits) < m) { m = bToDeci(bits); } } // 進行編碼 dst.at<uchar>(i-radius,j-radius) = m; bits.reset(); } } } int _tmain(int argc, _TCHAR* argv[]) { Mat img = imread("C:\\Users\\sony\\Desktop\\111.png",0); int radius = 1; Mat dst = Mat(img.rows-2*radius, img.cols-2*radius,CV_8UC1, Scalar(0)); elbp(img, dst, radius); imshow("src", img); imshow("circle", dst); waitKey(0); return 0; }