手势识别系列博文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而非RGBsplit(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 -> IplImageCvAdaptiveSkinDetector 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 framecvCopy(frame1, skinImg, maskImg);Mat tmp(skinImg);  //IplImage -> MatRIOresult= 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.0double huTmp = 0.0; //huTmp = 0.0const 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,返回值最大是1if (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);};

opencv手势识别(3_SVM算法识别)相关推荐

  1. opencv手势识别(1_手势分割)

    手势识别系列博文1:手势分割 琴某人辛辛苦苦码的报告,当然不能让你们这么容易复制过去(๑• . •๑)   后文有代码实现:    6种肤色检测方法的原理及实现:https://blog.csdn.n ...

  2. opencv手势识别(2_KNN算法识别)

    手势识别系列博文2:KNN算法识别手势 前言 原理介绍 代码实现 前言 书山有路勤为径,学海无涯苦做舟 琴某人辛辛苦苦码的报告,当然不能让你们这么容易复制过去(๑• . •๑) 运行视频见链接:htt ...

  3. 【OpenCV/C++】KNN算法识别数字的实现原理与代码详解

    KNN算法识别数字 一.KNN原理 1.1 KNN原理介绍 1.2 KNN的关键参数 二.KNN算法识别手写数字 2.1 训练过程代码详解 2.2 预测分类的实现过程 三.KNN算法识别印刷数字 2. ...

  4. 基于OpenCV的 SVM算法实现数字识别(四)---代码实现

    三.基于SVM算法实现手写数字识别 作为一个工科生,而非数学专业的学生,我们研究一个算法,是要将它用于实际领域的.下面给出基于OpenCV3.0的SVM算法手写数字识别程序源码(参考http://bl ...

  5. OpenCvSharp (C# OpenCV) DNN模块加载自己训练的TensorFlow模型做目标检测(含手势识别、骰子识别、菜品识别)(附源码)

    本文作者Color Space,文章未经作者允许禁止转载! 本文将介绍OpenCVSharp DNN模块加载自己训练的TensorFlow模型做目标检测(含手势识别.骰子识别.菜品识别)! 前言: 下 ...

  6. 利用opencv结合mfc实现识别圆形标记点并计算多个圆形标记点的三维坐标,拟合平面并计算法向量

    利用opencv结合mfc实现识别圆形标记点并计算多个圆形标记点的三维坐标,拟合平面并计算法向量 具体步骤 二.对应代码 1.引入库 2.标定 识别圆形标记点 左右图像中圆形标记点匹配 计算三维坐标 ...

  7. Python&Opencv手势识别系统

    Python手势识别与控制 概述 本文中的手势识别与控制功能主要采用 OpenCV 库实现, OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库, 可以运行在Linux, Windows ...

  8. OpenCV深度学习人脸识别示例——看大佬如何秀恩爱

    (欢迎关注"我爱计算机视觉"公众号,一个有价值有深度的公众号~) 提到OpenCV人脸识别,你是否还停留在Haar级联人脸检测+LBP特征人脸识别上?对于小规模数据库如何用Open ...

  9. 数字识别java开源_Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

最新文章

  1. 微软针对中国中小企业推出打包服务
  2. 安装JAVA内部错误61003_JDK1.8 安装报内部错误:61003
  3. linux更改默认版本,linux - 将某个软件版本设为ubuntu中的默认版本 - Ubuntu问答
  4. python【力扣LeetCode算法题库】7- 整数反转
  5. [NSTaggedPointerString text]:unrecognized selector sent to instance
  6. 计算机二级java邓伦单_2010年3月计算机等级考试二级Java笔试试题(文字版)
  7. 如何使用Orchard搭建敏捷个人的网站(2)
  8. moodle 页面联动效果
  9. 自动量程万用表的实现原理_自动量程万用表模块设计方案[图]
  10. 彻底删除aix下的oracle rac 数据库,AIX RAC 安装失败完全卸载
  11. python求平方根的代码_python求平方根
  12. PHP裂变红包源码,php版本微信裂变红包api详解
  13. 创新电影院5G的未来
  14. OpenCV-Python图像形态变换概述及morphologyEx函数介绍
  15. Java网络编程学习
  16. flashback的配置
  17. JPG、PNG、BMP、RAW傻傻分不清楚?一文帮你解析常见的图像文件格式及其差异
  18. Work20230605
  19. 浏览器是如何运作的?
  20. mac下关闭chrome浏览器的自动更新

热门文章

  1. 搞一下TDA4 | 03 TDA4 辅助引导加载程序SBL
  2. 读《大学生上课为什么一定要认真听讲》有感
  3. 新词发现:中文新词识别技术简介
  4. Vue packages version mismatch解决方案
  5. Dubbo的多种序列化算法
  6. 新华三智慧计算联盟,集结!
  7. 华为RH2288H V3服务器 从拆箱开始安装系统
  8. PTA甲 1069~1072题解
  9. wr703n刷openwrt离线下载及upnp共享播放
  10. 每个人都会经历一段迷茫