基于 OpenCV 的 LBP + SVM 人脸识别

19 篇文章 1 订阅
18 篇文章 2 订阅

本文中对人脸的LBP特征的提取,采用了LBP的圆形算子,通过对ORL92112人脸库中的样本进行识别,据统计,训练集与测试集的准确率均达到了100%;

经LBP处理后的图像如下图所示:

如上图所示,左侧图像为原图像,右侧图像为提取出的LBP图像;利用LBP圆形算子,可以非常清晰描述出人脸特征;

故,可以利用LBP算子对人脸特征进行提取并识别,而且在处理过程中,不受图像的光照、旋转、角度等因素的影响;

算法的处理代码如下所述:

LBP算法头文件:

head.h:

#ifndef _HEAD_H
#define _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

#include "head.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 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

LBP.cpp

#include "stdafx.h"
#include "LBP.h"
#include <iostream>
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<double>(neighbors));
		// 余弦弧度
		double cRadian = cos(2.0 * CV_PI * i / static_cast<double>(neighbors));

		// 采样点的计算
		double x = static_cast<double>(-radius * sRadian);
		double y = static_cast<double>(radius * cRadian);

		// 下取整的值
		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;


		// 循环处理图像数据
		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<double>::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<double>(vec[i]) / static_cast<double>(maxVal);
		hist.push_back(tmp);
	}

}


  • 1
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值