基于神经网络的实时手势识别

总体设计流程

在这里插入图片描述

1.手势特征提取

(1)打开摄像头获取输入

VideoCapture capture;
	capture.open(0);

	if (!capture.isOpened())
	{
		perror("open camera error!");
	}

	double rate = 24;
	bool stop(false);

	Mat frame;
	namedWindow("original img");
	namedWindow("output img");

	int delay = 1000 / rate;

(2)进行肤色检测

Mat img = src.clone();
	Mat ycrcb_img;
	cvtColor(img, ycrcb_img, COLOR_BGR2YCrCb);

	vector<Mat> y_cr_cb;

	split(ycrcb_img, y_cr_cb);

	Mat CR = y_cr_cb[1];
	Mat CB = y_cr_cb[2];
	Mat imgout = Mat::zeros(img.size(), CV_8UC1);

	for (int i = 0; i < img.rows; i++)
	{
		for (int j = 0; j < img.cols; j++)
		{
			if (CR.at<uchar>(i, j) > 133 && CR.at<uchar>(i, j) < 173 && CB.at<uchar>(i, j) > 77 && CB.at<uchar>(i, j) < 127)
			{
				imgout.at<uchar>(i, j) = 255;
			}
		}
	}
	img.copyTo(detected_one, imgout);

(3)获取轮廓和提取特征

  1. 完成肤色检测会获得皮肤区域图像,对这个图像进行二值化threshold(outimg, two_img, 50, 255, THRESH_BINARY);
  2. 二值化后对其膨胀dilate(two_img, close_noise, Mat());
  3. 然后腐蚀`erode(close_noise, two_two_img, Mat());
  4. 然后进行边缘检测,获得轮廓Canny(two_two_img, Canny_img, 50, 200);
  5. 接下来我们就可以提取特征了,我们使用傅里叶描绘子,这是一种边界描绘子,因为这种方法获取的特征对旋转和缩放都不敏感,符合我们需要。
	/***傅里叶变换后的系数储存在f[d]中***/
	vector<float>f;
	vector<float>fd;//最终傅里叶描绘子前14位
	Point p;
	for (int i = 0; i < max_size; i++)//主要的计算部分
	{
		float x, y, sumx = 0, sumy = 0;
		for (int j = 0; j < max_size; j++)
		{
			p = contours[contour_num].at(j);
			x = p.x;
			y = p.y;
			sumx += (float)(x * cos(2 * CV_PI * i * j / max_size) + y * sin(2 * CV_PI * i * j / max_size));
			sumy += (float)(y * cos(2 * CV_PI * i * j / max_size) - x * sin(2 * CV_PI * i * j / max_size));
		}
		f.push_back(sqrt((sumx * sumx) + (sumy * sumy)));
	}

	for (int k = 2; k < 16; k++)//进行归一化,然后放入最终结果中
	{
		f[k] = f[k] / f[1];
		fd.push_back(f[k]);
	}

	out = Mat::zeros(1, fd.size(), CV_32F);//out是用于输出的手势特征
	for (int i = 0; i < fd.size(); i++)
	{
		out.at<float>(i) = fd[i];
	}

这样我们得到了手势的特征。

2.神经网络

(1)采集样本手势并提取特征
提前准备好手势图片样本,建议获取不同人正反面手势,然后进行旋转获得更多手势。

void getXML()
{
	FileStorage fs("get_xml.xml", FileStorage::WRITE);
	if (!fs.isOpened())
	{
		perror("open xml error!");
	}

	Mat feture_data;
	Mat classes = Mat::zeros(30, 1, CV_8UC1);
	char path[100];
	Mat img_read;
	for (int i = 1; i <= 3; i++)//手势种类
	{
		for (int j = 1; j <= 10; j++)每个种类个数
		{
			sprintf_s(path, "sample_picture\\images\\%d_%d.png", i, j);
			img_read = imread(path);
			Mat back_img;

			thre_two(img_read, back_img);
			feture_data.push_back(big_contours(back_img));

			classes.at<uchar>((i - 1) * 10 + j - 1) = i - 1;
		}
	}

	fs << "Feture_Data" << feture_data;
	fs << "Classes" << classes;
	fs.release();
}

(2)神经网络训练

void ann_go(Ptr<ANN_MLP>& ann, int numC, int nlay)
{
	Mat feture_data, classes;
	FileStorage fs("get_xml.xml", FileStorage::READ);

	fs["Feture_Data"] >> feture_data;
	fs["Classes"] >> classes;

	Mat laySize(1, 3, CV_32SC1);
	laySize.at<int>(0) = feture_data.cols;
	laySize.at<int>(1) = nlay;//24
	laySize.at<int>(2) = numC;

	ann->setLayerSizes(laySize);
	ann->setTrainMethod(ANN_MLP::BACKPROP, 0.1, 0.1);
	ann->setActivationFunction(ANN_MLP::SIGMOID_SYM);
	ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 0.01));

	Mat tranClass;
	tranClass.create(feture_data.rows, numC, CV_32FC1);

	for (int i = 0; i < feture_data.rows; i++)
	{
		for (int j = 0; j < tranClass.cols; j++)
		{
			if (j == (int)classes.at<uchar>(i))
				tranClass.at<float>(i, j) = 1;
			else
				tranClass.at<float>(i, j) = 0;
		}
	}
	ann->train(feture_data, ROW_SAMPLE, tranClass);

}

现在可以开始训练了

getXML();//样本特征数据
	Ptr<ANN_MLP> ann = ANN_MLP::create();
	ann_go(ann,3,24);//样本特征训练
	ann->save("ann_param");//得到神经网络

3.进行手势特征匹配

(1)匹配特征预测结果
我就可以将得到的手势特征与神经网络里的特征数据进行比对,预测出结果了,那我们怎么比对呢,这就需要用到预测函数predict进行预测。最后找到逻辑最大值,作为结果输出:

int contrast_out(Ptr<ANN_MLP>& ann, Mat &img)
{
	int result = -1;
	Mat output(1,4,CV_32FC1); //1*4矩阵

	Mat img_1;
	img_1 = big_contours(img);
	ann->predict(img_1, output);

	Point maxL;
	double maxV;

	minMaxLoc(output, 0, &maxV, 0 ,&maxL); //对比值,看哪个是最符合的。

	result = maxL.x;

	return result;
}

(2)输出结果

我们将预测的结果与手势的名称对应上,然后显示到输出窗口上:
void write_num(Mat& img, int p)
{
	if (p == 2)
	{
		putText(img, "4", Point(20, 20), FONT_HERSHEY_PLAIN, 2.0, 255, 2);
	}
	else if (p == 0)
	{
		putText(img, "1", Point(20, 20), FONT_HERSHEY_PLAIN, 2.0, 255, 2);
	}
	else if (p == 1)
	{
		putText(img, "2", Point(20, 20), FONT_HERSHEY_PLAIN, 2.0, 255, 2);
	}
}

这个项目程序可以进行一些简单的手势识别,通过摄像头获取输入数据,用户只需要在摄像头面前摆出手势,程序便可以实时识别用户摆出什么样的手势,用框框住手势,并将与手势对应的手势名称显示出来,但是识别准确率不高,要想提高准确率,可以在神经网络中加入梯度算法,由于楼主接触神经网络不多,也不太懂,欢迎能力强的人加以改进。项目完成得益于借鉴了以下博客的思想,讲得很详细,有需要的同学也可以去看看。源码将稍后上传至我的博客,有需要的同学可以下载。

https://blog.csdn.net/qq_42884797/article/details/110917941?utm_source=app&app_version=4.10.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值