簡單實現(xiàn)一個虛擬形象系統(tǒng)
本文為來自 字節(jié)教育-成人與創(chuàng)新前端團隊 成員的文章,已授權(quán) ELab 發(fā)布。
?前言?
上周啟動居家開會的時候,看到有人通過「虛擬形象」功能,給自己帶上了口罩、眼鏡之類,于是想到了是不是也可以搞一個簡單的虛擬形象系統(tǒng)。
大致想來,分為以下幾個部分:
?卷積神經(jīng)網(wǎng)絡(luò)(CNN)?
下面講解一下三層CNN網(wǎng)絡(luò)模型:
卷積層——提取特征
卷積層的運算過程如下圖,用一個卷積核掃完整張圖片:
通過動圖能夠更好的理解卷積過程,使用一個卷積核(過濾器)來過濾圖像的各個小區(qū)域,從而得到這些小區(qū)域的特征值。
在具體應(yīng)用中,往往有多個卷積核,每個卷積核代表了一種圖像模式(特征規(guī)則),如果某個圖像塊與此卷積核卷積出的值大,則認為此圖像塊十分接近于此卷積核。如果有N個卷積核,那么就認為圖像中有N種底層紋理(特征),即用這N種基礎(chǔ)紋理就能描繪出一副圖像。
總結(jié): 卷積 層的通過卷積核的過濾提取出圖片中局部的特征。
疑問:上圖卷積后,存在邊緣數(shù)據(jù)特征提取減少,大家能想到什么方式處理呢?
池化層(下采樣)——數(shù)據(jù)降維,避免過擬合
池化層通常也被叫做下采樣,目的是降低數(shù)據(jù)的維度,減少數(shù)據(jù)處理量。其過程大致如下:
上圖輸入時是20×20的,先進行卷積采樣,卷積核為10×10,采用最大池化的方式,輸出為一個2×2大小的特征圖。這樣可將數(shù)據(jù)維度減少了10倍,方便后續(xù)模塊處理。
總結(jié):池化層相比 卷積 層可以更有效的降低數(shù)據(jù)維度,不僅可減少運算量,還可以避免 過擬合 。
過擬合是指訓(xùn)練誤差和測試誤差之間的差距太大。換句換說,就是模型復(fù)雜度高于實際問題,模型在訓(xùn)練集上表現(xiàn)很好,但在測試集上卻表現(xiàn)很差。模型對訓(xùn)練集"死記硬背"(記住了不適用于測試集的訓(xùn)練集性質(zhì)或特點),沒有理解數(shù)據(jù)背后的規(guī)律,泛化能力差。
全連接層——輸出結(jié)果
全鏈接層是將我們最后一個池化層的輸出連接到最終的輸出節(jié)點上。假設(shè),上述CNN的最后一個池化層的輸出大小為 [5×5×4],即 5×5×4=100 個節(jié)點。對于當(dāng)前任務(wù)(僅識別??、??、??),我們的輸出會是一個三維向量,輸出層共 3 個節(jié)點,如輸出[0.89, 0.1, 0.001],表示0.89的概率為貓。在實際應(yīng)用中,通常全連接層的節(jié)點數(shù)會逐層遞減,最終變?yōu)閚維向量。
舉個例子
假設(shè)我們有2個檢測的特征為「水平邊緣」和「垂直邊緣」?!复怪边吘墶咕矸e過程如下:
最終結(jié)果如下:
Q&A環(huán)節(jié)
沒錯啦,前面的問題的答案就是邊緣填充。
?face-api.js?
face-api.js 是基于 tensorflow.js 實現(xiàn)的,內(nèi)置了一些訓(xùn)練好的模型,這些模型應(yīng)該是這個方案的核心,通過這些預(yù)先訓(xùn)練好的模型,我們可以直接使用而不需要自己再去標(biāo)注、訓(xùn)練,極大的降低了成本。
主要提供的功能如下:
- 人臉檢測:獲取一張或多張人臉的邊界,可用于確認人臉的位置、數(shù)量和大小
- 人臉特征檢測:包含68個人臉特征點位,獲取眉毛、眼睛、鼻子、嘴、嘴唇、下巴等的位置和形狀
- 人臉識別:返回人臉特征向量,可用于辨別人臉
- 人臉表情識別:獲取人臉表情特征
性別和年齡檢測:判斷年齡和性別。其中“性別”是判斷人臉的女性化或男性化偏向,與真實性別不一定掛鉤
人臉檢測
針對人臉檢測,face-api 提供了 SSD Mobilenet V1 和 The Tiny Face Detector 兩個人臉檢測模型:
SSD Mobilenet V1:能夠計算圖像中每個人臉的位置,并返回邊界框以及每個框內(nèi)包含人臉的概率,但是這個模型有 5.4M,加載需要比較長的時間,弱網(wǎng)環(huán)境下加載過于耗時。
The Tiny Face Detector :這個模型性能非常好,可以做實時的人臉檢測,且只有190kB,但是檢測準(zhǔn)確性上不如 SSD Mobilenet V1,且在檢測比較小的人臉時不太可靠。相對而言,比較適合移動端或者設(shè)備資源優(yōu)先的條件下。
人臉特征檢測
針對人臉特征檢測, 提供了 68 點人臉特征檢測模型,檢測這 68 個點的作用是為了后續(xù)的人臉對齊,為后續(xù)人臉識別做準(zhǔn)備,這里提供了兩個大小的模型供選擇:350kb和80kb,大的模型肯定是更準(zhǔn)確,小的模型適合對精確度要求不高,對資源要求占用不高的場景。其輸出的區(qū)域特征點區(qū)間固定如下:
區(qū)域 | 區(qū)間 |
下巴 | [1, 16] |
左眉 | [18, 22] |
右眉 | [23, 27] |
鼻梁 | [28, 31] |
鼻子 | [32, 26] |
左眼 | [37, 42] |
右眼 | [43, 48] |
外嘴唇 | [49, 60] |
內(nèi)嘴唇 | [61, 68] |
人臉識別
經(jīng)過人臉檢測以及人臉對齊以后,將檢測到的人臉輸入到人臉識別網(wǎng)絡(luò)進行識別,從而獲得一個128維的人臉特征向量。通過計算兩個向量之間的距離(余弦值),就可以判斷相似度。
?虛擬形象系統(tǒng)?
獲取人臉圖像
目前主流瀏覽器提供了WebRTC能力,我們可以調(diào)用getUserMedia?方法指定設(shè)備采集音視頻數(shù)據(jù)。其中constrains詳情參考 MediaTrackConstraints - Web APIs | MDN[1]。
獲取人臉特征
根據(jù)官方文檔介紹,The Tiny Face Detector?模型與人臉特征識別模型組合的效果更好,故本文使用的人臉檢測模型是The Tiny Face Detector。
這個模型有兩個參數(shù)可以調(diào)整,包括 inputSize? 和 scoreThreshold,默認值是 416 和 0.5。
- inputSize:表示檢測范圍(人臉邊框),值越小檢測越快,但是對小臉的檢測準(zhǔn)確不足,可能會檢測不出,如果是針對視頻的實時檢測,可以設(shè)置比較小的值。
- scoreThreshold:是人臉檢測得分的閾值,假如在照片中檢測不到人臉,可以將這個值調(diào)低。
首先我們要選擇并加載模型(這里使用官網(wǎng)訓(xùn)練好的模型和權(quán)重參數(shù))
轉(zhuǎn)換人臉檢測模型。face-api的人臉檢測模型默認是SSD Mobilenet v1?,這里需要顯式調(diào)整為The Tiny Face Detector模型。
形象繪制
經(jīng)過上述計算,我們已經(jīng)拿到了人臉68點位特征集。需要先計算點位相對坐標(biāo)信息,然后進行形象繪制。
本文使用的是一張256*256的口罩圖片,選取1號和16號點位繪制口罩,根據(jù)兩點位之間的距離縮放口罩大小。
這里主要調(diào)研了兩種方式,分別是canvas繪制和媒體流繪制。
canvas繪制
首先想到的一種方式,video和canvas大小和位置固定,定時抓取video媒體流中圖片,進行識別人臉,然后繪制在canvas上。
媒體流繪制
canvas提供了一個api叫做 captureStream[2],會返回一個繼承MediaStream的實例,實時視頻捕獲畫布上的內(nèi)容(媒體流)。我們可以在canvas上以固定幀率進行圖像繪制,獲取視頻軌道。
這樣我們僅需保證video和canvas大小一致,位置無需固定,甚至canvas可以離屏不渲染。
對比
canvas繪制兼容性更好,但在實時通信場景下,需傳遞點位信息或端重復(fù)計算,容易受網(wǎng)絡(luò)波動以及硬件設(shè)備影響,導(dǎo)致實際繪制出現(xiàn)偏差(依賴端能力)
媒體流繪制兼容性較差,但是在直播等場景下效果會更好,在輸出端做好已經(jīng)做好媒體流融合,接收端依托媒體能力播放即可。同時內(nèi)容也不易篡改
?實際效果?
因為這里僅使用了2個點位的信息,所以效果一般般。我們完全可以充分利用68個點位全面換膚,實現(xiàn)各種效果。
?延伸思考?
測評場景下:判斷人臉數(shù)量、是否是用戶本人,自動提醒用戶,異常狀態(tài)記錄日志,監(jiān)控人員可以后臺查看
學(xué)習(xí)場景下:判斷用戶是否離開屏幕等,提醒用戶返回學(xué)習(xí)狀態(tài)。
彈幕場景下:檢測人臉,解決彈幕遮擋問題