大家都嫌公司以前使用的刷卡門禁太麻煩,正好借這個機會開發一個人臉識別的門禁系統,采用的SDK是虹軟公司開發的,接口調用比較簡單。
一、虹軟SDK接口性能
在配置為i5-7400 、16G內存的PC上測試性能如下:
1.FT 單次3ms左右
2.FD 單次10ms左右
3.FR 單次170ms左右
二、業務層需要解決的問題
1.FT和FR單次性能相差較大,速度不匹配
2.FT支持多人臉,單次檢測人臉較多,而FR只支持單人臉
3.視頻幀單次處理可能會超過幀間隔時間
三、優化方案
人臉識別過程包括三個步驟,檢測人臉、人臉特征提取、特征匹配,以人臉識別的門禁系統為例,主要步驟獲取視頻幀、檢
測視頻幀人臉、提取特征、特征匹配,門禁系統對於實時性要求比較高。
流程圖大概如下:
下面說下開發過程中多線程方面的優化點。
1. 獲取視頻幀
攝像頭視頻的幀率正常在25~30幀左右,並且一般的視頻幀的分辨率為1280x720,當然有的攝像頭可以達到更大的分辨率,
但是經過測試多款攝像頭,發現分辨率設置過大會導致獲取視頻幀的過程中卡頓。並且分辨率適中,會提升后面的FT的速度。獲取視頻幀的操作是在主線程進行的。
lock_guard<std::mutex> locker(g_CameraMutex); if (m_camera->getFrame(m_curFrame)) { return; } ftProcessor->faceDetect(m_curFrame, m_resizeImage);
2. 人臉識別處理優化
采用了獲取視頻幀和FT串行的方式,需要保證從獲取視頻幀一直到處理完成,必須在幀間隔時間內(幀率為25時,在40ms左右),否則可能會出現卡幀的情況。經測試,獲取視頻幀和FT串行的單次時間在幀間隔時間內,因此采用串行的方式。
FT支持多人臉檢測,我將檢測最大人臉數設置為5,所以在極限狀態下,人數較多的話,待識別的人數可能一直維持在5人,這就需要提高FR的有效識別率。
FR使用了一個自定義threadsafe_queue簡化多線程數據同步操作,在FT中將需要進行FR的人臉框以及對應的視頻幀和人臉框的trackid push_back到threadsafe_queue中,FR線程從中取人臉框信息和視頻幀,做提取特征和特征匹配處理。

我從以下幾個點優化人臉識別效率:
a.增加FR線程數量。
對於我來說,開線程池的方式可行性並不太高。首先機器的內核數限制的線程數,動態增加線程數聽起來不錯,但是門禁使用的一體機畢竟跟開發機器不一樣,性能限制太多,並且線程切換和數據同步都會更加復雜。但是為了提高FR的識別速度,我最終開了兩個FR線程,經測試,在i5-7400T 8G內存 配置的機器上,CPU大概處於40%左右。
b.提高FR識別效率
優化FR識別效率要求FR做到每次識別都是有效的,也就是說FR不做垃圾幀的處理。
1)每次在獲取到人臉后,判斷是否與前一幀檢測到的人臉結果屬於同一個人(通過trackid判定,這一部分留到后面的文章說明,敬請期待)。如果某個人臉框首次出現,或者如果某個trackid對應的人臉框仍未識別成功,都需要做FR操作。

case FTSucceed: case FRFailed: { newFaceInfo.faceStatus = FRWaiting; FRParam frParam = { 0 }; frParam.curImageInfo = m_curImageInfo; frParam.faceInfo.faceOrient = newFaceInfo.faceOrient; frParam.faceInfo.faceRect = { newFaceInfo.faceRect.wleft, newFaceInfo.faceRect.wtop, newFaceInfo.faceRect.wright, newFaceInfo.faceRect.wbottom }; frParam.trackid = newFaceInfo.trackid; m_frParamsQueue.push(frParam); }
當某個trackid對應的人臉已經在等待或者在做FR的時候,在后面檢測到的該trackid對應人臉將不再做FR,一直到FR檢測結果出來以后,再做處理。
case FRProcessing: case FRWaiting: { //default color } break;
2)識別成功后,同樣將該trackid所對應的人臉都設置為識別到的人,這樣下次就不需要再次做FR,不會浪費FR的時間片。
3)每次在從threadsafe_queue獲取的待處理的人臉框、trackid和視頻幀時,都檢測該trackid是否已消失,如果消失,就不做FR,因為拿到的結果毫無用處,直接丟棄。
std::lock_guard<std::mutex> locker(m_faceInfoListMutex); //找到對應的trackid 對應的人臉,設置為FRProcessing auto iter = find_if(m_prevFacesRes.begin(), m_prevFacesRes.end(), [=](const FaceInfo& lhs) { return lhs.trackid == frParam.trackid; }); if (iter != m_prevFacesRes.end()) { iter->faceStatus = FRProcessing; } else { //找不到 就不用做FR了 continue; }
`
好了,這次就說到這,大家有什么問題的話,可以留言,謝謝。
參考鏈接:
1.trackid 介紹:https://www.jianshu.com/p/4ae90634a79e