基于SeetaFace+VS2017+Qt的人臉識(shí)別

1 實(shí)驗(yàn)?zāi)康?/a>

????目前計(jì)算機(jī)視覺(jué)技術(shù)已經(jīng)比較成熟导帝,相關(guān)的開(kāi)源項(xiàng)目與算法很多,可以將這些開(kāi)源算法進(jìn)行整合,進(jìn)而做成一個(gè)小項(xiàng)目钦购,以供日后學(xué)習(xí)與研究。本實(shí)驗(yàn)主要將利用人臉識(shí)別開(kāi)源項(xiàng)目SeetaFace褂萧,結(jié)合使用OpenCV工具押桃,結(jié)合VS2017與Qt實(shí)現(xiàn)一個(gè)人臉識(shí)別的小項(xiàng)目。最后對(duì)實(shí)驗(yàn)系統(tǒng)進(jìn)行測(cè)試評(píng)估导犹。

2 相關(guān)知識(shí)與技術(shù)介紹

2.1 OpenCV簡(jiǎn)介

????OpenCV(Open Source Computer Vision Library)唱凯,是一個(gè)開(kāi)源的可以跨平臺(tái)運(yùn)行的計(jì)算機(jī)視覺(jué)庫(kù)羡忘,可以運(yùn)行在Linux、Windows磕昼、Android和Mac OS操作系統(tǒng)上卷雕。它輕量級(jí)而且高效,由一系列C函數(shù)和少量C++類(lèi)構(gòu)成票从,同時(shí)提供了Python漫雕、Ruby、MATLAB等語(yǔ)言的接口峰鄙,實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺(jué)方面的很多通用算法浸间。

????OpenCV的設(shè)計(jì)理念是所包含的函數(shù)能以最快的速度進(jìn)行編譯,之所以使用C代碼進(jìn)行編寫(xiě)吟榴,就是希望能夠利用多核處理器的優(yōu)勢(shì)達(dá)到最快的運(yùn)行速度魁蒜。它構(gòu)建了一個(gè)方便開(kāi)發(fā)人員使用的、簡(jiǎn)單易懂的計(jì)算機(jī)視覺(jué)框架吩翻,在這個(gè)基礎(chǔ)上兜看,開(kāi)發(fā)人員能都更方便的設(shè)計(jì)出更復(fù)雜的計(jì)算機(jī)視覺(jué)相關(guān)程序。OpenCV是由Intel發(fā)起的項(xiàng)目狭瞎,其中的源代碼都是開(kāi)源免費(fèi)的代碼铣减,因此可以用于科研人員的研究領(lǐng)域,也可以用于商業(yè)領(lǐng)域脚作。最新的版本OpenCV3.4.2已于2018年7月4日發(fā)布葫哗。

2.2 SeetaFace簡(jiǎn)介

????SeetaFace是由中科院山世光老師帶領(lǐng)的人臉識(shí)別研發(fā)組基于C++代碼研發(fā)的人臉識(shí)別算法。SeetaFace人臉識(shí)別引擎包括了搭建一套全自動(dòng)人臉識(shí)別系統(tǒng)所需的三個(gè)核心模塊球涛,即:人臉檢測(cè)模塊SeetaFace Detection劣针、面部特征點(diǎn)定位模塊SeetaFace Alignment以及人臉特征提取與比對(duì)模塊SeetaFace Identification。

????其中亿扁,SeetaFace Detection采用了一種結(jié)合傳統(tǒng)人造特征與多層感知機(jī)(MLP)的級(jí)聯(lián)結(jié)構(gòu)捺典,在FDDB上達(dá)到了84.4%的召回率(100個(gè)誤檢時(shí)),并可在單個(gè)i7 CPU上實(shí)時(shí)處理VGA分辨率的圖像从祝。

????面部特征點(diǎn)定位模塊SeetaFace Alignment通過(guò)級(jí)聯(lián)多個(gè)深度模型(棧式自編碼網(wǎng)絡(luò))來(lái)回歸5個(gè)關(guān)鍵特征點(diǎn)(兩眼中心襟己、鼻尖和兩個(gè)嘴角)的位置贮预,在AFLW數(shù)據(jù)庫(kù)上達(dá)到state-of-the-art的精度,定位速度在單個(gè)i7 CPU上超過(guò)200fps售葡。

????人臉識(shí)別模塊SeetaFace Identification采用一個(gè)9層的卷積神經(jīng)網(wǎng)絡(luò)(CNN)來(lái)提取人臉特征介却,在LFW數(shù)據(jù)庫(kù)上達(dá)到97.1%的精度(注:采用SeetaFace人臉檢測(cè)和SeetaFace面部特征點(diǎn)定位作為前端進(jìn)行全自動(dòng)識(shí)別的情況下),特征提取速度為每圖120ms(在單個(gè)i7 CPU上)所坯。


3 基于SeetaFace的人臉識(shí)別算法

????SeetaFace人臉識(shí)別引擎包括三個(gè)核心模塊,即:人臉檢測(cè)模塊SeetaFace Detection泻肯、面部特征點(diǎn)定位模塊SeetaFace Alignment以及人臉特征提取與比對(duì)模塊SeetaFace Identification惕医。下面對(duì)上述三個(gè)模塊的情況做簡(jiǎn)要介紹能岩。

3.1 人臉檢測(cè)模塊SeetaFace Detection

????該模塊基于一種結(jié)合經(jīng)典級(jí)聯(lián)結(jié)構(gòu)和多層神經(jīng)網(wǎng)絡(luò)的人臉檢測(cè)方法實(shí)現(xiàn)煌寇,其所采用的漏斗型級(jí)聯(lián)結(jié)構(gòu)(Funnel-Structured Cascade,F(xiàn)uSt)專(zhuān)門(mén)針對(duì)多姿態(tài)人臉檢測(cè)而設(shè)計(jì)逾雄,其中引入了由粗到精的設(shè)計(jì)理念阀溶,兼顧了速度和精度的平衡腻脏。如圖1所示,F(xiàn)uSt級(jí)聯(lián)結(jié)構(gòu)在頂部由多個(gè)針對(duì)不同姿態(tài)的快速LAB級(jí)聯(lián)分類(lèi)器構(gòu)成银锻,緊接著是若干個(gè)基于SURF特征的多層感知機(jī)(MLP)級(jí)聯(lián)結(jié)構(gòu)永品,最后由一個(gè)統(tǒng)一的MLP級(jí)聯(lián)結(jié)構(gòu)(同樣基于SURF特征)來(lái)處理所有姿態(tài)的候選窗口,整體上呈現(xiàn)出上寬下窄的漏斗形狀击纬。

????從上往下鼎姐,各個(gè)層次上的分類(lèi)器及其所采用的特征逐步變得復(fù)雜,從而可以保留人臉窗口并排除越來(lái)越難與人臉區(qū)分的非人臉候選窗口更振。?


圖1??SeetaFace人臉檢測(cè)模塊所采用的FuSt漏斗型級(jí)聯(lián)結(jié)構(gòu)

????整個(gè)算法采用漏斗型炕桨,先采用計(jì)算量小的特征,快速過(guò)濾大量非人臉窗口(圖像滑窗)肯腕,然后采用復(fù)雜結(jié)構(gòu)逐層篩選人臉献宫。由圖2所示SeetaFace檢測(cè)效果圖看到在人臉局部遮擋的情況下也能很好的檢測(cè)到人臉區(qū)域。

圖2 SeetaFace檢測(cè)效果圖

3.2 面部特征點(diǎn)定位模塊SeetaFace Alignment

????面部特征點(diǎn)定位(人臉對(duì)齊)在人臉識(shí)別实撒、表情識(shí)別姊途、人臉動(dòng)畫(huà)合成等諸多人臉?lè)治鋈蝿?wù)中扮演著非常重要的角色。由于姿態(tài)奈惑、表情吭净、光照和遮擋等因素的影響睡汹,真實(shí)場(chǎng)景下的人臉對(duì)齊任務(wù)是一個(gè)非常困難的問(wèn)題肴甸。形式上,該問(wèn)題可以看作是從人臉表觀到人臉形狀的復(fù)雜非線性映射囚巴。為此原在,SeetaFace Alignment采用一種由粗到精的自編碼器網(wǎng)絡(luò)(Coarse-to-Fine Auto-encoder Networks,?CFAN)來(lái)求解這個(gè)復(fù)雜的非線性映射過(guò)程。如圖3所示彤叉,CFAN級(jí)聯(lián)了多級(jí)棧式自編碼器網(wǎng)絡(luò)庶柿,其中的每一級(jí)都刻畫(huà)從人臉表觀到人臉形狀的部分非線性映射。具體來(lái)說(shuō)秽浇,輸入一個(gè)人臉區(qū)域(由人臉檢測(cè)模塊得到)浮庐,第一級(jí)自編碼器網(wǎng)絡(luò)直接從該人臉的低分辨率版本中快速估計(jì)大致的人臉形狀S0。然后柬焕,提高輸入人臉圖像的分辨率审残,并抽取當(dāng)前人臉形狀S0(相應(yīng)提升分辨率)各特征點(diǎn)位置的局部特征,輸入到下一級(jí)自編碼器網(wǎng)絡(luò)來(lái)進(jìn)一步優(yōu)化人臉對(duì)齊結(jié)果斑举。以此類(lèi)推搅轿,通過(guò)級(jí)聯(lián)多個(gè)棧式自編碼器網(wǎng)絡(luò),在越來(lái)越高分辨率的人臉圖像上逐步優(yōu)化人臉對(duì)齊結(jié)果富玷。


圖3人臉對(duì)齊方法

????此次開(kāi)源的SeetaFace Alignment基于上述CFAN方法實(shí)現(xiàn)了5個(gè)面部關(guān)鍵特征點(diǎn)(兩眼中心璧坟,鼻尖和兩個(gè)嘴角)的精確定位既穆,訓(xùn)練集包括23,000余幅人臉圖像(標(biāo)注了5點(diǎn))。需要注意的是雀鹃,為加速之目的幻工,在基本不損失精度的情況下,開(kāi)源實(shí)現(xiàn)中將CFAN級(jí)聯(lián)的數(shù)目減少到了2級(jí)黎茎,從而可在單顆Intel i7-3770 (3.4 GHz CPU)上達(dá)到每個(gè)人臉5ms的處理速度(不包括人臉檢測(cè)時(shí)間)会钝。

圖4是本文通過(guò)SeetaFace Alignment分別對(duì)不同人員在不同狀態(tài)特征點(diǎn)定位得到的效果圖。測(cè)試中發(fā)現(xiàn)通過(guò)多次驗(yàn)證工三,在不同表情迁酸、不同面部偏轉(zhuǎn)角度、抬頭低頭俭正、臉部局部遮擋等情況下都能有效的定位奸鬓。且對(duì)于戴眼鏡測(cè)試者,也能很好的定位到特征區(qū)域掸读。

圖4 SeetaFace人臉對(duì)齊

3.3人臉特征提取與比對(duì)模塊SeetaFace Identification

????人臉識(shí)別本質(zhì)上是要計(jì)算兩幅圖像中人臉的相似程度串远,其一為注冊(cè)階段(類(lèi)比人的相識(shí)過(guò)程)輸入系統(tǒng)的,另一幅為識(shí)別階段(即再見(jiàn)時(shí)的辨認(rèn)過(guò)程)的輸入儿惫。為此澡罚,如圖5所示,一套全自動(dòng)的人臉識(shí)別系統(tǒng)在完成前述的人臉檢測(cè)與人臉對(duì)齊兩個(gè)步驟之后肾请,即進(jìn)入第三個(gè)核心步驟:人臉特征提取和比對(duì)留搔。這個(gè)階段也是深度學(xué)習(xí)風(fēng)起云涌之后進(jìn)步最大的模塊,目前大多數(shù)優(yōu)秀的人臉識(shí)別算法均采用卷積神經(jīng)網(wǎng)絡(luò)(CNN)來(lái)學(xué)習(xí)特征提取器(即圖5中的函數(shù)F)铛铁。

圖5 人臉識(shí)別系統(tǒng)的核心流程

????SeetaFace開(kāi)源的人臉特征提取模塊也是基于卷積神經(jīng)網(wǎng)絡(luò)的隔显。具體地說(shuō),是深度卷積神經(jīng)網(wǎng)絡(luò)VIPLFaceNet:一個(gè)包含7個(gè)卷積層與2個(gè)全連接層的DCNN饵逐。其直接修改自Hinton教授的學(xué)生Alex Krizhevsky等于2012年設(shè)計(jì)的AlexNet(即引爆CNN在視覺(jué)中廣泛應(yīng)用的網(wǎng)絡(luò))括眠。

????與開(kāi)源的SeetaFace Identification代碼一起發(fā)布的人臉識(shí)別模型是使用140萬(wàn)人臉圖像訓(xùn)練出來(lái)的,這些訓(xùn)練圖像來(lái)自于約1.6萬(wàn)人倍权,其中既有東方人也有西方人掷豺。人臉特征直接采用VIPLFaceNet FC2層的2048個(gè)結(jié)點(diǎn)的輸出,特征比對(duì)可簡(jiǎn)單采用Cosine計(jì)算相似度薄声,然后進(jìn)行閾值比較(驗(yàn)證應(yīng)用)或排序(識(shí)別應(yīng)用)即可当船。

????在LFW standard Image-Restricted測(cè)試協(xié)議下,使用SeetaFace Detection與SeetaFace Alignment檢測(cè)并對(duì)齊人臉奸柬,采用SeetaFace Identification進(jìn)行特征提取和比對(duì)生年,可以達(dá)到97.1%的識(shí)別正確率(請(qǐng)注意:這是系統(tǒng)全自動(dòng)運(yùn)行的結(jié)果,對(duì)少量不能檢到人臉的圖像廓奕,截取中間區(qū)域輸入人臉對(duì)齊模塊即可)抱婉。速度方面档叔,在單顆Intel i7-3770 CPU上,開(kāi)源代碼提取一張人臉之特征的時(shí)間約為120ms(不含人臉檢測(cè)和特征點(diǎn)定位時(shí)間)蒸绩。

? ? 以上關(guān)于SeetaFace的理論介紹基本來(lái)源于SeetaFace官方的說(shuō)明衙四,想詳細(xì)了解SeetaFace更多的理論信息,請(qǐng)參考文章:SeetaFace開(kāi)源人臉識(shí)別引擎介紹患亿,該文章底部的參考文獻(xiàn)附有論文传蹈,有想深入研究的可以去啃啃論文(全英文的0_0)。

? ? 上面關(guān)于理論的確實(shí)介紹的有點(diǎn)多了步藕,不過(guò)了解一下惦界,可以增加一下自己的眼界。廢話不多說(shuō)了咙冗,下面進(jìn)入正題-應(yīng)用部分沾歪。

4 系統(tǒng)環(huán)境搭建與實(shí)驗(yàn)結(jié)果

通過(guò)對(duì)上述的理論知識(shí)的了解學(xué)習(xí),接下來(lái)進(jìn)行系統(tǒng)的環(huán)境搭建雾消,然后進(jìn)行具體的實(shí)驗(yàn)灾搏。然后對(duì)實(shí)驗(yàn)結(jié)果做一定的分析。

4.1系統(tǒng)的總體設(shè)計(jì)方案

首先使用攝像頭讀取采集的圖像立润,經(jīng)過(guò)人臉檢測(cè)模塊框出人臉狂窑,然后使用面部特征點(diǎn)定位(人臉對(duì)齊)模塊對(duì)5個(gè)面部關(guān)鍵特征點(diǎn)(兩眼中心,鼻尖和兩個(gè)嘴角)進(jìn)行標(biāo)記顯示桑腮,最后使用人臉特征提取與比對(duì)模塊進(jìn)行人臉特征提取和比對(duì)泉哈。在系統(tǒng)中設(shè)置一個(gè)閾值0.7,若比對(duì)后的相似度大于0.7到旦,就認(rèn)定為同一個(gè)人并觸發(fā)警報(bào)系統(tǒng)旨巷。系統(tǒng)的功能框圖如圖6所示巨缘。


圖6 系統(tǒng)功能框圖

4.2 配置系統(tǒng)環(huán)境

4.2.1 配置OpenCV

Open CV中包含很多圖像處理的算法添忘,因此學(xué)會(huì)正確使用Open CV也是人臉識(shí)別研究的一項(xiàng)重要工作。在?VS2017中應(yīng)用Open CV若锁,需要進(jìn)行手動(dòng)配置搁骑,下面給出在VS2017中配置Open CV的詳細(xì)步驟。

1.下載并安裝OpenCV3.4.1與VS2017的軟件又固。

2.配置Open CV環(huán)境變量仲器。

計(jì)算機(jī)->(右鍵)屬性->高級(jí)系統(tǒng)設(shè)置->高級(jí)->環(huán)境變量->(雙擊)系統(tǒng)變量中的path->在變量只里面添加相應(yīng)的路徑。添加的路徑為:“....opencv\build\x64\vc15\bin”仰冠。里面的省略號(hào)請(qǐng)換成自己電腦上的路徑乏冀,例如:E:\opencv\build\x64\vc15\bin。如圖7所示洋只。


圖7 配置OpenCV環(huán)境變量

3.配置工程目錄與鏈接庫(kù)

需要配置包含目錄和庫(kù)目錄辆沦,首先打開(kāi)昼捍,視圖->解決方案管理器->(右鍵)項(xiàng)目->屬性->VC++目錄。

1)配置包含目錄肢扯。

添加“...opencv\build\include;...opencv\build\include\opencv;...opencv\build

\incude\opencv2”即可妒茬,里面的省略號(hào)請(qǐng)換成自己電腦上的路徑,如圖8所示蔚晨。


圖8 配置包含目錄

2)配置庫(kù)目錄

添加“E:\opencv\build\x64\vc15\lib即可乍钻,里面的省略號(hào)請(qǐng)換成自己電腦上的路徑沮尿,如圖9所示刑赶。


圖9 配置庫(kù)目錄

3)配置鏈接庫(kù)

首先打開(kāi)他匪,視圖->解決方案管理器->(右鍵)項(xiàng)目->屬性->鏈接器->輸入->附加依賴(lài)項(xiàng)楼熄,

針對(duì)Debug配置添加“opencv_world341d.lib”,若在Release下彤敛,就添加“opencv_world341.

lib”俱箱。這是OpenCV3版本的方便之處廊移,OpenCV2版本需要添加很多項(xiàng)导披。操作如圖10所示笋粟。


圖10 配置鏈接庫(kù)

????上述三步配置要注意每次新建工程都要重新配置怀挠,但也可以只配置一次,達(dá)到以后都不用單獨(dú)配置的效果害捕,那么就在屬性管理器->展開(kāi)項(xiàng)目->Debug|x64(或者Release|x64)-(雙擊)進(jìn)Microsoft.Cpp.x64.user绿淋,然后后續(xù)操作和前面三步一樣。

4.2.2 配置SeetaFace

首先尝盼,在開(kāi)源項(xiàng)目平臺(tái)GitHub下載SeetaFace開(kāi)源項(xiàng)目吞滞。

下載鏈接:https://github.com/seetaface/SeetaFaceEngine。下載好以后盾沫,然后根據(jù)SeetaFace開(kāi)源項(xiàng)目中的配置說(shuō)明文檔進(jìn)行配置裁赠。

在VS2017中新建三個(gè)dll(動(dòng)態(tài)鏈接庫(kù))項(xiàng)目,將SeetaFace的三個(gè)模塊FaceDetection赴精、FaceAlignment和FaceIdentification分別制作成DLL項(xiàng)目佩捞,然后將生成的lib和dll文件保存下載,以便在項(xiàng)目中直接添加使用蕾哟。在這里就不詳細(xì)地進(jìn)行具體配置說(shuō)明了一忱。

4.3 系統(tǒng)的軟件實(shí)現(xiàn)

系統(tǒng)的硬件部分是基于一個(gè)720p羅技c310的USB攝像頭,進(jìn)行圖像采集工作谭确,所以重要的工作在軟件設(shè)計(jì)方面帘营。下面將進(jìn)行具體的軟件實(shí)現(xiàn)。

4.3.1 系統(tǒng)中的圖像類(lèi)型轉(zhuǎn)換

通過(guò)OpenCV中VideoCapture類(lèi)逐哈,可以進(jìn)行圖像的逐幀采集芬迄,采集到的圖像類(lèi)型為Mat類(lèi)型。SeetaFace中的圖像類(lèi)型為ImageData類(lèi)型昂秃,而且在后面的Qt平臺(tái)下會(huì)使用到的圖片類(lèi)型為QImage禀梳。以上這三種類(lèi)型在某些情況下需要進(jìn)行必要的轉(zhuǎn)換择诈,下面對(duì)具體的類(lèi)型轉(zhuǎn)換進(jìn)行說(shuō)明。

1)Mat類(lèi)型轉(zhuǎn)ImageData

首先將Mat類(lèi)型的圖片轉(zhuǎn)為單通道的灰度圖(如果已是灰度圖就不用轉(zhuǎn)了)出皇,具體轉(zhuǎn)化代碼如下:

cv::Mat img;

cv::cvtColor(frame, img_gray, cv::COLOR_BGR2GRAY);

seeta::ImageData image;

image.data = img.data;

image.width = img.cols;

image.height = img.rows;

image.num_channels = 1;

2)Mat類(lèi)型轉(zhuǎn)QImage

其中需要將Mat類(lèi)型的BGR通道順序變換為QImage的RGB順序羞芍,可以調(diào)用OpenCV中的cvtColor函數(shù)實(shí)現(xiàn),以上是對(duì)兩種圖像類(lèi)型的data部分的格式進(jìn)行調(diào)整郊艘,下一步只需要明確Mat的頭結(jié)構(gòu)里的變量與QImage的頭結(jié)構(gòu)里的變量的對(duì)應(yīng)關(guān)系即可實(shí)現(xiàn)轉(zhuǎn)換荷科,具體轉(zhuǎn)換代碼如下:?

Mat frame,temp;

??????cvtColor(frame, temp, CV_BGR2RGB);

?QImage image = QImage((const unsigned char *)(temp.data), temp.cols,

temp.rows, QImage::Format_RGB888);

3)QImage類(lèi)型轉(zhuǎn)Mat

與2)的類(lèi)似,將QImage的RGB通道順序變換為Mat類(lèi)型的BGR順序纱注,然后將QImage的頭結(jié)構(gòu)里的變量與Mat的頭結(jié)構(gòu)里的變量的對(duì)應(yīng)關(guān)系即可實(shí)現(xiàn)轉(zhuǎn)換畏浆,具體轉(zhuǎn)換代碼如下:

Mat frame;

cv::Mat Temp = cv::Mat(image.height(), image.width(), CV_8UC3,(void*)image.constBits(), image.bytesPerLine());

cvtColor(Temp,frame,CV_RGBA2BGR);

4.3.2 線程設(shè)計(jì)

把系統(tǒng)分為兩個(gè)線程,即主線程與子線程狞贱。主線程主要負(fù)責(zé)UI及一些一般的處理刻获,子線程負(fù)責(zé)人臉特征提取與比對(duì)模塊SeetaFace Identification部分的數(shù)據(jù)處理,因?yàn)檫@塊比較耗資源瞎嬉,如果都放在子線程的話蝎毡,會(huì)是UI卡頓。

1)主線程

主線程部分氧枣,主要進(jìn)行攝像頭的采集圖像并顯示在UI上沐兵,以及人臉對(duì)齊的顯示,然后就是一些其他的小功能便监,例如扎谎,拍照/截圖功能。

下面就主線程的主要功能進(jìn)行分析與說(shuō)明烧董。

因?yàn)槭菍?duì)攝像頭采集的圖像進(jìn)行基本類(lèi)似于實(shí)時(shí)的處理毁靶,因此在系統(tǒng)中設(shè)置定時(shí)器,進(jìn)行定時(shí)處理逊移,因?yàn)橹骶€程中有UI還要工作的原因预吆,故設(shè)置為死循環(huán)的方式不可取。在Qt中的定時(shí)器設(shè)置定時(shí)時(shí)間長(zhǎng)度設(shè)定為35ms螟左,理論上大概每秒能獲取28幀圖像啡浊,基本可以滿足實(shí)時(shí)性的要求。

在獲取攝像頭圖像的基礎(chǔ)上胶背,進(jìn)行5個(gè)面部關(guān)鍵特征點(diǎn)(兩眼中心,鼻尖和兩個(gè)嘴角)的定位并顯示在UI上喘先。主體流程圖如圖11所示钳吟。

圖11 主線程主體流程圖

2)子線程

????因?yàn)橄到y(tǒng)要基于Qt實(shí)現(xiàn)UI,其他線程中不能操作UI對(duì)象窘拯,只有主線程能對(duì)UI進(jìn)行操作红且,所以將人臉特征提取與比對(duì)模塊SeetaFace Identification放在子線程坝茎。將SeetaFace Identification部分的處理函數(shù)設(shè)置為可控的死循環(huán),設(shè)置bool變量來(lái)對(duì)死循環(huán)進(jìn)行控制暇番。然而嗤放,UI要將處理后的信息顯示出來(lái),因此線程之間要進(jìn)行通信壁酬。

3)線程通信

??????????線程的通信部分可以使用Qt的“信號(hào)與槽”功能次酌,使用連接函數(shù)將信號(hào)與槽函數(shù)連接,將線程中需要傳遞的數(shù)據(jù)放在信號(hào)中舆乔,一旦信號(hào)發(fā)射岳服,就執(zhí)行相應(yīng)的槽函數(shù),來(lái)完成線程之間的通信任務(wù)希俩。流程圖如圖12所示吊宋。

圖12 線程通信流程圖

????由圖5所知,要在系統(tǒng)中輸入一張需要進(jìn)行比對(duì)的圖片颜武,在UI中通過(guò)按鈕獲取該圖片的路徑信息璃搜,然后通過(guò)信號(hào)發(fā)射這個(gè)路徑,然后子線程那邊接收到信號(hào)后鳞上,將這個(gè)圖片路徑獲取下來(lái)以便進(jìn)行接下來(lái)的人臉特征提取與比對(duì)腺劣。當(dāng)主線程觸發(fā)人臉識(shí)別的信號(hào)以后,子線程接收后將攝像頭獲取的圖像與輸入圖像進(jìn)行人臉特征提取與比對(duì)因块。最后將人臉比對(duì)的相似度及被認(rèn)定為同一個(gè)人的裁剪人臉通過(guò)信號(hào)發(fā)送給主線程橘原,主線程在UI上進(jìn)行數(shù)據(jù)顯示。

4.4 基于Qt的UI設(shè)計(jì)

Qt是一個(gè)跨平臺(tái)的C++圖形用戶界面應(yīng)用程序框架涡上。它為應(yīng)用程序開(kāi)發(fā)者提供建立藝術(shù)級(jí)圖形界面所需的所有功能趾断。它是完全面向?qū)ο蟮模苋菀讛U(kuò)展吩愧,并且允許真正的組件編程芋酌。

Qt提供了一種稱(chēng)為信號(hào)與槽的方式,使得各個(gè)元件之間的協(xié)同工作變得十分簡(jiǎn)單雁佳。Qt擁有自己的集成開(kāi)發(fā)環(huán)境(IDE)名為Qt Creator脐帝,但為了使各個(gè)部分的銜接方便,在VS2017上安裝一個(gè)叫Qt Visual Studio Tools的Qt插件糖权。這樣就可以進(jìn)行VS加Qt的混合編程堵腹。

1)UI設(shè)計(jì)圖

圖13 UI的設(shè)計(jì)圖

2)信號(hào)與槽

UI部分主要使用的是按鈕來(lái)進(jìn)行操作,按鈕的點(diǎn)擊信號(hào)與對(duì)應(yīng)的槽函數(shù)如圖14所示星澳。具體的槽函數(shù)請(qǐng)參見(jiàn)附錄中的代碼疚顷。

圖14 信號(hào)與槽

4.5 實(shí)驗(yàn)結(jié)果與分析

在完成上述的各項(xiàng)準(zhǔn)備工作之后,下面就進(jìn)行最后的整體調(diào)試工作。運(yùn)行已經(jīng)寫(xiě)好的程序腿堤,然后生成應(yīng)用程序界面阀坏,界面樣式如圖15所示。采用了一些邏輯方法笆檀,去除了窗體的邊框忌堂,然后再右上角設(shè)置了一個(gè)關(guān)閉按鈕,這個(gè)關(guān)閉按鈕具有關(guān)閉線程酗洒,退出窗口等其他功能士修。

圖15 應(yīng)用程序界面

????接下來(lái),通過(guò)具體的實(shí)驗(yàn)來(lái)進(jìn)行演示操作寝蹈,具體操作為:打開(kāi)攝像頭->人臉檢測(cè)與對(duì)齊->輸入圖片->人臉識(shí)別李命。可以發(fā)現(xiàn)SeetaFace具有很好的識(shí)別能力箫老,在輸入同一個(gè)人的照片后封字,進(jìn)行人臉識(shí)別,可以達(dá)到0.9以上相似度(圖16中的相似度為0.911083)耍鬓,然后顯示發(fā)現(xiàn)目標(biāo)并發(fā)出警報(bào)阔籽,可見(jiàn)效果還是很不錯(cuò)的,如圖16所示牲蜀。

圖17 不同人的效果演示

????在實(shí)驗(yàn)過(guò)程中笆制,發(fā)現(xiàn)一個(gè)問(wèn)題,當(dāng)輸入一張同一個(gè)人之前的照片涣达,識(shí)別的相似度會(huì)有所下降在辆,會(huì)比輸入現(xiàn)拍的照片相似度低,但也基本大于0.7(圖18中的相似度為0.76884)度苔,即被判別為同一個(gè)人匆篓,如圖18所示】芤ぃ可能這是SeetaFace算法存在的小缺陷吧鸦概,證明算法還有優(yōu)化的空間。

圖18 輸入非現(xiàn)拍圖片的效果演示

????基于同樣的原理甩骏,該系統(tǒng)還具備視頻人臉識(shí)別功能窗市。打開(kāi)一段視頻,輸入一張圖片饮笛,即可實(shí)現(xiàn)在視頻中查找到與輸入圖片為同一個(gè)人的人臉咨察,然后將找到后人臉保存在應(yīng)用程序的文件夾中,實(shí)驗(yàn)效果如圖19所示(圖19中的相似度為0.7762)缎浇。

圖19 視頻人臉識(shí)別

5 總結(jié)

????本文主要利用人臉識(shí)別開(kāi)源項(xiàng)目SeetaFace扎拣,使用OpenCV作為圖像處理工具,結(jié)合集成開(kāi)發(fā)環(huán)境VS2017與C++圖形用戶界面應(yīng)用程序框架Qt實(shí)現(xiàn)一個(gè)人臉識(shí)別的小項(xiàng)目素跺。主要工作是首先進(jìn)行系統(tǒng)各項(xiàng)環(huán)境與功能的搭建與實(shí)施二蓝,在基于開(kāi)源庫(kù)與項(xiàng)目的基礎(chǔ)進(jìn)行了應(yīng)用性的實(shí)驗(yàn)與結(jié)果分析。

????本文首先進(jìn)行了基本知識(shí)與技術(shù)的介紹指厌,進(jìn)行了相關(guān)理論的了解與學(xué)習(xí)刊愚,為接下來(lái)的系統(tǒng)實(shí)施打下了基礎(chǔ)。OpenCV是圖像處理常用的工具踩验,在在學(xué)習(xí)與科研過(guò)程中具有很好的幫助性鸥诽。SeetaFace作為為數(shù)不多的開(kāi)源人臉識(shí)別項(xiàng)目,很值得學(xué)習(xí)與研究箕憾,是一個(gè)很不錯(cuò)的開(kāi)源項(xiàng)目牡借。

????接下來(lái)本文進(jìn)行了系統(tǒng)環(huán)境的搭建與實(shí)驗(yàn),然后根據(jù)實(shí)驗(yàn)進(jìn)行了結(jié)果分析袭异。在系統(tǒng)搭建環(huán)節(jié)钠龙,首先確定了系統(tǒng)的總體設(shè)計(jì)思路,然后進(jìn)行了系統(tǒng)環(huán)境配置御铃。首先配置的是OpenCV碴里,其次進(jìn)行SeetaFace的配置。在系統(tǒng)軟件設(shè)計(jì)中先進(jìn)行了系統(tǒng)中圖像類(lèi)型轉(zhuǎn)換的介紹上真,然后進(jìn)行了多線程的設(shè)計(jì)咬腋。

最后實(shí)施了具體的實(shí)驗(yàn),根據(jù)實(shí)驗(yàn)對(duì)系統(tǒng)進(jìn)行了分析與探討睡互。從實(shí)驗(yàn)結(jié)果發(fā)現(xiàn)該系統(tǒng)具有良好的人臉識(shí)別特性根竿,但也有一些需要優(yōu)化的空間。本文雖然基本整體實(shí)現(xiàn)了系統(tǒng)設(shè)計(jì)的功能就珠,但在許多方面還存在著瑕疵寇壳,還有很多值得改進(jìn)的地方。其實(shí)可以將該系統(tǒng)進(jìn)行嵌入式等相關(guān)平臺(tái)的移植嗓违,進(jìn)一步增加該系統(tǒng)的應(yīng)用價(jià)值九巡。

參考文獻(xiàn)

[1]朱興統(tǒng),習(xí)洋洋.基于C++和OpenCV的人臉識(shí)別系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[J].自動(dòng)化與儀器儀表,2014(08):127-128+131.

[2]劉長(zhǎng)亮. 基于眼睛與頭部狀態(tài)的疲勞檢測(cè)系統(tǒng)設(shè)計(jì)[D].大連海事大學(xué),2018.

[3]孫志. 基于OpenCV的人臉識(shí)別算法實(shí)驗(yàn)平臺(tái)研究與實(shí)現(xiàn)[D].吉林大學(xué),2014.

[4] 陶穎軍.基于OpenCV的人臉識(shí)別應(yīng)用[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2012,21(03):220-223.

[5] 毛星宇,冷雪飛等.OpenCV3編程入門(mén)[M].北京電子工業(yè)出版社蹂季,2015.

[6] VIPL_Face.SeetaFace開(kāi)源人臉識(shí)別引擎介紹.?微信公眾號(hào)-深度學(xué)習(xí)大講堂?2016-09-14


附錄

代碼

主線程部分:

頭文件(OpenVideo.h)

```?

/**

*Copyright (c) 2018 Young Fan.All Right Reserved.

*Filename:

```

*Author: Young Fan

*Date: 2018.5.29 - 7.5

*OpenCV version: 3.4.1

*IDE: Visual Studio 2017

*Description: Demo:Qt + Opecv + VS + SeetaFace(via VIPL)

*/


#pragma once


#include

#include "ui_OpenVideo.h"

#include

#include "MyThread.h" //自定義線程模板(類(lèi))

#include ?//線程頭文件


//SeetaFace

#include

#include

#include

#include

#include "cv.h"

#include "highgui.h"

#include "face_detection.h"

#include "face_alignment.h"


#include

#include

#include "face_identification.h"

#include "recognizer.h"

#include "face_detection.h"

#include "face_alignment.h"


#include "math_functions.h"


#include

#include

#include

#include



class OpenVideo : public QWidget

{

Q_OBJECT


public:

OpenVideo(QWidget *parent = Q_NULLPTR);


protected:

//重寫(xiě)繪圖事件

void paintEvent(QPaintEvent *event);

//重寫(xiě)鼠標(biāo)按下事件

void mousePressEvent(QMouseEvent *event);

//重寫(xiě)鼠標(biāo)移動(dòng)事件

void mouseMoveEvent(QMouseEvent *event);



public slots: //槽函數(shù)

void OpenVideoFile(); //打開(kāi)視頻文件

void OpenCamera(); //打開(kāi)攝像頭

void CloseCamera(); //關(guān)閉攝像頭

void ScreenShot(); //拍照/截圖

void REC(); ?//錄像

void EndREC(); //結(jié)束錄像

void FaceDetectionAlignment(); //人臉檢測(cè)與對(duì)齊

void InputImage(); //輸入圖片

void FaceRecognitionProcess(); //人臉識(shí)別


void nextFrame(); //獲取下一幀圖片

void nextSeetaFaceProcessFrame(); //獲取下一幀處理的圖片

void currentDateAndTime(); ?//動(dòng)態(tài)顯示當(dāng)前日期和時(shí)間

void CloseWindow(); //關(guān)閉窗口


//獲取子線程信號(hào)發(fā)來(lái)的信息

//獲取人臉檢測(cè)與對(duì)齊信息

//void getDetectionAndAlignmentInformation(int faceNum, QImage image);

//獲取人臉識(shí)別信息

void getRecognitionInformation(int gallery_face_num, int probe_face_num,QImage image,float sim);

void NoFace();


signals:


void mainSignal(QString str);

void VideoSignal(QImage image,bool flag);


private:

Ui::OpenVideoClass ui;


cv::Mat frame; //定義一個(gè)Mat變量冕广,用于存儲(chǔ)沒(méi)一幀的圖像

cv::Mat temp; //臨時(shí)變量

cv::VideoCapture capture; //定義VideoCapture對(duì)象,獲取攝像頭

cv::VideoWriter writer; ?//定義VideoWriter對(duì)象偿洁,錄制

QTimer *timer; ?//定時(shí)器

QTimer *timer2; ?//人臉識(shí)別定時(shí)器

QString ImagePath; //定義輸入圖片的路徑


int x = 0;

int y = 0;


bool openREC;

QPoint p; //定義的點(diǎn)


MyThread *myT; //自定義線程模塊(類(lèi))對(duì)象

QThread *thread; //子線程


//人臉檢測(cè)與對(duì)齊

int faceNum;

QImage imageDetectionAndAlignment;


//人臉識(shí)別

int GalleryFaceNum;

int ProbeFaceNum;

QImage CropFace;

float Sim;

bool CameraVideoFlag; //標(biāo)志位,true代表Camera撒汉,false代表Video

};

```


cpp文件(OpenVideo.cpp)

```

#include "OpenVideo.h"

#include

#include

#include

#include

#include

#include

#include

#include

#include

//使用Qt多媒體,需要在附加依賴(lài)項(xiàng)里加Qt5Multimedia.lib(debug)/Qt5Multimediad.lib(release)

#include




OpenVideo::OpenVideo(QWidget *parent)

: QWidget(parent)

{

ui.setupUi(this);


//設(shè)置窗口標(biāo)題

setWindowTitle("OpenVideo");


//讓圖片自動(dòng)適應(yīng)label大小

ui.labelOpenVideo->setScaledContents(true);

ui.labelGif->setScaledContents(true);


//-----【顯示Gif動(dòng)畫(huà)】-------

//創(chuàng)建動(dòng)畫(huà)

QMovie *myMovie = new QMovie("./UiFile/2.gif");

//設(shè)置動(dòng)畫(huà)

ui.labelGif->setMovie(myMovie);

//啟動(dòng)動(dòng)畫(huà)

myMovie->start();


//固定主窗口大小

setFixedSize(1125, 511);


//創(chuàng)建定時(shí)器

timer = new QTimer(this);


//-----------【不規(guī)則窗口】-------------

//給窗口去掉邊框涕滋,帶上窗口的flags(標(biāo)記)

setWindowFlags(Qt::FramelessWindowHint | windowFlags());

//把窗口背景設(shè)置為透明,這里不設(shè)置透明也可以睬辐,因?yàn)閳D片不透明

setAttribute(Qt::WA_TranslucentBackground);


//更改按鈕樣式表

ui.CloseButton->setFlat(true); //把按鈕設(shè)置為透明背景,此時(shí)只有按下的時(shí)候才有背景。

ui.CloseButton->setStyleSheet("QPushButton{"

//"background:rgb(176,0,0);"http://按鈕設(shè)置為透明背景溯饵,這里設(shè)置了設(shè)置了也不顯示

//但如果加上"border:2px outset green;"則可以在初始態(tài)侵俗,鼠標(biāo)懸停態(tài)顯示背景色,下面一樣

"}"

"QPushButton:hover{"

"border:1px;"http://當(dāng)按鈕背景是透明的丰刊,則必須加邊框?qū)挾劝ィ拍苡衕over效果,這樣寫(xiě)也行"border:1px outset red;"

"background:rgb(255,96,96);"http://按鈕設(shè)置為透明背景啄巧,這里設(shè)置了設(shè)置了也不顯示

//"border-image:url(E:/C++/Demo/OpenVideo/UiFile/closeWhite.png);"

"}"

"QPushButton:pressed{"

"background:rgb(255,77,77);"http://此時(shí)只有按下的時(shí)候才有背景寻歧。

//"border-image:url(E:/C++/Demo/OpenVideo/UiFile/closeWhite.png);"

"}");


//-----------------------------------------【多線程部分】--------------------------------------------------

//自定義模塊(類(lèi))對(duì)象,分配空間秩仆,不可以指定父對(duì)象码泛,因?yàn)橄旅嬉频阶泳€程

myT = new MyThread;

//創(chuàng)建子線程,指定父對(duì)象

thread = new QThread(this);

//把自定義模塊添加到子線程

myT->moveToThread(thread);

//啟動(dòng)子線程,但是并沒(méi)有啟動(dòng)線程的處理函數(shù),要用信號(hào)和槽啟動(dòng)線程的處理函數(shù)

thread->start();


//線程處理函數(shù)澄耍,必須通過(guò)signal-slot調(diào)用

//這里使用的是按鈕的信號(hào)來(lái)觸發(fā)噪珊,也可以用按鈕的轉(zhuǎn)到槽函數(shù),在里面去發(fā)射信號(hào)

//逾苫,用信號(hào)去調(diào)用模塊類(lèi)中的處理函數(shù)

connect(ui.buttonFaceRecognition,&QPushButton::pressed,myT,&MyThread::FaceRecognitionProcess);//人臉識(shí)別部分

//一旦子模塊類(lèi)中的處理函數(shù)啟動(dòng)卿城,就會(huì)發(fā)射UpdateProcess信號(hào)(線程代碼里寫(xiě)的)

connect(myT,&MyThread::UpdateRecognition,this,&OpenVideo::getRecognitionInformation);//人臉識(shí)別部分

connect(myT, &MyThread::NoFace, this, &OpenVideo::NoFace);


//主線程給子線程通信

connect(this, &OpenVideo::mainSignal, myT, &MyThread::getMainSignal);

connect(this,&OpenVideo::VideoSignal,myT,&MyThread::getVideoSignal);


}

//--------------------------------------------【重寫(xiě)鼠標(biāo)按下事件】--------------------------------------------

void OpenVideo::mousePressEvent(QMouseEvent *event)

{

if (event->button() == Qt::LeftButton) //event->button()返回的是Buttons事件

{

//求坐標(biāo)差值,窗口上鼠標(biāo)點(diǎn)擊當(dāng)前點(diǎn)的坐標(biāo)是相對(duì)于屏幕的

//求差:當(dāng)前點(diǎn)擊坐標(biāo) - 窗口左上角坐標(biāo)

//相對(duì)于窗口的坐標(biāo)用x铅搓,y瑟押,相對(duì)于屏幕用globalPos

//frameGeometry,是帶邊框的,用它可以獲取窗口左上角坐標(biāo)(相對(duì)于屏幕的坐標(biāo))

//移動(dòng)窗口時(shí)是以左上角來(lái)移動(dòng)的星掰,要想點(diǎn)哪就從哪移動(dòng)多望,則要求坐標(biāo)差

p = event->globalPos() - this->frameGeometry().topLeft();

}

}


void OpenVideo::mouseMoveEvent(QMouseEvent *event)

{

//這里因?yàn)槭前醋。适褂胋uttons(帶‘s’的button)

//左鍵按住移動(dòng)窗口

if (event->buttons() & Qt::LeftButton) //event->buttons()返回的是int位字段

{

//移動(dòng)窗口時(shí)是以左上角來(lái)移動(dòng)的氢烘,故要減去p(此時(shí)就相當(dāng)于從該點(diǎn)開(kāi)始移動(dòng))

this->move(event->globalPos() - p);

}

}


//----------------------------------------------【畫(huà)背景圖】--------------------------------------------------

void OpenVideo::paintEvent(QPaintEvent *event)

{

QPainter p(this);

p.drawPixmap(0, 0, width(), height(), QPixmap("./UiFile/background.png"));

}


//-----------------------------------------------【顯示當(dāng)前日期和時(shí)間】------------------------------------

void OpenVideo::currentDateAndTime()

{

//還有其他的顯示時(shí)間日期的方法怀偷,如C語(yǔ)言的宏__DATE__,__TIME__、再比如QTime里也有能顯示時(shí)間與日期

//用arg()也能顯示播玖,但都需要定時(shí)器

ui.labelDateTime->setText(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz A"));

}

//----------------------------------------------【關(guān)閉窗口】--------------------------------------------------

void OpenVideo::CloseWindow()

{

//寫(xiě)程序要養(yǎng)成一種習(xí)慣椎工,要做判斷

if (false == thread->isRunning()) //如果線程已停止,則按鈕不在往下執(zhí)行

{

return;

}

myT->setFlag(true); //跳出循環(huán)

thread->quit(); //溫柔地退出

thread->wait(); //等待蜀踏,回收資源

//但如果模塊(類(lèi))還在工作的話(如死循環(huán)while(1)维蒙,即不加這句myT->setFlag(true);),

//則線程并不會(huì)停止還是會(huì)在運(yùn)行


this->close(); //關(guān)閉窗口

delete myT; //釋放資源

}


//---------------------------------------------【打開(kāi)視頻文件】------------------------------------------------

void OpenVideo::OpenVideoFile()

{

//初始化標(biāo)志位

CameraVideoFlag = false;

myT->setFlag(false); //子線程處理函數(shù)循環(huán)


//顯示標(biāo)簽

ui.labelOpenVideo->setVisible(true);

ui.label->setVisible(true);


//創(chuàng)建文件對(duì)話框并獲取路徑

QString path = QFileDialog::getOpenFileName(this,

"open", "../", "Video(*.mp4 *.flv *.mkv *.avi)");


//讀入視頻

capture.open(path.toLocal8Bit().data()); //也可以path.toStdString() ??


//判斷定時(shí)器的激活狀態(tài)

if (false == timer->isActive())

{

//啟動(dòng)定時(shí)器

timer->start(35);

}


//定時(shí)器連接,獲取下一幀圖片

connect(timer, &QTimer::timeout, this, &OpenVideo::nextFrame);

connect(timer, &QTimer::timeout,

[=]()

{

ui.label->setText(QStringLiteral("視頻播放中..."));

ui.label->move(QPoint(x++, y++));

if (x >511)

{

x = 0;

y = 0;

}

}

);

}


//----------------------------------------------【打開(kāi)攝像頭】---------------------------------------------

void OpenVideo::OpenCamera()

{

//初始化標(biāo)志位

CameraVideoFlag = true;


//顯示標(biāo)簽

ui.labelOpenVideo->setVisible(true);


//讀入攝像頭

capture.open(0);


timer->start(35); //定時(shí)35ms


//定時(shí)器連接,獲取下一幀圖片

connect(timer, &QTimer::timeout, this, &OpenVideo::nextFrame);

}


//-----------------------------------【獲取并顯示下一幀圖片】---------------------------------------

void OpenVideo::nextFrame()

{

capture >> frame; //讀取當(dāng)前幀

//圖像在Qt顯示前,必須將Mat型轉(zhuǎn)化成QImage格式,將OpenCV中Mat的BGR格式轉(zhuǎn)化成QImage的RGB格式

cvtColor(frame, temp, CV_BGR2RGB);

//轉(zhuǎn)化成QImage格式

QImage image = QImage((const unsigned char *)(temp.data), temp.cols,

temp.rows, QImage::Format_RGB888);

ui.labelOpenVideo->setPixmap(QPixmap::fromImage(image));


if (!CameraVideoFlag)

{

emit VideoSignal(image, CameraVideoFlag);

}


}


//--------------------------------------【拍照/截圖】------------------------------------------------

void OpenVideo::ScreenShot()

{

//將Mat型temp轉(zhuǎn)為Qt的QImage型

QImage image = QImage((const unsigned char *)(temp.data), temp.cols,

temp.rows, QImage::Format::Format_RGB888);

ui.labelScreenShot->setPixmap(QPixmap::fromImage(image));

//讓圖片自動(dòng)適應(yīng)label大小

ui.labelScreenShot->setScaledContents(true);


//截圖保存

const QPixmap *img = ui.labelScreenShot->pixmap();

QString path = QFileDialog::getSaveFileName(this, "save", "../", "Image(*.png)");

if (false == path.isEmpty()) //判斷路徑是否有效

{

bool isOk = img->save(path);

if (true == isOk)

{


//#pragma execution_character_set("utf-8")

//設(shè)置為UTF-8格式顷锰,顯示中文,不然會(huì)亂碼,但是影響了vs的編碼格式斑响,不能摧毀中文名稱(chēng)窗口

//故選擇用QStringLiteral("中文")進(jìn)行修飾菱属。這樣雖不能全局設(shè)置utf-8,但可以正常摧毀中文名稱(chēng)窗口

QMessageBox::information(this, QStringLiteral("保存完成"), QStringLiteral("截圖保存完成"));

}

}

else

{

//保存文件路徑出錯(cuò)

return;

}

}


//---------------------------------------【錄制視頻】-------------------------------------------

void OpenVideo::REC()

{

double rate = 12.0;//保存視頻的幀率舰罚,12這個(gè)幀率很合適纽门,播放的時(shí)候不慢不快

cv::Size videoSize(frame.cols, frame.rows);

//錄制 VideoWriter writer; 已在頭文件中定義

writer.open("../REC.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), rate, videoSize, true);

openREC = true;

while (openREC)

{

capture >> frame;

writer.write(frame);//或writer << frame;


//加個(gè)窗口顯示。不加的話沸停,直接設(shè)置循環(huán)會(huì)卡住不動(dòng)

cv::namedWindow("錄制視頻");

imshow("錄制視頻", frame);

cv::waitKey(30);

}

}


//-------------------------------------------------【結(jié)束錄制】------------------------------------------

void OpenVideo::EndREC()

{

cv::destroyWindow("錄制視頻");

openREC = false;

writer.release();//結(jié)束錄制

}


//--------------------------------------------【人臉檢測(cè)與對(duì)齊】-----------------------------------------------

void OpenVideo::FaceDetectionAlignment()

{

//定時(shí)器連接,獲取下一幀圖片

connect(timer, &QTimer::timeout, this, &OpenVideo::nextSeetaFaceProcessFrame);

}


//-----------------------------------【獲取下一幀SeetaFace處理的圖片】----------------------------

void OpenVideo::nextSeetaFaceProcessFrame()

{


std::string MODEL_DIR = "./model/";


//-----------------------------------------【人臉檢測(cè)與對(duì)齊】--------------------------------------

////要檢測(cè)圖像上的人臉膜毁,首先應(yīng)該用模型文件的路徑實(shí)例化seeta::FaceDetection的對(duì)象昭卓。

seeta::FaceDetection detector("./model/seeta_fd_frontal_v1.0.bin");

detector.SetMinFaceSize(40);//設(shè)置要檢測(cè)的人臉的最小尺寸(默認(rèn)值:20愤钾,不受限制),也能設(shè)置最大:face_detector.SetMaxFaceSize(size);

detector.SetScoreThresh(2.f);////設(shè)置檢測(cè)到的人臉的得分閾值(默認(rèn)值:2.0)

detector.SetImagePyramidScaleFactor(0.8f);//設(shè)置圖像金字塔比例因子(0 <因子< 1,默認(rèn)值:0.8)

detector.SetWindowStep(4, 4);//設(shè)置滑動(dòng)窗口的步長(zhǎng)(默認(rèn):4),face_detector.SetWindowStep(step_x, step_y);


capture >> frame;//讀入攝像頭


int pts_num = 5;

cv::Mat img_gray;

if (frame.channels() != 1)

//非單通道的候醒,轉(zhuǎn)為灰度圖

cv::cvtColor(frame, img_gray, cv::COLOR_BGR2GRAY);

else

{

img_gray = frame;

}


seeta::ImageData image_data;

image_data.data = img_gray.data;

image_data.width = img_gray.cols;

image_data.height = img_gray.rows;

image_data.num_channels = 1;


//調(diào)用Detect()來(lái)檢測(cè)人臉能颁,它將作為seeta::FaceInfo的向量(容器)返回。

std::vector faces = detector.Detect(image_data);

int32_t face_num = static_cast(faces.size()); //獲取人臉數(shù)量


seeta::FacialLandmark points[5];//定義面部的5個(gè)標(biāo)記點(diǎn)


//首先應(yīng)該實(shí)例化seeta:: faceAlignment的對(duì)象與模型文件的路徑倒淫。

seeta::FaceAlignment point_detector((MODEL_DIR + "seeta_fa_v1.1.bin").c_str());


//檢測(cè)5點(diǎn)面部標(biāo)記

for (int f = 0; f < face_num; f++) {

point_detector.PointDetectLandmarks(image_data, faces[f], points);//檢測(cè)出人臉標(biāo)記

//將人臉用矩形框出(矩形的范圍由Detect()返回的faces給出)

cv::rectangle(frame, cv::Point(faces[f].bbox.x, faces[f].bbox.y),//bbox是Rect類(lèi)型的結(jié)構(gòu)體

cv::Point(faces[f].bbox.x + faces[f].bbox.width - 1, faces[f].bbox.y + faces[f].bbox.height - 1), CV_RGB(255, 0, 0));


// Visualize the results(形象化結(jié)果伙菊,即把檢測(cè)出的5點(diǎn)標(biāo)記(由PointDetectLandmarks函數(shù)內(nèi)部給出),用小圓環(huán)框出)

for (int i = 0; i < pts_num; i++)

{

cv::circle(frame, cv::Point(points[i].x, points[i].y), 2, CV_RGB(0, 255, 0), -CV_FILLED);

//cv::ellipse(frame, cv::Point(points[i].x, points[i].y),cv::Size(6,2),-45,0,360,cv::Scalar(255,129,0),1,8);

}

}


//圖像在Qt顯示前敌土,必須轉(zhuǎn)化成QImage格式镜硕,將RGBA格式轉(zhuǎn)化成RGB

cvtColor(frame, temp, CV_BGR2RGB);

//轉(zhuǎn)化成QImage格式

QImage image = QImage((const unsigned char *)(temp.data), temp.cols,

temp.rows, QImage::Format_RGB888);


ui.labelFaceNum->setText(QStringLiteral("檢測(cè)到的人臉個(gè)數(shù):%1").arg(face_num));

ui.labelOpenVideo->setPixmap(QPixmap::fromImage(image));


}



//----------------------------------------------【關(guān)閉攝像頭】-------------------------------------------

void OpenVideo::CloseCamera()

{

capture.release();

timer->stop();


//關(guān)閉攝像頭后,對(duì)標(biāo)簽狀態(tài)進(jìn)行控制返干,下面有好幾種方法

//ui.labelOpenVideo->hide(); ?//控件隱藏,Widget類(lèi)的方法

//ui.labelOpenVideo->setVisible(false);//控件是否可見(jiàn),Widget類(lèi)的方法

ui.labelOpenVideo->close();//控件關(guān)閉,Widget類(lèi)的方法兴枯,可與與show()搭配使用;


//關(guān)閉錄制(按鈕復(fù)用)

cv::destroyWindow("錄制視頻");

openREC = false;


//去除“視頻播放中”字樣

ui.label->close();


//先停止子線程

myT->setFlag(true); //跳出循環(huán)

}


//------------------------------------------------【輸入圖片】-------------------------------------------

void OpenVideo::InputImage()

{

ImagePath = QFileDialog::getOpenFileName(this,

QStringLiteral("打開(kāi)圖片"), "../", "Image(*.png *.jpg *.bmp)");


emit mainSignal(ImagePath); //發(fā)出信號(hào)給子線程


ui.InputImageLabel->setPixmap(QPixmap(ImagePath)); //其實(shí)直接放QString類(lèi)型的也可以(即直接放ImagePath)

//讓圖片自動(dòng)適應(yīng)label大小

ui.InputImageLabel->setScaledContents(true);


//動(dòng)態(tài)顯示當(dāng)前日期和時(shí)間(定時(shí)器已在打開(kāi)攝像頭時(shí)激活)

connect(timer, &QTimer::timeout, this, &OpenVideo::currentDateAndTime);

}


//------------------------------------【獲取子線程信號(hào)發(fā)來(lái)的信息】------------------------------------

//獲取人臉識(shí)別模塊的信息

void OpenVideo::getRecognitionInformation(int gallery_face_num, int probe_face_num,QImage image,float sim)

{

GalleryFaceNum = gallery_face_num;

ProbeFaceNum = probe_face_num;

CropFace = image;

Sim = sim;

//更新操作

FaceRecognitionProcess();

}


//-------------------------------------------------【人臉識(shí)別】--------------------------------------------

//描述: 從視頻中找出與輸入圖片對(duì)應(yīng)的人臉

void OpenVideo::FaceRecognitionProcess()

{


if (GalleryFaceNum != 0 && ProbeFaceNum != 0)

{

//顯示相似度

ui.SimilarityLabel->setText(QStringLiteral("相似度為:%1").arg(Sim)); ?//組包,用arg來(lái)顯示float型


if (Sim > 0.7)

{

ui.SimilarityLabel->setText(QStringLiteral("發(fā)現(xiàn)目標(biāo) | 相似度為:%1").arg(Sim));

//printf("\a"); //轉(zhuǎn)義響鈴符矩欠,在Qt中無(wú)效财剖,改用下面的Qt的方式


//播放音頻

QMediaPlayer *player = new QMediaPlayer;

//QUrl::fromLocalFile該函數(shù)接受由斜線分隔(/)的路徑以及該平臺(tái)的本機(jī)分隔符。

//此函數(shù)還接受具有雙前導(dǎo)斜杠(反斜杠)(\\)的路徑來(lái)指示遠(yuǎn)程文件

player->setMedia(QUrl::fromLocalFile("./mp3/Recognition.mp3"));

player->setVolume(80);

player->play();


//----------------------------------【將裁剪的臉部顯示在界面標(biāo)簽上】------------------------------------------

ui.OutputImageLabel->setPixmap(QPixmap::fromImage(CropFace));

ui.OutputImageLabel->setScaledContents(true);

}

}


}


void OpenVideo::NoFace()

{

ui.SimilarityLabel->setText(QStringLiteral("sorry癌淮!檢測(cè)不到人臉"));

}


主函數(shù)cpp文件(main.cpp)


#include "OpenVideo.h"

#include


int main(int argc, char *argv[])

{

QApplication a(argc, argv);

OpenVideo w;

w.show();

return a.exec();

}

```

子線程部分

頭文件(MyThread.h)

```

#ifndef MYTHREAD_H ?//防止頭文件被重復(fù)包含躺坟,這是C語(yǔ)言方式,C++是:#pragma once

#define MYTHREAD_H


//SeetaFace

#include

#include

#include

#include

#include "cv.h"

#include "highgui.h"

#include "face_detection.h"

#include "face_alignment.h"


#include

#include

#include "face_identification.h"

#include "recognizer.h"

#include "face_detection.h"

#include "face_alignment.h"


#include "math_functions.h"


#include

#include

#include

#include


#include

#include

#include //定時(shí)器

#include //QImage頭文件


class MyThread : public QObject

{

Q_OBJECT

public:

explicit MyThread(QObject *parent = nullptr);

//線程處理函數(shù)

void FaceRecognitionProcess(); //人臉識(shí)別


//獲取主線程信號(hào)發(fā)來(lái)的數(shù)據(jù)

void getMainSignal(QString str);

void getVideoSignal(QImage image,bool flag);


//標(biāo)志位函數(shù)

void setFlag(bool flag = true); //括號(hào)里定義了一個(gè)默認(rèn)參數(shù)乳蓄,不傳參的時(shí)候就使用默認(rèn)的


signals:

void UpdateRecognition(int GalleryFaceNum,int ProbeFaceNum,QImage image,float sim);

void NoFace();

private:

cv::Mat frame; //定義一個(gè)Mat變量咪橙,用于存儲(chǔ)每一幀的圖像

cv::Mat temp; //臨時(shí)變量

cv::VideoCapture capture; //定義VideoCapture對(duì)象,獲取攝像頭


QImage image;


//標(biāo)志位

bool isStop;

bool CameraVideoFlag;//標(biāo)志位,true代表Camera虚倒,false代表Video


QString path;

};


#endif // MYTHREAD_H

```

cpp文件(MyThread.cpp)

?```

#include "MyThread.h"


MyThread::MyThread(QObject *parent) : QObject(parent)

{

//設(shè)置初始標(biāo)志位

isStop = false;

CameraVideoFlag = true;

}


void MyThread::setFlag(bool flag)

{

isStop = flag;

}


//----------------------------------------------------【人臉識(shí)別】------------------------------------------------

void MyThread::FaceRecognitionProcess()

{

using namespace seeta;


std::string MODEL_DIR = "./model/";


//用模型文件的路徑實(shí)例化seeta::FaceDetection的對(duì)象

seeta::FaceDetection detector("./model/seeta_fd_frontal_v1.0.bin");

detector.SetMinFaceSize(40);//設(shè)置要檢測(cè)的人臉的最小尺寸(默認(rèn)值:20美侦,不受限制),也能設(shè)置最大:face_detector.SetMaxFaceSize(size);

detector.SetScoreThresh(2.f); //設(shè)置檢測(cè)到的人臉的得分閾值(默認(rèn)值:2.0)

detector.SetImagePyramidScaleFactor(0.8f);//設(shè)置圖像金字塔比例因子(0 <因子< 1,默認(rèn)值:0.8)

detector.SetWindowStep(4, 4);//設(shè)置滑動(dòng)窗口的步長(zhǎng)(默認(rèn):4),face_detector.SetWindowStep(step_x, step_y);


//初始化的人臉對(duì)齊模型

seeta::FaceAlignment point_detector("./model/seeta_fa_v1.1.bin");


//初始化的人臉識(shí)別模型

FaceIdentification face_recognizer((MODEL_DIR + "seeta_fr_v1.0.bin").c_str());


//輸入一張圖片

cv::Mat gallery_img_color = cv::imread(path.toStdString(), 1);

cv::Mat gallery_img_gray;

cv::cvtColor(gallery_img_color, gallery_img_gray, CV_BGR2GRAY);


cv::Mat probe_img_color;


if (CameraVideoFlag)

{

capture.open(0);

}


while (!isStop)

{

//獲取攝像頭/視頻的每一幀畫(huà)面

if (CameraVideoFlag)

{

capture >> probe_img_color;

}

else

{

//接收QImage的格式是RGB裹刮,要轉(zhuǎn)成OpenCV中Mat的BGR格式

probe_img_color = frame;

}

cv::Mat probe_img_gray;

cv::cvtColor(probe_img_color, probe_img_gray, CV_BGR2GRAY);


ImageData gallery_img_data_color(gallery_img_color.cols, gallery_img_color.rows, gallery_img_color.channels());

gallery_img_data_color.data = gallery_img_color.data;//data ?uchar型的指針音榜。Mat類(lèi)分為了兩個(gè)部分:矩陣頭和指向矩陣數(shù)據(jù)部分(所有矩陣值)的指針,data就是指向矩陣數(shù)據(jù)的指針捧弃。


ImageData gallery_img_data_gray(gallery_img_gray.cols, gallery_img_gray.rows, gallery_img_gray.channels());

gallery_img_data_gray.data = gallery_img_gray.data;


ImageData probe_img_data_color(probe_img_color.cols, probe_img_color.rows, probe_img_color.channels());

probe_img_data_color.data = probe_img_color.data;


ImageData probe_img_data_gray(probe_img_gray.cols, probe_img_gray.rows, probe_img_gray.channels());

probe_img_data_gray.data = probe_img_gray.data;


// Detect faces

std::vector gallery_faces = detector.Detect(gallery_img_data_gray);

int32_t gallery_face_num = static_cast(gallery_faces.size());


std::vector probe_faces = detector.Detect(probe_img_data_gray);

int32_t probe_face_num = static_cast(probe_faces.size());


if (gallery_face_num != 0 && probe_face_num != 0)

{

//檢測(cè)5點(diǎn)面部標(biāo)記

seeta::FacialLandmark gallery_points[5];

point_detector.PointDetectLandmarks(gallery_img_data_gray, gallery_faces[0], gallery_points);


seeta::FacialLandmark probe_points[5];

point_detector.PointDetectLandmarks(probe_img_data_gray, probe_faces[0], probe_points);


for (int i = 0; i < 5; i++)

{

cv::circle(gallery_img_color, cv::Point(gallery_points[i].x, gallery_points[i].y), 2,

CV_RGB(0, 255, 0));

cv::circle(probe_img_color, cv::Point(probe_points[i].x, probe_points[i].y), 2,

CV_RGB(0, 255, 0));

}

cv::imwrite("gallery_point_result.jpg", gallery_img_color);

cv::imwrite("probe_point_result.jpg", probe_img_color);


//提取面部特性特征

float gallery_fea[2048];

float probe_fea[2048];

face_recognizer.ExtractFeatureWithCrop(gallery_img_data_color, gallery_points, gallery_fea);

face_recognizer.ExtractFeatureWithCrop(probe_img_data_color, probe_points, probe_fea);


//-------------------------------------------【裁剪臉部】----------------------------------------------------

//創(chuàng)建一個(gè)圖像來(lái)存儲(chǔ)裁剪的臉部

cv::Mat dst_img(face_recognizer.crop_height(),

face_recognizer.crop_width(),

CV_8UC(face_recognizer.crop_channels()));

ImageData dst_img_data(dst_img.cols, dst_img.rows, dst_img.channels());

dst_img_data.data = dst_img.data;


//裁剪

face_recognizer.CropFace(probe_img_data_color, probe_points, dst_img_data);

//保存裁剪的臉部

cv::imwrite("dst_img.jpg", dst_img);


//圖像在Qt顯示前赠叼,必須將Mat型轉(zhuǎn)化成QImage格式擦囊,將RGBA格式轉(zhuǎn)化成RGB

cvtColor(dst_img, temp, CV_BGR2RGB);

//轉(zhuǎn)化成QImage格式

QImage CropFace = QImage((const unsigned char *)(temp.data), temp.cols,

temp.rows, QImage::Format_RGB888);


//計(jì)算兩張臉部的相似度

float sim = face_recognizer.CalcSimilarity(gallery_fea, probe_fea);


//Qt多線程的信號(hào)傳參只能傳基本數(shù)據(jù)類(lèi)型

//Mat或者庫(kù)里面的int_8 / int_32(自定義的數(shù)據(jù)類(lèi)型) 什么的不能傳

//類(lèi)型轉(zhuǎn)換

int GalleryFaceNum = gallery_face_num;

int ProbeFaceNum = probe_face_num;

//發(fā)射信號(hào)

emit UpdateRecognition(GalleryFaceNum, ProbeFaceNum, CropFace, sim);

}


else

{

emit NoFace();

}

}

}


void MyThread::getMainSignal(QString str)

{

//獲取輸入圖片的路徑

path = str;

}


void MyThread::getVideoSignal(QImage image,bool flag)

{

this->image = image;

CameraVideoFlag = flag;

frame = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());

//不能放在這里轉(zhuǎn)格式,會(huì)造成不能及時(shí)接收

//故把格式轉(zhuǎn)換放在處理函數(shù)里了

}

```

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞬场,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涧郊,更是在濱河造成了極大的恐慌贯被,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妆艘,死亡現(xiàn)場(chǎng)離奇詭異彤灶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)批旺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)幌陕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人汽煮,你說(shuō)我怎么就攤上這事搏熄。” “怎么了暇赤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鞋囊。 經(jīng)常有香客問(wèn)我古戴,道長(zhǎng)喳逛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任娜氏,我火速辦了婚禮郁岩,結(jié)果婚禮上挤茄,老公的妹妹穿的比我還像新娘如叼。我一直安慰自己,他們只是感情好穷劈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布笼恰。 她就那樣靜靜地躺著片酝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挖腰。 梳的紋絲不亂的頭發(fā)上雕沿,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音猴仑,去河邊找鬼审轮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辽俗,可吹牛的內(nèi)容都是我干的疾渣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼崖飘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼榴捡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起朱浴,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吊圾,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后翰蠢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體项乒,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年梁沧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了檀何。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡廷支,死狀恐怖频鉴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恋拍,我是刑警寧澤垛孔,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站芝囤,受9級(jí)特大地震影響似炎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悯姊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一羡藐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悯许,春花似錦仆嗦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谆甜。三九已至,卻和暖如春集绰,著一層夾襖步出監(jiān)牢的瞬間规辱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工栽燕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罕袋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓碍岔,卻偏偏與公主長(zhǎng)得像浴讯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蔼啦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容