本章咱們學習LBP圖像的原理和使用,由於接下來教程咱們要使用LBP圖像的直方圖來進行臉部識別。html
參考資料:ios
http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html算法
http://www.cnblogs.com/mikewolf2002/p/3438166.htmlapp
LBP的基本思想是以圖像中某個像素爲中心,對相鄰像素進行閾值比較。若是中心像素的亮度大於等於它的相鄰像素,把相鄰像素標記爲1,不然標記爲0。咱們能夠用二進制數字來表示LBP圖中的每一個像素的LBP編碼,好比下圖中的中心像素,它的LBP編碼爲:00010011,其十進制值爲19。函數
用公式表示就是:學習
其中(xc,yc)是中心像素,ic是灰度值,in是相鄰像素的灰度值,s是一個符號函數:ui
在OpenCV的LBP算法中,使用圓形的LBP算子:編碼
其中R是半徑,p是樣本點的個數。3d
若是就算的結果不在像素座標上,咱們則使用雙線性插值進行近似處理。
下面的代碼中,咱們分別實現了一般LBP圖和圓形算子LBP圖。
elbp是圓形算子LBP函數,elbp1是一般LBP圖,咱們分別對lena的圖像進行了處理,結果以下所示,從途中能夠看出來,使用圓形算子的效果銳度更強。
#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
void elbp(Mat& src, Mat &dst, int radius, int neighbors)
{
for(int n=0; n<neighbors; n++)
{
// 採樣點的計算
float x = static_cast<float>(-radius * sin(2.0*CV_PI*n/static_cast<float>(neighbors)));
float y = 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;
}
}
}
}
void elbp1(Mat& src, Mat &dst)
{
// 循環處理圖像數據
for(int i=1; i < src.rows-1;i++)
{
for(int j=1;j < src.cols-1;j++)
{
uchar tt = 0;
int tt1 = 0;
uchar u = src.at<uchar>(i,j);
if(src.at<uchar>(i-1,j-1)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i-1,j)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i-1,j+1)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i,j+1)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i+1,j+1)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i+1,j)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i+1,j-1)>u) { tt += 1 <<tt1; }
tt1++;
if(src.at<uchar>(i-1,j)>u) { tt += 1 <<tt1; }
tt1++;
dst.at<uchar>(i-1,j-1) = tt;
}
}
}
int main()
{
Mat img = cv::imread("../lenna.jpg", 0);
namedWindow("image");
imshow("image", img);
int radius, neighbors;
radius = 1;
neighbors = 8;
//建立一個LBP
//注意爲了溢出,咱們行列都在原有圖像上減去2個半徑
Mat dst = Mat(img.rows-2*radius, img.cols-2*radius,CV_8UC1, Scalar(0));
elbp1(img,dst);
namedWindow("normal");
imshow("normal", dst);
Mat dst1 = Mat(img.rows-2*radius, img.cols-2*radius,CV_8UC1, Scalar(0));
elbp(img,dst1,1,8);
namedWindow("circle");
imshow("circle", dst1);
while(1)
cv::waitKey(0);
}
咱們換另一張圖,該圖包括不一樣光照下的四副照片,再來看看LBP圖的效果:
程序代碼:
FirstOpenCV36