您的位置:首页 > 编程语言 > C语言/C++

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

2016-12-15 13:17 555 查看
之前说要实现一个最简单的表情识别来着,用之前尝试的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……)

//祝感冒菌早点回家。

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐