opencv手势识别(3_SVM算法识别)

手势识别系列博文3:SVM算法识别手势

前言

书山有路勤为径,学海无涯苦做舟
琴某人辛辛苦苦码的报告,当然不能让你们这么容易复制过去(๑• . •๑)
运行视频见链接:https://www.bilibili.com/video/BV1RN411d73D/

原理介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

1.程序中有很多冗余的函数
2.要运行此代码首先需要把轮廓的傅里叶描述子保存在本地,然后用其训练SVM模型,最后才能用训练好的参数进行预测。这些函数在程序中都能找到,所以一定要看懂程序。
3.代码有点乱,不想整了

#include <iostream>
#include <string>
#include <opencv2\opencv.hpp>
#include <stdio.h>
#include <opencv2/ml/ml.hpp>
#include <fstream>
using namespace cv;
using namespace std;


float desc[6*25][32];
int labels[6*25][1];
//-----调参窗口的回调函数-------------------------------------//
int minH = 2, maxH = 13;//肤色分割阈值白天建议3 14
int minR = 95, minG = 40, minB = 20, max_min = 15, absR_G= 10;
int minCr = 138, maxCr = 243, minCb = 77, maxCb = 127;
int ecl_x = 113, ecl_y = 156, leng_x = 24, leng_y = 23, ang =43;
int match_number = -1, temp_number = -1;//第几种手势中的第几个模板

//图片编号
int num = 0, flag = 0, hand_num = 0 ;//调试时用来保存图片
Mat frame, frameH, frameHSV, frameYCrCb; //不同颜色空间下的图片
Mat  RIOframe, RIOresult, resultRGB; //二值化得到的图像,识别出的皮肤区域,最终结果,将结果显示在原图
Mat allContRIO, delContRIO,delContframe; //所有轮廓二值图片, 筛选后轮廓二值图片, 筛选后轮廓的RGB图片
float fd[32];//提取到的傅里叶描述子

vector <Mat> RGBchannels, HSVchannels;     //RGB通道分离,HSV通道分离
vector< vector<Point> > mContoursProc;  //当前图片的轮廓
vector< vector< vector<Point>> > mContoursLib; //模板库轮廓5*6条
vector< vector< Mat > > tempImageLib;  //模板库照片


//-----调参窗口的回调函数-------------------------------------//
//HSV调参滑块的函数
void trackBarMinH(int pos, void* userdata) {}	//分割H通道时的最小值
void trackBarMaxH(int pos, void* userdata) {}	//分割H通道时的最大值
//RGB
void trackBarMinR(int pos, void* userdata) {}	//分割R通道时的最小值
void trackBarMinG(int pos, void* userdata) {}	//分割G通道时的最小值
void trackBarMinB(int pos, void* userdata) {}	//分割B通道时的最小值
void trackBarmax_min(int pos, void* userdata) {}//max(RGB)-min(RGB)
void trackBarabsR_G(int pos, void* userdata) {}	//absR_G
//YCrCb
void trackBarMinCr(int pos, void* userdata) {}	
void trackBarMaxCr(int pos, void* userdata) {}	
void trackBarMinCb(int pos, void* userdata) {}	
void trackBarMaxCb(int pos, void* userdata) {}	
//YCrCb_eclipse
void trackBarecl_x(int pos, void* userdata) {}	
void trackBarecl_y(int pos, void* userdata) {}	
void trackBarleng_x(int pos, void* userdata) {}	
void trackBarleng_y(int pos, void* userdata) {}	
void trackBarang(int pos, void* userdata) {}	

//-----6种肤色识别方法-------------------------------------//
void hand_HSV();
void hand_RGB();
void hand_YCbCr_ellipse();//椭圆模型
void hand_YCbCr();
void hand_YCbCr_Otsu();
void hand_opencv();

//-------手势识别的功能函数----------------------------------//
void loadTemplate();// 载入模板的轮廓
void find_contours(Mat srcImage);//提取二值化图形的边界
void calcute_fft();//计算傅里叶描述子,这里没有用到
void hand_match();//与模板进行匹配
void draw_result();//得到匹配结果
void trainSVM();//训练svm模型
void predictSVM();//根据svm模型识别手势

int main()
{
	//训练svm模型
	trainSVM();
	VideoCapture capture("handData//hand.mp4");
	while (true)
	{
		//获取图片帧
		capture >> frame;
		flag++;
		if(flag % 3 == 0)
		{
			//break;
			continue;
		}

		if (true == frame.empty())
		{
			cout << "get no frame" << endl;
			break;
		}
		resize(frame, frame, Size(frame.cols*0.3, frame.rows*0.3));//降采样
		//cout<<frame.size()<<endl;

		namedWindow("1.原始图片", CV_WINDOW_NORMAL);
		imshow("1.原始图片",frame);

		//--------------------------滤波处理------------------------------------------//
		//medianBlur(frameH, frameH, 5);	//中值滤波,用来去除椒盐噪声
		//GaussianBlur(frame, frame, Size(7, 7), 1, 1);// 高斯滤波,用来平滑图像
		//namedWindow("2.滤波后的图像", CV_WINDOW_NORMAL);
		//imshow("2.滤波后的图像",frame);
	
		//--------------------------6种肤色检测方法------------------------------------//
	    //hand_HSV();
		//hand_RGB();
		//hand_YCbCr_ellipse();//椭圆模型
		hand_YCbCr();
		//hand_YCbCr_Otsu();
		//hand_opencv();
	    //namedWindow("3.肤色分割之后的图片", CV_WINDOW_NORMAL);
	    //imshow("3.肤色分割之后的图片", RIOframe);// 显示肤色分割之后的图片


	
		----------------------------------------形态学运算-----------------------------//
		Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5)); //函数返回指定形状和尺寸的结构元素
		//morphologyEx(RIOresult, RIOresult, MORPH_CLOSE, kernel);//函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换
	    //morphologyEx(RIOresult, RIOresult, MORPH_OPEN, kernel);//函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换
	    namedWindow("4.形态学处理后的图片", CV_WINDOW_NORMAL);
	    imshow("4.形态学处理后的图片", RIOframe);// 显示肤色分割之后的图片


		----------------------------------------检测边缘-----------------------------//
		find_contours(RIOresult);

		flag++;
		if(flag % 5 == 1)
		{ 
			num ++;
			calcute_fft( );//计算傅里叶描述子
		};

		//----------------------------------------SVM分类-----------------------------//
		predictSVM();
		//----------------------------------------绘制结果-----------------------------//
		draw_result();

		RIOframe.setTo(0);//否则会出现重影
		waitKey(1);
	}
	system("pause");
	return 0;
}

//--------手势识别的功能函数----------------------------------//
//-----6种肤色识别方法-------------------------------------//
void hand_HSV()
{
	//----------------------------肤色分割调参窗口---------------------//
	//namedWindow("调参窗口", CV_WINDOW_AUTOSIZE);
	//createTrackbar("H_min", "调参窗口", &minH, 20, trackBarMinH);
	//createTrackbar("H_max", "调参窗口", &maxH, 40, trackBarMaxH);

	cvtColor(frame, frameHSV, CV_BGR2HSV);//在opencv中,其默认的颜色制式排列是BGR而非RGB
	split(frameHSV, HSVchannels);//分离后, channels[0]对应H, channels[1]对应S, channels[2]对应
	frameH = HSVchannels[0];
	//namedWindow("2.H通道图片", CV_WINDOW_NORMAL);
	//imshow("2.H通道图片", frameH);//显示H通道图片

	//--------------------------------------滤波平滑-----------------------------------// 
	//medianBlur(frameH, frameH, 7);	// 中值滤波,可以很好的去除椒盐噪声,而且ksize越大效果越好。
	//GaussianBlur(frameH, frameH, Size(5, 5), 1, 1);

	inRange(frameH, Scalar(minH), Scalar(maxH), RIOresult);
	frame.copyTo(RIOframe, RIOresult);
	 namedWindow("分割得到的RIO", CV_WINDOW_NORMAL);
	 imshow("分割得到的RIO",RIOresult);// 显示肤色分割之后的图片
	 namedWindow("提取到的区域RGB", CV_WINDOW_NORMAL);
	 imshow("提取到的区域RGB", RIOframe);// 显示肤色分割之后的图片
};

void hand_RGB()
{
	//----------------------------肤色分割调参窗口---------------------//
	//namedWindow("调参窗口", CV_WINDOW_AUTOSIZE);
	//createTrackbar("R_min", "调参窗口", &minR, 255, trackBarMinR);
	//createTrackbar("G_min", "调参窗口", &minG, 255, trackBarMinG);
	//createTrackbar("B_min", "调参窗口", &minB, 255, trackBarMinB);
	//createTrackbar("max_min","调参窗口",&max_min, 255, trackBarmax_min);
	//createTrackbar("R_G",   "调参窗口", &absR_G, 255, trackBarabsR_G);

	//imshow("4.肤色分割之后的图片", frame);
	Mat tempresult = Mat(frame.rows, frame.cols, CV_8UC3, Scalar(0));
	for (int i = 0; i < frame.rows; i++)
     {
        for (int j = 0; j < frame.cols;j++)
        {
			int r,g,b;
			r = frame.at<cv::Vec3b>(i,j)[2];
			g = frame.at<cv::Vec3b>(i,j)[1];
			b = frame.at<cv::Vec3b>(i,j)[0];

			if( r>minR && g>minG && b>minB && max(max (r,g),b) - min(min (r,g),b)> max_min && abs(r-g)>absR_G && r>g && r>b)
			//if( r>95 && g>40 && b>20 && max(max (r,g),b) - min(min (r,g),b)> 15 && abs(r-g)>15 && r>g && r>b )
			{
				tempresult.at<cv::Vec3b>(i,j) = Vec3b(255, 255, 255);
			}
			else if( r>220 && g>210 &&b>170 && abs(r-g)<=15 && r>b && g<b)
			{
				tempresult.at<cv::Vec3b>(i,j) = Vec3b(255, 255, 255);
			}
		}

	  };

	 RIOresult = tempresult.clone();
	 frame.copyTo(RIOframe, tempresult);
	 //namedWindow("分割得到的RIO", CV_WINDOW_NORMAL);
	 //imshow("分割得到的RIO",RIOresult);// 显示肤色分割之后的图片
	 //namedWindow("提取到的区域RGB", CV_WINDOW_NORMAL);
	 //imshow("提取到的区域RGB", RIOframe);// 显示肤色分割之后的图片

};

//椭圆模型
void hand_YCbCr_ellipse()
{
	//----------------------------肤色分割调参窗口---------------------//
	//namedWindow("调参窗口", CV_WINDOW_AUTOSIZE);
	//createTrackbar("ecl_x", "调参窗口", &ecl_x, 255, trackBarecl_x);
	//createTrackbar("ecl_y", "调参窗口", &ecl_y, 255, trackBarecl_y);
	//createTrackbar("leng_x", "调参窗口", &leng_x, 255, trackBarleng_x);
	//createTrackbar("leng_y", "调参窗口", &leng_y, 255, trackBarleng_y);
	//createTrackbar("ang", "调参窗口", &ang, 360, trackBarang);

	/*椭圆皮肤模型*/
	Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);
    //ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 23.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);
	ellipse(skinCrCbHist, Point(ecl_x, ecl_y), Size(leng_x, leng_y), ang, 0.0, 360.0, Scalar(255, 255, 255), -1);

	cvtColor(frame, frameYCrCb , CV_BGR2YCrCb);
	Mat tempresult = Mat(frame.rows, frame.cols, CV_8UC3, Scalar(0));
	for (int i = 0; i < frame.rows; i++)
     {
        for (int j = 0; j < frame.cols;j++)
        {
			int y, cr, cb;
			y = frameYCrCb.at<cv::Vec3b>(i,j)[0];
			cr = frameYCrCb.at<cv::Vec3b>(i,j)[1];
			cb = frameYCrCb.at<cv::Vec3b>(i,j)[2];
            
			if( skinCrCbHist.at<uchar>(cr,cb) >0 )
			{
			 tempresult.at<cv::Vec3b>(i,j) = Vec3b(255, 255, 255);
			}



			//if( cr>minCr && cr<maxCr && cb>minCb && cb<maxCb )
			//{
			//	tempresult.at<cv::Vec3b>(i,j) = Vec3b(255, 255, 255);
			//}
		}
	  };
	//imshow("tuoyuan",skinCrCbHist);
	 namedWindow("4.肤色分割之后的图片", CV_WINDOW_NORMAL);
	 imshow("4.肤色分割之后的图片",tempresult);// 显示肤色分割之后的图片

	RIOresult = tempresult.clone();

	frame.copyTo(RIOframe, tempresult);
	namedWindow("12.肤色分割之后的图片", CV_WINDOW_NORMAL);
	imshow("12.肤色分割之后的图片", RIOframe);// 显示肤色分割之后的图片
	//imwrite("data\\YCbCr.jpg",RIOframe);

	 waitKey(1);
};

void hand_YCbCr()
{
	//----------------------------肤色分割调参窗口---------------------//
	//namedWindow("调参窗口", CV_WINDOW_AUTOSIZE);
	//createTrackbar("Cr_min", "调参窗口", &minCr, 255, trackBarMinCr);
	//createTrackbar("Cr_max", "调参窗口", &maxCr, 255, trackBarMaxCr);
	//createTrackbar("Cb_min", "调参窗口", &minCb, 255, trackBarMinCb);
	//createTrackbar("Cb_max", "调参窗口", &maxCb, 255, trackBarMaxCb);

	cvtColor(frame, frameYCrCb , CV_BGR2YCrCb);
	Mat tempresult = Mat(frame.rows, frame.cols, CV_8UC3, Scalar(0));
	//for (int i = 0; i < frame.rows; i++)
 //    {
 //       for (int j = 0; j < frame.cols;j++)
 //       {
	//		int y, cr, cb;
	//		y = frameYCrCb.at<cv::Vec3b>(i,j)[0];
	//		cr = frameYCrCb.at<cv::Vec3b>(i,j)[1];
	//		cb = frameYCrCb.at<cv::Vec3b>(i,j)[2];          

	//		if( cr>minCr && cr<maxCr && cb>minCb && cb<maxCb )
	//		{
	//			tempresult.at<cv::Vec3b>(i,j) = Vec3b(255, 255, 255);
	//		}
	//	}
	//  };
		
	 inRange(frameYCrCb, Scalar(0,minCr,minCb), Scalar(255, maxCr, maxCb), RIOresult);
	 //namedWindow("分割得到的RIO", CV_WINDOW_NORMAL);
	 //imshow("分割得到的RIO",RIOresult);// 显示肤色分割之后的图片

	 frame.copyTo(RIOframe, RIOresult);
	 //namedWindow("提取到的区域RGB", CV_WINDOW_NORMAL);
	 //imshow("提取到的区域RGB", RIOframe);// 显示肤色分割之后的图片
};

void hand_YCbCr_Otsu()
{
	cvtColor(frame, frameYCrCb , CV_BGR2YCrCb);
	Mat tempresult = Mat(frame.rows, frame.cols, CV_8UC3, Scalar(0));

	Mat detect;
	vector<Mat> channels;
	split(frameYCrCb, channels);
	RIOresult = channels[1];
	threshold(RIOresult, RIOresult, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

	frame.copyTo(RIOframe, RIOresult);

	 //namedWindow("分割得到的RIO", CV_WINDOW_NORMAL);
	 //imshow("分割得到的RIO",RIOresult);// 显示肤色分割之后的图片
	 //namedWindow("提取到的区域RGB", CV_WINDOW_NORMAL);
	 //imshow("提取到的区域RGB", RIOframe);// 显示肤色分割之后的图片
};

void hand_opencv()
{
	IplImage *frame1;
    frame1 = &IplImage(frame);  //Mat -> IplImage
	CvAdaptiveSkinDetector filter(1, CvAdaptiveSkinDetector::MORPHING_METHOD_ERODE_DILATE);

	IplImage *maskImg = cvCreateImage(cvSize(frame.cols, frame.rows), IPL_DEPTH_8U, 1);
    IplImage *skinImg = cvCreateImage(cvSize(frame.cols, frame.rows), IPL_DEPTH_8U, 3);
	cvZero(skinImg);
	filter.process(frame1, maskImg);    // process the frame
	cvCopy(frame1, skinImg, maskImg);
    Mat tmp(skinImg);  //IplImage -> Mat
    RIOresult= tmp.clone();
    cvReleaseImage(&skinImg);
    cvReleaseImage(&maskImg);
   
	 frame.copyTo(RIOframe, RIOresult);
	 //namedWindow("分割得到的RIO", CV_WINDOW_NORMAL);
	 //imshow("分割得到的RIO",RIOresult);// 显示肤色分割之后的图片
	 //namedWindow("提取到的区域RGB", CV_WINDOW_NORMAL);
	 //imshow("提取到的区域RGB", RIOframe);// 显示肤色分割之后的图片

	 //waitKey(1);
};

//提取二值化图形的边界
void find_contours(Mat srcImage)
{
	Mat imageProc = srcImage.clone();
	Size sz = srcImage.size();//尺寸
	Mat draw = Mat::zeros(sz, CV_8UC3);
	vector< vector<Point> > mContours;//轮廓点集
	vector< Vec4i > mHierarchy;//轮廓之间的索引号
	//findContours只能处理单通的二值化图像
	Mat binframe;
	if(srcImage.channels() == 3)
	{	
		vector <Mat> channel;     
		split(srcImage, channel);//分离通道
		binframe = channel[0].clone();
	}
	else
	{
		binframe = srcImage.clone();
	}

	findContours(binframe, mContours, mHierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//只查找最外层轮廓

	mContoursProc.clear();//清空上次图像处理的轮廓

	if (mContours.size() > 0)
	{
		drawContours(draw, mContours, -1, Scalar(0, 0, 255), 2, 8 , mHierarchy);// 绘制所有轮廓()
		allContRIO = draw.clone();
		namedWindow("5.所有轮廓", CV_WINDOW_NORMAL);
		imshow("5.所有轮廓", allContRIO);//显示所有轮廓
	    //imwrite("data//frame6.jpg", allContRIO);

		double contArea = 0;
		double imageArea = sz.width * sz.height;
		const int SIZE = mContours.size(); 
		Rect bound; //Rect矩形类,矩形界限

		for (int i = 0; i < SIZE; i++)
		{
			contArea = contourArea(mContours[i]);
			if (contArea / imageArea < 0.015)// 过滤小面积的轮廓,原函数是0.015
			{
				continue;
			}
			mContoursProc.push_back(mContours[i]);//剩下的轮廓就是基本符合条件的轮廓,保存起来
		}
      
		draw = Scalar::all(0); //将矩阵所有元素赋值为某个值
		drawContours(draw, mContoursProc,0 , Scalar(0, 0, 255), 2, 8);
		delContRIO = draw.clone();
		namedWindow("6.过滤后的轮廓", CV_WINDOW_NORMAL);
		imshow("6.过滤后的轮廓", delContRIO); //显示过滤后的轮廓
		//imwrite("data//frame7.jpg", delContRIO); 

		delContframe = frame.clone();
		drawContours(delContframe, mContoursProc, -1, Scalar(0, 0, 255), 4, 8);
		namedWindow("8.原图的轮廓", CV_WINDOW_NORMAL);
		imshow("8.原图的轮廓", delContframe); //显示过滤后的轮廓
		imwrite("data//frame8.jpg", delContframe); 
		cout<<"lunjkuo:"<<mContoursProc.size()<<endl;
	}
}

//加载模板库中的图片
void loadTemplate()
{	
	//vector< vector<vector< vector<Point>>> > mContoursLib;  //模板库轮廓5*6条
	Mat tempImg;
	//读取5*6个模板
	for (int i = 0; i < 6; i++)
	{
		//vector< vector< Mat > > tempImageLib;  //模板库照片
		vector< Mat > tempImages;               //每种手势的模板集
		vector< vector<Point> > mContoursTemp;  //每种手势的的轮廓模板集
		//vector< vector<vector< vector<Point>>> > mContoursLib;  //模板库轮廓5*6条
		for (int j = 0; j < 5; j++)
		{
			ostringstream oss;
		    oss<< "hand_data//template Library//"<< i <<"_"<<j<<".jpg";
			tempImg = imread(oss.str(),1);
			if (true == tempImg.empty())
			{
				cout << "Failed to load image: " <<  oss.str() << endl;
				continue;
			}
			tempImages.push_back(tempImg);
			//提取单通道
			Mat sigalframe;
			if(tempImg.channels() == 3)
			{	
				vector <Mat> channel;     
				split(tempImg, channel);//分离通道
				sigalframe = channel[0].clone();
			}
			else
			{
				sigalframe = tempImg.clone();
			}
			//提取轮廓
			vector< vector<Point> > mContours;//轮廓的点集
	        vector< Vec4i > mHierarchy;       //轮廓的点集的索引
		    findContours(sigalframe, mContours, mHierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));
			//筛选轮廓
			for (int i = 0; i < mContours.size(); i++)
			{
				double contArea = contourArea(mContours[i]);
				double imageArea = tempImg.rows * tempImg.cols;
				if (contArea / imageArea < 0.015)// 过滤小面积的轮廓,原函数是0.015
				{
					continue;
				}
				//imshow("srcimage ", srcImage);
				//由于是模板库中的照片提取轮廓,假定每个模板只有一个轮廓满足要求
				mContoursTemp.push_back(mContours[i]);
				//Size sz = tempImg.size();//尺寸
				//Mat temp = Mat::zeros(sz, CV_8UC3);
				//drawContours(temp, mContours, i, Scalar(255, 255, 255), 4, 8);
				//namedWindow("10.原图的轮廓", CV_WINDOW_NORMAL);
				//imshow("srcimage ", temp);
				//waitKey(1000);
			}
		}
	    mContoursLib.push_back(mContoursTemp);//每种手势每个模板轮廓的集合
		tempImageLib.push_back(tempImages);   //每种手势每个模板图像的集合
	}
	//查看轮廓中点的数量
	//for (int i = 0; i < mContoursLib.size(); i++)
	//{
	//	for (int j= 0; j < mContoursLib[i].size(); j++)
	//	{
	//		cout<< "size: "<< mContoursLib[i][j].size()<<endl;
	//	}
	//	cout<<endl;
	//}
	cout<<"all templates have been loaded!"<<endl;
}

//训练SVM模型
void trainSVM()
{
	//加载训练数据及标签
	for (int i = 0; i < 6; i++)
	{
		for (int j = 0; j  < 25; j ++)
		{
			int num = i*25 + j;
			ostringstream oss;
			oss<< "handData\\descripers\\" << i<<"_"<<j <<".txt";
			ifstream data(oss.str().c_str());
			float da;
            int k = 0;
			while (data>>desc[num][k])
			{
				k++;
			}
			labels[num][0] = i;
			//cout<<i<<endl;
         }
	}
	//训练svm模型
	bool train_update = false;
	int train_sample_count = 6*25;//数据的数量
	int train_sample_size = 32;//数据的维度


	//--------------------------------------------------------------------
	CvMat *data_mat = NULL;//要训练的数据  
	CvMat *class_mat = NULL;//数据的类别
	data_mat = cvCreateMat(train_sample_count,train_sample_size,CV_32FC1);
	class_mat = cvCreateMat(train_sample_count,1,CV_32FC1);
	for (int i = 0; i < train_sample_count; i++)
	{
		class_mat->data.fl[i]=labels[i][0];
		for (int j = 0; j < train_sample_size ; j++)
		{
			data_mat->data.fl[i*train_sample_size +j] = desc[i][j];
			//cout<< data_mat->data.fl[i*train_sample_size +j] <<endl;
		}
	}

	CvSVMParams svm_params;
	svm_params.svm_type = CvSVM::NU_SVR;
	svm_params.kernel_type = CvSVM::RBF;
	svm_params.gamma = 1./train_sample_size;
	svm_params.nu = 0.5;
	svm_params.C = 8;

	svm_params.term_crit.epsilon = 0.001;
	//svm_params.term_crit.max_iter = 50000;
	svm_params.term_crit.type = CV_TERMCRIT_EPS|CV_TERMCRIT_EPS;

	CvSVM *tSvm = new CvSVM();
	//tSvm->train(data_mat,class_mat,0,0,svm_params);
	tSvm->train_auto(data_mat,class_mat,0,0,svm_params);
	tSvm->save("handData//svm_model.xml",0);
    //float C = params_re.C;
	//float P = params_re.p;
	//float gamma = params_re.gamma;
	//printf("\nParms: C = %f, P = %f,gamma = %f \n",C,P,gamma);
}

//计算轮廓傅里叶描述子
void calcute_fft()
{
//计算轮廓的傅里叶描述子
	Point p;
	int x, y, s;
	int i = 0,j = 0,u=0;
	s = (int)mContoursProc[0].size();
	Mat src1(Size(s,1),CV_8SC2);
	float f[9000];//轮廓的实际描述子
	//float fd[32];//归一化后的描述子,并取前15个
	for (u = 0; u < s; u++)
	{
		float sumx=0, sumy=0;
		for (j = 0; j < s; j++)
		{
			p = mContoursProc[0].at(j);
			x = p.x;
			y = p.y;
			sumx += (float)(x*cos(2*CV_PI*u*j/s) + y*sin(2 * CV_PI*u*j / s));
			sumy+= (float)(y*cos(2 * CV_PI*u*j / s) - x*sin(2 * CV_PI*u*j / s));
		}
		src1.at<Vec2b>(0, u)[0] = sumx;
		src1.at<Vec2b>(0, u)[1] = sumy;
		f[u] = sqrt((sumx*sumx)+(sumy*sumy));
	}
	//傅立叶描述字的归一化
	f[0] = 0;
	fd[0] = 0;
	for (int k = 2; k < 33; k++)
	{
		f[k] = f[k] / f[1];
		fd[k - 1] = f[k];
		//cout << fd[k-1] << endl;
	}
//	//保存数据
//	ostringstream oss;
//	oss<< "hand_data\\descripers\\" << hand_num<<"_"<<num <<".txt";
//	for (int k = 0; k < 32; k++)
//	{
//		FILE *fp = fopen(oss.str().c_str(), "a");
//		fprintf(fp, "%8f\t", fd[k]);
//        fclose(fp);
//	}
///*	FILE *fp = fopen(oss.str().c_str(), "a");
//	fprintf(fp, "\n");
//	fclose(fp)*/;
//	ostringstream oss1;
//	oss1<< "handData\\img\\" << hand_num<<"_"<<num <<".jpg";
//	imwrite(oss1.str(),frame);
}

//根据SVM模型预测结果
void predictSVM()
{
	int train_sample_size = 32;
	CvSVM *pSvm = new CvSVM();
	pSvm->load("handData//svm_model.xml",0);
    CvMat *sample = cvCreateMat(1,train_sample_size,CV_32FC1);
	for (int i = 0; i < train_sample_size; i++)
	{
		sample->data.fl[i] = fd[i];
	}
	match_number = int (pSvm->predict(sample) + 0.5);//因为svm输出的是小数,要四舍五入一下
	cout << match_number<<endl;
}


//与模板进行匹配
void hand_match()
{
	if ((mContoursProc.size() == 0) || (mContoursLib.size() == 0))//如果目标轮廓的尺寸=0或模板轮廓的尺寸=0则返回,||是逻辑或运算符
	{
		cout << "There are no contours to match" << endl;
		return;
	}

	//cout << "mContoursTemp size = " << mContoursProc.size() << endl;
	double hu = 1;	//hu = 1.0
	double huTmp = 0.0;	//huTmp = 0.0
	const int SIZE = mContoursProc.size();
	int m = -1;
	int n = -1;
	int l = -1;

	for (int i = 0; i < mContoursProc.size(); i++)	//第i条轮廓
	{
		for (int j = 0; j < 6; j++)      //第j种模板
		{
			for (int k = 0; k < 2; k++)      //第i种模板的第k个模板
			{
				huTmp = matchShapes(mContoursProc[i], mContoursLib[j].at(k), CV_CONTOURS_MATCH_I1, 0);//根据计算比较两张图像Hu不变距的函数,函数返回值代表相似度大小,完全相同的图像返回值是0,返回值最大是1
				if (huTmp < hu)//hu矩越小,匹配度越高
				{
					hu = huTmp;//保存好,是哪个轮廓和哪个模板匹配上了
					m = i;  //第n条轮廓
					n = j;  //第j种模板
					l = k;  //第j种第k个模板
				}
			}
		}
	}

	cout << "match number = " << n <<endl;
	match_number = n ; // 匹配到的数字
	temp_number = l;
}
//绘制结果
void draw_result( )
{
	if (num <0)//如果未识别到任何数字则返回
	{
		return;
	}
	 //在图像上绘制文字
	putText(delContframe , std::to_string(match_number), Point(50 ,50 ), FONT_HERSHEY_SIMPLEX, 2, Scalar( 255,0, 0), 4);
	namedWindow("手势识别结果", CV_WINDOW_NORMAL);
	imshow("手势识别结果",delContframe);

};

  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值