0%

基于FPGA的手势识别系统设计

​ 之前做图像处理的时候,也没有涉及过太多的深度学习,所以都是用传统算法做的,这个是本科期间做的大创(感谢陈老师帮该项目申请成了省级大创),其实不能说做的多好,因为真的发现图像处理领域很多传统算法被深度学习吊锤,而且现在FPGA加速器有崛起的势头,但是我们还是要惊羡于那些数学家的美妙构思(数学真是最美妙的学科),不多扯了,进入正题。

注:这里没有提到待卷积窗口的生成(3*3 or 5*5的),大家可以参考这篇文章

深刻认识shift_ram IP core

以后有时间会在加速器设计里讲如何生成待卷积窗口,最近有点忙。。。

2.1 系统介绍
该项目通过FPGA驱动摄像头ov7725,通过配置寄存器使得ov7725采集到RGB565格式的数据,由于摄像头工作频率和FPGA工作频率不匹配,所以先存入ram中然后再由FPGA读出摄像头数据,为了进行图像分割,去掉除了手势以外的其他部分,我们对其进行了色域转换,将RGB565格式转为YCBCR格式,通过控制CBCR域值进行提取肤色。然后,滤除图像中的杂波,我们进行分别进行了两次腐蚀操作和两次膨胀操作,为了提高后续识别算法的鲁棒性,我们对其进行了sobel算子边缘化提取,最后,我们通过hu不变矩算法进行识别手势。其系统框图如下RjL0gg.png

2.3图像处理算法模块

此块详细描述

(1)肤色提取

为了进行肤色提取,我们需要先将RGB565格式转为YCbCr域进行识别。

YCbCr是通过有序的三元组来表示的,三元由Y(Luminance)、Cb(Chrominance-Blue)、和Cr(Chrominance-Red)组成,其中Y表示颜色的明亮度和浓度,而Cb和Cr则分别表示颜色的蓝色浓度偏移量和红色浓度偏移量。人的肉眼对由YCbCr色彩空间编码的视频中的Y分量更敏感,而Cb和Cr的微小变化不会引起视觉上的不同,根据该原理,通过对Cb和Cr进行子采样来减小图像的数据量,使得图像对存储需求和传输带宽的要求大大降低,从而达到在完成图像压缩的同时也保证了视觉上几乎没有损失的效果,进而使得图像的输出速度更快,存储更加方便。值得一提的是,如果需要得到灰度图像的话,要将采集到的彩色图像转化为YCbCr后,将Y分量分别送给VGA的R、G、B,即此时R、G、B数值上是一致的(其实由于位数原因会不一致,比如RGB565,G是6位而此时RB只有5位),这样便得到了灰度图,此处我们没有用到灰度图,所以不需要Y分量,但下述算法依旧会一并提及。

我们配置摄像头采集到的数据是RGB565的格式,官方给出的转化公式是严格的RGB888转为YCbCr888,所以先需要将RGB565转化为RGB888,这个时候我们又遇到了一个有意思的问题,平时像素数据一般RGB565,但很多FPGA开发板支持的VGA不是RGB565的,EGO1就是RGB444的,那么如何将RGB565变为RGB444呢?又如何将RGB565变为RGB888?此处便提到了压缩和量化补偿思想。

对于压缩,主要是一个思维:取高位。如果是RGB565格式想变成RGB444,那么再FPGA中always块只要如下三句即可:

即分别取了原像素点RGB的高四位作为RGB444。

对于量化补偿,是用在扩充位数的时候用的,比如此处需要的RGB888。以RGB565转RGB888为例。

16bit RGB565 -> 24bit RGB888的转换(高位补低位)

16bit RGB565:

24bit RGB888:

官方给出的RGB888->YCbCr888转化公式如下:

由于FPGA无法实现浮点数运算,所以需要把系数变为整数,我们不妨将Y、Cb和Cr都扩大1024倍,然后所有系数取整,那么变成如下公式:

Y=306R+601G+116B

Cb=-176R-347G+523B+131072

Cr=523R-438G-85B+131072

这样便可以得到整型的运算,最后得到的结果右移10位即可,但为了时序的科学严谨性,我们不应该一次在always块中算出Y、Cb、Cr,因为一个关系式中涉及到三次乘法和两次加法,越多的运算量就越可能导致时序延时错乱,此处或许不会有问题,但不在一个块中用太复杂的运算式是一种好的习惯,我们应该选择业界普遍使用的流水线做法,将乘法在一个always块里实现,在另一个always块中实现加法。

在转换为YCBCR后,通过限制CBCR阈值,可以提取出肤色,在FPGA中设定的初始值肤色范围为:

这是一个经典的人的肤色阈值,但是为了个别人肤色差别需要调节,FPGA上给出了调节按键。最终在该范围内的像素使其为黑色,否则为白色,那么VGA上只显示出一只手。

(2)腐蚀

腐蚀是一种消除边界点,使边界向内部收缩的过程,可以用来消除小且无意义的物体。由于在第一步手势提取的可能出现杂波,那么为了消除这些小黑点杂波,我们进行两次腐蚀算法便可以将其消除。

简单来说,腐蚀操作需要用3×3的结构元素,扫描图像的每一个像素点,用结构元素与其覆盖的二值图像做“与”操作,如果全为1,结果图像的该像素是1,否则为0。结果会使二值图像小一圈,消除像素杂波。算法原理如下:

从原理上来说,其是比较简单的,但是FPGA实现时,我们需要考虑如何得到这个33的矩阵,因为FPGA扫描像素点是一个一个进行的,一行有640个数据,如何得到三行中的三个数据呢?这个时候我们需要用到FPGA中shift ram IP核,可以说这个IP核是为了构建矩阵量身定制,其是一个只有一行的循环移位的IP核,那么我们需要两个这样的IP核,进行循环移位得到33的矩阵,然后进行腐蚀滤波。

(3)膨胀

膨胀与腐蚀效果相反,是将与物体接触的所有背景点合并到该物体中,使边界向外部扩张的过程,由于在腐蚀的时候,是为了消除杂波,但不可避免的减小了有效的手势区域,那么我们如何来恢复被消除的手势区域,那么此时便用到了膨胀算法。

膨胀算法思维核腐蚀思维类似,都需要构建3*3的矩阵,用3×3的结构元素,扫描图像的每一个像素点,用结构元素与其覆盖的二值图像做“或”操作,如果全为0,结果图像的该像素是0,否则为1。结果会使二值图像扩大一圈。算法原理如下:

在其实现过程中,依旧需要shift ram IP核,然后得到3*3矩阵,然后用流水线法实现膨胀操作,恢复手势区域。

(4)Sobel边缘化

此步我们进行Sobel算子边缘化提取,为什么进行Sobel化呢?Sobel化边缘提取可以提取出手势的边缘而不是整只手,这样是为了提高后续的hu不变矩识别算法的稳定性。

Sobel边缘检测的核心在于像素矩阵的卷积,卷积对于数字图像处理非常重要,很多图像处理算法都是做卷积来实现的。卷积运算的本质就是对制定的图像区域的像素值进行加权求和的过程,其计算过程为图像区域中的每个像素值分别与卷积模板的每个元素对应相乘,将卷积的结果作求和运算,运算到的和就是卷积运算的结果。

3×3的窗口M与卷积模板C 的卷积运算如下:

G_x和G_y是Sobel的卷积因子,将这两个因子和原始图像做如下卷积,其中A表示原视图像。

得到图像中的每一个点的横向纵向梯度G_x、G_y。最后通过如下公式来计算该点总体梯度的大小。

我们此时还需要设定一个阈值,该如果算出的G大于设定的阈值,那么认为此处是边缘处,使其为黑色,否则认为不是边缘,使其为白色。

上述是算法原理,很显然,这里也用到了3*3矩阵,那么又需要shift ram IP核,所以如果需要学习图像滤波等处理,shift ram需要熟练掌握。

(5)手势识别算法:Hu不变矩

对于一个提取出来的手势,我们需要有固定且唯一的特征来对其进行记录,且该特征不会受到手势的大小,旋转,平移而变化,且鲁棒性较好,所以此处引入hu不变矩算法,下面进行原理介绍:

1、普通矩(也叫p+q阶不变矩),和p+q中心矩的定义

对于像素分布为f(x,y)的图像,其(p+q)阶矩定义为:

(p+q)阶中心矩定义为:

其中矩心(x_0,y_0)为:

上述都是在连续量上引出的,但是FPGA只能存储离散量,得到的是离散的320 240或者640 480的离散图像,那么下面引入适用于离散图像的hu不变矩:

对于数字图像,离散化得到,公式如下:

式中p、q=0,1,2….

直接用普通矩或中心矩进行特征表示,不能使特征同时具有平移、旋转和比例不变性,所以我们下面进行归一化使得手势平移、旋转核比例不变性。

2.归一化中心矩定义

当图像发生变化时,m_pq也发生变化,而\mu_pq则具有平移不变性但对旋转依然敏感。

归一化中心矩:

如果利用归一化中心矩,则特征不仅具有平移不变性,而且还具有比例不变性。

至此我们得到了最终可以应用的不变矩,为了说明其又平移、寻转、放缩不变性,下面我们进行对各个性质进行证明:

(1)中心矩对于f(x,y)的平移具有不变性:

假如新的坐标(x’,y’)

其中alpha和beta是常数,通过简单的变量代换,可以发现最终的常数会消去,得到f(x,y)和f(x’,y’)的中心矩是相同的。

(2)中心矩对于缩放具有不变性:

alpha是个常数,(x’,y’)可以看作是(x,y)分别乘以系数alpha得到,对于每一个alpha系数有公式

因为alpha是个常数,那么变换前后的中心矩有这样的关系:

最后可以得到:

式中p+q=2,3…

这也可以称为相似不变矩性。

(3)中心矩对于旋转具有不变性:

旋转矩阵的模是1。

将(8)和(9)式与公式(2)结合,也可以得到mu’_pq与mu_pq的关系,称为正交不变性。

HU矩利用二阶和三阶归一化中心矩构造了7个不变矩,他们在连续图像条件下可保持平移、缩放和旋转不变,具体定义如下:

上述共有七个不变矩,如果只需要识别数十个手势的话,只需要实现前两个不变矩即可,在FPGA实现时,需要注意的是,由于算法中有除法运算,需要调用FPAG的除法器IP核,此外,但不得不说,FPGA在处理复杂的算法方面有劣势,一个几十位的除法器延时了二十几个时钟。此外,在上述的特征值中,其特征值数值较小,是比较小的小数,所以过程中最好放大2的20次方左右这样得到整数。

(6)识别实现

在识别之前,先用是个reg寄存器存储十个hu不变矩特征值,这十个值可以是自己做的十个不同手势得到的hu不变矩结果,识别的时候,将识别的手势hu结果和存储的十个hu特征值对比,最靠近谁即认为识别的结果就是该手势,当然,也得设置一个阈值,同时还有误差小于这个阈值,防止图片中没有手势的时候都误识别出结果。

(7)展示

最终采集的图片格式是320*240的(EGO1中没有DDR,存不下640 * 480的图片),结果如下:

RjLsDs.png

下面展示四个手势被处理后的效果:

手势一:

RjLruj.png

手势二:

RjLBvQ.png

手势三:

RjLw8S.png

手势四:

RjLybn.png

经过灰度化二值化膨胀腐蚀边缘化后提取的效果如下(由于摄像头插上板卡的时候斜了90度,所以VGA显示的时候也斜了90度):

手势一:

RjLcEq.png

手势二:

RjLgU0.png

手势三:

RjL25V.png

手势四:

RjLWCT.png

经过Hu不变矩后便可以得到不同的特征值,结果如下:

手势一:

RjLh2F.png

更多的就不放了,会拖慢博客速度,哈哈哈。

- - - - - - - - - - - - - - 本文结束啦,感谢您的观看 - - - - - - - - - - - - - -