表情识别的简单实现(实时,C++,SVM+Dlib)



之前说要实现一个最简单的表情识别来着,用之前尝试的SVM分类器加上Dlib特征点的归一化之后。每个表情训练了50张图片,测试结果如下:

//这里简单分了三类:高兴,厌恶和平静


厌恶:



平静:


高兴:



动图如下:




--------------------------------------------准备工作-------------------------------------



之前也写过利用Opencv3实现SVM分类器:http://blog.csdn.net/zmdsjtu/article/details/53610244

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


在这些的基础之上我们来进行表情的分类。



--------------------------------------------下面进入正片-------------------------------------


所有程序CPP+训练模型+训练原始数据打包下载地址:http://download.csdn.net/detail/zmdsjtu/9712182



主要思路:(训练模块+测试模块)

1.训练模块:

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

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

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

2.测试模块:

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


先放下测试部分的代码:

和Dlib提取特征点的代码没有很大的区别,只有一行很关键的代码(读取XML)

		cv::Ptr<SVM> svm = StatModel::load<SVM>("SVM_DATA.xml");


------------------利用训练好的XML进行表情识别的测试代码-------------------

#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")//去除CMD窗口
#include <dlib/opencv.h>  
#include <opencv2/opencv.hpp>  
#include <dlib/image_processing/frontal_face_detector.h>  
#include <dlib/image_processing/render_face_detections.h>  
#include <dlib/image_processing.h>  
#include <dlib/gui_widgets.h>  
//@author 2016-12-15 朱铭德 zmdsjtu@163.com
using namespace dlib;
using namespace std;
//using namespace cv;
using namespace cv::ml;
int main()
{
	try
	{
		cv::VideoCapture cap(0);
		if (!cap.isOpened())
		{
			cerr << "Unable to connect to camera" << endl;
			return 1;
		}

		//image_window win;  

		// Load face detection and pose estimation models.  
		frontal_face_detector detector = get_frontal_face_detector();
		shape_predictor pose_model;
		deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;

		cv::Ptr<SVM> svm = StatModel::load<SVM>("SVM_DATA.xml");

		// Grab and process frames until the main window is closed by the user.  
		while (cv::waitKey(30) != 27)
		{
			// Grab a frame  
			cv::Mat temp;
			cap >> temp;

			cv_image<bgr_pixel> cimg(temp);
			// Detect faces   
			std::vector<rectangle> faces = detector(cimg);
			// Find the pose of each face.  
			std::vector<full_object_detection> 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;
				}
				//	cout<<	svm->predict(query)<<endl;
			//	cout << 结果 << endl;
			}
			//Display it all on the screen  
			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;
	}
}


训练模块:


①训练部分采集用代码:(采集特征点并存入TXT)

//@zmdsjtu@163.com
//2016-12-15
//#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")//去除CMD窗口
#include <dlib/opencv.h>
#include <opencv2/opencv.hpp>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include<string>

using namespace dlib;
using namespace std;
void 写入TXT(string 名字, string 内容, bool 是否覆盖);
int main()
{

	try
	{
		
		cv::VideoCapture cap(0);

		//image_window win;

		// Load face detection and pose estimation models.
		frontal_face_detector detector = get_frontal_face_detector();
		shape_predictor pose_model;
		deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;
		std::vector<rectangle> faces2;
		// Grab and process frames until the main window is closed by the user.
		int 文件名 = 0;
		//	while (cv::waitKey(30) != 27)

	//	while (cv::waitKey(30) != 27){
		//	cv::VideoCapture cap("1.avi");
			if (!cap.isOpened())
		{
			cerr << "Unable to connect to camera" << endl;
			return 1;
		}

			int 帧数 = cap.get(7);
		//	while (cv::waitKey(30) != 27 && --帧数 != 14)

			while (cv::waitKey(30) != 27)
			{
				// Grab a frame
				cv::Mat temp;
				cap >> temp;
				//	temp = cv::imread("1.bmp");
				cv_image<bgr_pixel> cimg(temp);
				// Detect faces 
				std::vector<rectangle> faces = detector(cimg);
				if (!faces.empty())faces2 = faces;
				// Find the pose of each face.
				std::vector<full_object_detection> shapes;
				if (faces.empty()) {

					//		for (unsigned long i = 0; i < faces2.size(); ++i)
							//	shapes.push_back(pose_model(cimg, faces2[i]));
				}
				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);
					//----------------------------------------------------------------------------
					}
			//		Drawarrow(temp, shapes[0].part(36).x(), shapes[0].part(48).x(), shapes[0].part(30).x(), shapes[0].part(45).x(), shapes[0].part(54).x(), shapes[0].part(36).y(), shapes[0].part(48).y(), shapes[0].part(30).y(), shapes[0].part(45).y(), shapes[0].part(54).y());
				}
				//Display it all on the screen
		//		cv::namedWindow("Dlib特征点", CV_WINDOW_NORMAL);
		//		cv::setWindowProperty("Dlib特征点", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
				imshow("Dlib特征点", temp);
				//	cv::waitKey(-1);
			}
		//}
		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;
	}
	//catch (exception& e)
	//{
		//cout << e.what() << endl;
	//}
}
void 写入TXT(string 名字, string 内容, bool 是否覆盖) {
	ofstream 写入(名字, 是否覆盖 ? ios::trunc : ios::app);

	if (写入.is_open()) {//如果成功的话       
		写入 << 内容 << endl;
		写入.close();
	}
}


②训练(读入TXT并进行训练存入XML):

#include<opencv2\opencv.hpp> 
#include<string>
#include<fstream>    
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;
	}

	//训练需要用到的数据
	//	int 标签[4] = { 1, 2, 3, 4 };
	//	float 训练数据[4][2] = { { 31, 12 },{ 65, 220 },{ 440, 350 },{ 400, 400 } };
	//转为Mat以调用
	Mat 训练Mat(150, 136, CV_32FC1, 一个数组);//这边数据类型要处理好,不然粗事儿
	Mat 标签label(150, 1, CV_32SC1, 人脸标签);
	//训练的初始化
	Ptr<SVM> 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 读文件;
	//  读文件.imbue(std::locale("chs")); 


	for (int i = 0; i < 50; i++) {
		读文件.open(目录 +"\\"+ to_string(i+1) + ".txt");
		for (int j = 0; j < 136; j++) {
			string 读入的东西;
			读文件 >> 读入的东西;
//			cout << 读入的东西;			
			数组[i+开始][j] = atof(读入的东西.c_str());

		}
		读文件.close();//关闭是个好习惯 
	}

}


//有点感冒了,裹着最厚的羽绒服来码点字儿……(有点病恹恹的感觉,皂片凑合着看,hah……)



//祝感冒菌早点回家。


欢迎留言交流哈,祝开发愉快~



评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱铭德

五毛也是爱٩(●´৺`●)૭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值