本文中对人脸的LBP特征的提取,采用了LBP的圆形算子,通过对ORL92112人脸库中的样本进行识别,据统计,训练集与测试集的准确率均达到了100%;
经LBP处理后的图像如下图所示:
如上图所示,左侧图像为原图像,右侧图像为提取出的LBP图像;利用LBP圆形算子,可以非常清晰描述出人脸特征;
故,可以利用LBP算子对人脸特征进行提取并识别,而且在处理过程中,不受图像的光照、旋转、角度等因素的影响;
算法的处理代码如下所述:
LBP算法头文件:
head.h:
#ifndef _HEAD_H
#define _HEAD_H
#include
#include
#include
#include
#include
#include
#include
#include
#endif // _HEAD_H
LBP.h
#ifndef _LBP_H
#define _LBP_H
#include "head.h"
typedef std::vector VecDouble;
typedef std::vector VecInt;
typedef std::vector 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 setRadius(int radius) { m_radius = radius; }
// 设置领域数
void setNeighbors(int neighbor) { m_neighbor = neighbor; }
// 计算特征值
void calLBPFeatures(IplImage* srcImg, IplImage* dstImg, VecDouble& hist, LBP_TYPE lbpType);
private:
int m_radius;
int m_neighbor;
};
#endif // _LBP_H
#include "stdafx.h"
#include "LBP.h"
#include
using namespace std;
LBP::LBP()
: m_radius(0)
, 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(neighbors));
// 余弦弧度
double cRadian = cos(2.0 * CV_PI * i / static_cast(neighbors));
// 采样点的计算
double x = static_cast(-radius * sRadian);
double y = static_cast(radius * cRadian);
// 下取整的值
int fx = static_cast(floor(x));
int fy = static_cast(floor(y));
// 上取整的值
int cx = static_cast(ceil(x));
int cy = static_cast(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;
// 循环处理图像数据
for (int rows = radius; rows < src->height - radius; ++rows)
{
for (int cols = radius; cols < src->width - radius; ++cols)
{
// 计算插值
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::epsilon();
uchar c = ((t > val) || abs(t - val) < epsilon);
uchar tmp = c * pow(2, i);
double v = cvGetReal2D(dst, rows - radius, cols - radius);
v += tmp;
cvSetReal2D(dst, rows - radius, cols - radius, v);
}
}
}
}
// 计算 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)
{
throw "please call setRadius() and setNeighbor() to set values.";
}
else
{
calCirLBP(srcImg, dstImg, m_radius, m_neighbor);
}
}
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(vec[i]) / static_cast(maxVal);
hist.push_back(tmp);
}
}