机器学习之——简单表情识别

      自开学以来,我们导师就让我们研究在线视频教育下的微表情识别。上学期课程太多,一直没时间写微博,趁着现在做项目,正好借此机会回顾一下之前学的机器学习。

      从研究的角度讲,表情识别是一种典型的模式识别问题,对于表情的识别,通常采用机器学习的方式对表情数据进行训练,然后用训练好的分类器或回归器对表情进行预测。

一、表情识别基本步骤

1、图像输入

     输入的图像一般来自一些公开的表情数据集,如JAFFE,CK,NVIE,IMM,Recola,Semaine等,当然,小的测试,也可用自己或身边人的照片,但是有可能会出现过拟合。

2、人脸检测

      在表情识别过程中,输入的图像,除了我们的人脸,还包括的非人脸区域,因此,需用人脸检测算法检测出我们感兴趣的人脸区域。

3、图像预处理

      得到的人脸图片,他们的大小,长宽比例,光照条件通常是不一样的,因此,为了提取的特征的时候可以统一处理,需对图片的尺寸、光照等进行归一化处理。

4、特征提取

      通常用于区分不同的表情,因此,需选择区分度高的特征给计算机。特征可分为纹理特征和几何特征,换句话说,可分为全局特征和局部特征,特征提取的好坏直接影响后续的分类或回归结果。常用的特征提取方法有LBP,Gabor,SIFT,DCT,FAP等。

5、模式分类\回归

      主要是用机器学习算法对提取到的特征进行训练,训练完后会得到一个分类器或回归器。常用的机器学习算法有:SVM、KNN、NN(Neural Network)、HMM、SVR、RVM等

6、识别结果

      简言之,就是把经过前面几步提取到的表情特征送入训练好的分类器或回归器,让分类器或回归器给出一个预测的值。

二、简单表情识别的实现

相关环境配置

Dlib提取特征点:http://blog.csdn.net/zmdsjtu/article/details/53454071

利用opencv3实现SVM分类器:http://blog.csdn.net/zmdsjtu/article/details/53610244

Dlib+opencv实时提取人脸轮廓:https://blog.csdn.net/zmdsjtu/article/details/52422847

人脸库下载链接:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

主要思路

训练模块:

    1、人做出特定的表情后,提取Dlib特征点并进行归一化。

    2、每张图片存储136维数据存储进行特定编号的TXT,方便调用。

    3、利用SVM分类器进行分类,训练成XML

测试模块:

    读入训练好的XML,对每帧图片进行分类,显示结果。

相关代码

训练部分:

1、特征点采集并存入TXT

//2018-4-2
#include 
#include 
#include 
#include 
#include 
#include 
#include

using namespace dlib;
using namespace std;
void 写入TXT(string 名字, string 内容, bool 是否覆盖);
int main()
{
	try
	{		
		cv::VideoCapture cap(0);
		
		// 加载人脸检测和姿态估计模型
		frontal_face_detector detector = get_frontal_face_detector();
		shape_predictor pose_model;
		deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;
		std::vector faces2;
		// 抓取并处理框架,直到用户关闭主窗口
		int 文件名 = 0;			    
		if (!cap.isOpened())
		{
			cerr << "Unable to connect to camera" << endl;
			return 1;
		}
		int 帧数 = cap.get(7);
		while (cv::waitKey(30) != 27)
		{
			// 抓取框架
			cv::Mat temp;
			cap >> temp;			
			cv_image cimg(temp);
			// 面部检测
			std::vector faces = detector(cimg);
			if (!faces.empty())faces2 = faces;
			// 找出每张脸的状态
			std::vector shapes;
			if (faces.empty()) {                     
			}
			else {
				for (unsigned long i = 0; i < faces.size(); ++i)
					shapes.push_back(pose_model(cimg, faces[i]));
			}
			if (!shapes.empty()) {
				cv::line(temp, cvPoint(faces[0].left(), faces[0].top()), cvPoint( faces[0].right(), faces[0].top()), cv::Scalar(255, 0, 0));
				cv::line(temp, cvPoint(faces[0].left(), faces[0].top()), cvPoint(faces[0].left(), faces[0].bottom()), cv::Scalar(255, 0, 0));	
				文件名++;
				float 系数 =  -(faces[0].top() - faces[0].bottom()) / 300.0;
				cout << 系数 << endl;
				for (int i = 0; i < 68; i++) {
					circle(temp, cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), 3, cv::Scalar(0, 0, 255), -1);
						putText(temp, to_string(i), cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), CV_FONT_HERSHEY_PLAIN, 1, cv::Scalar(255, 0, 0), 1, 4);
					   //--------------这部分是想用来采集训练数据的
							
					  写入TXT((to_string(文件名)+".txt"), to_string((shapes[0].part(i).x()-faces[0].left())/ 系数), 0);
					 写入TXT((to_string(文件名) + ".txt"), to_string((shapes[0].part(i).y()- faces[0].top())/ 系数), 0);
					//----------------------------------------------------------------------------
					}
				}
			imshow("Dlib特征点", temp);
			}
		cv::destroyAllWindows();
	}
	catch (serialization_error& e)
	{
		cout << "You need dlib's default face landmarking model file to run this example." << endl;
		cout << "You can get it from the following URL: " << endl;
		cout << "   http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2" << endl;
		cout << endl << e.what() << endl;
	}
}
void 写入TXT(string 名字, string 内容, bool 是否覆盖) {
	ofstream 写入(名字, 是否覆盖 ? ios::trunc : ios::app);
	if (写入.is_open()) {//如果成功的话       
		写入 << 内容 << endl;
		写入.close();
	}
}

2、对采集的数据进行读取,即读入TXT并进行训练存入XML

#include 
#include
#include    
using namespace std;
using namespace cv;
using namespace cv::ml;

void 读入数据(float 数组[150][136], int 开始, string 目录);
int main()
{
	float 一个数组[150][136];
	读入数据(一个数组, 0, "平静");
	读入数据(一个数组, 50, "高兴");
	读入数据(一个数组, 100, "厌恶");
	int 人脸标签[150];
	for (int i = 0; i < 50; i++) {
		人脸标签[i] = 170;
		人脸标签[i+50] = 250;
		人脸标签[i + 100] = 300;
	}
	//转为Mat以调用
	Mat 训练Mat(150, 136, CV_32FC1, 一个数组);//这边数据类型要处理好,不然粗事儿
	Mat 标签label(150, 1, CV_32SC1, 人脸标签);
	//训练的初始化
	Ptr svm = SVM::create();
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
	//开始训练
	svm->train(训练Mat, ROW_SAMPLE, 标签label);
	svm->save("SVM_DATA.xml");
}
void 读入数据(float 数组[150][136], int 开始, string 目录) {
	fstream 读文件;
	for (int i = 0; i < 50; i++) {
		读文件.open(目录 +"\\"+ to_string(i+1) + ".txt");
		for (int j = 0; j < 136; j++) {
			string 读入的东西;
			读文件 >> 读入的东西;			
			数组[i+开始][j] = atof(读入的东西.c_str());
		}
		读文件.close();//关闭是个好习惯 
	}

}

测试部分:

利用训练好的XML进行表情识别

#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")//去除CMD窗口
#include   
#include   
#include   
#include   
#include   
#include   

using namespace dlib;
using namespace std;
using namespace cv::ml;
int main()
{
	try
	{
		cv::VideoCapture cap(0);
		if (!cap.isOpened())
		{
			cerr << "Unable to connect to camera" << endl;
			return 1;
		} 
		// 加载人脸检测和姿态估计模型
		frontal_face_detector detector = get_frontal_face_detector();
		shape_predictor pose_model;
		deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;
               //读取XMl
		cv::Ptr svm = StatModel::load("SVM_DATA.xml");
		// 抓取并处理框架,直到用户关闭主窗口。
		while (cv::waitKey(30) != 27)
		{
			cv::Mat temp;
			cap >> temp;
			cv_image cimg(temp);
			// 面部检测
			std::vector faces = detector(cimg);
			//找出每张脸的状态 
			std::vector shapes;
			for (unsigned long i = 0; i < faces.size(); ++i)
				shapes.push_back(pose_model(cimg, faces[i]));
			if (!shapes.empty()) {
				float testData[1][136];
				float 系数 = -(faces[0].top() - faces[0].bottom()) / 300.0;
				for (int i = 0; i < 68; i++) {
					circle(temp, cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), 2, cv::Scalar(255, 0, 0), -1);
					testData[0][i * 2] = (shapes[0].part(i).x() - faces[0].left()) / 系数;
					testData[0][i * 2+1] = (shapes[0].part(i).y() - faces[0].top()) / 系数;
					//  shapes[0].part(i).x();//68个  
				}
				cv::Mat 结果;			
				cv::Mat query(1, 136, CV_32FC1, testData);
				if (svm->predict(query) == 250) {
					cv::putText(temp, "Happy" , cv::Point(20, 60),3, 2, cvScalar(0, 0, 255));
					cout << "高兴" << endl;
				}					
				if (svm->predict(query) == 170) {
					cv::putText(temp, "Common", cv::Point(20, 60), 3, 2, cvScalar(0, 0, 255));
					cout << "平静" << endl;
				}
				if (svm->predict(query) == 300) {
					cv::putText(temp, "Disgust", cv::Point(20, 60), 3, 2, cvScalar(0, 0, 255));
					cout << "厌恶" << endl;
				}
			}
			//在屏幕上显示所有  
			imshow("表情识别      ESC退出", temp);
		}
	}
	catch (serialization_error& e)
	{
		cout << "You need dlib's default face landmarking model file to run this example." << endl;
		cout << "You can get it from the following URL: " << endl;
		cout << "   http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2" << endl;
		cout << endl << e.what() << endl;
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
}

以上代码是我在别人博客中看到了,觉得挺不错的,因此保存了下来。本人刚接触这方面的知识,很多地方都不是很了解。(此篇博客纯属个人梳理和记录学习所写,有不当之处,望指正)。


你可能感兴趣的:(个人笔记)