近期使用C++编写LBP和SVM进行人脸表情检测,经过实际测试,效果没有其他博主的那么好,把代码贴上来,大家帮忙提些意见,看看是哪里出了问题。测试数据库为JAFFE人脸表情数据库。
1、LBP特征提取+SVM训练生成训练模型
#include
#include
#include
#include
#include
#include
#include
#include //查找文件相关函数
using namespace std;
using namespace cv;
using namespace cv::ml;
//计算原始LBP值
void getOriginLBPFeature(Mat& src, Mat& dst)
{
dst.create(src.rows - 2, src.cols - 2, CV_8UC1);
dst.setTo(0);
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
uchar center = src.at<uchar>(i, j);
unsigned char lbpCode = 0;
lbpCode |= (src.at<uchar>(i - 1, j - 1) > center) << 7;
lbpCode |= (src.at<uchar>(i - 1, j) > center) << 6;
lbpCode |= (src.at<uchar>(i - 1, j + 1) > center) << 5;
lbpCode |= (src.at<uchar>(i, j + 1) > center) << 4;
lbpCode |= (src.at<uchar>(i + 1, j + 1) > center) << 3;
lbpCode |= (src.at<uchar>(i + 1, j) > center) << 2;
lbpCode |= (src.at<uchar>(i + 1, j - 1) > center) << 1;
lbpCode |= (src.at<uchar>(i, j - 1) > center) << 0;
dst.at<uchar>(i - 1, j - 1) = lbpCode;
}
}
}
//计算一个LBP特征图像块的直方图
Mat getLocalRegionLBPH(const Mat& src, int minValue, int maxValue, bool normed)
{
//定义存储直方图的矩阵
Mat result;
//计算得到直方图bin的数目,直方图数组的大小
int histSize = maxValue - minValue + 1;
//定义直方图每一维的bin的变化范围
float range[] = { static_cast<float>(minValue),static_cast<float>(maxValue + 1) };
//定义直方图所有bin的变化范围
const float* ranges = { range };
//计算直方图,src是要计算直方图的图像,1是要计算直方图的图像数目,0是计算直方图所用的图像的通道序号,从0索引
//Mat()是要用的掩模,result为输出的直方图,1为输出的直方图的维度,histSize直方图在每一维的变化范围
//ranges,所有直方图的变化范围(起点和终点)
calcHist(&src, 1, 0, Mat(), result, 1, &histSize, &ranges, true, false);
//归一化
if (normed)
{
result /= (int)src.total();
}
//结果表示成只有1行的矩阵
return result.reshape(1, 1);
}
//计算LBP特征图像的直方图LBPH
Mat getLBPH(Mat& src, int numPatterns, int grid_x, int grid_y, bool normed)
{
int width = src.cols / grid_x;
int height = src.rows / grid_y;
int pca_num = 128;//降至128维
Mat result_PCA;//存放降维后的特征向量
//定义LBPH的行和列,grid_x*grid_y表示将图像分割成这么些块,numPatterns表示LBP值的模式种类
Mat result = Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1);
if (src.empty())
{
return result.reshape(1, 1);
}
int resultRowIndex = 0;
//对图像进行分割,分割成grid_x*grid_y块,grid_x,grid_y默认为8
for (int i = 0; i < grid_x; i++)
{
for (int j = 0; j < grid_y; j++)
{
//图像分块
Mat src_cell = Mat(src, Range(i * height, (i + 1) * height), Range(j * width, (j + 1) * width));
//计算直方图
Mat hist_cell = getLocalRegionLBPH(src_cell, 0, (numPatterns - 1), true);
//将直方图放到result中
Mat rowResult = result.row(resultRowIndex);
hist_cell.reshape(1, 1).convertTo(rowResult, CV_32FC1);
resultRowIndex++;
}
}
//return result.reshape(1, 1);
//加入PCA降维
PCA pca(result, Mat(), PCA::DATA_AS_ROW, pca_num);
result_PCA = pca.eigenvectors.clone();//eigenvectors用来存放降维后的特征向量
return result_PCA.reshape(1, 1);
}
//将LBP直方图数据写入文件
void Write_Data(vector<Mat> M) {
ofstream outputfile;
outputfile.open("LBPH.txt");
Mat DATA;
vector<Mat>::iterator it;
for (it = M.begin(); it != M.end(); ++it)
{
DATA = *it;
outputfile << DATA << endl;
}
}
void getFiles(string path, vector<string>& files)
{
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p; int i = 30;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
//获取NE样本//并贴标签为1
void getNEBubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\NE"; //NE样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
Mat Gray_Image, dst1;
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
//normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(1);//该样本为NE图像
}
}
//获取AN样本//并贴标签为0
void getANBubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\AN"; //AN样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
//normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(0); //该样本为AN图像
}
}
//
获取DI样本//并贴标签为2
void getDIBubble(Mat& trainingImages, vector<int>& trainingLabels)
{ string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\DI_JPEG"; //DI样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(2); //该样本为DI图像
}
}
获取FE样本//并贴标签为3
void getFEBubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\FE"; //FE样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(3); //该样本为FE图像
}
}
获取HA样本//并贴标签为4
void getHABubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\HA"; //HA样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(4); //该样本为HA图像
}
}
//获取SA样本//并贴标签为5
void getSABubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\SA"; //SA样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(5); //该样本为SA图像
}
}
//获取SU样本//并贴标签为6
void getSUBubble(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\Code\\Train_Image\\LOSO_Train\\Train_Image_1\\SU"; //SU样本路径
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
Mat Gray_Image, dst1;
cvtColor(SrcImage, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat LBPH_SrcImage = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
normalize(LBPH_SrcImage, LBPH_SrcImage);
trainingImages.push_back(LBPH_SrcImage);
trainingLabels.push_back(6); //该样本为SU图像
}
}
int main(int argc, char** argv)
{
Mat classes;
Mat TrainingData;
Mat TrainingImages;
vector<int> TrainingLabels;
//getBubble()与getNoBubble()将获取一张图片后会将图片(特征)写入
// 到容器中,紧接着会将标签写入另一个容器中,这样就保证了特征
// 和标签是一一对应的关系push_back(0)或者push_back(1)其实就是
// 我们贴标签的过程。
getNEBubble(TrainingImages, TrainingLabels);
getANBubble(TrainingImages, TrainingLabels);
getDIBubble(TrainingImages, TrainingLabels);
getFEBubble(TrainingImages, TrainingLabels);
getHABubble(TrainingImages, TrainingLabels);
getSABubble(TrainingImages, TrainingLabels);
getSUBubble(TrainingImages, TrainingLabels);
//在主函数中,将getBubble()与getNoBubble()写好的包含特征的矩阵拷贝给trainingData,将包含标签的vector容器进行类
//型转换后拷贝到trainingLabels里,至此,数据准备工作完成,trainingData与trainingLabels就是我们要训练
Mat(TrainingImages).copyTo(TrainingData);
TrainingData.convertTo(TrainingData, CV_32FC1);
Mat(TrainingLabels).copyTo(classes);
//classes.convertTo(classes, CV_32SC1);
// 创建分类器并设置参数
Ptr<SVM> SVM_params = SVM::create();
SVM_params->setType(SVM::C_SVC);
SVM_params->setKernel(SVM::RBF); //核函数
SVM_params->setDegree(0);
SVM_params->setGamma(1);
SVM_params->setCoef0(0);
SVM_params->setC(1);
SVM_params->setNu(0);
SVM_params->setP(0);
SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));
Ptr<TrainData> tData = TrainData::create(TrainingData, ROW_SAMPLE, classes);
// 训练分类器
//SVM_params->train(tData);
SVM_params->trainAuto(tData);
cout << "Training......" << endl;
//保存模型
SVM_params->save("svm.xml");
cout << "训练好了!!!" << endl;
getchar();
return 0;
}
2、使用训练好的模型进行测试
#include
#include
#include
#include
#include
#include
#include
#include //查找文件相关函数
using namespace std;
using namespace cv;
using namespace cv::ml;
//计算原始LBP值
void getOriginLBPFeature(Mat& src, Mat& dst)
{
dst.create(src.rows - 2, src.cols - 2, CV_8UC1);
dst.setTo(0);
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
uchar center = src.at<uchar>(i, j);
unsigned char lbpCode = 0;
lbpCode |= (src.at<uchar>(i - 1, j - 1) > center) << 7;
lbpCode |= (src.at<uchar>(i - 1, j) > center) << 6;
lbpCode |= (src.at<uchar>(i - 1, j + 1) > center) << 5;
lbpCode |= (src.at<uchar>(i, j + 1) > center) << 4;
lbpCode |= (src.at<uchar>(i + 1, j + 1) > center) << 3;
lbpCode |= (src.at<uchar>(i + 1, j) > center) << 2;
lbpCode |= (src.at<uchar>(i + 1, j - 1) > center) << 1;
lbpCode |= (src.at<uchar>(i, j - 1) > center) << 0;
dst.at<uchar>(i - 1, j - 1) = lbpCode;
}
}
}
//计算一个LBP特征图像块的直方图
Mat getLocalRegionLBPH(const Mat& src, int minValue, int maxValue, bool normed)
{
//定义存储直方图的矩阵
Mat result;
//计算得到直方图bin的数目,直方图数组的大小
int histSize = maxValue - minValue + 1;
//定义直方图每一维的bin的变化范围
float range[] = { static_cast<float>(minValue),static_cast<float>(maxValue + 1) };
//定义直方图所有bin的变化范围
const float* ranges = { range };
//计算直方图,src是要计算直方图的图像,1是要计算直方图的图像数目,0是计算直方图所用的图像的通道序号,从0索引
//Mat()是要用的掩模,result为输出的直方图,1为输出的直方图的维度,histSize直方图在每一维的变化范围
//ranges,所有直方图的变化范围(起点和终点)
calcHist(&src, 1, 0, Mat(), result, 1, &histSize, &ranges, true, false);
//归一化
if (normed)
{
result /= (int)src.total();
}
//结果表示成只有1行的矩阵
return result.reshape(1, 1);
}
//计算LBP特征图像的直方图LBPH
Mat getLBPH(Mat& src, int numPatterns, int grid_x, int grid_y, bool normed)
{
int width = src.cols / grid_x;
int height = src.rows / grid_y;
int pca_num = 128;//降至128维
Mat result_PCA;//存放降维后的特征向量
//定义LBPH的行和列,grid_x*grid_y表示将图像分割成这么些块,numPatterns表示LBP值的模式种类
Mat result = Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1);
if (src.empty())
{
return result.reshape(1, 1);
}
int resultRowIndex = 0;
//对图像进行分割,分割成grid_x*grid_y块,grid_x,grid_y默认为8
for (int i = 0; i < grid_x; i++)
{
for (int j = 0; j < grid_y; j++)
{
//图像分块
Mat src_cell = Mat(src, Range(i * height, (i + 1) * height), Range(j * width, (j + 1) * width));
//计算直方图
Mat hist_cell = getLocalRegionLBPH(src_cell, 0, (numPatterns - 1), true);
//将直方图放到result中
Mat rowResult = result.row(resultRowIndex);
hist_cell.reshape(1, 1).convertTo(rowResult, CV_32FC1);
resultRowIndex++;
}
}
//return result.reshape(1, 1);
//加入PCA降维
PCA pca(result, Mat(), PCA::DATA_AS_ROW, pca_num);
result_PCA = pca.eigenvectors.clone();//eigenvectors用来存放降维后的特征向量
return result_PCA.reshape(1, 1);
}
//将LBP直方图数据写入文件
void Write_Data(vector<Mat> M) {
ofstream outputfile;
outputfile.open("LBPH.txt");
Mat DATA;
vector<Mat>::iterator it;
for (it = M.begin(); it != M.end(); ++it)
{
DATA = *it;
outputfile << DATA << endl;
}
}
void getFiles(string path, vector<string>& files)
{
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p; int i = 30;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
int main(int argc, char** argv)
{
int result = 0;
string filePath = "D:\\Code\\Test_Image\\LOSO_Test\\Test_Image_1";
vector<string>files;
getFiles(filePath,files);
Ptr<SVM> svm = SVM::create();
string modelpath = "svm.xml";
FileStorage svm_fs(modelpath, FileStorage::READ);
if (svm_fs.isOpened())
{
svm = SVM::load(modelpath.c_str());
}
Mat Gray_Image, dst1;
int number = files.size();
cout << "共有测试图片" << number << "张" << endl;
for(int i=0; i<number; i++)
{
Mat inMat = imread(files[i].c_str());
cvtColor(inMat, Gray_Image, COLOR_BGR2GRAY);
getOriginLBPFeature(Gray_Image, dst1);
Mat p = getLBPH(dst1, 59, 4, 4, true).reshape(1, 1);
//normalize(p, p);
p.convertTo(p, CV_32FC1);
int response = (int)svm->predict(p);
if (response == 0)
{
result++;
}
cout << response << endl;
if (response == 1)
cout <<files[i]<< "这是一幅自然表情" << endl;
if (response == 0)
cout << files[i] << "这是一幅生气表情" << endl;
if (response == 2)
cout << files[i] << "这是一幅厌恶表情" << endl;
if (response == 3)
cout << files[i] << "这是一幅恐惧表情" << endl;
if (response == 4)
cout << files[i] << "这是一幅高兴表情" << endl;
if (response == 5)
cout << files[i] << "这是一幅悲伤表情" << endl;
if (response == 6)
cout << files[i] << "这是一幅惊讶表情" << endl;
}
//getchar();
return 0;
}