基于OPENCV的手势识别技术
前言:
本篇博客主要介绍基于OPENCV的手势识别程序,代码为C++,OPENCV版本为OPENCV4会有较为详细的实现流程和源码,并且做到源码尽量简单,注释也自认为较为清晰,希望能帮助到大家。(源码将放在文章末尾的链接中,代码较为粗糙,有错误欢迎大家指出。)
一、手势识别流程图
首先是对于流程图的简单说明:两条线是分开进行的,两者在比对分类之前是不会互相影响的(当然有部分函数,例如提取特征的函数,是在两条线中都是会使用到的),因此可以分别完成两条线的工作。如果作为需要分工的项目,可以按照这两部分进行分工,最后进行整合。
而关于两条线的顺序,个人认为是优先进行神经网络的训练部分(也就是下方线路),原因是对于要识别的手势,首先要有对应的样本,优先去寻找样本,以此确定能够识别的手势。(当然,要是已经找到了样本,先做上方线路也没啥太大问题)
本篇文章的顺序:
先讲对于图片的处理:
因为先明白图片如何处理,并知道提取特征的方法,才能理解要把什么东西放到神经网络里,得到的数据又是什么。
再讲神经网路的搭建:
神经网路的搭建其实就是一个模板性的东西,计算机并不知道你要识别的东西到底是什么,是数字还是手势,对于计算机来说它只是一堆数据,它只负责给你找到——你输入的数据在网络里跟哪个数据最为匹配,然后就给你输出。
二、读取图片、获取皮肤部分及二值化
1)读取图片部分:
并没有过多好说的,直接使用imread函数对图片进行读入。(注意,读入的图片应该是彩色的而不是灰度图,否则无法进行后面的皮肤区域获取)
2)获取皮肤部分及二值化:
关于皮肤部分的获取,这里列出几种算法。由于图片的光照等的不同,不同算法的优劣也很难对比,各位自行选择算法。
①基于RGB颜色空间的简单阈值肤色识别:
根据他人的研究,我们可以知道有这样的一条判别式来用于肤色检测
R>95 && G>40 && B>20 && R>G && R>B && Max(R,G,B)-Min(R,G,B)>15 && Abs(R-G)>15
有了条判别式,我们就能够很容易地写出代码来实现肤色检测。但该算法对于光线的抗干扰能力较弱,光线稍微不好就识别不出皮肤点。
Mat getSkin(Mat& ImageIn)//获取皮肤的区域,返回二值化图像
{
vector<Mat> r_g_b;//用于存放RGB分量
split(ImageIn,r_g_b);//分离RGB分量,顺序为B,G,R
Mat Binary = Mat::zeros(ImageIn.size(),CV_8UC1);
Mat R = r_g_b[2];
Mat G = r_g_b[1];
Mat B = r_g_b[0];
for (int i = 0; i < ImageIn.rows; i++)
{
for (int j = 0; j < ImageIn.cols; j++)
{
if (R.at<uchar>(i, j) > 95 && G.at<uchar>(i, j) > 40 && B.at<uchar>(i, j) > 20 &&
R.at<uchar>(i, j) > G.at<uchar>(i, j) && R.at<uchar>(i, j) > B.at<uchar>(i, j) &&
MyMax(R.at<uchar>(i, j), G.at<uchar>(i, j), B.at<uchar>(i, j)) - MyMin(R.at<uchar>(i, j), G.at<uchar>(i, j), B.at<uchar>(i, j)) > 15
&& abs(R.at<uchar>(i, j) - G.at<uchar>(i, j)) > 15)
{
Binary.at<uchar>(i, j) = 255;
}
}
}
return Binary;
}
代码说明:首先对彩色图片分离开R、G、B分量,然后根据公式,将每一个点的R、G、B分量代入公式中进行判断,符合条件的点我们可以认为它是皮肤中的一个点。(其中的MyMax和MyMin是自写的判断大小函数)将认为是皮肤的点的值置为255,就可以达到二值化的效果。
可以看到除了部分因为光线问题导致的阴影没有被识别出来以外,几乎所有皮肤点都被识别出来了,效果还过得去。
②基于椭圆皮肤模型的皮肤检测
研究发现,将皮肤映射到YCrCb空间,则在YCrCb空间中皮肤的像素点近似成一个椭圆的分布。因此如果我们得到了一个CrCb的椭圆,对于一个点的坐标(Cr, Cb),我们只需判断它是否在椭圆内(包括边界)就可以得知它是不是肤色点。
该算法对于光线的敏感性没有这么高,基本上该检测到的皮肤都能够检测到,抗干扰能力相对较强。(原因大概是YCrCb中Y分量表示明亮度,而而“Cr”和“Cb” 表示的则是色度,作用是描述影像色彩及饱和度)
Mat getSkin2(Mat& ImageIn)
{
Mat Image = ImageIn.clone();//复制输入的图片
//利用OPENCV自带的ellipse函数生成一个椭圆的模型
Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);
ellipse(skinCrCbHist, Point