




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
信息科学与技术学院毕业论文课题名称:基于特征识别的人脸检测系统学院:信息科学与技术学院完成日期:二o—年五月十九日摘要摘要摘要摘要#理论和技术2.1理论2.1.1检测部分的LBP特征+GentieAdaBoost分类器此处描述的算法是该理论的一般性算法,并不和本课题程序中实际使用的算法完全相同,课
题中检测部分使用的检测器是于仕祺老师公布的人脸检测库,只提供了函数接口和对应的
DLL库,并未公开内部具体的实现细节,只公开了特征使用的是LBP特征,分类器是Gentle
AdaBoostClassifier,训练的正样本大约是150,000张图片,故此处只做一般性的算法介绍。1)LBP特征12295&531LBP全称LocalBinaryPattern(局部二值特征),它的处理对象是灰度图片,作用是提取图片的纹理特征,它原理并不复杂,计算也比较简单迅速,对旋转和光照不具有一定的鲁棒性,因此其在纹理分析和人脸检测与识别的等应用中取得了非常好的效果。在本课题中,人脸检测部分提取的特征就是滑动子窗口的LBP特征,提取到的特征将作为分类器的输入,最原始的LBP定义是在某个像素的8领域内,将位于中心像素周围的像素的灰度值与中心点像素的灰度值做比较,若大于中心像素的灰度值,则记为1,小于则记为0,然后将得到的八个01值依次排列看成是一个二进制数,再转换为十进制表示就得到了该中心点的LBP值,其计算过程如图2-1所示:Binary:00010011Decirrtal:1&图2-1用公式来表示的话:(2-1)其中()表示3X3领域的中心像素点,它的像素值为,领域的其他像素值为,s(x)为符号函数:(2-2)原始的LBP算子存在一定的缺陷,它只覆盖了一定范围的小区域,区域内的像素数量固定不变(如图2-1中的9个像素点),无法适应不同尺度的纹理需要,正对这个缺陷,一些科学家提出了一些改进的LBP算子,Ojala等人将3X3领域扩展到任意领域,然后使用圆形的包围区域,改进后的LBP算子可以包含任意多个像素点,满足了对不同尺度的纹理分析需求,一种改进的LBP算子如图2-2所示,其定义了5X5的领域,在圆形包围线上取8个采样点:图2-2采样点的坐标需要经过一定的计算,采用取整或双线性插值的方法来求得采样点的整数值坐标,再利用基本LBP算子中求解中心点LBP特征值的方法(公式(2-1))求解LBP值,在人脸检测中,LBP算子的范围包围区域经常是椭圆形的,人脸,眼睛,嘴巴都是椭圆形的结构,用椭圆的包围区域可以获得更强的表达力,在此不再赘述。2)GentleAdaBoost分类器GentleAdaBoost全称GentleAdaptiveBoostClassifier(温和自适应强化分类器),它是基于BoostClassifier的一种改进版本,BoostClassifier简单说来就是指将许多的弱分类器级联起来组成一个增强型的强分类器,许多弱分类器通过类似“投票”的方式决定最终的分类结果。一种弱分类器的训练算法如图2-3所示fori=l:n(n为矩更特征夠个数)1)令valuef]^featiire[i][],把隽i个矩形特征下所有样本的特征值,拷貝给一纯数笙询叽并对valued从小到大的排序2)forj=(巾为样木个数)a.leftvaliB^£^(wkrAySL^(前t个样本的集中度)H「ightvalue二f丄叫・人)/丫:+W(后巾一j个样本的集中度)其中叽是笫k个样本的权重,齐是第k个样本的标记%=1标示为人脸样本,yK=-l标示非人脸样本9//计算该矩形特征下的加权均方误差'(^terror=XL<yk"leftvalue)2(前j个样木的蔑散皮)「ighrtBrcx=》:j十帆心十rightvalue)"后nn-j个样本勾离散度}jf(lefterror+righterror<fault)-faultlefterror+righterror(更新当营的均方谋差)*询闾*(记录弱分类器的阚{<)■a3=!eftvalue,a2=rrghtvalue(记录弱分类器的分类置信度)3)氾录均方误差最小対矩形特征鞠壓标"图2-3一种GentleAdaBoost分类器的训练算法如图2-4所示:1.假设共有N个训练棒本,并冃标记为;(為,融“瑤禹*其中%心,衍y,=i表示为人脸样本,否则表示非人脸样本口2样本的权重初始化为;叫二1/N’j=1…很。forj-l:M(M表示训练的绘数)从所有的矩形特征中,挑选隽j轮中,最佳的弱分类器hj(X),使得在该样本权重的分布下.样本的加权均方误差最小口对权重进有更新丄exp(-yh((x))7i=l,...Nu。归一化权重使得:£莓魁=】&隸出强分类戕:H(x)=sgn[^%(X)]当xMO,sign(x)为匚竹则为-1.图2-4其中,Gentie是指此分类器相对于传统的AdaptiveBoostClassifier在训练时错误分类的样本的权值增加策略上更加平稳(图2-4中的3.b),以便获得更好的泛化能力;Adaptive是指相对于传统的BoostClassifier,AdaptiveBoostClassifier能动态调节各个样本在训练时所占的权重,就是增加错误分类样本的权值,降低正确分类样本的权值。识别部分的GoogleFaceNet无论是人脸检测还是人脸识别,都面临着来自光照,姿态,表情变化带来的巨大挑战,人工神经网络模型以其强大的非线性拟合能力和极强的鲁棒性能够在很大程度上面对以上挑战,而且深度学习是近几年才兴起的新理论,本课题勇于追赶研究前沿,在识别部分使用了DavidSandberg根据Google在2015发表的一篇文章《FaceNet:AUnifiedEmbeddingforFaceRecognitionandClustering》使用Tensoflow实现的一个人工神经网络,并已经使用大量训练样本(百万级)预先训练完毕,在使用时只需要加载这个固化的网络模型至内存中,输入经过预处理的人脸图片,再执行一次前向计算,即可得到用于识别人脸的特征。FaceNet在LFW(LabeledFacesintheWild)数据库上的准确率达到了99.63%,刷新了当时的记录。FaceNet使用深度卷积神经网络直接学习人脸在高维欧式空间的映射,映射得到的坐标点的欧式距离即代表了样本的相似性,距离越大表示相似性越低,反之越高。FaceNet的大体架构如图2-5所示:图2-5其中DeepArchitecture就是一般的卷积神经网络去掉softmax后的结构,经过L2的归一化后得到人脸图片再高维欧式空间的的特征表示,在训练时以最小化后面的三联子损失函数为目标不断调整网络中各个节点的权值。目标函数:三联子损失函数,其学习过程如图2-6所示NegativeAnchor®PositiveLEARNINGPositivNegativeAnchor®PositiveLEARNINGPositiveNegativeAnchor亠图2-6三联子表示在训练样本中抽取的三个人脸样本(抽取方法不做深入讨论),其中Anchor和Positive是来自同一个人的两张脸部图片,Anchor和Negative是来自不同人的两张脸部图片,学习的过程就是要使经过网络映射的Anchor和Positive的欧式距离尽可能小而Anchor和Negative的欧式距离尽可能的大,依此来获得对人脸特征的描述。用公式描述:(2-3)其中表示映射过程,,,,分别代表锚点,正样本,负样本,表示正负样本之间的距离,表示所有可能的三联子集合。两个样本之间的相似性就使用公式来计算的。将(2-3)做一下转换便可以得到目标函数:(2-4)训练的过程就是最小化,训练方法使用一般的卷积神经网络的训练方法即可,其具体内容不做讨论,实现上各种深度学习框架已经集成了各种最为先进的优化算法,只需要调用相应地接口便可以完成对神经网络的训练过程。关于DeepArchitecture,论文中给出了几种可选的网络结构,其中一种是Zeiler&Fergus结构,如图2-7所示:
IlIjitsize-insize-uuikernelp^rnmFL1-Sconvl220x220x3110x110x647x7x3?29K115Mpooll110x110x6455x55x643x3>c64t20morml55x55xfr455x55xfyl0«>nv2a55x55xfrl55x55x6d1xlxG^I,14K13M^/>nv255x55x^155x&5xl923x3x64}111IK335Mrnorm255x55x19255x55x1920pool255冥55x19228x28x1S)23x^xl92,20<onv3a28x28x19228x28x192lxlxl92,137K29Mconv328x28x19228x28x3843x3x1號J664K521Mfw*0】328x38411x14x38^3x^x3S4,20conv4aI4xblx38^11^1x14x3841148K29M<vnv414x14x38414x14x2563x^x3S4?1885K173MTOnv5a14x14x25614x14x256lxlx25fi:l66K13M14x14x25614x14x2563x^x256,1590KH6M<onv6n14x11x25(114x14x2561x1x256,166K«>nv614x14x25614x14x2563xJx256,1590KJI6Mpaol414xl4x25fi7x7x2^63x3x256,20<oncat7x7x2567x7x2560fcl7x7x2561x32x128maxoutp—2103MI03Mfc21x32x1281x32x128maxoul尸234M34M&712S1x32x1281x1x128524K05ML21x1x1281X1x128ntotal140ML6B图2-7另外一种是基于GoogleInception的结构,如图2-8所示:
typeoutputsizeconv『(7x7x3,2)112x112x64nuxptM)l+norm56x56x64incepiion(2)MX56X192norm+maxpx>12Sx2«xt92inccplion(3n)28x28x256inception(3h)28x28x320iuccplioii(3cj14x14x640inception(4a)I4x14x64()inccpiion(4h)14x14x640inceplioii(4c)14x14x640inccpiion(4d)14xldx640inception(4c)7x7x1024inccplion(5a)7x7x1024inccplian(5h)7x7x1024avgpool1x1x1024l\i11y^onn1X1xl?8L2nornuih1x1x128图2-8其中一种Inception的结构如图2-9所示:图2-9课题中使用的网络结构目前还不知道其具体形式,Tensorflow提供了可视化固化网络模型的工具,但在实际使用时出了一些问题,因此很遗憾的没有看到实际使用的网络结构。2.2技术MFC简介MFC全称MicrosoftFoundationClasses(微软基础类库),这是Microsoft推出的一套C++类,把大部分常用的WindowsAPI函数封装在其中。MFC中的类和函数接口成千上万,对如此庞大的类库进行介绍和对其内部的复杂机制进行讨论已经超出本文的范围,因此在接下来的论述中,文章仅介绍本课题中显式使用的一些内容,本课题使用的集成开发工具是VisualStudio2013Premium,在其中新建一个MFC项目的同时,VS会自动生成项目的主框架,我们要做的就是往这个主框架中添加内容和修改一些相应地修改,必要时重写某些函数。UI:本课题是基于对话框结构的项目,因此涉及到的UI知识仅有对话框中的内容,MFC封装了窗体和控件的底层绘制操作,用户可以在资源文件添加对话框类型的资源文件,然后用vs提供的可视化编辑工具编辑自己的对话框,而不用考虑其底层的绘制细节,各种控件的添加可以通过拖拽放置到对话框中。用可视化工具编辑完界面后,vs会自动为这个对话框生成一个相应的类CXXXDlgCalss,这个类就封装了对话框中的所有内容,与控件关联的变量全部作为类的成员变量来实现,各种消息相应函数也是做为这个类的成员函数来实现的。本课题使用的默认MFC样式如图2-10所示:图2-10数据类型:MFC中定义了许多常用的数据类型,本课题使用最多是CString类型,窗体控件中显示的字符串的类型全部都是CString类型,写入数据库和从数据库中取出来的数据类型也都是CString类型,因此关于CString和其他数据类型的转换是本课题实现部分的一个重点。MFC中提供了许多类型转换函数和类型转换宏如宏CT2A()可以将CString类型转换为string类型,CString类型的本身也提供了一个强大的转换函数Format(),它可以方便地实现许多基本的类型转换,但是一些更为复杂的类型转换还需要自己写函数去实现。消息机制:在Windows中,消息和线程一一对应,当系统检测到某个消息时,会将相应的消息发送给与该消息对应的线程。按照类型,Windows将消息按照类型分为如图2-11所示的几类:图2-11Windows将整个消息系统分为3个层级:Windows内核的系统消息队列App的UI线程消息队列处理消息的窗体对象Windows内核会维护一个全局的系统消息队列,按照线程的不同,系统消息队列中的消息会被发送到应用程序的UI线程的消息队列中,应用程序的每一个UI线程都有自己的消息循环,会不停的从自己的消息队列中取出消息,并发送给Windows窗体对象,每一个窗体对象都使用窗体过程函数WindowProc()来处理接受的各种消息。本课题中相应地消息一般都是第三层级的消息。MFC框架封装了对消息处理的大部分操作,我们可以在生成的窗体类中添加消息的响应函数,当系统检测到用户或者系统的行为产生了已经被定义的消息,会自动调用我们编写的消息相应函数,执行对消息的响应操作,在本课题实现中具体相应的消息有窗口绘制消息ON_WM_PAINT,系统时间消息ON_WM_TIMER按键消息ON_BN_CLICKED等。多线程:MFC中的线程主要有两类,一类是用户界面线程,另一类是工作者线程,二者的重要区别在于前者有自己的消息队列和消息循环,而后者没有,用户界面线程的工作一般是处理界面绘制,响应用户操作和系统产生的事件及消息等,vs自动生成的窗口类中的成员函数大多都是作为用户界面线程执行的,一种不恰当的设计方式是将所有代码都放在用户界面线程中,这样做确实简单方便,但是它会带来一些严重的效率问题,比如一些占用大量内存和耗时较长的计算若放在用户界面线程中会导致界面长时间处于无响应状态,因此应该将计算量较大的处理另开工作者线程单独执行,用消息传递或者共享内存的方式与用户界面线程同步。MFC中多线程编程常用的函数如下几个:启动一个工作者线程:CWinThreac*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,nPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,LPSECRITY_ATTRIBUTESlpSecurityAttrs=NULL);在某个信号量上阻塞线程:DWORDWaitForSingleObject(HANDLEhHancle,DWORDcwMilliseconces);临界区的访问:进入临界区的使用CCriticalSection对象的Lock()函数,离开使用Unlock()函数.Tensorflow简介TensorFlow是一个开源的使用数据流图的数值计算框架,被广泛的用于实现各种神经网络,数据流图中的节点代表数学运算,而图中的线条代表数据数组(又称为张量)。TensorFlow最初由Google大脑小组的研究员和工程师门开发,用于机器学习和深度神经网络方面的研究,但是因为这个框架具有通用性,也可以被用于其他的计算领域。它灵活的架构可以让你将节点部署在一个或者多个CPU或者GPU上进行并行计算。关于数据流图:数据流图是一种以数据流动为驱动力的计算模型,用noces和edges的有向图来描述数学计算,Nodes—般用来表示对数据施加的数学操作,但也可以表示数据的输入的起点或者是数据输出的终点。本课题尝试直接编译Tensorflow源码,以使用其C++接口,但官方给出的编译方式是使用Google内部使用的编译工具Bazel,这个工具在Windows平台很不稳定,目前只有测试版,在使用的过程中出现了各种问题,其中的一些无法解决致使编译失败。官方推荐的方式是使用Python接口,而且教程大多数也是基于Python接口的,于是本课题使用C++,Python混编技术将Python的接口封装成C语言的形式,如此就可以在程序中间接使用Tensorflow了。课题中使用的FaceNet模型是预先训练完成的,其数据流图的结构被固化存储在.meta的文件中,图中的权值被固化存储在.ckpt的文件中。本课题使用的Python接口主要有如下几个:导入数据流图即网络结构至默认的图(tensorflow会在Python环境的上下文中提供)中使用函数tensorflow.train.import_meta_graph(meta_file_path)加载权值至数据流图中saver.restore(sess,ckpt_file_path)根据Tensor的名字获得输入输出graph.get_tensor_by_name(tensor_name)执行前向计算sess.run(embedding,feed_dict=feed_dict)ADO组件简介ADO全称ActiveXDataObjects是一个用于存取数据源的组件,它提供了编程和统一数据访问方式OLEDB的一个中间层,允许开发人员编写访问数据的代码为不用关心数据库是如何实现的,而只关心数据库的链接。本课题将对数据库的操作封装在一个类ADOConn中,封装后的类提供用于初始化连接数据库,直接执行SQL语句,获得查询语句的结果集指针和断开连接等数据库操作的接口。需求分析及概要设计需求分析本课题主要目的是设计一个用于实时监控的人脸识别系统,将摄像头部署在需要进行监控的场合,系统初始化完毕后便自动检测和识别出现在画面中的人脸,能记录已经识别人物的身份信息和出现在画面的时间并予以记录,将不能识别的人物标记为Unknown并记录时间戳。系统只能识别预先经过信息采集的人物,在信息采集阶段,要求记录人物的姓名和头像。概要设计整个系统分为两个功能模块,一个是信息采集模块,另一个是实时监控模块,如图3-1所示:4.1核心理论的实现4.1.1检测部分的实现检测部分使用的是于仕祺老师公开的人脸检测库,配置好dll库后将相应地头文件导入到项目文件中即可使用这个库的接口,这个库针对不同的应用场合提供了四个不同的接口,本课题中使用的是检测效果最好但最耗时的一个接口int*facedetect_multiview_reinforce(parameterlist)函数形参列表包括了指向存放检测结果区域的指针,指向存放输入图片数据区域的指针,输入图片的长宽和一些其他的参数,函数返回一个指向存放检测结果区域的指针,关键就是要提取这个指针所指向的数据,假设返回的指针是pResult,则pResult指向的第一个整型数据就表示在输入图片中检测到的人脸数量,在下来连续的内存区域中存放了每一个人的人脸信息,每个人占142个整型数据,前四个数表示了人脸包围盒的左上角和右下角的左边,第五个数据表示了名为neighbor的信息(其具体含义目前还未知),第六个数据表示了人脸的转角,范围是0~60度,之后每两个数据组成一个坐标点,标记了人脸的68个特征点,这些特征点标记了人脸的轮廓,眼睛,眉毛,鼻子,嘴巴的位置和轮廓,共占用136个数据,如图4-1所示:pResult第一吓人脸信息第二吓人脸信息Boundiingbax*人脸数里图4-1检测的结果如图4-2所示:图4-2基于监控的检测只需要隔一定的时间间隔从摄像头中取出画面,将画面送入检测函数后把检测结构绘制在画面上在输出到用户界面上即可。4.1.2提取识别特征部分的实现提取识别特征部分采用基于Tensorflow实现的一个FaceNet神经网络模型,前文提到过未能成功使用Tensorflow的C++API,因此使用其PythonAPI,用Python编写好直接使用Tensorflow的函数,通过Cython将用Python定义的函数转换为C形式定义函数,Cython会自动生成对应函数的头文件和源文件,将生成的文件导入项目中即可使用C形式定义的函数。所有关于Tensorflow的使用都被封装在一个叫TensorflowInference的类中,这个类封装了包括session对象的获取和维护操作、Python环境的初始化、网络模型的加载、计算映射特征以及所有的C++和Python数据交互工作,对类外的使用者完全隐藏了所有涉及Python的内容,具体见4.2.3节。在这其中有两点需要注意:1)Session对象:值得注意的是,Tensorflow中计算都是通过session对象来完成的,session对象中保存了数据流图的结构信息和权值信息,课题中使用的网络模型非常庞大(141MB),如果每次计算都重新将模型从磁盘调入内存,会严重增加程序的耗时(每次加载模型大约耗时两分钟),因此合理的设计应该是在整个系统初始化时就将模型载入内存,并获取其session对象使其常驻内存,每次计算时调用session对象的成员方法即可完成一次计算(每次计算大约耗时0.3~0.4秒)。在Python脚本中,session对象的获取是通过Tensorflow提供的接口廿.Session()获取的,不过在获取session对象之前,需要将数据流图的结构信息载入默认的graph中,再将图中的权值恢复到session对象中,这样之后的session对象才能正确执行FaceNet的前向计算。获得的session对象会在C++中以PyObject的类型出现。2)C++和Python的数据交互:本课题的一个难点就是C++和Python的数据交互问题,数据交互的形式是通过函数的形参和返回值进行的,主要包括两个方向,一是从C++部分的数据流入Python,二是Python部分执行计算的结果返回C++,按照Python官方给出的做法是将所有C++类型的数据转换成PyObject对象进行交互,但是由于Python所有的对象在C++中全部都是继承自PyObject类型,如此灵活的数据类型带来一定的复杂度,而且本课题中使用了图片类型数据,在Python中是numpy.ndarray类型,numpy官方给出了对C++版本的ndarray支持,但是在实际使用中显示使用的函数接口已经过时。好在Cython允许在Python脚本中使用C类型的数据结构,使得从C++到Python的数据转换方便了不少。具体的数据流向和转换过程如图4-3所示:CV:Mattypesize:160*160*3C++GuEllGMBi#short*sizc:1^768ooPreWhiten处理d(160*160*3),doubleh咗sizc:l*768ot)共辜数据类型getembeddingresult(5essrdoublenumpy.ndarraysize:160^160*3图4-3Python共享数据类型C++4.2类People类People类是本课题设计的一个最为重要的类,它的抽象层级最高,是抽象的“人”,有姓名,映射向量,存储其头像图片的路径等属性,封装了计算人脸的映射特征,根据设定的阈值判断两个人是否是同一个人,以及所有的数据操作,其UML如图4-4所示:People-Name:匚Stnng-Embedding:double'-PortraitShawPath:CString-PortraitShow:cv::Mat-PortraitInput:cv::Mat+Peopled+-PeopleQ+GetNampO:CString+SetNameQ:void+GetEmbeddingQ:double*+SetEmbedding(double*]:void+GetPortraitShowPathQ:CString+SetPoraitShowPath(CString):void+GetPortraitShowf):cv::Mat+GetPortraitlnputf):cv::Mat+SetPDrtraitShDW(cv::Mat):void+SetPDrtraitInputl60(cv::Mat):void+SetPDrtraitInput(cv::Mat.1cv::Rect):void+SavePortraitShowO:void+GetDistance2(People):longdouble+IsSamePpopletPeople,dDuble):BOOL+sqljnsert(Jvoid+sql_deletetCString):void+sql_updatp(CString):VDid+GetDataByName(CString):void+GetDataByRDwNunn(int):void+HaveName(CString):BOOL图4-4下面给出各成员变量的含义:1)Name:这个变量用于标记人物身份信息,在信息采集时作为必要信息录入数据库,在监控时作为对人物身份的记录,并可以通过它找到存储在数据库中人物的更多信息。Embedding:这个变量是一个double型的指针,指向了一小块用于记录人物的识别特征的数据区域,他是根据另一个成员变量PortraitInput计算得出的,相当于2.2.1节中的f()的结果。PortraitShowPath:记录了用于展示人物肖像图片的存储路径。Portraitshow:保存用于展示人物肖像的图片,其规格为400X400。PortraitInput:作为FaceNetModel的输入数据,用于计算人脸识别特征,必须经过重新剪裁和缩放,其规格160X160。下面给出部分成员函数描述:函数SetPortraitInput:其功能是根据从摄像头获取的帧画面和检测部分给出的人脸包围盒矩形坐标剪裁图片,然后将其缩放至统一规格160X160,这样会得到一张160X160的规范化的人脸,图片需要剪裁是因为在检测部分给出的人脸包围盒太过紧致,丢失了诸如头发,额头上半部的信息,因此需要增大包围盒恢复这些信息;需要缩放是因为FaceNet要求的输入图片就是160X160的规格。voidPeople::SetPortraitInput(MatRawImage,cv::RectvRectangle){计算剪裁矩形:左上角(XI,Y1),右下角(X2,Y2);检查剪裁矩形是否越界:X1=X1>0?X1:0;Y1=Y1>0?Y1:0;剪裁原图片:temp二Rawimage(X1,Y1,X2,Y2);放缩剪裁图片:cv::resize(temp,160,160);Portraitinput=temp;}函数GetDistance2:功能是根据两个人的映射特征Embedding计算两人的相似性。inlinedoublePeople::GetDistance2(PeoplevPeople){doubleDistance2=0;foriin128//Embedding为128个元素的一维数组Distance2+=(People.Embedding[i]-vPeople.Embedding[i])2returnDistance2;}Tensorflowinference类TensorflowInferenee类是作为对本课题使用到的几个TensorflowPythonAPI、所有的C++,Python数据交互操作和对人脸图片的预处理操作的封装,这个类提供了三个纯C风格的接口,维护了Session这个重要的对象,在实现中它是作为全局类CXXXApp的一个成员变量使用的,在整个项目初始化时会单独开工作者线程完成非常耗时的FaeeNetModel加载工作,一旦初始化完毕,在项目的任何位置都可以通过引用全局对象theApp中的TensorflowInferenee对象来使用Tensorflow计算人脸的映射特征,其UML如图4-5所示:Tens-orflowlnferenceSe55:PyObject*ResultPyObjectdata:double"-i-InitPythonEnv():void-i-LoadTensorflowModelOvoid-i-CalculateEmbedding(cv::Mat,double*)图4-5各成员变量的含义:Sess:指向Session对象的指针,Session对象保存了FaeeNetModel的结构信息和权值信息。Result:指向用来存放从Python中取出的结果的一小片内存区域。data:用来存放最终输入到FaeeNetModel中的经过预处理后的图片数据。各成员函数:1)函数InitTensorflowModel:加载FaeeNet模型,get_embedding_session函数实际是用Python编写的,经过Cython转换为C类型的函数以供使用。其代码如下:voidTensorflowInference::InitTensorflowModel(){Sess=get_embedding_session();}get_embedding_session()函数在Python中的定义如下:cdefpublicget_embedding_session():sess=tf.Session()saver=tf.train.import_meta_graph(meta_path)saver.restore(sess,ckpt_path)returnsess由于得到的Session对象在使用时会作为用Python定义的函数的形参,所以C++并不需要解析这个PyObject对象,只需要在内存中维护它的存在即可。3)函数CalculateEmbedding:这是本课题最为核心的一个函数之一,功能是计算人脸的映射特征,它接受一个规格化的160X160的人脸图片,图片格式为cv::Mat型,和一个double类型的指针,计算的结果将存放在double类型的指针所指向的一小片内存区域内。描述如下:voidTensorflowInference::CalculateEmbedding(cv::MatvMat160,double*vDoubleArray){检查输入图片vMatl60的规格,若不符合要求则退出;‘将OpenCVC默认的BGR格式图片转为RGB格式;将160X160X3的图片拉伸为一维向量;PreWhiten“图片",将PreWhiten处理后的结果存在data中;Result=get_embedding_result(Sess,data);//解析PyObject类型的返回结果foriin128vDoubleArray[i]=PyFloat_AsDouble(PyList_GetItem(Result,i));}其中get_embedding_result()函数同get_session()函数一样,也是使用Python定义的,并经过Cython转换的,由于C++中对numpy.ndarray的解析函数已过时,因此需要将Model中输出的numpy.ndarray类型的Tensor转换为C++可以成功解析的List类型。另外,根据Cython官方提供的资料,在用Cython语法编写的Python脚本中是用C的数据类型定义变量可以加快程序执行的速度,这是因为Python的底层本身就是C,因此定义中能用C定义的变量全部使用C定义。函数的原始定义如下:cdefpublicget_embedding_result(sess,short*data):phase_train_placeholder=cdefintheight=160cdefintwidth=160cdefintchannels=3cdefinthindex=0cdefintwindex=0cdefintcindex=0cdefintdindex=0cdefnp.ndarrayimg_feedin=np.zeros([1,160,160,3])//解析C++传入的数据,转换为numpy.ndarray类型forhindexinrange(height):forwindexinrange(width):forcindexinrange(channels):img_feedin[0,hindex,windex,cindex]=data[dindex]dindex=dindex+1feed_dict={images_placeholder:img_feedin,phase_train_placeholder:False}result=sess.run(embeddings,feed_dict=feed_dict)output=[]foriinrange(128):output.append(result[0,i])returnoutputADOConn类ADOConn类封装了所有对数据库的操作,比People类的抽象层级要低一级,People类通过对ADOConn类对象的引用来完成对数据库的操作,其UML如图4-6所示:ADO匚onn+m_p匚□门riEt:tic3n:_〔onnE(:tiic)门Ptr十m_pRecordset;_RecordfetPtr+ADO匚onnQ+virtual-ADOConnQ+OnlnitDEConn^ct{):void+GatReeordSet{_bstr_t):_R^cordsatPtrfiL+E^ecuteSQL(_b5tr_tj:BOOL+Exit匚ormetztO:\/c?ici+R.estore(_b5tr_t):void图4-6这个类的抽象层次比较低,技术也比较落后,多数时候作为库来使用,而且本课题的数据库部分不是作为重点来考虑的,因此仅做简单介绍。形参为_bstr_t的函数可以直接执行在C++中编写的SQL语句,数据库返回的结果被保存在_RecordsetPtr指向的区域中,通过一定的解析就可以得到我们想要的数据,这其中涉及一些数据类型转换问题,People类中的对数据库操作的函数已经封装了这些操作,包括CString转换为double型数组,double型数组转换成CString型的字符串等,在此不做详细介绍。主界面主界面的主要功能是初始化系统的Python运行环境,加载FaceNetModel,和展示信息采集模块以及实时监控模块的入口。流程主界面的执行流程图如图4-7所示:
荼用于橙以2按柏单击信息采棄►单击退出甲击冥吋监JiH进人实时觸栉诀咗入倍启禾生樟块开欢子整块入口隹钮用户卉面战程启肃颍外的初始化工作者线担图4-7荼用于橙以2按柏单击信息采棄►单击退出甲击冥吋监JiH进人实时觸栉诀咗入倍启禾生樟块开欢子整块入口隹钮用户卉面战程启肃颍外的初始化工作者线担图4-7实现在VS为主界面生成的类中重写了以下几个函数:1)OnInitDialog函数:用来执行简单的不耗时的初始化工作,若在这个函数中执行耗时的初始化工作,会导致窗口卡死在调用绘制函数之前,一种折中的设计是把耗时的初始化工作放在OnPaint()函数中,在窗口全部绘制完毕后再执行耗时的初始化工作。本课题中虽然将耗时的初始化工作另开线程执行,但经过测试发现若在OnInitDialog函数中启动线程也有可能会导致窗体无法正常绘制。2)OnPaint函数:在此函数的结尾启动额外的初始化线程,可以保证在窗体绘
制完毕后执行耗时的初始化工作,需要注意的是:若用户拖动窗口或者改变窗口的大小,也会调用该函数,为了避免初始化的重复执行,系统中用一个BOOL类型的变量标记初始化是否完成,只有在未完成的情况下才启动额外的初始化线程,一旦初始化完成,就立刻将这个BOOL类型的变量取反,这样就可以避免在之后的绘制中启动额外的初始化线程。3)OnTimer函数:系统会在程序的SetTimer处开始,每隔一小段时间就调用此函数,其重要功能是不断查询InitLevel的值,根据这个值将初始化进度显示给用户。面给出额外的初始化工作者线程函数的代码:21222324252122232425262728293&323436昶39494-14245444546474@49505254{〃九检查Portraits文件夹是否存在,若不存在则创建之if(!PatfileDirectory(_T("Portraits")})InitLeye.1=1;SLeep(InitPT_DECAY);if(!CreateDirectory(_T("Portraits"),NULL))AfxMessageEoxf_T("ERROR:无法创建Portraits文件夹!'"));工门itLevel=-1;returr8;"2-检查耐如5文件夹是否存在、若不存在则报错并退出程序"注意:此处假设血血b文件夹一旦有在,则其内制的所有文件也正常存在"因为不清楚问价夹内部的文件是否存在依赖关系,且文件较多,不便一一"检查!故此处不检查文件夹內部的文件if(!PathlsDirectory(_T(”modeIs")))AfxMe£sageBax(_T(,rERRDR:UDdels文件夹不存在!"));InitLeye.1=-1;return0;初始化Python环境InizLevel=2;theApp.IF.InitPythonEnv();■加裁TenserflowFaceNetModelIni^Level=,3;timer=clockO;theApp.IF.LoadTensorflowModel();timer=clock()-zimer;InizLevel=4;return0;
4.4信息采集4.4.1流程通过点击主界面的信息采集按钮进入信息采集功能模块,他的功能是收集人物的图4-84.4.2实现信息采集模块的实现使用的是一个MFC生成的对话框类,消息响应函数都是作为这个类的成员函数实现。其中捕获按钮的消息响应函数ClnfoCollectDlg::OnBnClickedButtonCaptrue()的处理流程图如图4-9所示:
检测到脸咅15?N瞬戰量为1脸^大小合适(开始)q針00X400的规格化图片显示在检测到脸咅15?N瞬戰量为1脸^大小合适(开始)q針00X400的规格化图片显示在1口匚曲0惟件上保存捕捉到的画面和规格■^的圏片剪裁、放缩图片至400霑00给出相应的提示信息执行检测函数畑像头中抽取_帧画面图4-9确定按钮的消息响应函数ClnfoCollectDlg::OnBnClickedButtonComfier()的处理流程图如图4-10所示:
幵始N是再输入姓各?是否捕荻到人像?结束图4-10Portaritlnpu诗HPortraitShaw^00X400®^化的人物头像存入文件系疑幵始N是再输入姓各?是否捕荻到人像?结束图4-10Portaritlnpu诗HPortraitShaw^00X400®^化的人物头像存入文件系疑新建一Peoplej^l>.Per5c>nHBPerson的映射特征取出捕魏到的画面和检测结果^Person的信慝写入数据库输岀提示信慝4.5实时监控实时监控模块的主要功能是识别出现在摄像头画面中的人物的身份信息并予以记录。该模块的存在巨大的挑战,对实时性的要求难以满足,主要的原因是识别部使用了深度神经网络模型,这种方案的优势在于识别准确率高,对光照,姿态拥有较强的鲁棒性,但其缺点是计算量偏大,每次识别耗时0.3~0.5秒,这个速度基本可以满足实时性,但是无法以肉眼可见的流畅程度(fps15以上)予以呈现。因此课题的设计中,并不总是实时显示识别的过程,而只显示检测的过程。本课题的代码实现了三种监控模式:1)单用户界面线程监控:这种方式把监控显示,检测,识别处理全部放到用户界面线程中去,这样做的好处是检测和识别的结果可以实时的显示在画面上,缺点是会使得画面非常卡顿,只能达到2~5的fps,这还是在匹配数据库规模很小的情况下。2)单用户界面线程,单工作者线程:这种方式把监控显示放到用户界面线程中去,把检测和识别处理放到一个另开的工作者线程中去,识别的结果存入一块共享的List类型的内存区域,里面存放了识别出来的人物的姓名信息,用户界面线程从中取出姓名信息并显示在窗体上,这种方式可以使监控显示的画面比较流畅,但未实际加快识别效率。对共享的List型内存区域的操作已经算是一个极简版本的生产者工作者模型了。3)单用户界面线程,双工作者线程:这种方式把监控显示放到用户界面线程中去,把检测和识别处理分别放到两个工作者线程中去,这涉及三线程的同步问题,较为复杂,也是本课题的难点之一。此种方式实现了两个生产者消费者模型,在第一个模型中,检测(又称采样)线程是生产者,识别线程是消费者;在第二个模型中,识别线程是生产者,而用户界面线程是消费者。需要注意的是,此方式实际也并未加快单次识别的速度,但是通过检测和识别部分之间共享的一个ListvPeople>对象作为缓冲区可以充分利用时间提高整体的识别效率,具体说来是基于一个这样的事实,在实际场合中,画面并不是一直会有人脸出现的,人脸的出现存在一定的时间间隔,此种方案就是有效利用画面中不出现人脸的时间来提高效率的,缓冲区可以保存在画面人脸数量较多或者采样间隔较小的情况下识别部分未能及时处理完毕的人脸信息,一旦画面中没有人脸出现,检测线程就不会像缓冲区中添加人物,而识别线程会在这个空档完成之前未能及时完成的工作,因此可以最大限度的保留现场的实际信息,系统默认使用此种方案。4.5.1流程此处仅给出单用户界面线程,双工作者线程的流程图,如图4-11所示:
4.5.2实现类似4.4.2的实现,采样线程流程图如4-12所示,识别线程的流程图如图4-13所示:幵皓画面为空?画面中育人脸?霍中区荷?ndThread-=TRUE?根括当前画面和检测结果设SPerson的Portrmtlnput设旨信号eAmyNctEmpty^Person加入爭llPeopI。[]中去阻魏灌,等特信弓eArrayNotFull从摄僦头半拒取一帧画面,并获取当前时间对每吓检测到的人脸新建—Peoplej^f^Person执行检测画数退岀因程)图4-12呈否找到此人?ndThread==TRUE?斟轄)^Person的宿字和耒样旳问放:Alist<Nam?Time>i:lz将Fe%呈否找到此人?ndThread==TRUE?斟轄)^Person的宿字和耒样旳问放:Alist<Nam?Time>i:lz将Fe%on依次和数据库由的一People対输曲亍比较阻室堤程.等待信号eArrayNotEmpty^Person的容字设軍为数据阵中的呂字Person的容字设为Unknown从险opleQ中窓出一个7^Person逅佢号eArrayNotFull开始图4-13以上流程图未给出多线程共享内存的互斥访问控制,在实际的代码中使用MFC中的CCriticalSection类实现对临界区的互斥访问。4.6数据库本课题的数据库部分不作为重点,仅给出数据库中的表的设计,为了简化数据库的设计,假设所有人物不重名,People表的结构如图4-14:LEWOVO-F>C\SQLE...em-dbaFmoplmxLENOVO-PC\£QLE..,erd列名Embedding舞类1允许血疽varchar(5O)口varchsrfEVlAX]□PortraitPathvarcharfSO)图4-145.1主界面测试5测试正在初始化的主界面如图5-1所示:HfttmtiftPvlhonFiH-fCAllRj?Mk.lUrMrvadJ0t7-S~13实时监控DR与节H.退出岛FaceDRSy^temFaceDRSystem图5-1初始化完毕的主界面如图5-2所示:图5-25.2信息采集测试单击捕获按钮之后如图5-3所示:图5-3单击确定按钮之后(图片中的人物是者名歌手ToveLo),可以看到数据库中已经增加了她的信息,如图5-4所示:NameEmbe-ddinqPortraitPath1:〉•逬二[盧蠢蠡鏤1-0,047241397202014923,-0,077077955...Portraits//Tov&Lcjpgheyi-0.11537960171699524,0.02419247^5...Portraits/,/heyi.jpgNULL-0.0&6199786&61C78&44-0.136705607...Ni/LLPortrs张克E月.jpgNULL图5-45.3实时监控测试进入实时监控后的情况如图5-5所示,可以看到ListBox中已经出现ToveLoRddTi™诃卜曲诗備丈J]LrtnMn->]T£<cLBLMmvtn-?H:Z%3ip・A個迢:站tw™bh->13^39gr丽叶》2(]|也25Tm-^£-^»cSL^3Tn-<£-J-aQ:S:94丁“士心心狂:力Tsdf2ftSS总TiMiaLj&->21JffiH5Tt-,fli£^>21jWiMTcfteL£->]ljWi]iT冬戒A理上□;,)TM-<£->ai:L&:4]ToaD-xjiji.j?T^euci->2356i2]TiKCLfi-s-ZSlOLlMTm戎XlTMM:如Fz">2HI»FLWnNH->13:lL:4a仙“曲芒皿TiMiHLj&-s-23lZ?lt]JTE3A23i:®i25t^i±s->2333i«TmH?.>23:37;MTMM£-?4ftS:49TM-^E-?4a3!-:K轴:丹陆IT障Wsintfl±.*iETE^Ii图5-5双击ListBox中的人名后右边的控件会显示在信息采集时采集到的人像图片,如图5-6所示:XIhe¥i->16:5O:Q9hEyi->16:52:30heyi->l&;56l24Unknown->17:00:18Unknowns15:29:38heyi->lSr32:30heyi->IS:44:28Unknown-A19:44:38Unknown-》20:10:25TcueLo->2a:45:l&ToweLo->20:4~:55TcweLo->20!51'02TQveLo->2Q^53:54Tqi'eLo->20拓&30I^c-.eLc,-;•2Fi:5?:5j]沁三:左:占lTo-.eL3-?.2-:Li6:24|TO'.'e_-->2:!12:5=]飞疋上7応;⑸三]-工七山心£:丐:■<|TG-.eLO->2i:23:u4_G.-9Lc-?-22!5E!2j|Tc-.eLc-J-25;'ji;3'?ITo-.-eL□->23:u5:u?ITa-.'eLo->£j:u9:19I•_■<-:■■.■--》訂:11:-L-■■<'!:■■..■■-:<2!15!£u^RToveLa-/23;19;28|ToueLo->23!23!07ToveLo->25:2?:01ToveLo->23:30:25(To-.'eLo->23i33i-l3To'.'eLo-\00i5Bi45T0l£!_□->C0:5c:姓名;TqveLq开始僭停冑空Limt退岀正在监控图5-66结论本次毕业设计从选题,算法的研究、选取、测试,到编码、测试历时两个多月,期间学到了很多东西,也遇到了不少困难。现在,我已经能够使用MFC写出一般的应用程序了,也了解了许多关于图像处理的知识,其中还学到了很多关于人工智能和机器学习的东西,能够简单使用Tensorflow这个计算框架,能够成功使用多线程技术到实际应用中提高效率,还学习到了Python这门以前从未接触过的语言。本课题遇到的最大的两个难点是数据类型的转换问题和多线程同步问题。数据类型的转换比较
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 考前必做的营养师试题及答案
- 演出经纪人考试的核心试题及答案
- 2025导游证资格考试指导手册试题及答案
- 2024年演出经纪人资格证备战试题与答案
- 保安证考试基本知识试题及答案
- 演出经纪人资格证考前冲刺试题与答案
- 2024年营养师证书问答试题及答案
- 高效营养师考试试题及答案数据库
- 房地产经纪行业资格认证要求试题及答案
- 营养师资格考试经验分享与试题及答案
- (2025)全国交管12123学法减分测试题库及答案(带图版)
- 2024年海南省中考满分作文《点亮勤奋这盏灯》
- 2024-2025学年第二学期天域全国名校协作体高三3月联考 生物试卷(含答案)
- 保洁人员派遣协议
- DeepSeek培训课件-清华大学-DeepSeek+DeepResearch应用报告
- 科室如何做好护理质控
- 危重患者营养支持教学课件
- 2025年常州机电职业技术学院单招职业适应性测试题库及完整答案
- 《主题三 我的毕业季》说课稿-2023-2024学年六年级下册综合实践活动辽师大版
- 能源领域中的电力安全管理与规范
- 投行估值模型-洞察分析
评论
0/150
提交评论