人臉識別畢業(yè)設(shè)計_第1頁
人臉識別畢業(yè)設(shè)計_第2頁
人臉識別畢業(yè)設(shè)計_第3頁
人臉識別畢業(yè)設(shè)計_第4頁
人臉識別畢業(yè)設(shè)計_第5頁
已閱讀5頁,還剩48頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

信息科學(xué)與技術(shù)學(xué)院畢業(yè)論文課題名稱:基于特征識別的人臉檢測系統(tǒng)學(xué)院:信息科學(xué)與技術(shù)學(xué)院完成日期:二O一七年五月十九日其中,Gentle是指此分類器相對于傳統(tǒng)的AdaptiveBoostClassifier在訓(xùn)練時錯誤分類的樣本的權(quán)值增加策略上更加平穩(wěn)(圖2-4中的3.b),以便獲得更好的泛化能力;Adaptive是指相對于傳統(tǒng)的BoostClassifier,AdaptiveBoostClassifier能動態(tài)調(diào)節(jié)各個樣本在訓(xùn)練時所占的權(quán)重,就是增加錯誤分類樣本的權(quán)值,降低正確分類樣本的權(quán)值。2.1.2識別部分的GoogleFaceNet無論是人臉檢測還是人臉識別,都面臨著來自光照,姿態(tài),表情變化帶來的巨大挑戰(zhàn),人工神經(jīng)網(wǎng)絡(luò)模型以其強大的非線性擬合能力和極強的魯棒性能夠在很大程度上面對以上挑戰(zhàn),而且深度學(xué)習(xí)是近幾年才興起的新理論,本課題勇于追趕研究前沿,在識別部分使用了DavidSandberg根據(jù)Google在2015發(fā)表的一篇文章《FaceNet:AUnifiedEmbeddingforFaceRecognitionandClustering》使用Tensoflow實現(xiàn)的一個人工神經(jīng)網(wǎng)絡(luò),并已經(jīng)使用大量訓(xùn)練樣本(百萬級)預(yù)先訓(xùn)練完畢,在使用時只需要加載這個固化的網(wǎng)絡(luò)模型至內(nèi)存中,輸入經(jīng)過預(yù)處理的人臉圖片,再執(zhí)行一次前向計算,即可得到用于識別人臉的特征。FaceNet在LFW(LabeledFacesintheWild)數(shù)據(jù)庫上的準(zhǔn)確率達到了99.63%,刷新了當(dāng)時的記錄。FaceNet使用深度卷積神經(jīng)網(wǎng)絡(luò)直接學(xué)習(xí)人臉在高維歐式空間的映射,映射得到的坐標(biāo)點的歐式距離即代表了樣本的相似性,距離越大表示相似性越低,反之越高。FaceNet的大體架構(gòu)如圖2-5所示:I亙。BatchDEEPARCHITECTUREI亙。BatchDEEPARCHITECTUREUMIQED01W,G一圖2-5其中DeepArchitecture就是一般的卷積神經(jīng)網(wǎng)絡(luò)去掉softmax后的結(jié)構(gòu),經(jīng)過L2的歸一化后得到人臉圖片再高維歐式空間的的特征表示,在訓(xùn)練時以最小化后面的三聯(lián)子損失函數(shù)為目標(biāo)不斷調(diào)整網(wǎng)絡(luò)中各個節(jié)點的權(quán)值。目標(biāo)函數(shù):三聯(lián)子損失函數(shù),其學(xué)習(xí)過程如圖2-6所示圖2-6三聯(lián)子表示在訓(xùn)練樣本中抽取的三個人臉樣本(抽取方法不做深入討論),其中Anchor和Positive是來自同一個人的兩張臉部圖片,Anchor和Negative是來自不同人的兩張臉部圖片,學(xué)習(xí)的過程就是要使經(jīng)過網(wǎng)絡(luò)映射的Anchor和Positive的歐式距離盡可能小而Anchor和Negative的歐式距離盡可能的大,依此來獲得對人臉特征的描述。用公式描述:(2-3)其中表示映射過程,,,,分別代表錨點,正樣本,負樣本,表示正負樣本之間的距離,表示所有可能的三聯(lián)子集合。兩個樣本之間的相似性就使用公式來計算的。將(2-3)做一下轉(zhuǎn)換便可以得到目標(biāo)函數(shù):(2-4)訓(xùn)練的過程就是最小化,訓(xùn)練方法使用一般的卷積神經(jīng)網(wǎng)絡(luò)的訓(xùn)練方法即可,其具體內(nèi)容不做討論,實現(xiàn)上各種深度學(xué)習(xí)框架已經(jīng)集成了各種最為先進的優(yōu)化算法,只需要調(diào)用相應(yīng)地接口便可以完成對神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程。關(guān)于DeepArchitecture,論文中給出了幾種可選的網(wǎng)絡(luò)結(jié)構(gòu),其中一種是Zeiler&Fergus結(jié)構(gòu),如圖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的結(jié)構(gòu),如圖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的結(jié)構(gòu)如圖2-9所示:1x1Conv(64)AvgPookig其中一種Inception的結(jié)構(gòu)如圖2-9所示:1x1Conv(64)AvgPookigFilletconcat1x1Conv(64)3x3Conv196)Fitlerooncat圖2-9課題中使用的網(wǎng)絡(luò)結(jié)構(gòu)目前還不知道其具體形式,Tensorflow提供了可視化固化網(wǎng)絡(luò)模型的工具,但在實際使用時出了一些問題,因此很遺憾的沒有看到實際使用的網(wǎng)絡(luò)結(jié)構(gòu)。2.2技術(shù)MFC簡介MFC全稱MicrosoftFoundationClasses(微軟基礎(chǔ)類庫),這是Microsoft推出的一套C++類,把大部分常用的WindowsAPI函數(shù)封裝在其中。MFC中的類和函數(shù)接口成千上萬,對如此龐大的類庫進行介紹和對其內(nèi)部的復(fù)雜機制進行討論已經(jīng)超出本文的范圍,因此在接下來的論述中,文章僅介紹本課題中顯式使用的一些內(nèi)容,本課題使用的集成開發(fā)工具是VisualStudio2013Premium,在其中新建一個MFC項目的同時,VS會自動生成項目的主框架,我們要做的就是往這個主框架中添加內(nèi)容和修改一些相應(yīng)地修改,必要時重寫某些函數(shù)。1)UI:本課題是基于對話框結(jié)構(gòu)的項目,因此涉及到的UI知識僅有對話框中的內(nèi)容,MFC封裝了窗體和控件的底層繪制操作,用戶可以在資源文件添加對話框類型的資源文件,然后用vs提供的可視化編輯工具編輯自己的對話框,而不用考慮其底層的繪制細節(jié),各種控件的添加可以通過拖拽放置到對話框中。用可視化工具編輯完界面后,vs會自動為這個對話框生成一個相應(yīng)的類CXXXDlgCalss,這個類就封裝了對話框中的所有內(nèi)容,與控件關(guān)聯(lián)的變量全部作為類的成員變量來實現(xiàn),各種消息相應(yīng)函數(shù)也是做為這個類的成員函數(shù)來實現(xiàn)的。本課題使用的默認MFC樣式如圖2-10所示:圖2-102)數(shù)據(jù)類型:MFC中定義了許多常用的數(shù)據(jù)類型,本課題使用最多是CString類型,窗體控件中顯示的字符串的類型全部都是CString類型,寫入數(shù)據(jù)庫和從數(shù)據(jù)庫中取出來的數(shù)據(jù)類型也都是CString類型,因此關(guān)于CString和其他數(shù)據(jù)類型的轉(zhuǎn)換是本課題實現(xiàn)部分的一個重點。MFC中提供了許多類型轉(zhuǎn)換函數(shù)和類型轉(zhuǎn)換宏如宏CT2A()可以將CString類型轉(zhuǎn)換為string類型,CString類型的本身也提供了一個強大的轉(zhuǎn)換函數(shù)Format(),它可以方便地實現(xiàn)許多基本的類型轉(zhuǎn)換,但是一些更為復(fù)雜的類型轉(zhuǎn)換還需要自己寫函數(shù)去實現(xiàn)。3)消息機制:在Windows中,消息和線程一一對應(yīng),當(dāng)系統(tǒng)檢測到某個消息時,會將相應(yīng)的消息發(fā)送給與該消息對應(yīng)的線程。按照類型,Windows將消息按照類型分為如圖2-11所示的幾類:圖2-11Windows將整個消息系統(tǒng)分為3個層級:①Windows內(nèi)核的系統(tǒng)消息隊列②App的UI線程消息隊列③處理消息的窗體對象Windows內(nèi)核會維護一個全局的系統(tǒng)消息隊列,按照線程的不同,系統(tǒng)消息隊列中的消息會被發(fā)送到應(yīng)用程序的UI線程的消息隊列中,應(yīng)用程序的每一個UI線程都有自己的消息循環(huán),會不停的從自己的消息隊列中取出消息,并發(fā)送給Windows窗體對象,每一個窗體對象都使用窗體過程函數(shù)WindowProc()來處理接受的各種消息。本課題中相應(yīng)地消息一般都是第三層級的消息。MFC框架封裝了對消息處理的大部分操作,我們可以在生成的窗體類中添加消息的響應(yīng)函數(shù),當(dāng)系統(tǒng)檢測到用戶或者系統(tǒng)的行為產(chǎn)生了已經(jīng)被定義的消息,會自動調(diào)用我們編寫的消息相應(yīng)函數(shù),執(zhí)行對消息的響應(yīng)操作,在本課題實現(xiàn)中具體相應(yīng)的消息有窗口繪制消息ON_WM_PAINT,系統(tǒng)時間消息ON_WM_TIMER按鍵消息ON_BN_CLICKED等。4)多線程:MFC中的線程主要有兩類,一類是用戶界面線程,另一類是工作者線程,二者的重要區(qū)別在于前者有自己的消息隊列和消息循環(huán),而后者沒有,用戶界面線程的工作一般是處理界面繪制,響應(yīng)用戶操作和系統(tǒng)產(chǎn)生的事件及消息等,vs自動生成的窗口類中的成員函數(shù)大多都是作為用戶界面線程執(zhí)行的,一種不恰當(dāng)?shù)脑O(shè)計方式是將所有代碼都放在用戶界面線程中,這樣做確實簡單方便,但是它會帶來一些嚴(yán)重的效率問題,比如一些占用大量內(nèi)存和耗時較長的計算若放在用戶界面線程中會導(dǎo)致界面長時間處于無響應(yīng)狀態(tài),因此應(yīng)該將計算量較大的處理另開工作者線程單獨執(zhí)行,用消息傳遞或者共享內(nèi)存的方式與用戶界面線程同步。MFC中多線程編程常用的函數(shù)如下幾個:①啟動一個工作者線程:CWinThread*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,nPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,LPSECRITY_ATTRIBUTESlpSecurityAttrs=NULL);②在某個信號量上阻塞線程:DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMillisecondes);③臨界區(qū)的訪問:進入臨界區(qū)的使用CCriticalSection對象的Lock()函數(shù),離開使用Unlock()函數(shù).Tensorflow簡介TensorFlow是一個開源的使用數(shù)據(jù)流圖的數(shù)值計算框架,被廣泛的用于實現(xiàn)各種神經(jīng)網(wǎng)絡(luò),數(shù)據(jù)流圖中的節(jié)點代表數(shù)學(xué)運算,而圖中的線條代表數(shù)據(jù)數(shù)組(又稱為張量)。TensorFlow最初由Google大腦小組的研究員和工程師門開發(fā),用于機器學(xué)習(xí)和深度神經(jīng)網(wǎng)絡(luò)方面的研究,但是因為這個框架具有通用性,也可以被用于其他的計算領(lǐng)域。它靈活的架構(gòu)可以讓你將節(jié)點部署在一個或者多個CPU或者GPU上進行并行計算。關(guān)于數(shù)據(jù)流圖:數(shù)據(jù)流圖是一種以數(shù)據(jù)流動為驅(qū)動力的計算模型,用nodes和edges的有向圖來描述數(shù)學(xué)計算,Nodes一般用來表示對數(shù)據(jù)施加的數(shù)學(xué)操作,但也可以表示數(shù)據(jù)的輸入的起點或者是數(shù)據(jù)輸出的終點。本課題嘗試直接編譯Tensorflow源碼,以使用其C++接口,但官方給出的編譯方式是使用Google內(nèi)部使用的編譯工具Bazel,這個工具在Windows平臺很不穩(wěn)定,目前只有測試版,在使用的過程中出現(xiàn)了各種問題,其中的一些無法解決致使編譯失敗。官方推薦的方式是使用Python接口,而且教程大多數(shù)也是基于Python接口的,于是本課題使用C++,Python混編技術(shù)將Python的接口封裝成C語言的形式,如此就可以在程序中間接使用Tensorflow了。課題中使用的FaceNet模型是預(yù)先訓(xùn)練完成的,其數(shù)據(jù)流圖的結(jié)構(gòu)被固化存儲在.meta的文件中,圖中的權(quán)值被固化存儲在.ckpt的文件中。本課題使用的Python接口主要有如下幾個:①導(dǎo)入數(shù)據(jù)流圖即網(wǎng)絡(luò)結(jié)構(gòu)至默認的圖(tensorflow會在Python環(huán)境的上下文中提供)中使用函數(shù)tensorflow.train.import_meta_graph(meta_file_path)②加載權(quán)值至數(shù)據(jù)流圖中saver.restore(sess,ckpt_file_path)③根據(jù)Tensor的名字獲得輸入輸出graph.get_tensor_by_name(tensor_name)④執(zhí)行前向計算sess.run(embedding,feed_dict=feed_dict)ADO組件簡介ADO全稱ActiveXDataObjects是一個用于存取數(shù)據(jù)源的組件,它提供了編程和統(tǒng)一數(shù)據(jù)訪問方式OLEDB的一個中間層,允許開發(fā)人員編寫訪問數(shù)據(jù)的代碼為不用關(guān)心數(shù)據(jù)庫是如何實現(xiàn)的,而只關(guān)心數(shù)據(jù)庫的鏈接。本課題將對數(shù)據(jù)庫的操作封裝在一個類ADOConn中,封裝后的類提供用于初始化連接數(shù)據(jù)庫,直接執(zhí)行SQL語句,獲得查詢語句的結(jié)果集指針和斷開連接等數(shù)據(jù)庫操作的接口。3需求分析及概要設(shè)計需求分析本課題主要目的是設(shè)計一個用于實時監(jiān)控的人臉識別系統(tǒng),將攝像頭部署在需要進行監(jiān)控的場合,系統(tǒng)初始化完畢后便自動檢測和識別出現(xiàn)在畫面中的人臉,能記錄已經(jīng)識別人物的身份信息和出現(xiàn)在畫面的時間并予以記錄,將不能識別的人物標(biāo)記為Unknown并記錄時間戳。系統(tǒng)只能識別預(yù)先經(jīng)過信息采集的人物,在信息采集階段,要求記錄人物的姓名和頭像。概要設(shè)計整個系統(tǒng)分為兩個功能模塊,一個是信息采集模塊,另一個是實時監(jiān)控模塊,如圖3-1所示:11-3

圖核心理論的實現(xiàn)檢測部分的實現(xiàn)檢測部分使用的是于仕祺老師公開的人臉檢測庫,配置好dll庫后將相應(yīng)地頭文件導(dǎo)入到項目文件中即可使用這個庫的接口,這個庫針對不同的應(yīng)用場合提供了四個不同的接口,本課題中使用的是檢測效果最好但最耗時的一個接口int*facedetect_multiview_reinforce(parameterlist)函數(shù)形參列表包括了指向存放檢測結(jié)果區(qū)域的指針,指向存放輸入圖片數(shù)據(jù)區(qū)域的指針,輸入圖片的長寬和一些其他的參數(shù),函數(shù)返回一個指向存放檢測結(jié)果區(qū)域的指針,關(guān)鍵就是要提取這個指針?biāo)赶虻臄?shù)據(jù),假設(shè)返回的指針是pResult,則pResult指向的第一個整型數(shù)據(jù)就表示在輸入圖片中檢測到的人臉數(shù)量,在下來連續(xù)的內(nèi)存區(qū)域中存放了每一個人的人臉信息,每個人占142個整型數(shù)據(jù),前四個數(shù)表示了人臉包圍盒的左上角和右下角的左邊,第五個數(shù)據(jù)表示了名為neighbor的信息(其具體含義目前還未知),第六個數(shù)據(jù)表示了人臉的轉(zhuǎn)角,范圍是0~60度,之后每兩個數(shù)據(jù)組成一個坐標(biāo)點,標(biāo)記了人臉的68個特征點,這些特征點標(biāo)記了人臉的輪廓,眼睛,眉毛,鼻子,嘴巴的位置和輪廓,共占用136個數(shù)據(jù),如圖4-1所示:pResult第一個人臉信息 第二個人臉信息.....Boundiingbax*人臉數(shù)里圖4-1檢測的結(jié)果如圖4-2所示:圖4-2基于監(jiān)控的檢測只需要隔一定的時間間隔從攝像頭中取出畫面,將畫面送入檢測函數(shù)后把檢測結(jié)構(gòu)繪制在畫面上在輸出到用戶界面上即可。提取識別特征部分的實現(xiàn)提取識別特征部分采用基于Tensorflow實現(xiàn)的一個FaceNet神經(jīng)網(wǎng)絡(luò)模型,前文提到過未能成功使用Tensorflow的C++API,因此使用其PythonAPI,用Python編寫好直接使用Tensorflow的函數(shù),通過Cython將用Python定義的函數(shù)轉(zhuǎn)換為C形式定義函數(shù),Cython會自動生成對應(yīng)函數(shù)的頭文件和源文件,將生成的文件導(dǎo)入項目中即可使用C形式定義的函數(shù)。所有關(guān)于Tensorflow的使用都被封裝在一個叫TensorflowInference的類中,這個類封裝了包括session對象的獲取和維護操作、Python環(huán)境的初始化、網(wǎng)絡(luò)模型的加載、計算映射特征以及所有的C++和Python數(shù)據(jù)交互工作,對類外的使用者完全隱藏了所有涉及Python的內(nèi)容,具體見4.2.3節(jié)。在這其中有兩點需要注意:Session對象:值得注意的是,Tensorflow中計算都是通過session對象來完成的,session對象中保存了數(shù)據(jù)流圖的結(jié)構(gòu)信息和權(quán)值信息,課題中使用的網(wǎng)絡(luò)模型非常龐大(141MB),如果每次計算都重新將模型從磁盤調(diào)入內(nèi)存,會嚴(yán)重增加程序的耗時(每次加載模型大約耗時兩分鐘),因此合理的設(shè)計應(yīng)該是在整個系統(tǒng)初始化時就將模型載入內(nèi)存,并獲取其session對象使其常駐內(nèi)存,每次計算時調(diào)用session對象的成員方法即可完成一次計算(每次計算大約耗時0.3~0.4秒)。在Python腳本中,session對象的獲取是通過Tensorflow提供的接口tf.Session()獲取的,不過在獲取session對象之前,需要將數(shù)據(jù)流圖的結(jié)構(gòu)信息載入默認的graph中,再將圖中的權(quán)值恢復(fù)到session對象中,這樣之后的session對象才能正確執(zhí)行FaceNet的前向計算。獲得的session對象會在C++中以PyObject的類型出現(xiàn)。C++和Python的數(shù)據(jù)交互:本課題的一個難點就是C++和Python的數(shù)據(jù)交互問題,數(shù)據(jù)交互的形式是通過函數(shù)的形參和返回值進行的,主要包括兩個方向,一是從C++部分的數(shù)據(jù)流入Python,二是Python部分執(zhí)行計算的結(jié)果返回C++,按照Python官方給出的做法是將所有C++類型的數(shù)據(jù)轉(zhuǎn)換成PyObject對象進行交互,但是由于Python所有的對象在C++中全部都是繼承自PyObject類型,如此靈活的數(shù)據(jù)類型帶來一定的復(fù)雜度,而且本課題中使用了圖片類型數(shù)據(jù),在Python中是numpy.ndarray類型,numpy官方給出了對C++版本的ndarray支持,但是在實際使用中顯示使用的函數(shù)接口已經(jīng)過時。好在Cython允許在Python腳本中使用C類型的數(shù)據(jù)結(jié)構(gòu),使得從C++到Python的數(shù)據(jù)轉(zhuǎn)換方便了不少。具體的數(shù)據(jù)流向和轉(zhuǎn)換過程如圖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++共享數(shù)據(jù)類型Python共享數(shù)據(jù)類型C++4.2類People類People類是本課題設(shè)計的一個最為重要的類,它的抽象層級最高,是抽象的“人”,有姓名,映射向量,存儲其頭像圖片的路徑等屬性,封裝了計算人臉的映射特征,根據(jù)設(shè)定的閾值判斷兩個人是否是同一個人,以及所有的數(shù)據(jù)操作,其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:這個變量用于標(biāo)記人物身份信息,在信息采集時作為必要信息錄入數(shù)據(jù)庫,在監(jiān)控時作為對人物身份的記錄,并可以通過它找到存儲在數(shù)據(jù)庫中人物的更多信息。2)Embedding:這個變量是一個double型的指針,指向了一小塊用于記錄人物的識別特征的數(shù)據(jù)區(qū)域,他是根據(jù)另一個成員變量Portraitinput計算得出的,相當(dāng)于2.2.1節(jié)中的f()的結(jié)果。3)PortraitShowPath:記錄了用于展示人物肖像圖片的存儲路徑。4)PortraitShow:保存用于展示人物肖像的圖片,其規(guī)格為400X400。5)Portraitinput:作為FaceNetModel的輸入數(shù)據(jù),用于計算人臉識別特征,必須經(jīng)過重新剪裁和縮放,其規(guī)格160X160。下面給出部分成員函數(shù)描述:1)函數(shù)SetPortraitinput:其功能是根據(jù)從攝像頭獲取的幀畫面和檢測部分給出的人臉包圍盒矩形坐標(biāo)剪裁圖片,然后將其縮放至統(tǒng)一規(guī)格160X160,這樣會得到一張160X160的規(guī)范化的人臉,圖片需要剪裁是因為在檢測部分給出的人臉包圍盒太過緊致,丟失了諸如頭發(fā),額頭上半部的信息,因此需要增大包圍盒恢復(fù)這些信息;需要縮放是因為FaceNet要求的輸入圖片就是160X160的規(guī)格。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)函數(shù)GetDistance2:功能是根據(jù)兩個人的映射特征Embedding計算兩人的相似性。inlinedoublePeople::GetDistance2(PeoplevPeople){doubleDistance2=0;foriin128 //Embedding為128個元素的一維數(shù)組Distance2+=(People.Embedding[i]-vPeople.Embedding[i])2returnDistance2;}4.2.2Tensorflowinference類Tensorflowinference類是作為對本課題使用到的幾個TensorflowPythonAPI、所有的C++,Python數(shù)據(jù)交互操作和對人臉圖片的預(yù)處理操作的封裝,這個類提供了三個純C風(fēng)格的接口,維護了Session這個重要的對象,在實現(xiàn)中它是作為全局類CXXXApp的一個成員變量使用的,在整個項目初始化時會單獨開工作者線程完成非常耗時的FaceNetModel加載工作,一旦初始化完畢,在項目的任何位置都可以通過引用全局對象theApp中的Tensorflowinference對象來使用Tensorflow計算人臉的映射特征,其UML如圖4-5所示:Tens-orflowlnferenceSesszPyObject*ResultPyObjectdata:double,InitPythonEnv():voidLoadTensarflowModelQvoidC己IculatEEmb已dding(匚己111cl口11印已加)圖4-5各成員變量的含義:Sess:指向Session對象的指針,Session對象保存了FaceNetModel的結(jié)構(gòu)信息和權(quán)值信息。Result:指向用來存放從Python中取出的結(jié)果的一小片內(nèi)存區(qū)域。data:用來存放最終輸入到FaceNetModel中的經(jīng)過預(yù)處理后的圖片數(shù)據(jù)。各成員函數(shù):1)函數(shù)InitTensorflowModel:加載FaceNet模型,get_embedding_session函數(shù)實際是用Python編寫的,經(jīng)過Cython轉(zhuǎn)換為C類型的函數(shù)以供使用。其代碼如下:voidTensorflowInference::InitTensorflowModel(){Sess=get_embedding_session();}get_embedding_session()函數(shù)在Python中的定義如下:cdefpublicget_embedding_session():sess=tf.Session()saver=tf.train.import_meta_graph(meta_path)saver.restore(sess,ckpt_path)returnsess由于得到的Session對象在使用時會作為用Python定義的函數(shù)的形參,所以C++并不需要解析這個PyObject對象,只需要在內(nèi)存中維護它的存在即可。3)函數(shù)CalculateEmbedding:這是本課題最為核心的一個函數(shù)之一,功能是計算人臉的映射特征,它接受一個規(guī)格化的160X160的人臉圖片,圖片格式為cv::Mat型,和一個double類型的指針,計算的結(jié)果將存放在double類型的指針?biāo)赶虻囊恍∑瑑?nèi)存區(qū)域內(nèi)。描述如下:voidTensorflowInference::CalculateEmbedding(cv::MatvMat160,double*vDoubleArray){檢查輸入圖片vMat160的規(guī)格,若不符合要求則退出;‘將OpenCVC默認的BGR格式圖片轉(zhuǎn)為RGB格式;將160X160X3的圖片拉伸為一維向量;PreWhiten“圖片”,將PreWhiten處理后的結(jié)果存在data中;Result=get_embedding_result(Sess,data);〃解析PyObject類型的返回結(jié)果foriin128vDoubleArray[i]=PyFloat_AsDouble(PyList_GetItem(Result,i));}其中g(shù)et_embedding_result()函數(shù)同get_session()函數(shù)一樣,也是使用Python定義的,并經(jīng)過Cython轉(zhuǎn)換的,由于C++中對numpy.ndarray的解析函數(shù)已過時,因此需要將Model中輸出的numpy.ndarray類型的Tensor轉(zhuǎn)換為C++可以成功解析的List類型。另外,根據(jù)Cython官方提供的資料,在用Cython語法編寫的Python腳本中是用C的數(shù)據(jù)類型定義變量可以加快程序執(zhí)行的速度,這是因為Python的底層本身就是C,因此定義中能用C定義的變量全部使用C定義。函數(shù)的原始定義如下: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++傳入的數(shù)據(jù),轉(zhuǎn)換為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類封裝了所有對數(shù)據(jù)庫的操作,比People類的抽象層級要低一級,People類通過對ADOConn類對象的引用來完成對數(shù)據(jù)庫的操作,其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這個類的抽象層次比較低,技術(shù)也比較落后,多數(shù)時候作為庫來使用,而且本課題的數(shù)據(jù)庫部分不是作為重點來考慮的,因此僅做簡單介紹。形參為_bstr_t的函數(shù)可以直接執(zhí)行在C++中編寫的SQL語句,數(shù)據(jù)庫返回的結(jié)果被保存在_RecordsetPtr指向的區(qū)域中,通過一定的解析就可以得到我們想要的數(shù)據(jù),這其中涉及一些數(shù)據(jù)類型轉(zhuǎn)換問題,People類中的對數(shù)據(jù)庫操作的函數(shù)已經(jīng)封裝了這些操作,包括CString轉(zhuǎn)換為double型數(shù)組,double型數(shù)組轉(zhuǎn)換成CString型的字符串等,在此不做詳細介紹。4.3主界面主界面的主要功能是初始化系統(tǒng)的Python運行環(huán)境,加載FaceNetModel,和展示信息采集模塊以及實時監(jiān)控模塊的入口。流程主界面的執(zhí)行流程圖如圖4-7所示:

初立缶七對不率¥學(xué)用于楂塊入口按鈕用戶界面在程單擊信皂采第 A單擊實時監(jiān)苴 H 進人實時監(jiān)及幅塊初立缶七對不率¥學(xué)用于楂塊入口按鈕用戶界面在程單擊信皂采第 A單擊實時監(jiān)苴 H 進人實時監(jiān)及幅塊單擊退出進入信目深空模塊開放子晅塊人口按鈕呂和題外由初始化工作者或程工作者送程圖4-7實現(xiàn)在VS為主界面生成的類中重寫了以下幾個函數(shù):1)OnInitDialog函數(shù):用來執(zhí)行簡單的不耗時的初始化工作,若在這個函數(shù)中執(zhí)行耗時的初始化工作,會導(dǎo)致窗口卡死在調(diào)用繪制函數(shù)之前,一種折中的設(shè)計是把耗時的初始化工作放在OnPaint()函數(shù)中,在窗口全部繪制完畢后再執(zhí)行耗時的初始化工作。本課題中雖然將耗時的初始化工作另開線程執(zhí)行,但經(jīng)過測試發(fā)現(xiàn)若在OnInitDialog函數(shù)中啟動線程也有可能會導(dǎo)致窗體無法正常繪制。2)OnPaint函數(shù):在此函數(shù)的結(jié)尾啟動額外的初始化線程,可以保證在窗體繪制完畢后執(zhí)行耗時的初始化工作,需要注意的是:若用戶拖動窗口或者改變窗口的大小,也會調(diào)用該函數(shù),為了避免初始化的重復(fù)執(zhí)行,系統(tǒng)中用一個BOOL類型的變量標(biāo)記初始化是否完成,只有在未完成的情況下才啟動額外的初始化線程,一旦初始化完成,就立刻將這個BOOL類型的變量取反,這樣就可以避免在之后的繪制中啟動額外的初始化線程。3)OnTimer函數(shù):系統(tǒng)會在程序的SetTimer處開始,每隔一小段時間就調(diào)用此函數(shù),其重要功能是不斷查詢InitLevel的值,根據(jù)這個值將初始化進度顯示給用戶。下面給出額外的初始化工作者線程函數(shù)的代碼:UINTInitPTfLPVOIDLpP^ram) 值無用{/八.檢查文件夾是否存在,若不存在則創(chuàng)建之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:無法創(chuàng)建Pcirt「氯士5文件夾!”));3& InitLevel=-1;31 returr明Q }33 )M ”2.檢查加加Im文件夾是否■存在、若不存在則報錯并退出程序〃注意:此處假設(shè)旭如店文件夾一旦存在n則其內(nèi)烹的所有文件也正常存在〃因為不清楚問價夾內(nèi)部的文件是否存在依賴關(guān)系,且文件極多,不便一一〃檢查!故此處不檢查文件夾內(nèi)部的文件38 if(1PathlsDirectory(_T("modeIs")))39 1 49 AFxMcssageBox(_T("ERROR:mcided±文件夾不存在!”));4-1 InitLevel=-1;42 return不 )44 初始化Python環(huán)境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流程通過點擊主界面的信息采集按鈕進入信息采集功能模塊,他的功能是收集人物的信息并寫入數(shù)據(jù)庫,其運行流程如圖4-8:進入信息界集畫回為主四回主界面若退出按鈕被單擊f ■(SS磁鉆裱單擊苦嘯定按鈕裱單擊系蛻演奇肖息隊翻內(nèi)包蜀盒繪制在回面上并顯示在通過點擊主界面的信息采集按鈕進入信息采集功能模塊,他的功能是收集人物的信息并寫入數(shù)據(jù)庫,其運行流程如圖4-8:進入信息界集畫回為主四回主界面若退出按鈕被單擊f ■(SS磁鉆裱單擊苦嘯定按鈕裱單擊系蛻演奇肖息隊翻內(nèi)包蜀盒繪制在回面上并顯示在1火_¥1?!昕诳丶线M入確癥按田單擊啊應(yīng)畫椒A 進入捕獲報田單擊啊應(yīng)函教擰Vf亍粒測函繳!—?從攝像頭中曲取一頂1旦直繪制對話用窗口對話柢窗口初上抖t圖4-84.4.2實現(xiàn)信息采集模塊的實現(xiàn)使用的是一個MFC生成的對話框類,消息響應(yīng)函數(shù)都是作為這個類的成員函數(shù)實現(xiàn)。其中捕獲按鈕的消息響應(yīng)函數(shù)CInfoCollectDlg::OnBnClickedButtonCaptrue()的處理流程圖如圖4-9所示:

檢測到瞄B?瞄啜量為1(開蛤)才科00X400的規(guī)格化圖片顯示在IDCJH。檢測到瞄B?瞄啜量為1(開蛤)才科00X400的規(guī)格化圖片顯示在IDCJH。岫劌牛上保苗tf捉到的畫面和柳格化的圖片剪裁.放縮圖片至4。口乂4口。給出相應(yīng)的提示信息執(zhí)行檢測函數(shù)從攝像頭中抽取一幀畫面圖4-9確定按鈕的消息響應(yīng)函數(shù)CInfoCollectDlg::OnBnClickedButtonComfier()的處理流程圖如圖4-10所示:

圖4-104.5實時監(jiān)控實時監(jiān)控模塊的主要功能是識別出現(xiàn)在攝像頭畫面中的人物的身份信息并予以記錄。該模塊的存在巨大的挑戰(zhàn),對實時性的要求難以滿足,主要的原因是識別部使用了深度神經(jīng)網(wǎng)絡(luò)模型,這種方案的優(yōu)勢在于識別準(zhǔn)確率高,對光照,姿態(tài)擁有較強的魯棒性,但其缺點是計算量偏大,每次識別耗時0.3~0.5秒,這個速度基本可以滿足實時性,但是無法以肉眼可見的流暢程度(fps15以上)予以呈現(xiàn)。因此課題的設(shè)計中,并不總是實時顯示識別的過程,而只顯示檢測的過程。本課題的代碼實現(xiàn)了三種監(jiān)控模式:1)單用戶界面線程監(jiān)控:這種方式把監(jiān)控顯示,檢測,識別處理全部放到用戶界面線程中去,這樣做的好處是檢測和識別的結(jié)果可以實時的顯示在畫面上,缺點是會使得畫面非??D,只能達到2~5的fps,這還是在匹配數(shù)據(jù)庫規(guī)模很小的情況下。2)單用戶界面線程,單工作者線程:這種方式把監(jiān)控顯示放到用戶界面線程中去,把檢測和識別處理放到一個另開的工作者線程中去,識別的結(jié)果存入一塊共享的List類型的內(nèi)存區(qū)域,里面存放了識別出來的人物的姓名信息,用戶界面線程從中取出姓名信息并顯示在窗體上,這種方式可以使監(jiān)控顯示的畫面比較流暢,但未實際加快識別效率。對共享的List型內(nèi)存區(qū)域的操作已經(jīng)算是一個極簡版本的生產(chǎn)者工作者模型了。3)單用戶界面線程,雙工作者線程:這種方式把監(jiān)控顯示放到用戶界面線程中去,把檢測和識別處理分別放到兩個工作者線程中去,這涉及三線程的同步問題,較為復(fù)雜,也是本課題的難點之一。此種方式實現(xiàn)了兩個生產(chǎn)者消費者模型,在第一個模型中,檢測(又稱采樣)線程是生產(chǎn)者,識別線程是消費者;在第二個模型中,識別線程是生產(chǎn)者,而用戶界面線程是消費者。需要注意的是,此方式實際也并未加快單次識別的速度,但是通過檢測和識別部分之間共享的一個List<People>對象作為緩沖區(qū)可以充分利用時間提高整體的識別效率,具體說來是基于一個這樣的事實,在實際場合中,畫面并不是一直會有人臉出現(xiàn)的,人臉的出現(xiàn)存在一定的時間間隔,此種方案就是有效利用畫面中不出現(xiàn)人臉的時間來提高效率的,緩沖區(qū)可以保存在畫面人臉數(shù)量較多或者采樣間隔較小的情況下識別部分未能及時處理完畢的人臉信息,一旦畫面中沒有人臉出現(xiàn),檢測線程就不會像緩沖區(qū)中添加人物,而識別線程會在這個空檔完成之前未能及時完成的工作,因此可以最大限度的保留現(xiàn)場的實際信息,系統(tǒng)默認使用此種方案。4.5.1流程此處僅給出單用戶界面線程,雙工作者線程的流程圖,如圖4-11所示:對話框窗口枷白化u國雌三消巳隊列始制對話柜由口退出按田桂單擊清空LNeeeTiee啟動采樣戰(zhàn)if后動in引潴程4類似所示:對話框窗口枷白化u國雌三消巳隊列始制對話柜由口退出按田桂單擊清空LNeeeTiee啟動采樣戰(zhàn)if后動in引潴程4類似所示:圖4-11.5.2實現(xiàn)4.4.2的實現(xiàn),采樣線程流程圖如4-12所示,識別線程的流程圖如圖4-13開始畫面為空畫面中有■人臉圜中區(qū)黃ndThread-=TRUE?根據(jù)當(dāng)前畫面和檢測結(jié)果設(shè)SPerson?Portraitinput設(shè)置信號日Ait即NctEmpty將P9rson力口人到P口凸pI白口中去阻塞線程,等符信號eArrayNotFull從攝像頭中抽取一幀畫面,并獲取當(dāng)前時間對每個檢冽到的人臉新建—^Peoplej^f^Person執(zhí)行檢測函數(shù)退出線程)圖4-12以上流程圖未給出多線程共享內(nèi)存的互斥訪問控制,在實際的代碼中使用MFC中的CCriticalSection類實現(xiàn)對臨界區(qū)的互斥訪問。4.6數(shù)據(jù)庫本課題的數(shù)據(jù)庫部分不作為重點,僅給出數(shù)據(jù)庫中的表的設(shè)計,為了簡化數(shù)據(jù)庫的設(shè)計,假設(shè)所有人物不重名,People表的結(jié)構(gòu)如圖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),可以看到數(shù)據(jù)庫中已經(jīng)增加了她的信息,如圖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實時監(jiān)控測試進入實時監(jiān)控后的情況如圖5-5所示,可以看到ListBox中已經(jīng)出現(xiàn)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->??谌杖?TO'.e!_0->C0:56:姓名;TaveLo開始喈停 宵空List 退出v正在監(jiān)控圖5-66結(jié)論本次畢業(yè)設(shè)計從選題,算法的研究、選取、測試,到編碼、測試歷時兩個多月,期間學(xué)到了很多東西,也遇到了不少困難?,F(xiàn)在,我已經(jīng)能夠使用MFC寫出一般的應(yīng)用程序了,也了解了許多關(guān)于圖像處理的知識,其中還學(xué)到了很多關(guān)于人工智能和機器學(xué)習(xí)的東西,能夠簡單使用Tensorflow這個計算框架,能夠成功使用多線程技術(shù)到實際應(yīng)用中提高效率,還學(xué)習(xí)到了Python這門以前從未接觸過的語言。本課題遇到的最大的兩個難點是數(shù)據(jù)類型的轉(zhuǎn)換問題和多線程同步問題。數(shù)據(jù)類型的轉(zhuǎn)換比較困難的有兩個:一是C++和Python進行數(shù)據(jù)交互時遇到類型轉(zhuǎn)換,Python的C++接口中把Python對象全部定義為PyObject類型,這個類型內(nèi)部的結(jié)構(gòu)比較復(fù)雜,為類型轉(zhuǎn)換帶來了一定的麻煩,不過后來使用Cython和PythonList類型勉強解決了這個問題;二是映射特征在數(shù)據(jù)庫和C++的交互中的類型轉(zhuǎn)換,Embedding特征在C++中double類型的數(shù)組,而數(shù)據(jù)庫并不支持數(shù)組類型,本課題采用的策略是在C++中將double類型的數(shù)組轉(zhuǎn)換成C

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論