基于LBP和SVM的人脸表情识别

近期使用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;
}

你可能感兴趣的:(opencv,计算机视觉,人脸识别)