手势识别系列博文2:KNN算法识别手势
- 前言
- 原理介绍
- 代码实现
前言
书山有路勤为径,学海无涯苦做舟
琴某人辛辛苦苦码的报告,当然不能让你们这么容易复制过去(๑• . •๑)
原理介绍
代码实现
1.程序中有很多冗余的函数
2.要运行此代码还需要提前制作好模板库,否则识别个der啊
3.代码有点乱,不想改了
#include <iostream>
#include <string>
#include <opencv2\opencv.hpp>
#include <stdio.h>
using namespace cv;
using namespace std;
//-----调参窗口的回调函数-------------------------------------//
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图片
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();//得到匹配结果
int main()
{
// 载入模板的轮廓
loadTemplate();
VideoCapture capture("handData//hand.mp4");
while (true)
{
//获取图片帧
capture >> frame;
//对视频进行降采样
flag++;
if(flag %6 != 0)
{
//cout<<"sdf ------------"<<endl;
continue;
}
if (true == frame.empty())
{
cout << "get no frame" << endl;
break;
}
resize(frame, frame, Size(frame.cols*0.5, frame.rows*0.5));//降采样
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);// 显示肤色分割之后的图片
--------------------------保存图片,可以用来制作模板----------------------------------------//
//
//flag++;
//if(flag % 20 == 1)
//{
// ostringstream oss;
// oss<< "hand_data//template Library2//"<< num <<".jpg";
// imwrite(oss.str(), result);
// num ++;
//};
----------------------------------------检测边缘-----------------------------//
find_contours(RIOresult);
//----------------------------------------匹配边缘-----------------------------//
hand_match();
//----------------------------------------绘制结果-----------------------------//
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), 4, 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 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 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 < 32; k++)
//{
// f[k] = f[k] / f[1];
// fd[k - 1] = f[k];
// cout << fd[k-1] << endl;
//}
//保存数据
//for (int k = 0; k < 16; k++)
//{
// ostringstream oss;
// oss<< "hand_data\\descripers\\" << hand_num<<"_"<<num <<".txt";
// FILE *fp = fopen(oss.str().c_str(), "a");
// fprintf(fp, "%8f\t", fd[k]);
// fclose(fp);
//}
//FILE *fp = fopen("1.txt", "a");
//fprintf(fp, "\n");
//fclose(fp);
}
//加载模板库中的图片
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<< "handData//templateLibrary//"<< 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;
}
//与模板进行匹配
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;
}
//加载并绘制识别到的模板
cv::Mat roi = tempImageLib[match_number].at(temp_number).clone();
resize(roi, roi, Size(frame.cols/6, frame.rows/6));
roi.copyTo(delContframe(cv::Rect(0, 0, roi.cols, roi.rows)));
//在图像上绘制文字
putText(delContframe , std::to_string(match_number), Point(roi.cols + 10 ,roi.rows ), FONT_HERSHEY_SIMPLEX, 4, Scalar( 255,0, 0), 8);
namedWindow("手势识别结果", CV_WINDOW_NORMAL);
imshow("手势识别结果",delContframe);
};