# 基于 OpenCV 的 LBP + SVM 人脸识别

LBP算法头文件：

#ifndef _HEAD_H

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cassert>
#include <vector>
#include <numeric>
#include <algorithm>
#include <functional>
#include <iterator>

#endif		// _HEAD_H
LBP.h

#ifndef _LBP_H
#define _LBP_H

typedef std::vector<double> VecDouble;
typedef std::vector<int> VecInt;
typedef std::vector<float> VecFloat;

class LBP
{
public:
LBP();
~LBP();

public:

enum LBP_TYPE
{
LBP_UNIFORM = 0,		// uniform LBP
LBP_NORMAL = 1,			// lbp
LBP_CIRCLE = 2			// circular operator of lbp
};

public:
// 传统的 LBP 算法
void calLBP(IplImage* srcImg, IplImage* dstImg);
// LBP 圆形算子算法
void calCirLBP(IplImage* srcImg, IplImage* dstImg, int radius, int neighbor);
// uniform LBP 算法
void calUniformLBP(IplImage* src, IplImage* dst);

public:
// 设置半径
// 设置领域数
void setNeighbors(int neighbor) { m_neighbor = neighbor; }
// 计算特征值
void calLBPFeatures(IplImage* srcImg, IplImage* dstImg, VecDouble& hist, LBP_TYPE lbpType);

private:
int m_neighbor;

};

#endif  // _LBP_H


LBP.cpp

#include "stdafx.h"
#include "LBP.h"
#include <iostream>
using namespace std;

LBP::LBP()
, m_neighbor(0)
{

}

LBP::~LBP()
{

}
//基于旧版本的opencv的LBP算法opencv1.0
// 3 x 3 矩阵如下所示
// [ 1, 2, 3]
// [ 8, ij,4]
// [ 7, 6, 5]

void LBP::calLBP(IplImage* srcImg, IplImage* dstImg)
{
// 原图像为单通道图像，目标图像为单通道图像
assert(srcImg != NULL && srcImg->nChannels == 1);
assert(dstImg != NULL && dstImg->nChannels == 1);

unsigned tmp[8] = { 0 };

// 遍历图像
for (int rows = 1; rows < srcImg->height - 1; ++rows)
{
for (int cols = 1; cols < srcImg->width - 1; ++cols)
{
int sum = 0;
double center = cvGetReal2D(srcImg, rows, cols);

//　顺时针遍历
double val = 0.0;
// 左上角
val = cvGetReal2D(srcImg, rows - 1, cols - 1);
val > center ? tmp[0] = 1 : tmp[0] = 0;
// 正上方
val = cvGetReal2D(srcImg, rows, cols - 1);
val > center ? tmp[1] = 1 : tmp[1] = 0;
// 右上角
val = cvGetReal2D(srcImg, rows + 1, cols - 1);
val > center ? tmp[2] = 1 : tmp[2] = 0;
// 右侧
val = cvGetReal2D(srcImg, rows + 1, cols);
val > center ? tmp[3] = 1 : tmp[3] = 0;
// 右下角
val = cvGetReal2D(srcImg, rows + 1, cols + 1);
val > center ? tmp[4] = 1 : tmp[4] = 0;
// 下方
val = cvGetReal2D(srcImg, rows, cols + 1);
val > center ? tmp[5] = 1 : tmp[5] = 0;
// 左下角
val = cvGetReal2D(srcImg, rows - 1, cols + 1);
val > center ? tmp[6] = 1 : tmp[6] = 0;
// 左侧
val = cvGetReal2D(srcImg, rows - 1, cols);
val > center ? tmp[7] = 1 : tmp[7] = 0;

// 计算 LBP 编码
for (int i = 0; i < 8; ++i)
{
sum += tmp[i] * pow(2, i);
}
cvSetReal2D(dstImg, rows, cols, sum);
}

}// end for

}

// uniform LBP,一致局部二值模式
void LBP::calUniformLBP(IplImage *src, IplImage *dst)
{
assert(src != NULL && src->nChannels == 1);
assert(dst != NULL && dst->nChannels == 1);

int tmp[8] = { 0 };

int rows = src->height - 1;
int cols = src->width - 1;

for (int i = 1; i < rows; ++i)
{
for (int j = 1; j < cols; ++j)
{
int sum = 0;
double center = cvGetReal2D(src, i, j);

// 顺时针遍历
double val = 0.0;
val = cvGetReal2D(src, i - 1, j - 1);		// 左上角
val > center ? tmp[0] = 1 : tmp[0] = 0;
val = cvGetReal2D(src, i, j - 1);			// 正上方
val > center ? tmp[1] = 1 : tmp[1] = 0;
val = cvGetReal2D(src, i + 1, j - 1);		// 右上角
val > center ? tmp[2] = 1 : tmp[2] = 0;
val = cvGetReal2D(src, i + 1, j);			// 右侧
val > center ? tmp[3] = 1 : tmp[3] = 0;
val = cvGetReal2D(src, i + 1, j + 1);		// 右下角
val > center ? tmp[4] = 1 : tmp[4] = 0;
val = cvGetReal2D(src, i, j + 1);			// 正下方
val > center ? tmp[5] = 1 : tmp[5] = 0;
val = cvGetReal2D(src, i - 1, j + 1);		// 左下角
val > center ? tmp[6] = 1 : tmp[6] = 0;
val = cvGetReal2D(src, i - 1, j);			// 左侧
val > center ? tmp[7] = 1 : tmp[7] = 0;

//计算0、1翻转次数
for (int k = 0; k < 8; k++)
{
if (k != 7)
{
sum += abs(tmp[k] - tmp[k + 1]);
}
else
{
sum += abs(tmp[k] - tmp[0]);
}
}
//通过翻转次数判断具体特征值
if (sum <= 2)
{
for (int i = 0; i < 8; ++i)
{
sum += tmp[i] * pow(2, (7 - i));
}

}
else
{
sum = 5; //将不满足的取5
}
cvSet2D(dst, i, j, cvScalar(sum));
}
}
}

// 圆形 LBP 算子
void LBP::calCirLBP(IplImage* src, IplImage* dst, int radius, int neighbors)
{
// 处理的图像为单通道图像
assert(src->nChannels == 1);

for (int i = 0; i < neighbors; ++i)
{
// 正弦弧度
double sRadian = sin(2.0 * CV_PI * i / static_cast<double>(neighbors));
// 余弦弧度
double cRadian = cos(2.0 * CV_PI * i / static_cast<double>(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));
// 小数部分
double tx = x - fx;
double ty = y - fy;

// 设置插值权重
double w1 = (1 - tx) * (1 - ty);
double w2 = tx * (1 - ty);
double w3 = (1 - tx) * ty;
double w4 = tx * ty;

// 循环处理图像数据
{
{
// 计算插值
double t1 = w1 * cvGetReal2D(src, rows + fy, cols + fx);
double t2 = w2 * cvGetReal2D(src, rows + fy, cols + cx);
double t3 = w3 * cvGetReal2D(src, rows + cy, cols + fx);
double t4 = w4 * cvGetReal2D(src, rows + cy, cols + cx);
double t = t1 + t2 + t3 + t4;

double val = cvGetReal2D(src, rows, cols);
double epsilon = std::numeric_limits<double>::epsilon();

uchar c = ((t > val) || abs(t - val) < epsilon);
uchar tmp = c * pow(2, i);
v += tmp;
}
}

}

}

// 计算 LPB 特征值
void LBP::calLBPFeatures(IplImage* srcImg, IplImage* dstImg, VecDouble& hist, LBP_TYPE lbpType)
{
assert(srcImg != NULL && srcImg->nChannels == 1);
assert(dstImg != NULL && dstImg->nChannels == 1);

// 计算 LBP 图像
if (lbpType == LBP_NORMAL)
{
calLBP(srcImg, dstImg);
}
else if (lbpType == LBP_UNIFORM)
{
calUniformLBP(srcImg, dstImg);
}
else if (lbpType == LBP_CIRCLE)
{

try
{
if (m_radius == 0 || m_neighbor == 0)
{
}
else
{
}
}
catch (char* e)
{
cout << e << endl;
}

}// end if

// 计算直方图
VecInt vec;
vec.resize(256);

for (int i = 0; i < dstImg->width; ++i)
{
for (int j = 0; j < dstImg->height; ++j)
{
CvScalar s = cvGet2D(dstImg, j, i);
int val = s.val[0];
++vec[val];
}
}// end for

// 将直方图归一化
int maxVal = *max_element(vec.begin(), vec.end());
int minVal = *min_element(vec.begin(), vec.end());

for (int i = 0; i < vec.size(); ++i)
{
double tmp = 0.0;
tmp = static_cast<double>(vec[i]) / static_cast<double>(maxVal);
hist.push_back(tmp);
}

}