1. 程式人生 > >opencv中實現LBP

opencv中實現LBP

LBP的改進版本:

       原始的LBP提出後,研究人員不斷對其提出了各種改進和優化。

1)圓形LBP運算元:

        基本的 LBP運算元的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,這顯然不能滿足不同尺寸和頻率紋理的需要。為了適應不同尺度的紋理特徵,並達到灰度和旋轉不變性的要求,Ojala等對 LBP 運算元進行了改進,將 3×3鄰域擴充套件到任意鄰域,並用圓形鄰域代替了正方形鄰域,改進後的 LBP 運算元允許在半徑為 R 的圓形鄰域內有任意多個畫素點。從而得到了諸如半徑為R的圓形區域內含有P個取樣點的LBP運算元;

LBP的基本思想是以影象中某個畫素為中心,對相鄰畫素進行閾值比較。如果中心畫素的亮度大於等於它的相鄰畫素,把相鄰畫素標記為1,否則標記為0。我們可以用二進位制數字來表示LBP圖中的每個畫素的LBP編碼,比如下圖中的中心畫素,它的LBP編碼為:00010011,其十進位制值為19。

image

用公式表示就是:

image

其中(xc,yc)是中心畫素,ic是灰度值,in是相鄰畫素的灰度值,s是一個符號函式:

image

在OpenCV的LBP演算法中,使用圓形的LBP運算元:

對於一個點image, 它的近鄰點 image用以下公式計算:

image

其中R是半徑,p是樣本點的個數。

如果就算的結果不在畫素座標上,我們則使用雙線性插值進行近似處理。

image

並且為適應選擇不變性,將得到的八位二進位制進行位迴圈處理,將最小值作為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;
}