




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
信息科学与技术学院毕业论文课题名称:基于特征识别的人脸检测系统学院:信息科学与技术学院完成日期:二O一七年五月十九日其中,Gentle是指此分类器相对于传统的AdaptiveBoostClassifier在训练时错误分类的样本的权值增加策略上更加平稳(图2-4中的3.b),以便获得更好的泛化能力;Adaptive是指相对于传统的BoostClassifier,AdaptiveBoostClassifier能动态调节各个样本在训练时所占的权重,就是增加错误分类样本的权值,降低正确分类样本的权值。2.1.2识别部分的GoogleFaceNet无论是人脸检测还是人脸识别,都面临着来自光照,姿态,表情变化带来的巨大挑战,人工神经网络模型以其强大的非线性拟合能力和极强的鲁棒性能够在很大程度上面对以上挑战,而且深度学习是近几年才兴起的新理论,本课题勇于追赶研究前沿,在识别部分使用了DavidSandberg根据Google在2015发表的一篇文章《FaceNet:AUnifiedEmbeddingforFaceRecognitionandClustering》使用Tensoflow实现的一个人工神经网络,并已经使用大量训练样本(百万级)预先训练完毕,在使用时只需要加载这个固化的网络模型至内存中,输入经过预处理的人脸图片,再执行一次前向计算,即可得到用于识别人脸的特征。FaceNet在LFW(LabeledFacesintheWild)数据库上的准确率达到了99.63%,刷新了当时的记录。FaceNet使用深度卷积神经网络直接学习人脸在高维欧式空间的映射,映射得到的坐标点的欧式距离即代表了样本的相似性,距离越大表示相似性越低,反之越高。FaceNet的大体架构如图2-5所示:I亘。BatchDEEPARCHITECTUREI亘。BatchDEEPARCHITECTUREUMIQED01W,G一图2-5其中DeepArchitecture就是一般的卷积神经网络去掉softmax后的结构,经过L2的归一化后得到人脸图片再高维欧式空间的的特征表示,在训练时以最小化后面的三联子损失函数为目标不断调整网络中各个节点的权值。目标函数:三联子损失函数,其学习过程如图2-6所示图2-6三联子表示在训练样本中抽取的三个人脸样本(抽取方法不做深入讨论),其中Anchor和Positive是来自同一个人的两张脸部图片,Anchor和Negative是来自不同人的两张脸部图片,学习的过程就是要使经过网络映射的Anchor和Positive的欧式距离尽可能小而Anchor和Negative的欧式距离尽可能的大,依此来获得对人脸特征的描述。用公式描述:(2-3)其中表示映射过程,,,,分别代表锚点,正样本,负样本,表示正负样本之间的距离,表示所有可能的三联子集合。两个样本之间的相似性就使用公式来计算的。将(2-3)做一下转换便可以得到目标函数:(2-4)训练的过程就是最小化,训练方法使用一般的卷积神经网络的训练方法即可,其具体内容不做讨论,实现上各种深度学习框架已经集成了各种最为先进的优化算法,只需要调用相应地接口便可以完成对神经网络的训练过程。关于DeepArchitecture,论文中给出了几种可选的网络结构,其中一种是Zeiler&Fergus结构,如图2-7所示:
size*insize-uuikernelparamFL1啥convlpool!morml<?<>nv2arnorm2poo】2<onv3ap<*013conv4a<vnv4<onv5a■cmv5<onv6n«>nv6ptxMtoncatfclfc2fc712SL2220x220x3110x110x6455x55xfr455x55x6455x55xt155x55x19255x55x19228x28x19228x28x19228x38414x14x38414x14x38414x14x25614x14x25614x11x25(114x14x25614xl4x25fi7x7x2567x7x2561x32x1281x32x1281x1x128110x110x6455x55x6455x55x6455x55x6455x&5xl9255x55x19228x28x1S)228x28x19228x28x38111x14x38^14x14x38414x14x25614x14x25614x14x2561-1x14x25614x14x2567x7x2567x7x2561x32x1281x32x1281x1x1281X1x1287x7x3723K3K64*]xlx64.13乂3又64,13x^xl92,21x1x192,13x3x192,13乂3乂3&1,213x3x3M1lxlx25fij3x3x256J1x1x256,13x3x256,13x3x256,2maxoutp—2maxoulfrf9K04K11IK3i664K148K885K66K590K66K590K103M34M524K0115M13M335M29M52IM29M173M13M116M17M116M103M34M05Mtotal140ML6B图2-7另外一种是基于GoogleInception的结构,如图2-8所示:
typeoutputsizeconvF(7乂了乂312)112xQ2x(Mmaxpool+norm56x56x64incepiion(2)50X56X192norm+maxpx>128x28x192incepiion(3川28x28x256inception(3h)2Sx28x320iiiLcpiion(.k)14X14X6如inception(4a)|4xl4x(i1Oincepiion(4h)14x14x640incepiion(4c)14xMx(M0incepiion(4d)14x1-1x640incepiion(4c)7x7x1024incepiion(5n)7x7x1024inception(5b)7x7x1024avgpool1x1x1024l\i11y^unn1x1x129L2noniuihzalinn1x1x128图2-8其中一种Inception的结构如图2-9所示:1x1Conv(64)AvgPookig其中一种Inception的结构如图2-9所示:1x1Conv(64)AvgPookigFilletconcat1x1Conv(64)3x3Conv196)Fitlerooncat图2-9课题中使用的网络结构目前还不知道其具体形式,Tensorflow提供了可视化固化网络模型的工具,但在实际使用时出了一些问题,因此很遗憾的没有看到实际使用的网络结构。2.2技术MFC简介MFC全称MicrosoftFoundationClasses(微软基础类库),这是Microsoft推出的一套C++类,把大部分常用的WindowsAPI函数封装在其中。MFC中的类和函数接口成千上万,对如此庞大的类库进行介绍和对其内部的复杂机制进行讨论已经超出本文的范围,因此在接下来的论述中,文章仅介绍本课题中显式使用的一些内容,本课题使用的集成开发工具是VisualStudio2013Premium,在其中新建一个MFC项目的同时,VS会自动生成项目的主框架,我们要做的就是往这个主框架中添加内容和修改一些相应地修改,必要时重写某些函数。1)UI:本课题是基于对话框结构的项目,因此涉及到的UI知识仅有对话框中的内容,MFC封装了窗体和控件的底层绘制操作,用户可以在资源文件添加对话框类型的资源文件,然后用vs提供的可视化编辑工具编辑自己的对话框,而不用考虑其底层的绘制细节,各种控件的添加可以通过拖拽放置到对话框中。用可视化工具编辑完界面后,vs会自动为这个对话框生成一个相应的类CXXXDlgCalss,这个类就封装了对话框中的所有内容,与控件关联的变量全部作为类的成员变量来实现,各种消息相应函数也是做为这个类的成员函数来实现的。本课题使用的默认MFC样式如图2-10所示:图2-102)数据类型:MFC中定义了许多常用的数据类型,本课题使用最多是CString类型,窗体控件中显示的字符串的类型全部都是CString类型,写入数据库和从数据库中取出来的数据类型也都是CString类型,因此关于CString和其他数据类型的转换是本课题实现部分的一个重点。MFC中提供了许多类型转换函数和类型转换宏如宏CT2A()可以将CString类型转换为string类型,CString类型的本身也提供了一个强大的转换函数Format(),它可以方便地实现许多基本的类型转换,但是一些更为复杂的类型转换还需要自己写函数去实现。3)消息机制:在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等。4)多线程:MFC中的线程主要有两类,一类是用户界面线程,另一类是工作者线程,二者的重要区别在于前者有自己的消息队列和消息循环,而后者没有,用户界面线程的工作一般是处理界面绘制,响应用户操作和系统产生的事件及消息等,vs自动生成的窗口类中的成员函数大多都是作为用户界面线程执行的,一种不恰当的设计方式是将所有代码都放在用户界面线程中,这样做确实简单方便,但是它会带来一些严重的效率问题,比如一些占用大量内存和耗时较长的计算若放在用户界面线程中会导致界面长时间处于无响应状态,因此应该将计算量较大的处理另开工作者线程单独执行,用消息传递或者共享内存的方式与用户界面线程同步。MFC中多线程编程常用的函数如下几个:①启动一个工作者线程:CWinThread*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,nPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,LPSECRITY_ATTRIBUTESlpSecurityAttrs=NULL);②在某个信号量上阻塞线程:DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMillisecondes);③临界区的访问:进入临界区的使用CCriticalSection对象的Lock()函数,离开使用Unlock()函数.Tensorflow简介TensorFlow是一个开源的使用数据流图的数值计算框架,被广泛的用于实现各种神经网络,数据流图中的节点代表数学运算,而图中的线条代表数据数组(又称为张量)。TensorFlow最初由Google大脑小组的研究员和工程师门开发,用于机器学习和深度神经网络方面的研究,但是因为这个框架具有通用性,也可以被用于其他的计算领域。它灵活的架构可以让你将节点部署在一个或者多个CPU或者GPU上进行并行计算。关于数据流图:数据流图是一种以数据流动为驱动力的计算模型,用nodes和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语句,获得查询语句的结果集指针和断开连接等数据库操作的接口。3需求分析及概要设计需求分析本课题主要目的是设计一个用于实时监控的人脸识别系统,将摄像头部署在需要进行监控的场合,系统初始化完毕后便自动检测和识别出现在画面中的人脸,能记录已经识别人物的身份信息和出现在画面的时间并予以记录,将不能识别的人物标记为Unknown并记录时间戳。系统只能识别预先经过信息采集的人物,在信息采集阶段,要求记录人物的姓名和头像。概要设计整个系统分为两个功能模块,一个是信息采集模块,另一个是实时监控模块,如图3-1所示:11-3
图核心理论的实现检测部分的实现检测部分使用的是于仕祺老师公开的人脸检测库,配置好dll库后将相应地头文件导入到项目文件中即可使用这个库的接口,这个库针对不同的应用场合提供了四个不同的接口,本课题中使用的是检测效果最好但最耗时的一个接口int*facedetect_multiview_reinforce(parameterlist)函数形参列表包括了指向存放检测结果区域的指针,指向存放输入图片数据区域的指针,输入图片的长宽和一些其他的参数,函数返回一个指向存放检测结果区域的指针,关键就是要提取这个指针所指向的数据,假设返回的指针是pResult,则pResult指向的第一个整型数据就表示在输入图片中检测到的人脸数量,在下来连续的内存区域中存放了每一个人的人脸信息,每个人占142个整型数据,前四个数表示了人脸包围盒的左上角和右下角的左边,第五个数据表示了名为neighbor的信息(其具体含义目前还未知),第六个数据表示了人脸的转角,范围是0~60度,之后每两个数据组成一个坐标点,标记了人脸的68个特征点,这些特征点标记了人脸的轮廓,眼睛,眉毛,鼻子,嘴巴的位置和轮廓,共占用136个数据,如图4-1所示:pResult第一个人脸信息 第二个人脸信息.....Boundiingbax*人脸数里图4-1检测的结果如图4-2所示:图4-2基于监控的检测只需要隔一定的时间间隔从摄像头中取出画面,将画面送入检测函数后把检测结构绘制在画面上在输出到用户界面上即可。提取识别特征部分的实现提取识别特征部分采用基于Tensorflow实现的一个FaceNet神经网络模型,前文提到过未能成功使用Tensorflow的C++API,因此使用其PythonAPI,用Python编写好直接使用Tensorflow的函数,通过Cython将用Python定义的函数转换为C形式定义函数,Cython会自动生成对应函数的头文件和源文件,将生成的文件导入项目中即可使用C形式定义的函数。所有关于Tensorflow的使用都被封装在一个叫TensorflowInference的类中,这个类封装了包括session对象的获取和维护操作、Python环境的初始化、网络模型的加载、计算映射特征以及所有的C++和Python数据交互工作,对类外的使用者完全隐藏了所有涉及Python的内容,具体见4.2.3节。在这其中有两点需要注意:Session对象:值得注意的是,Tensorflow中计算都是通过session对象来完成的,session对象中保存了数据流图的结构信息和权值信息,课题中使用的网络模型非常庞大(141MB),如果每次计算都重新将模型从磁盘调入内存,会严重增加程序的耗时(每次加载模型大约耗时两分钟),因此合理的设计应该是在整个系统初始化时就将模型载入内存,并获取其session对象使其常驻内存,每次计算时调用session对象的成员方法即可完成一次计算(每次计算大约耗时0.3~0.4秒)。在Python脚本中,session对象的获取是通过Tensorflow提供的接口tf.Session()获取的,不过在获取session对象之前,需要将数据流图的结构信息载入默认的graph中,再将图中的权值恢复到session对象中,这样之后的session对象才能正确执行FaceNet的前向计算。获得的session对象会在C++中以PyObject的类型出现。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所示:出160S60叼)l_result(5essrdouble")CV::Mattypesize:160*160*3numpy.ndarraysize:160i160*3numpy.ndarraysizc:l*i28short*sizc:1^768oo.doublehWsizc:l*768ot)PreWhiten处理图4-3C++共享数据类型Python共享数据类型C++4.2类People类People类是本课题设计的一个最为重要的类,它的抽象层级最高,是抽象的“人”,有姓名,映射向量,存储其头像图片的路径等属性,封装了计算人脸的映射特征,根据设定的阈值判断两个人是否是同一个人,以及所有的数据操作,其UML如图4-4所示:PeopleName:匚StringEmbedding:double"PortraitShowPath:CStringPortraitShow:cv::Mat-Portraitlnput:cv::Mat+Peopled+-PeopleQ+GetNampO:CString+SetNameQ:void+GetEmbeddingQ:double*+SetEmbedding(double*]:void+RctPci「t「mit0hciwP』th。:CString+SetPoraitShowPath(CString):void+GetPortraitShowf):cv::Mat+GetPortraitlnputf):cv::Mat+G白tPci「traitGhciwkv7M』t)7CJid+G白tPci「traitInputl60k,:Mjt):\/ciid+SetPDrtraitInput(cv::Mat.1cv::Rect):void+SavePortraitShowO:void+GetDistance2(People):longdouble+IsSamePpopletPeople,dDuble):BOOL+sql_in£c「tO:uciicl+sql_deletetCString):void+sql_updatp(CString):VDid+GetDataByName(CString):void+GetDataByRDwNunn(int):void+HaveName(CString):BOOL图4-4下面给出各成员变量的含义:1)Name:这个变量用于标记人物身份信息,在信息采集时作为必要信息录入数据库,在监控时作为对人物身份的记录,并可以通过它找到存储在数据库中人物的更多信息。2)Embedding:这个变量是一个double型的指针,指向了一小块用于记录人物的识别特征的数据区域,他是根据另一个成员变量Portraitinput计算得出的,相当于2.2.1节中的f()的结果。3)PortraitShowPath:记录了用于展示人物肖像图片的存储路径。4)PortraitShow:保存用于展示人物肖像的图片,其规格为400X400。5)Portraitinput:作为FaceNetModel的输入数据,用于计算人脸识别特征,必须经过重新剪裁和缩放,其规格160X160。下面给出部分成员函数描述:1)函数SetPortraitinput:其功能是根据从摄像头获取的帧画面和检测部分给出的人脸包围盒矩形坐标剪裁图片,然后将其缩放至统一规格160X160,这样会得到一张160X160的规范化的人脸,图片需要剪裁是因为在检测部分给出的人脸包围盒太过紧致,丢失了诸如头发,额头上半部的信息,因此需要增大包围盒恢复这些信息;需要缩放是因为FaceNet要求的输入图片就是160X160的规格。voidPeople::SetPortraitInput(MatRawImage,cv::RectvRectangle){计算剪裁矩形:左上角(X1,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;}2)函数GetDistance2:功能是根据两个人的映射特征Embedding计算两人的相似性。inlinedoublePeople::GetDistance2(PeoplevPeople){doubleDistance2=0;foriin128 //Embedding为128个元素的一维数组Distance2+=(People.Embedding[i]-vPeople.Embedding[i])2returnDistance2;}4.2.2Tensorflowinference类Tensorflowinference类是作为对本课题使用到的几个TensorflowPythonAPI、所有的C++,Python数据交互操作和对人脸图片的预处理操作的封装,这个类提供了三个纯C风格的接口,维护了Session这个重要的对象,在实现中它是作为全局类CXXXApp的一个成员变量使用的,在整个项目初始化时会单独开工作者线程完成非常耗时的FaceNetModel加载工作,一旦初始化完毕,在项目的任何位置都可以通过引用全局对象theApp中的Tensorflowinference对象来使用Tensorflow计算人脸的映射特征,其UML如图4-5所示:Tens-orflowlnferenceSesszPyObject*ResultPyObjectdata:double,InitPythonEnv():voidLoadTensarflowModelQvoidC己IculatEEmb已dding(匚己111cl口11印已加)图4-5各成员变量的含义:Sess:指向Session对象的指针,Session对象保存了FaceNetModel的结构信息和权值信息。Result:指向用来存放从Python中取出的结果的一小片内存区域。data:用来存放最终输入到FaceNetModel中的经过预处理后的图片数据。各成员函数:1)函数InitTensorflowModel:加载FaceNet模型,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){检查输入图片vMat160的规格,若不符合要求则退出;‘将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):images_placeholder=tf.get_default_graph().get_tensor_by_name("input:0")embeddings=tf.get_default_graph().get_tensor_by_name("embeddings:0")phase_train_placeholder=tf.get_default_graph().get_tensor_by_name("phase_train:0")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所示:ADOConn+m_pCcinnection:_ConnectionPtr+m_pRecord5et:_RecordfetPtr+ADOConn。+virtual-ADOConnQ+OnlnitDEConnectQvoid+GatReeordSet{_bstr_t):_R^cordsatPtrfiL+E^ecuteSQLt_bstr_tj:BOOL+E>itConnect():void+R.estore(_bstr_t):void图4-6这个类的抽象层次比较低,技术也比较落后,多数时候作为库来使用,而且本课题的数据库部分不是作为重点来考虑的,因此仅做简单介绍。形参为_bstr_t的函数可以直接执行在C++中编写的SQL语句,数据库返回的结果被保存在_RecordsetPtr指向的区域中,通过一定的解析就可以得到我们想要的数据,这其中涉及一些数据类型转换问题,People类中的对数据库操作的函数已经封装了这些操作,包括CString转换为double型数组,double型数组转换成CString型的字符串等,在此不做详细介绍。4.3主界面主界面的主要功能是初始化系统的Python运行环境,加载FaceNetModel,和展示信息采集模块以及实时监控模块的入口。流程主界面的执行流程图如图4-7所示:
初立缶七对不率¥学用于楂块入口按钮用户界面在程单击信皂采第 A单击实时监苴 H 进人实时监及幅块初立缶七对不率¥学用于楂块入口按钮用户界面在程单击信皂采第 A单击实时监苴 H 进人实时监及幅块单击退出进入信目深空模块开放子晅块人口按钮吕和题外由初始化工作者或程工作者送程图4-7实现在VS为主界面生成的类中重写了以下几个函数:1)OnInitDialog函数:用来执行简单的不耗时的初始化工作,若在这个函数中执行耗时的初始化工作,会导致窗口卡死在调用绘制函数之前,一种折中的设计是把耗时的初始化工作放在OnPaint()函数中,在窗口全部绘制完毕后再执行耗时的初始化工作。本课题中虽然将耗时的初始化工作另开线程执行,但经过测试发现若在OnInitDialog函数中启动线程也有可能会导致窗体无法正常绘制。2)OnPaint函数:在此函数的结尾启动额外的初始化线程,可以保证在窗体绘制完毕后执行耗时的初始化工作,需要注意的是:若用户拖动窗口或者改变窗口的大小,也会调用该函数,为了避免初始化的重复执行,系统中用一个BOOL类型的变量标记初始化是否完成,只有在未完成的情况下才启动额外的初始化线程,一旦初始化完成,就立刻将这个BOOL类型的变量取反,这样就可以避免在之后的绘制中启动额外的初始化线程。3)OnTimer函数:系统会在程序的SetTimer处开始,每隔一小段时间就调用此函数,其重要功能是不断查询InitLevel的值,根据这个值将初始化进度显示给用户。下面给出额外的初始化工作者线程函数的代码:UINTInitPTfLPVOIDLpP^ram) 值无用{/八.检查文件夹是否存在,若不存在则创建之23 if(!Patlhl5Directory(_T("Portraits")))TOC\o"1-5"\h\z24 {InitLe\/el=1;26 SLeep(LnitPT_DECAY);27 if (!CreateDirectory(_T(11Portraits11)NULL))2S {29 A-FxMessageEox(_T("ER.R-OR:无法创建Pcirt「氯士5文件夹!”));3& InitLevel=-1;31 returr明Q }33 )M ”2.检查加加Im文件夹是否■存在、若不存在则报错并退出程序〃注意:此处假设旭如店文件夹一旦存在n则其内烹的所有文件也正常存在〃因为不清楚问价夹内部的文件是否存在依赖关系,且文件极多,不便一一〃检查!故此处不检查文件夹内部的文件38 if(1PathlsDirectory(_T("modeIs")))39 1 49 AFxMcssageBox(_T("ERROR:mcided±文件夹不存在!”));4-1 InitLevel=-1;42 return不 )44 初始化Python环境45 InitLevel=2;theApp.IF.LnitPythonEnv()-47 〃4■加载丁£心口「干1口刑 FaceNetModel48 InitLevel=力timer=clack();theApp.IF.LoadTensorfLowModel();timer=tldck()-二irr后「;InitLevel=4;return0;")
4.4信息采集4.4.1流程通过点击主界面的信息采集按钮进入信息采集功能模块,他的功能是收集人物的信息并写入数据库,其运行流程如图4-8:进入信息界集画回为主四回主界面若退出按钮被单击f ■(SS磁钻裱单击苦啸定按钮裱单击系蜕演奇肖息队翻内包蜀盒绘制在回面上并显示在通过点击主界面的信息采集按钮进入信息采集功能模块,他的功能是收集人物的信息并写入数据库,其运行流程如图4-8:进入信息界集画回为主四回主界面若退出按钮被单击f ■(SS磁钻裱单击苦啸定按钮裱单击系蜕演奇肖息队翻内包蜀盒绘制在回面上并显示在1火_¥1。£口控件上进入确症按田单击啊应画椒A 进入捕获报田单击啊应函教拧Vf亍粒测函缴!—►从摄像头中曲取一顶1旦直绘制对话用窗口对话柢窗口初上抖t图4-84.4.2实现信息采集模块的实现使用的是一个MFC生成的对话框类,消息响应函数都是作为这个类的成员函数实现。其中捕获按钮的消息响应函数CInfoCollectDlg::OnBnClickedButtonCaptrue()的处理流程图如图4-9所示:
检测到瞄B?瞄啜量为1(开蛤)才科00X400的规格化图片显示在IDCJH。检测到瞄B?瞄啜量为1(开蛤)才科00X400的规格化图片显示在IDCJH。岫刿牛上保苗tf捉到的画面和柳格化的图片剪裁.放缩图片至4。口乂4口。给出相应的提示信息执行检测函数从摄像头中抽取一帧画面图4-9确定按钮的消息响应函数CInfoCollectDlg::OnBnClickedButtonComfier()的处理流程图如图4-10所示:
图4-104.5实时监控实时监控模块的主要功能是识别出现在摄像头画面中的人物的身份信息并予以记录。该模块的存在巨大的挑战,对实时性的要求难以满足,主要的原因是识别部使用了深度神经网络模型,这种方案的优势在于识别准确率高,对光照,姿态拥有较强的鲁棒性,但其缺点是计算量偏大,每次识别耗时0.3~0.5秒,这个速度基本可以满足实时性,但是无法以肉眼可见的流畅程度(fps15以上)予以呈现。因此课题的设计中,并不总是实时显示识别的过程,而只显示检测的过程。本课题的代码实现了三种监控模式:1)单用户界面线程监控:这种方式把监控显示,检测,识别处理全部放到用户界面线程中去,这样做的好处是检测和识别的结果可以实时的显示在画面上,缺点是会使得画面非常卡顿,只能达到2~5的fps,这还是在匹配数据库规模很小的情况下。2)单用户界面线程,单工作者线程:这种方式把监控显示放到用户界面线程中去,把检测和识别处理放到一个另开的工作者线程中去,识别的结果存入一块共享的List类型的内存区域,里面存放了识别出来的人物的姓名信息,用户界面线程从中取出姓名信息并显示在窗体上,这种方式可以使监控显示的画面比较流畅,但未实际加快识别效率。对共享的List型内存区域的操作已经算是一个极简版本的生产者工作者模型了。3)单用户界面线程,双工作者线程:这种方式把监控显示放到用户界面线程中去,把检测和识别处理分别放到两个工作者线程中去,这涉及三线程的同步问题,较为复杂,也是本课题的难点之一。此种方式实现了两个生产者消费者模型,在第一个模型中,检测(又称采样)线程是生产者,识别线程是消费者;在第二个模型中,识别线程是生产者,而用户界面线程是消费者。需要注意的是,此方式实际也并未加快单次识别的速度,但是通过检测和识别部分之间共享的一个List<People>对象作为缓冲区可以充分利用时间提高整体的识别效率,具体说来是基于一个这样的事实,在实际场合中,画面并不是一直会有人脸出现的,人脸的出现存在一定的时间间隔,此种方案就是有效利用画面中不出现人脸的时间来提高效率的,缓冲区可以保存在画面人脸数量较多或者采样间隔较小的情况下识别部分未能及时处理完毕的人脸信息,一旦画面中没有人脸出现,检测线程就不会像缓冲区中添加人物,而识别线程会在这个空档完成之前未能及时完成的工作,因此可以最大限度的保留现场的实际信息,系统默认使用此种方案。4.5.1流程此处仅给出单用户界面线程,双工作者线程的流程图,如图4-11所示:对话框窗口枷白化u国雌三消巳队列始制对话柜由口退出按田桂单击清空LNeeeTiee启动采样战if后动in引潴程4类似所示:对话框窗口枷白化u国雌三消巳队列始制对话柜由口退出按田桂单击清空LNeeeTiee启动采样战if后动in引潴程4类似所示:图4-11.5.2实现4.4.2的实现,采样线程流程图如4-12所示,识别线程的流程图如图4-13开始画面为空画面中有■人脸圜中区黄ndThread-=TRUE?根据当前画面和检测结果设SPerson®Portraitinput设置信号日Ait即NctEmpty将P9rson力口人到P口凸pI白口中去阻塞线程,等符信号eArrayNotFull从摄像头中抽取一帧画面,并获取当前时间对每个检冽到的人脸新建—^Peoplej^f^Person执行检测函数退出线程)图4-12以上流程图未给出多线程共享内存的互斥访问控制,在实际的代码中使用MFC中的CCriticalSection类实现对临界区的互斥访问。4.6数据库本课题的数据库部分不作为重点,仅给出数据库中的表的设计,为了简化数据库的设计,假设所有人物不重名,People表的结构如图4-14:
LEWOVO-PC\SQLE...em-dbo.PsoplexLENOVO-PC\£QLE„.erf列名允许Null1Name|varchair(50)□Embedding varchsrfMAX]□PortiraitPsthvarcharfSO)回图4-145.1主界面测试5测试正在初始化的主界面如图5-1所示:正在书脸念PyIw拜琢DRSyvlBn?1FaceDRSystem图5-1初始化完毕的主界面如图5-2所示:图5-25.2信息采集测试单击捕获按钮之后如图5-3所示:图5-3单击确定按钮之后(图片中的人物是著名歌手ToveLo),可以看到数据库中已经增加了她的信息,如图5-4所示:NameEmbe-ddinqPortraitPath]T6乩-0,047241397202014923,-0,077077955...Portraits//Tov&Lcjpgheyi-0.11537960171699524,0,02419247^5...Portra也Z/heyi.jpg张克由NULL-0.0&61§9788g61073644,-0136705607...NULLPortrs冠/僦克E月,jpgNULL图5-45.3实时监控测试进入实时监控后的情况如图5-5所示,可以看到ListBox中已经出现ToveLo:RddTirw皿•,耳sum卜—体玄3aSnMH■皿力CcLBLMmvtn-nH:Z%3%口、血以为曲电2516如£-下Er啊总Tm口酎Tm^£-^»cSL^3"Tx<£>»qR:“TsaTREtSS工TiMiaLj&->21JffiH5丁西一£-也110》TseLsLAZiiiBiHTedkXtll;*丁6・0-»]上15:制Tn-<£>>ai:L&:4]Tk3%1上注刃*ToaD-xjiji.j?T^euci->2356i2]FwagZSnuJaTs召LE才语俱丁“过〉乂¥由书LWnNH->13:lL:4aSnmm口;1殳M~Th*e-&3^血图Ti>fli»23:23L0ZTiMiHLj&-s-23lZ?lt]JTg=簿3f*i2E16出-»以33阕丁鹏曰bN¥A;ETMM£-?4ftS:49姓三开好U瘴 WSlnt fl±.图5-5双击ListBox中的人名后右边的控件会显示在信息采集时采集到的人像图片,如图5-6所示:Iheyi->16:50:09hEyi->16:52:30heyi->l&;56l24Unknown->17:00:ISUnknowns15:29:38heyi->lSr32:30heyi->IS:44:28Unknown->19:44:38Unknown-》20:10:25TcueLo->2a:45:l&ToweLo->20:4~:55TcweLo->20:51:02 TQveLo->2Q^53:54Tgl'eLd-、20)S占30・,口工二口-2u:59:5j|七一近二,7七三点之:二工l-o-.eL3->21:25:24■TO'.'e_-->2:!12:5=]飞光Rd—35;三1一口一江二,-储二9北I<.-.SL3-?<::23:L-r[xfkt•近值3次[飞一旧工」.二匠匕工I-o..slo->23:u5:u?|Ta-.'eLo->£j:u9:■I■■<-:■■.---ii:-■:<2]:3三。^RToveLa-/23;19;28|ToueLo->23!23:07ToveLo->25:27:01ToveLo->23:30:25|To..'eLo->231331-18■To'.'eLo->。口日三—5TO'.e!_0->C0:56:姓名;TaveLo开始喈停 宵空List 退出v正在监控图5-66结论本次毕业设计从选题,算法的研究、选取、测试,到编码、测试历时两个多月,期间学到了很多东西,也遇到了不少困难。现在,我已经能够使用MFC写出一般的应用程序了,也了解了许多关于图像处理的知识,其中还学到了很多关于人工智能和机器学习的东西,能够简单使用Tensorflow这个计算框架,能够成功使用多线程技术到实际应用中提高效率,还学习到了Python这门以前从未接触过的语言。本课题遇到的最大的两个难点是数据类型的转换问题和多线程同步问题。数据类型的转换比较困难的有两个:一是C++和Python进行数据交互时遇到类型转换,Python的C++接口中把Python对象全部定义为PyObject类型,这个类型内部的结构比较复杂,为类型转换带来了一定的麻烦,不过后来使用Cython和PythonList类型勉强解决了这个问题;二是映射特征在数据库和C++的交互中的类型转换,Embedding特征在C++中double类型的数组,而数据库并不支持数组类型,本课题采用的策略是在C++中将double类型的数组转换成C
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年吉林省白山长白县联考初三年级英语试题周测三含答案
- 江西婺源茶业职业学院《幼儿园综合活动设计与指导》2023-2024学年第二学期期末试卷
- 2025年湖南长沙市雅礼洋湖实验中学普通高中初三下学期学业质量监测(期末)化学试题含解析
- 击剑基础知识
- 幼儿园教案:用电安全
- 国家职业保育员培训
- 我的心愿习作课件
- 2025《地籍调查》不动产登记代理人考前冲刺必会300题-含详解
- DB-T29-324-2025 天津市轨道交通综合控制中心系统建设与接口技术标准
- 儿童安全乘车知识
- 中国少年先锋队入队申请书 带拼音
- 环氧粉末涂料爆炸危险性评估
- 拉斐尔课件完整版
- 机加工日语词汇
- 化疗药物灌注
- 集群企业住所托管服务协议书
- GB/Z 28828-2012信息安全技术公共及商用服务信息系统个人信息保护指南
- 中小企业智能制造数字转型
- GB/T 23149-2008洗衣机牵引器技术要求
- GB/T 12729.1-2008香辛料和调味品名称
- GB/T 1228-2006钢结构用高强度大六角头螺栓
评论
0/150
提交评论