资源描述:
《毕设论文 于基单目手势识别交互系统.doc》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库。
本科毕业设计(论文)基于单目手势识别交互系统的设计与实现学院机电工程学院 专业数字媒体技术 基于单目手势识别交互系统的设计与实现李林钢机电工程学院 摘要随着计算机技术的逐渐发展,出现了一些符合人的习惯的人机交互技术。其中,手势的检测和识别技术作为一种有着自然性、简洁性和直接性的一种新型的交互方式,是这些交互技术当中倍受重视的研究和应用的技术之一。近几年来,已经有相当一部分的应用采用了这种手势识别的方式作为跟机器交互的手段,受到了世界各个国家的科研机构和科技企业的重视与青睐。基于计算机视觉的手势识别需要解决一系列的问题。首先是要做到的是从背景中将手分割出来;然后将有用的信息与冗余的信息分离开来,并将相关的手势信息提取出来;最后从相同的手势中根据实际情况,解读出它的特定的含义。针对这些问题,本文讨论里一种采用基于单目的特定颜色模型的背景分离方法和基于关键信息的手势提取方法,用VisualC++2010和OpenCV进行开发,实现在多种摄像头下的手势识别交互系统,主要包括以下方面:(1)针对不同摄像头画质的缺陷,采用中值滤波或均值滤波然后还原细节的方法实现图像较好的降噪,再用不同采样半径的USM锐化的方式实现图像信息的强化。(2)根据人手和背景色相等方面的差异,采用亮度+两个颜色通道的色彩模型进行色彩分离,并将未选择到的阴影和高光等区域利用连续色的原理进行进一步的选取。(3)根据选取结果有很多干扰区域的情况,实现最大面积连通区域的提取算法,以排除关键信息提取时的干扰。(4)根据手的区域的情况和交互的自然性,采用稳定的手的重心作为手的位置,利用近大远小的关系,采用手占摄像头面积的大小判断手离摄像头的距离。(5)针对以上识别的结果,实现了一些常见的人机交互应用,包括屏幕画图器、鼠标模拟器等。关键词:手势识别;VisualC++;背景消除;连通区域;关键信息提取 AbstractWiththedevelopmentofcomputertechnology,somehuman-computerinteractiontechniqueswhichmeetpeople'shabitsappear.Amongthem,gesturedetectionandrecognitiontechnology,asanewnatural,simpleanddirecttechnology,ishighlyvalued.Inrecentyears,therehavebeenconsiderablepartoftheapplicationusingthisgesturerecognitionapproachasameansofaninteractionwiththemachine,anditdrawsattentionofandwasfavoredbythescientificresearchandtechnologyenterprisesofvariouscountries.Computervision-basedgesturerecognitionneedstosolveaseriesofproblems.Thefirstisishowtosplitthehandfromthebackground;thenseparatetheusefulinformationandredundantone,andextracttherelevantinformationgesture;finally,decipherthespecificmeaningofthesamegestureinaccordancewiththeactualsituation.Tosolvetheseproblems,thispaperdiscussesagesturerecognitioninteractivesystemwithakindofbackgroundseparationmethodsbasedonspecificcolormodel,andakindofgestureextractionmethodonkeyinformation,developedbyVisualC++2010andOpenCV,fitsavarietyofcamera,whichincludesthefollowingaspects:(1)Fordifferentqualitydefectsamongcameras,thissystemusesmedianfilterormeanfilterandrestoresthedetailmethodtoreducetheimagenoiseconservatively.ThenusedifferentsamplingradiusofUSMsharpeningtoenhancetheimageinformation.(2)Accordingtothedifferenceofcolorsbetweenbackgroundandhands,thissystemusesthebrightnessandthetwocolorchannelsasacolormodelforcolorseparation.Thenotchosendistinct,suchastheshadowandhighlightareaswillbefurtherselectedusingacontinuouscolormethod.(3)Accordingtotheresultstherearealotofinterferingregionsofselectedareas.Thesystemachievesthemaximumconnectedarearegionextractionalgorithm,whichexcludestheinterferenceofthekeyinformationextraction.(4)Accordingtothehandregionofthecaseandforthenaturalistinteractive,thesystemusesthestablepoint--gravitycenterofthehandasthehandposition,andusesthesizeofthehandtoknowthedistancefromtheareawiththenear-farsmallerprinciple.(5)Accordingtotheresultsabove,thesystemachievessomecommoninteractiveapplications,includingscreendrawingcontrol,mousesimulator,etc.Keywords:GestureRecognition;VisualC++;BackgroundRemoval;ConnectedRegion;KeyInformationExtraction 目录1绪论11.1手势识别交互的背景11.2手势及识别方式的分类21.3基于计算机视觉的手势识别技术需要解决的问题21.4本程序主要实现的功能32摄像头信息的采集和预处理52.1OpenCV关于摄像头操作的API52.2在控件中呈现有关摄像头的图像62.2.1HDC的获取62.2.2将IplImage*位图渲染到控件当中62.3摄像头图像的预处理72.3.1图片的缩放72.3.2图片的降噪82.3.3图片的锐化113手的区域的提取133.1肤色种子的获取133.1.1获取鼠标相对图像位置的颜色133.1.2自定义肤色种子143.2基于手与背景在一定颜色模型上的区别的初步提取143.3将手的范围扩展到其他区域153.4外部孤立点的消去163.4.1获取连通区域的面积和标记区域ID183.4.2最大面积和对应区域的获取193.5内部孤立点的修补194手的关键点的提取214.1手的重心和面积的确定214.2手的边缘的提取21 5程序主体代码的实现235.1工程的环境和架构235.2主类成员变量的定义245.3程序的初始化代码245.4从输入控件中获得识别参数265.5总体识别语句的实现286应用设计306.1屏幕画图的设计306.1.1获取CWindowDC306.1.2定义和设置笔刷306.1.3绘图语句306.2模拟鼠标功能的实现316.2.1手的有效偏移量的获取326.2.2鼠标点击功能的实现327手势识别的综合测试34结论36总结36展望与改进36参考文献38致谢39 1绪论1.1手势识别交互的背景随着整个社会逐步的信息化,带给了我们一种新的交互方式——人机交互(human-computerinteraction)。而这一交互活动越来越成为人们日常生活的一个重要内容。近几年来,随着计算机技术的逐渐发展,出现了一些符合人的习惯的人机交互技术,如头部跟踪、人脸识别、表情识别、唇读、体势识别以及手势识别等等[1],并逐步向以人为本的方向迈进。其中,手势的检测和识别技术作为一种新型的方式,是这些交互技术当中倍受重视的研究和应用的技术之一。手势是一种有着自然性、简洁性和直接性的一种人机交互手段。[1]如果能只用人手作为计算机的输入设备,那么我们就无需学习其他繁重的硬件设备的操作,仅需根据人们的习惯,确定一些适当的手势,即可用简单的方式控制机器。而手势是人的一种很常用的交流方式。像人可以用手指的方向来表示物体的位置,用挥手来表示过来或再见,用一定的手势表示数字等等[2]。近几年来,已经有相当一部分的应用采用了这种手势识别的方式作为跟机器交互的手段。受到了世界各个国家的科研机构和科技企业的重视与青睐,如IEEE自1995年起召开的人脸与姿势自动检测年会FG(IEEEConferenceonAutomaticFaceandGestureRecognition)以促进包括手势检测识别技术在内的技术交流与发展。[2]图1.1手势识别应用39 1.2手势及识别方式的分类手势(gesture)本身具有多义性和多样性,由于领域、文化背景等的不同,对手势的定义也会不同。这里把手势定义为:手势是人手产生的各种姿势和动作,手势和姿势(posture)的主要区别在于姿势更为强调身体的总体形态而手势更强调手的细节。[1]手势按时间关系可分为静态手势(指姿态,又称手形)和动态手势(指动作,由一系列姿态组成)。静态手势对应时间上的一个点,而动态手势对应着一段时间内的一条轨迹,需要使用随时间变化的空间特征来表述[3]。动态手势具有丰富和直观的表达能力,与静态手势结合在一起,能创造出更丰富的语义。按手势目的又可以分为操控手势和交际手势[10]。在人机交互系统中,操控手势的一种典型应用就是在二维和三维的虚拟环境中来操控物体。交流手势主要是指在自然环境中伴随人们自然对话的自由形式手势。手势相对于语言来说是一种表达人们想法的单独的补充模式。在对话中与手势相关联的信息是一种语句的时间和空间结构,可以用机器来提取它们[11]。手势的识别方式可分为利用机械装置的识别和基于计算机视觉的识别。机械装置,如数据手套是虚拟现实中重要组成部分,可以将手指的复杂的三维位置和动作利用附着的感应器传到到计算机当中去。然后可以重新呈现手部动作。数据手套传感的识别方法的技术关键是手套能不能将手指、手掌、手腕的弯曲真实的以数据形势反演到系统数据库中,让系统根据模型对手势进行有效识别。由于手部软组织和计算复杂性,数据手套的计算速度总是存在延时,同时从人机交互的角度手套佩戴也十分不方便。如果多人使用还存在卫生等问题[4],再加上这些传感器昂贵的价格,因此数据手套等机械识别的方式在推广上会受到很多的障碍。近些年来,基于计算机视觉的识别方法提供了一种成本较低的能减少用户限制的手势识别的方法,这种方法也是本系统的主攻方向。 在基于计算机视觉的识别方式中,通常的做法就是把手势识别问题看成模式识别问题来对待。通过摄像头等设备来获取图像或视频,再把它们分解成特征集,然后用这些特征集与预先定义好的模式进行匹配。1.3基于计算机视觉的手势识别技术需要解决的问题39 无论是利用数据手套还是用手部粘贴高亮标签使作为输入设备会给使用者带来不舒适感如出汗等,且设备价格昂贵,难以推广。在上述研究的基础上,研究者渐渐把研究的重心转移到的不佩戴手套或不粘贴设备的自然手识别检测技术之上,以追求使用者更舒适的体验,其中从计算机视觉的研究方式入手是当前比较主流的研究方向,然而实现起来难度更大。首先是要做到的是从背景中将手分割出来,通常会用肤色分割的方法基于皮肤的色调将不同于周围背景手的区域分离出来.但是会受到光线等因素干扰,分离出手后需要用合适的模型对手的动作建模。之后从模型中估算出参数,根据估算出的参数对手势进行分类,得出结论。由于在数据采集时容易受到背景干扰,往往要求背景颜色和手的颜色有较好的区分度。[4]其次人体由多个部分组成,其中手指往往包含丰富的信息,而头和手臂所以及人手本身因光滑而产生的大量阴影等信息则大多为冗余信息,故如何将有用的信息与冗余的信息分离也是提高手势识别能力的关键。再次,人手是弹性物体,同样的手势的表现效果可能差别很大[5],并且同样的用户者要重复做出完全相同的手势也是不可能的,如何降低甚至消除这个因素的影响对整个识别的准确率也有一定影响。最后,手势往往具有复杂、多重的含义,单一的方法很难准确地解读人手的具体内涵进而实现最终的识别,故需要综合多种方法。[6]针对上面提到的多种难题,不同的研究和开发人员提出了许多种不一样的方案。综合以上,本文将提出一种能适应各种摄像头的,能抵抗一定干扰信息的一种基于计算机视觉的进行手势识别解决方案。1.4本程序主要实现的功能(1)实现对摄像头的发现和选取,与对每一帧图像的采集和缩放。(2)实现对图像的清晰化,包括点状噪声的去除、细节的还原以及之后整体的锐化。(3)实现对背景信息的剔除,包括对肤色的提取和分割,对阴影和高光区域的扩散和对一些孤立点的剔除。(4)实现关键信息,如手的重心、手的远近(手的面积)等的确定。39 (5)利用以上关键信息,实现一些应用。为了保证软件的实时性,同时便于以上功能的实现,开发工具选用VisualC++2010,第三方类库采用OpenCV2.0来完成这项工程。39 2摄像头信息的采集和预处理2.1OpenCV关于摄像头操作的API要进行手势信息的获取,首先需要拥有一定的硬件,如数据手套、摄像头等。对于软件方面则要安装相应的驱动程序和了解它们的API。幸好OpenCV提供了一系列通用而且简洁的外部接口以便于实现以上的工作,对摄像头操作代码流程如图2.1所示:m_Video=cvCreateCameraCapture(i);//获取第i个摄像头信息给视频指针CvCapture*m_Video;//定义视频指针m_Video;//视º¨®频¦Ì指?针?m_Video=cvCreateCameraCapture(-1);//获取任一个摄像头信息给视频指针提示错误并退出程序m_Video!=NULL//是否找到摄像头IplImage*m_Captured=cvQueryFrame(m_Video);//获取当前摄像头图片,不能用cvReleaseImage手动释放图片内存。进行关于m_Captured和它以前的副本的图像处理和应用用户手动退出?cvReleaseCapture(&(dlg.m_Video));//释放视频指针结束延迟一定的时间hijian开始hi==否是否是图2.1摄像头启动和释放的代码流程39 在实际的应用当中,当用户拥有多个摄像头时,cvCreateCameraCapture方法的参数应该取零或正整数,来选择不同编号的摄像头。当找不到该摄像头时,可以自动选择成默认的摄像头,一般来说是第0号摄像头,仍未找到才进行错误退出。2.2在控件中呈现有关摄像头的图像为了便于观察,需要将摄像头的源数据或数据的处理情况呈现到控件中,而MFC类库和OpenCV共同提供了这些方法。2.2.1HDC的获取MFC的CDC(设备上下文)和它对应的句柄HDC提供了一系列有关画图的方法,对于一个有一定ID(如IDC_STATIC1)的控件来说,获取的方法如下:CWnd*pwnd=GetDlgItem(IDC_STATIC1);CDC*pDC=pwnd->GetDC();HDChDC=pDC->GetSafeHdc();if(hDC==NULL){MessageBox("无法获取hdc!");returnTRUE;}这里将ID为IDC_STATIC1的控件通过pWnd获取控件本身的窗口信息,然后通过这个pWnd来获得有关设备的上下文CDC和HDC,获取不到则出现错误对话框并退出。如果已经设置了控件的Control类型的变量(如m_s1),则CDC和HDC可以通过这个变量直接获取:CDC*pDC=m_s1.GetDC();HDChDC=pDC->GetSafeHdc();2.2.2将IplImage*位图渲染到控件当中OpenCV的IplImage*类型的图片渲染到控件的HDC前需要得到它的头部信息,39 这需要开辟一段内存并将它的结构体提取出来,然后分别赋值:staticucharbuffer[sizeof(BITMAPINFOHEADER)+1024];BITMAPINFO*bmi=(BITMAPINFO*)buffer;BITMAPINFOHEADER*bmih=&(bmi->bmiHeader);memset(bmih,0,sizeof(*bmih));bmih->biSize=sizeof(BITMAPINFOHEADER);bmih->biWidth=image->width;bmih->biHeight=image->origin?abs(image->height):-abs(image->height);bmih->biPlanes=1;bmih->biBitCount=(unsignedshort)((image->depth&255)*image->nChannels);bmih->biCompression=BI_RGB;其中image变量是IplImage*的实例。之后调用MFC的SetDIBitsToDevice方法,参数为(hDC,0,0,image->width,image->height,0,0,0,image->height,image->imageData,bmi,DIB_RGB_COLORS),即可将OpenCV的IplImage*画入到HDC为hDC的控件中。但其后不能执行pDC->Invalidate()操作,否则原先画的结果会被清空。2.3摄像头图像的预处理由于摄像头的图像尺寸千变万化,不一定能满足手的关键点位置的精准度或者图片太大造成延迟和卡顿,而且可能包含了大量的阻碍识别的信息(如点状噪声等),因而需要一些预处理工作。2.3.1图片的缩放图片需要缩放到一个合理的大小,如320x240,为了加强精确度有条件的可以加到640x480。OpenCV提供了一个函数cvResize可以实现这个功能。如原IplImage*图片是m_Captured,目标图片是m_Frame,则函数可写成:cvResize(m_Captured,m_Frame,(m_Captured->width)>(m_Frame->width)?CV_INTER_LINEAR:CV_INTER_AREA);39 当原图片比目标图片小时应使用双线性或双三次插值的放大算法,来使得手的运动轨迹连续变化,反之,则采用平均值的缩小算法,来减少波纹和边缘的不连续现象。2.3.2图片的降噪噪声可以理解为“妨碍人和传感器对所接收的信源信息理解的因素”。而图像中各种妨碍信息接受的因素即可称为图像噪声。噪声被定义为“不可预测而只能用概率统计的方法,来认识的随机误差”。图片噪声按产生的原因可分为外部噪声和内部噪声。外部噪声是指由于系统外部干扰以电磁波或由电源串进系统内部而引起的噪声。如电气设备,天体放电现象等引起的噪声,而这种噪声可能就是高斯噪声、脉冲噪声等多个噪声合成累计的。内部噪声主要是由光和电的基本性质所引起的噪声。如电流的产生是由空穴或电子的集合,定向运动所形成的,而这些粒子运动的带有随机性,产生了散粒噪声;而导体中也有自由电子,它们会进行无规则热运动,形成热噪声。为了消除这些随机因素,可以通过中值滤波或均值滤波的方法法进行滤波。对应的OpenCV函数是:voidcvSmooth(constCvArr*src,CvArr*dst,intsmoothtype=CV_GAUSSIAN,intparam1=3,intparam2=0,doubleparam3=0,doubleparam4=0)。其中smoothtype是CV_GAUSSIAN时将对图像进行大小为param1×param2的高斯卷积的均值滤波。而smoothtype是CV_MEDIAN时将对图像进行大小为param1×param1的中值滤波,注意为param1奇数,且不大于7,其效果结果如下图所示:39 (a)原图(b)CV_GAUSSIAN(c)CV_MEDIAN图2.2CvSmooth的不同smoothtype的比较可以看出,中值滤波对边缘的保留效果较强,但实际执行的速度较慢。不过这些降噪变换都会导致有效细节的丢失。为此可以将图片用按一定阈值还原,这分为两种方法——还原与原图像相差小的部分和与还原原图像相差大的部分,为了保持还原后的连续性,可以设置成不在符合阈值内的图像的保留度再按跟还原原图像相差值线性变化,其效果如图2.3至图2-6所示:图2.3中值滤波后的图形39 图2.4原图图2.5低阈值保留后的图形图2.5高阈值保留后的图形39 从上图可以看出,还原与原图像相差小的部分可以消除一些突兀的部分,可称为椒盐噪声,使得图像看起来比较柔和,但形体内部的与原图像相差小斑纹仍然存在;而还与原原图像相差大的部分则可以让形体内部的斑纹大大减弱或消失,使得图像看起来比较平滑,但是突兀的部分也因此更加明显。因此,这两种还原算法需要配合起来使用才能起到效果,以便于识别为目的,可以使用先还原相差小的部分,并用这个结果作为原图,再还原相差大的部分。如图所示,效果已经综合了上述两种效果的优点。图2.6原图图2.7双重还原后的图像2.3.3图片的锐化经过图片的降噪处理后会显得比较朦胧,有时候需要进行USM39 锐化方可实现更好的边缘提取。为此可以先用cvSmooth,smoothtype选CV_GAUSSIAN进行均值滤波到一个临时图像,然后按一定阈值将原图像与临时图像的像素反差扩大即可,反差扩大的公式为:式中,x表示原图像的亮度,表示临时图像的亮度,k表示锐化的强度(k>1),X表示目标图像的亮度。而通过原图像与临时图像的色差的阈值的调整,可以减少一些不需要锐化的区域,但如上所述,会造成边缘突兀和不连续,为此也可以将位于原图像与临时图像的亮度差在阈值内部分的锐化强度,按这个亮度差线性减弱。如图2.8所示:(a)锐化前(b)锐化后图2.8锐化前后的图像比较39 3手的区域的提取基于计算机视觉的手势识别的首要任务就是要将手和背景区分开,而背景环境往往会十分复杂,前面通过一系列的降噪和锐化实现了冗余信息的剔除和重要信息的强化,从而为之后的工作提供了便利。而这里将讨论如何尽可能准确地将手所在的区域从复杂的背景中提取出来。3.1肤色种子的获取如图所示,随着不同人之间的差异和光线的变化,以及某些摄像头的自动对光功能,均会导致肤色出现变化,为此,很多时候需要以一种快捷的手段根据现场情况进行调整。从上图中还可以看出,手自身的颜色特别是亮度是有变化的,如果以手的平均颜色作为肤色的种子,将会导致可选择的范围的减少。因此这里采用了在图上手的位置单击某一点,并获得这一点的颜色,用试探法选择到最合适的肤色作为手的颜色种子来跟背景区分开来;同时也提供了用自定义的颜色作为种子来灵活应对。3.1.1获取鼠标相对图像位置的颜色要在单击鼠标时获取鼠标相对图像位置的颜色,需要处理这个图像所在控件的点击事件。CStatic控件还需要首先将其Notify属性设置成True,以指定这个控件在单击和双击时对父级控件发出通知。要获取鼠标相对于图像的坐标可以通过先获取鼠标的屏幕坐标,再将该坐标转成相对于控件的坐标来实现,如下图。CPointpoint;//定义点↓GetCursorPos(&point);//获取鼠标在屏幕上的坐标↓GetDlgItem(IDC_STATIC1)->ScreenToClient(&point);//获取相对于控件上的坐标图3-1获取鼠标相对于某个控件的坐标39 要在单击鼠标时获取鼠标相对图像位置的颜色,需要处理这个图像所在控件的点击事件。CStatic控件还需要首先将其Notify属性设置成True,以指定这个控件在单击和双击时对父级控件发出通知。设m_Frame为IplImage*的图片,src为它的imageData的开头,则鼠标在图像的坐标(x0,y0)上的像素指针为mByte=src+x0*(m_Frame->nChannels)+y0*(m_Frame->widthStep);关于这一像素的COLORREF颜色:colour=(int)mByte[0]<<16|(int)mByte[1]<<8|(int)mByte[2];需要注意的是,图像不一定充满整个控件,如果在空间外将会出现一些越界的情况,为此需要判断这个点是否在图像上:if(x0>=0&&x0width||y0>=0&&y0height)3.1.2自定义肤色种子自定义种子颜色可以用CColorDialog——Windows自带的调色对话框来实现,然后将其写入到控件中即可实现,它的构造方法是:CColorDialogdlg(colour0,CC_FULLOPEN);//参数为默认颜色,打开方式(这里是全打开)而通过dlg.GetColor()的方法可以返回调好的COLORREF颜色。3.2基于手与背景在一定颜色模型上的区别的初步提取从上所述,肤色在颜色空间的分布相当集中,但会受到光照强度和不同人之间的很大影响。为了减少肤色受照明强度影响,通常将颜色空间从RGB转换到亮度与色度分离的某个颜色空间,比如YUV、Lab或HSL,然后放弃亮度分量。在双色差或色调饱和度平面上,不同人的肤色区别较小,肤色的不同更多在亮度上而不是色度上。[7]OpenCV中实现上述方法的函数为voidcvCvtColor(constCvArr*src,CvArr*dst,intcode),其中src输入的是原图像指针,而IplImage继承了CvArr,有时需要将用整数信息存储的图片转换为一定精度的浮点图;dst输入的是目标图像的指针39 ;code是色彩空间转换的模式,该code来实现不同类型的颜色空间转换。比如当code选用CV_BGR2HSV时,对于8位图,需要将RGB值归一化到0-1之间。这样得到HSV图中的H取值范围才是[0,360),S和V的取值范围是[0-1]。由于颜色模型的不同,转换以后的对手的范围的提取方法也有所不同。具体来讲,如果用原始的rgb颜色模型的图片来提取,那么可以用r、g、b三种通道各自与肤色种子的色差的最大值来判定图像上的某点是否在手上;如果用颜色模型为一个亮度通道+两个颜色通道的图片,则可以通过先遍历整个图像,把每一点的像素归化到[0,255]的整数范围中,再将两个颜色通道移到统一的位置进行统一的色差最大值判定,提取的效果如图3.2所示。图3.2初步提取轮廓3.3将手的范围扩展到其他区域在复杂光线条件下,比如图所示的肤色在高光区或阴影区中导致亮度存在不同,有些部分并没有被选上;还有如图所示的情况,由于手在不同位置的代谢情况的不同和指纹关系,导致颜色也存在着不同,从而有些另外的部分也没有被选上。但是从图中又可以看得出,这些区域与选取到的区域之间并不存在明显的边缘,因此可以通过一种多次迭代的方法,将这些区域周边按一定的色差阈值进行扩散,直到扩散了0个像素为止。如图3.3所示39 (a)扩散前(b)扩散后图3.3选取扩散前后的对比3.4外部孤立点的消去经过如上图所示的处理中,可以发现图片中存在一些比较小的一些孤立的区域被选中了,如人的头部,书架、椅子、窗帘等,它们的颜色与手的颜色相近,但是其共同点是都在在手的后面,而且所占的面积比手的要小。为此可以通过获取最大的连通区域的方法来去掉一些不合理的选取,目标效果如图3.3所示。39 (a)消去前(b)消去后图3.3外部孤立点的消去目标获取最大的连通区域包括三步:获取连通区域的面积并在每个像素标记区域的id→找出面积的最大值和对应的区域id→将面积信息非最大的区域置为未选中。可以通过递归的方法实现以上功能,但由于递归要调用windows系统的堆栈,而这个堆栈的大小不能超过2M,因此还需要将这个算法实现成非递归的。这里由于栈的大小不会超过整个图片面积的整数倍,因而提供的栈可以选择一个中间的IplImage作为存储,这就要求这个IplImage的通道数足够。为此可以选择一个彩色的24位RGB图作为栈来39 储存当前遍历的位置信息。为此需要将位置的两个值——x和y的坐标通过移位运算的方式压缩在24位长的变量中。整理以上之后,算法可以分以下几步完成。3.4.1获取连通区域的面积和标记区域ID当一个点首次到达一个连通区域(未计算)时,即开始计算这个区域的面积,算完该面积后再进去这个区域(已计算)将不会再重新算。计算面积的步骤如下:进入堆栈计算面积循环,并将当前坐标压入堆栈。面积计数加一堆栈弹出一格上下左右是否为被选中区域且未被计算读取栈中坐标被选中的相邻节点分别压入堆栈该坐标置为已计算并标记区域ID栈是否为空返回面积计数,结束是否否是图3.4外部孤立点的消去算法在数据的结构上,将原图像副本信息拷贝后,两个临时图片memopic和temppic将分别储存不同的中间信息。memopic的每个像素的24位整形中,前12位(unsignedchara,b)表示遍历游标的横坐标(inti),后12位(unsignedcharb,c)表示遍历游标的纵坐标(intj)。其读取和写入的方法如下:读取:i=(int)a<<4|(int)b>>4;39 j=((int)c&15)<<8|(int)c;写入:a=(uchar)(i>>4&255);b=(uchar)((i&15)<<4|j>>8&15);c=(uchar)(j&255);temppic的每个像素的24位整形中,储存像素点遍历的状态:0:原先未选取;1:原先已选取但未参与面积计算;>1:原先已选取并已参与面积计算,表示区域的ID(从2开始)。3.4.2最大面积和对应区域的获取最大面积的获取的语句是if(countmaximageData)+i*(temppic->nChannels)+j*(temppic->widthStep)对应的值是否跟countmaxid的后24位相等即可。3.5内部孤立点的修补上面的的提取工作已经比较理想了,但是手的内部仍然存在着一些缺失的区域,这会对边缘的检测带来不必要的麻烦。类似地也可以通过第3.4节建立堆栈的方法来实现,但是要将选区做暂时的反转操作,效果如图3.4所示。39 图3.内部孤立点的修补39 4手的关键点的提取经过以上的步骤,我们已经获得了手的像素信息。然而只通过像素信息来判断手势将导致判断过于复杂,不利于应用的实现。为此可以通过提取关键点的方式来给手势识别应用一些简明而稳定的信息。4.1手的重心和面积的确定手的重心可以通过选取图片(如memopic)中手的区域的内的x和y坐标的平均值来实现,而手的面积可以反映手离摄像头的距离。如图4.1所示:图4.1重心区域(手中央红色区域)4.2手的边缘的提取手的边缘可以通过像素与其旁边相差的大小来确定,但为了保持边缘的宽度在1像素内,仅需判断这个像素点与其右方和下方的颜色是否相同即可,如图4.2所示:39 图4.2边缘提取(手中央红色区域)39 5程序主体代码的实现5.1工程的环境和架构手势识别系统GestureTest的开发环境采用VisualC++2010,项目类型采用了MFC基于对话框的应用程序,第三方库选用OpenCV作为识别的辅助工具。为此,必须先进行工程环境的配置:(1)安装OpenCV2.0,并在其安装目录下的include和lib下的所有文件复制到C:ProgramFilesMicrosoftVisualStudio10.0VC下的同名目录中。(2)打开所建立的工程,选择项目-GestureTest属性,配置选“所有配置”,并在其下的配置属性-链接器-输入中输入cxcore200d.lib;cv200d.lib;highgui200d.lib;%(AdditionalDependencies)(3)将OpenCV安装目录下的bin目录里的cv200d.dll、cxcore200.dll、cxcore200d.dll和highgui200d.dll复制到项目生成的可执行文件的相同目录下。如果不能运行,则复制该bin目录里文件名不带d结尾(不含扩展名)的相应文件,并将程序旁dll文件的文件名后加上d即可。建立的项目主要包含以下文件:GestureTest.hGestureTestDlg.hresource.hstdafx.htargetver.hGestureTest.cppGestureTestDlg.cppstdafx.cpp其中的GestureTestDlg.cpp,即CGestureTestApp类在InitInstance()方法的dlg.DoModel后需要加上if(dlg.m_Video!=NULL){39 cvReleaseCapture(&(dlg.m_Video));}用来在程序结束后释放视频指针变量m_Video,否则程序在关闭后无法彻底退出。主要的实现方法将在CGestureTestDlg类中实现。5.2主类成员变量的定义CvCapture*m_Video;//视频指针IplImage*m_Captured;//视频中的图片IplImage*m_Frame;//暂存图片IplImage*m_GrabFrame;//预览图片IplImage*m_temp;//临时图片boolm_FrameCreated;//暂存图片是否为手动创建的intsmoothMode;//平滑模式intcentx;//手的重心横坐标intcenty;//手的重心纵坐标intzone;//手的面积//IDC_STATIC1(摄像头的图像所画在的控件)的各个属性CDC*pDC;//设备上下文HDChDC;//设备上下文的句柄CWnd*pwnd;//控件的窗口CRectrect;//控件的宽高度intscreenwid;//屏幕宽度intscreenhei;//屏幕高度5.3程序的初始化代码在构造函数中将成员变量和控件变量赋上初始值,并在OnInitDialog中进行如图5.1所示的操作。39 获取要绘图的控件的HDC和大小获取摄像头获取屏幕分辨率开始计时改变控件大小图5.1程序的初始化流程并在OnInitDialog方法中加入以下代码://获取HDC和控件尺寸pwnd=GetDlgItem(IDC_STATIC1);pDC=pwnd->GetDC();hDC=pDC->GetSafeHdc();pwnd->GetClientRect(&rect);if(hDC==NULL){MessageBox("无法获取hdc");returnTRUE;}//获取摄像头if(m_Video==NULL){m_Video=cvCreateCameraCapture(m_e01);}39 if(m_Video==NULL){MessageBox("无法打开摄像头");returnTRUE;}Invalidate(TRUE);UpdateWindow();//获取屏幕分辨率screenwid=GetSystemMetrics(SM_CXSCREEN);screenhei=GetSystemMetrics(SM_CYSCREEN);//开始计时SetTimer(1,100,NULL);//设置装载图片的控件的默认尺寸是640*480并且将右边控件位置和窗口大小调到适当位置MoveControls(640,480);5.4从输入控件中获得识别参数从控件中获得参数可以用UpdateData(TRUE),但当用户输入了一些非数字等不能转换为参数的字符串时,会不断地弹出对话框。为了防止这种情况,处理方式如图5.3所示,并在OnTimer方法的开头中加入代码:39 获取弹出的对话框的指针是否获取到对话框获取控件的数据是否因获取失败而弹出了对话框根据控件数据开始识别否否是是返回,不进行识别对话框关闭后控件的值复位并返回图5.2能防止不断弹出对话框的获取控件信息流程CWnd*cwdg=FindWindowEx(NULL,NULL,NULL,"GestureTest");//获取UpdateData自动生成的对话框,标题为工程名CWnd*cwdt=GetForegroundWindow();//获取活动的窗口if(this!=cwdt&&cwdg==cwdt){//当自动生成的对话框弹出时不更新控件,也不进行识别CDialogEx::OnTimer(nIDEvent);return;}if(!UpdateData(TRUE)){//读取对话框数据//UpdateData(FALSE);CDialogEx::OnTimer(nIDEvent);return;}39 5.5总体识别语句的实现总体识别的步骤包括:中值滤波、均值滤波、细节还原和锐化等图像的初步润饰操作,也包括了选取的提取和扩散操作,还有孤立点去除和关键信息的提取,代码在OnTimer方法中的获取控件信息的语句之后。在识别的过程中,需要对识别的每一步结果分别进行呈现,为此需要进行适当的流程控制,并选择适当的IplImage*图片画到控件中。而后面的应用实现的代码将被写在总体识别的语句之后。intcolourmax=0;//最大色差boolpreshow=false;//显示m_Frameif(smoothMode==1){cvSmooth(m_Frame,m_GrabFrame,CV_MEDIAN,2*m_e11+1,0,0,0);//中值滤波}elseif(smoothMode==2){cvSmooth(m_Frame,m_GrabFrame,CV_GAUSSIAN,2*m_e11+1,2*m_e11+1,0,0);//均值滤波}if(smoothMode!=0){blurFixFilter(m_Frame,m_GrabFrame,m_e12,m_e13);//滤波后的有限还原}if(m_c1==TRUE){ruihuaFilter(m_Frame,m_GrabFrame,m_erhsl,m_erhbj,0);//锐化cvCopy(m_GrabFrame,m_Frame);}if(m_r1>=1){preshow=true;//显示m_GrabFramecolourSelect(m_Frame,m_GrabFrame,m_e21,m_e22,m_e23,m_c2==TRUE,false);//按肤色种子选取和扩散39 }if(m_r1>=2){preshow=true;//显示m_GrabFrameareaCut(m_GrabFrame,m_temp);//消除手区域外的孤立点cvNot(m_GrabFrame,m_temp);areaCut(m_temp,m_GrabFrame,true);//消除手区域内的孤立点cvNot(m_temp,m_GrabFrame);}if(m_r1>=3){preshow=true;//显示m_GrabFrameint*joints=getJoints(m_GrabFrame);//获取重心坐标和手的面积centx=joints[0];centy=joints[1];zone=joints[2];}DrawToHdc(preshow?m_GrabFrame:m_Frame);//将IplImage*的图片画到控件中39 6应用设计6.1屏幕画图的设计在屏幕上画图的功能可借助CWindowDC并定义和设置笔刷,然后在CWindowDC中调用MoveTo和LineTo的方法来实现。6.1.1获取CWindowDCCWnd*pWndw=CWnd::GetDesktopWindow();//获取桌面窗口CWindowDCwinDC(pWndw);//创建桌面窗口的设备上下文CWindowDC*pWinDC=&winDC;//获取桌面窗口设备上下文的指针6.1.2定义和设置笔刷CPenpent;//定义笔刷pent.CreatePen(PS_SOLID,1,RGB(255,0,0));//创建红色笔刷CPen*pen=&pent;//获取笔刷指针6.1.3绘图语句定义一个数组intpos[4]用来储存手的重心位置,偶数下标表示横坐标,奇数坐标表示纵坐标;用intposleng来储存pos手的重心的存储情况。posleng==0时没有储存手的位置;或只储存了一个手的初始位置(pos[2],pos[3]),可作为今后的前一点,但不能画线;posleng==2时,(pos[0],pos[1])是上一点坐标,(pos[2],pos[3])是当前点,可以用来画线,绘图结果如图6.1所示。if(posleng>0){//储存上一点坐标pos[0]=pos[2];pos[1]=pos[3];}//更新当前坐标pos[2]=centx;pos[3]=centy;39 if(posleng>0){//将手的坐标转为屏幕坐标并画图pWinDC->MoveTo(screenwid-screenwid/wid*pos[0],screenhei/hei*pos[1]);pWinDC->LineTo(screenwid-screenwid/wid*pos[2],screenhei/hei*pos[3]);}else{posleng=2;}图6.1屏幕画图效果6.2模拟鼠标功能的实现模拟鼠标的功能可以通过SetCurPos(x,y)与mouse_event(MOUSEEVENTF_LEFTUP/MOUSEEVENTF_LEFTDOWN,0,0,0,0)的连用来实现,但由于摄像头的分辨率往往小于屏幕的分辨率,造成精确度不高。为此可以通过手在图像上的离摄像头中心远,产生鼠标移动的增量来实现。而为了交互的的自然性,鼠标是否按下用手离摄像头的远近,即手在图中的面积来实现,效果如图6.2所示。图6.2模拟鼠标效果39 6.2.1手的有效偏移量的获取doublexrate=1.0-(double)centx/wid;//将手的坐标归化到[0,1]区间内并将横坐标镜像doubleyrate=(double)centy/hei;//将手的坐标归化到[0,1]区间内并将横坐标镜像doublemovermin=0;//手在x和y位于[0.5-movermin,0.5+movermin]的矩形内鼠标不动,有效偏移量=手离重心的距离-movermin。doublemovementx=0;//有效偏移量的横坐标doublemovementy=0;//有效偏移量的纵坐标if(xrate>0.5+movermin){movementx=(int)(xrate-0.5-movermin);//获取手的有效偏移量}elseif(xrate<0.5-movermin){movementx=(int)(xrate-0.5+movermin);}if(yrate>0.5+movermin){movementy=(int)(yrate-0.5-movermin);}elseif(yrate<0.5-movermin){movementy+=(int)(yrate-0.5+movermin);}6.2.2鼠标点击功能的实现CPointmousePoint;//定义点变量GetCursorPos(&mousePoint);//获取鼠标坐标到这个变量intmpdx,mpdy;//获取鼠标偏移量getMouseDxy(movementx,movementy,&mpdx,&mpdy);//从手的有效偏移量按一定关系获取鼠标偏移量SetCursorPos(mousePoint.x+mpdx,mousePoint.y+mpdy);//设置鼠标坐标doublesize=(double)zone/(wid*hei);//获取手的面积占整个摄像头画面的比值if(size>0.15){mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//手的面积较大则执行鼠标按下指令39 }else{mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//手的面积较小则执行鼠标抬起指令}39 7手势识别的综合测试在手势识别的测试工作中,中值滤波、均值滤波和还原的表现结果比较稳定,锐化的效果也比较显著。这些能够实时而有效地对摄像机的视频进行加工。背景的消除算法可以在对几个高低端的摄像头的不太强的光线的测试下顺利完成。经过测试发现,当手的区域因为不连贯而缺失时可以通过中值滤波的还原的算法来完成;而当手的区域跟背景其他地方相连时,使用较高缩放系数锐化操作可以有效地让将这些区域排除,但可能会增加手的区域的不完整性,如图7.1、图7.2所示。(a)中值滤波前(b)中值滤波后图7.1中值滤波对识别结果的比较39 锐化前锐化后图7.2锐化对识别结果的比较在手的关键信息的获取中,手的重心会有时会有一些突然移动的情况,为此需要对此进行一些屏蔽。39 结论l总结本系统针对不同摄像头成像的情况,根据实际情况,采用VisualC++和OpenCV进行开发,而手势识别的工作也实现了包括了图像的采集、噪声处理和清晰化,手的区域的提取,以及手的关键点的获取,并根据结果开发一些常见的应用。能够在大部分摄像头的情况下方便人们对计算机的交互操作。图像的采集实现了所有连接且支持的摄像头都能使用的功能,使得有内置摄像头的机器能够用外置摄像头进行识别。初步的处理也减少了摄像头图像的一些干扰性因素,使得识别的结果更加理想。背景的消除算法根据实际情况出现的各种问题,想出并设计和实现了一系列有效的辅助的方法,如方便的肤色获取、亮度+颜色的色彩模型;还有一些后面补充的方法、如选区扩散和孤立点的消除等,这使得手的区域的提取能够做到越来越高的适应性和准确性。关键信息的提取包括了手的重心坐标和手离摄像头的距离(手在图上的面积)等。这些使得交互应用更容易实现。应用的实现选择了一些常用的,如画图,鼠标模拟输入等,并根据实际情况选择了一些易用而可行的交互方式。如一般摄像头的分辨率比屏幕的分辨率小,为了保持精确度,鼠标的坐标采用了在图上手离摄像头较远而产生位置增量的方法;还有是否画图、是否按下鼠标采用了手离摄像头的距离大小进行判断等。l展望与改进当今的人机交互技术(HumanComputerInteraction,HCI)已经成为了制约计算机应用和发展的一道瓶颈之一。然而随着一些新的无障碍的自然交互方式的出现,如用的人的各种感觉和动作对计算机进行交互,将会大大地提高交互的效率和自然度。[8]手势作为最常用的人跟人的交流手段之一,在人机交互的方面也越来越受到了广泛的关注和得到了迅猛的发展。从辅助元件到我们使用的智能手机到带动作识别的高级游戏机,手势识别将扮演着越来越重要的角色。39 随着计算机性能的提高和摄像头等传感器的清晰度、灵敏度的提升,手势作为一种自然而灵活的交互手段,带来的畅快和沉浸感将会越来越强。[9]而同样技术实现的成本将会随着这些硬件的提升而降低,但由用户所的带来的技术要求也将会越来越高。为此需要不断根据当前的需求,不断跟进合适的技术,来不断完善这个系统的开发。在开发这个系统的过程中,虽然经过不断的努力,想到并实现了许多有效的方法,但仍然存在一些不足之处。例如由于按颜色选取算法的局限性,在光线过强的情况下,手的颜色会基本上显示成灰色;还有当人脸和手同时在摄像头的图像上时,会出现区域不稳定的情况;另外选择到区域的手指的位置有些不稳定(中值滤波下可以一定程度上缓解这种情况)。上述问题可以设法设计一些合适的方案,通过对手和人脸的特征的提取,来实现更准确地将手的区域提取出来。另外,由于当前个人技术所限,并没有做出更多的应用,可以通过的不断的学习和实践,来更好地完善这个系统。39 参考文献[1]胡友树.手势识别技术综述[J].中国科技信息,2005,(2):42,41.[2]任雅祥.基于手势识别的人机交互发展研究[J].计算机工程与设计,2006,27(7):1201-1204.[3]邹晨,张树有,谭建荣等.VR环境中产品设计手势的定义与合成[J].工程图学学报,2000,21(2):107-110.[4]葛继东,贾绍文,蔡慧敏等.手势识别综述[J].科技视界,2012,(19):79,19.[5]汪成为,高文,王行仁.灵境(虚拟现实)技术的理论、实现及应用[M].北京:清华大学出版社,1996:1-570.[6]关然,徐向民,罗雅愉等.基于计算机视觉的手势检测识别技术[J].计算机应用与软件,2013,30(1):155-159,164.[7]陈锻生,刘政凯.肤色检测技术综述[J].计算机学报,2006,29(2):194-207.[8]黄季冬.动态手势识别技术研究与实现[D].华中科技大学,2012.[9]万华根,肖海英,邹松等.面向新一代大众游戏的手势交互技术[J].计算机辅助设计与图形学学报,2011,23(7):1159-1165.[10]Pavlovic, V., Sharma, R. & Huang, T. Visual interpretation of hand gestures for human-c-omputer interaction: A review. IEEE Transactions on Pattern Analysis and Machine Intel-ligence, 19(7), 677–695,1997.[11]Quek, F. Gesture, speech, and gaze cues for discourse segmentation. In IEEE Conferen-ce on Computer Vision and Pattern Recognition. pp. 247–254,2000.39 致谢本论文是在我的导师冯开平教授的悉心指导下完成的。从论文的选题到项目的不断完善,冯老师都给出了很多有用的的方法和很多有价值的建议。冯老师平易近人的性格、深厚的专业知识功底、精益求精的学术作风、执着的科研精神、敏锐的思维、广阔的视野都使我受益匪浅。这些不仅使我树立了远大的学术目标,更学到了实事求是、扎实做好每一件事的学习态度和方法。在此,谨向导师表示衷心的感谢和崇高的敬意!在大学三年的学习期间,得到黄飞室友,万科师兄等同学的关心和帮助,他们给予了我很多的关照与帮助,与他们的友情是相当宝贵的一笔财富,在此表示深深的感谢!39