簡單實現一個虛擬形象系統

本文為來自

位元組教育-成人與創新前端團隊

成員的文章,已授權 ELab 釋出。

前言

上週啟動居家開會的時候,看到有人透過「虛擬形象」功能,給自己帶上了口罩、眼鏡之類,於是想到了是不是也可以搞一個簡單的虛擬形象系統。

大致想來,分為以下幾個部分:

簡單實現一個虛擬形象系統

卷積神經網路(CNN)

下面講解一下三層CNN網路模型:

簡單實現一個虛擬形象系統

卷積層——提取特徵

卷積層的運算過程如下圖,用一個卷積核掃完整張圖片:

簡單實現一個虛擬形象系統

透過動圖能夠更好的理解卷積過程,使用一個卷積核(過濾器)來過濾影象的各個小區域,從而得到這些小區域的特徵值。

在具體應用中,往往有多個卷積核,每個卷積核代表了一種影象模式(特徵規則),如果某個影象塊與此卷積核卷積出的值大,則認為此影象塊十分接近於此卷積核。如果有N個卷積核,那麼就認為影象中有N種底層紋理(特徵),即用這N種基礎紋理就能描繪出一副影象。

總結:

卷積

層的透過卷積核的過濾提取出圖片中區域性的特徵。

疑問:上圖卷積後,存在邊緣資料特徵提取減少,大家能想到什麼方式處理呢?

池化層(下采樣)——資料降維,避免過擬合

池化層通常也被叫做下采樣,目的是降低資料的維度,減少資料處理量。其過程大致如下:

簡單實現一個虛擬形象系統

上圖輸入時是20×20的,先進行卷積取樣,卷積核為10×10,採用最大池化的方式,輸出為一個2×2大小的特徵圖。這樣可將資料維度減少了10倍,方便後續模組處理。

總結:池化層相比

卷積

層可以更有效的降低資料維度,不僅可減少運算量,還可以避免

過擬合

過擬合是指訓練誤差和測試誤差之間的差距太大。換句換說,就是模型複雜度高於實際問題,模型在訓練集上表現很好,但在測試集上卻表現很差。模型對訓練集“死記硬背”(記住了不適用於測試集的訓練集性質或特點),沒有理解資料背後的規律,泛化能力差。

簡單實現一個虛擬形象系統

全連線層——輸出結果

全連結層是將我們最後一個池化層的輸出連線到最終的輸出節點上。假設,上述CNN的最後一個池化層的輸出大小為 [5×5×4],即 5×5×4=100 個節點。對於當前任務(僅識別、、),我們的輸出會是一個三維向量,輸出層共 3 個節點,如輸出[0。89, 0。1, 0。001],表示0。89的機率為貓。在實際應用中,通常全連線層的節點數會逐層遞減,最終變為n維向量。

舉個例子

假設我們有2個檢測的特徵為「水平邊緣」和「垂直邊緣」。「垂直邊緣」卷積過程如下:

簡單實現一個虛擬形象系統

最終結果如下:

簡單實現一個虛擬形象系統

Q&A環節

沒錯啦,前面的問題的答案就是邊緣填充。

簡單實現一個虛擬形象系統

face-api。js

face-api。js 是基於 tensorflow。js 實現的,內建了一些訓練好的模型,這些模型應該是這個方案的核心,透過這些預先訓練好的模型,我們可以直接使用而不需要自己再去標註、訓練,極大的降低了成本。

主要提供的功能如下:

人臉檢測:獲取一張或多張人臉的邊界,可用於確認人臉的位置、數量和大小

人臉特徵檢測:包含68個人臉特徵點位,獲取眉毛、眼睛、鼻子、嘴、嘴唇、下巴等的位置和形狀

人臉識別:返回人臉特徵向量,可用於辨別人臉

人臉表情識別:獲取人臉表情特徵

性別和年齡檢測:判斷年齡和性別。其中“性別”是判斷人臉的女性化或男性化偏向,與真實性別不一定掛鉤

人臉檢測

針對人臉檢測,face-api 提供了 SSD Mobilenet V1 和 The Tiny Face Detector 兩個人臉檢測模型:

SSD Mobilenet V1:能夠計算影象中每個人臉的位置,並返回邊界框以及每個框內包含人臉的機率,但是這個模型有 5。4M,載入需要比較長的時間,弱網環境下載入過於耗時。

The Tiny Face Detector :這個模型效能非常好,可以做實時的人臉檢測,且只有190kB,但是檢測準確性上不如 SSD Mobilenet V1,且在檢測比較小的人臉時不太可靠。相對而言,比較適合移動端或者裝置資源優先的條件下。

人臉特徵檢測

針對人臉特徵檢測, 提供了 68 點人臉特徵檢測模型,檢測這 68 個點的作用是為了後續的人臉對齊,為後續人臉識別做準備,這裡提供了兩個大小的模型供選擇:350kb和80kb,大的模型肯定是更準確,小的模型適合對精確度要求不高,對資源要求佔用不高的場景。其輸出的區域特徵點區間固定如下:

區域

區間

下巴

[1, 16]

左眉

[18, 22]

右眉

[23, 27]

鼻樑

[28, 31]

鼻子

[32, 26]

左眼

[37, 42]

右眼

[43, 48]

外嘴唇

[49, 60]

內嘴唇

[61, 68]

簡單實現一個虛擬形象系統

人臉識別

經過人臉檢測以及人臉對齊以後,將檢測到的人臉輸入到人臉識別網路進行識別,從而獲得一個128維的人臉特徵向量。透過計算兩個向量之間的距離(餘弦值),就可以判斷相似度。

簡單實現一個虛擬形象系統

簡單實現一個虛擬形象系統

虛擬形象系統

獲取人臉影象

目前主流瀏覽器提供了WebRTC能力,我們可以呼叫

getUserMedia

方法指定裝置採集音影片資料。其中

constrains

詳情參考

MediaTrackConstraints - Web APIs | MDN

[1]

const constraints = { audio:

true

, video: { width: 1280, height: 720 } };

const setLocalMediaStream = (mediaStream: MediaStream) => {

videoRef。current。srcObject = mediaStream;

}

navigator

。mediaDevices

。getUserMedia(constraints)

then

(setLocalMediaStream)

獲取人臉特徵

根據官方文件介紹,

The Tiny Face Detector

模型與人臉特徵識別模型組合的效果更好,故本文使用的人臉檢測模型是

The Tiny Face Detector

這個模型有兩個引數可以調整,包括

inputSize

scoreThreshold

,預設值是 416 和 0。5。

inputSize

:表示檢測範圍(人臉邊框),值越小檢測越快,但是對小臉的檢測準確不足,可能會檢測不出,如果是針對影片的實時檢測,可以設定比較小的值。

scoreThreshold

:是人臉檢測得分的閾值,假如在照片中檢測不到人臉,可以將這個值調低。

首先我們要選擇並載入模型(這裡使用官網訓練好的模型和權重引數)

// 載入人臉檢測模型

await faceApi。nets。tinyFaceDetector。loadFromUri(

‘xxx/weights/’

);

// 載入特徵檢測模型

await faceApi。nets。faceLandmark68Net。loadFromUri(

‘xxx/weights/’

);

轉換人臉檢測模型。face-api的人臉檢測模型預設是

SSD Mobilenet v1

,這裡需要顯式調整為

The Tiny Face Detector

模型。

const options = new faceApi。TinyFaceDetectorOptions({

inputSize,

scoreThreshold,

});

// 人臉68點位特徵集

const result = await faceApi

。detectSingleFace(videoEl, options) // 人臉檢測

。withFaceLandmarks(); // 特徵檢測

形象繪製

經過上述計算,我們已經拿到了人臉68點位特徵集。需要先計算點位相對座標資訊,然後進行形象繪製。

const canvas = canvasRef。current;

const canvasCtx = canvas。getContext(

‘2d’

);

const dims = faceApi。matchDimensions(canvas, videoEl,

true

);

const resizedResult = faceApi。resizeResults(result, dims);

本文使用的是一張256*256的口罩圖片,選取1號和16號點位繪製口罩,根據兩點位之間的距離縮放口罩大小。

簡單實現一個虛擬形象系統

這裡主要調研了兩種方式,分別是canvas繪製和媒體流繪製。

canvas繪製

首先想到的一種方式,video和canvas大小和位置固定,定時抓取video媒體流中圖片,進行識別人臉,然後繪製在canvas上。

const { positions } = resizedResult。landmarks;

const leftPoint = positions[0];

const rightPoint = positions[16];

const length = Math。sqrt(

Math。pow(leftPoint。x - rightPoint。x, 2) +

Math。pow(leftPoint。y - rightPoint。y, 2),

);

canvasCtx?。drawImage(

mask,

0,

0,

265,

265,

leftPoint。x,

leftPoint。y,

length,

length,

);

媒體流繪製

canvas提供了一個api叫做

captureStream

[2]

,會返回一個繼承MediaStream的例項,實時影片捕獲畫布上的內容(媒體流)。我們可以在canvas上以固定幀率進行影象繪製,獲取影片軌道。

簡單實現一個虛擬形象系統

這樣我們僅需保證video和canvas大小一致,位置無需固定,甚至canvas可以離屏不渲染。

const stream = canvasRef。current。captureStream()!;

mediaStream = res[0]。

clone

();

mediaStream。addTrack(stream。getVideoTracks()[0]);

videoRef。current!。srcObject = mediaStream;

對比

canvas繪製相容性更好,但在實時通訊場景下,需傳遞點位資訊或端重複計算,容易受網路波動以及硬體裝置影響,導致實際繪製出現偏差(依賴端能力)

媒體流繪製相容性較差,但是在直播等場景下效果會更好,在輸出端做好已經做好媒體流融合,接收端依託媒體能力播放即可。同時內容也不易篡改

實際效果

因為這裡僅使用了2個點位的資訊,所以效果一般般。我們完全可以充分利用68個點位全面換膚,實現各種效果。

延伸思考

測評場景下:判斷人臉數量、是否是使用者本人,自動提醒使用者,異常狀態記錄日誌,監控人員可以後臺檢視

學習場景下:判斷使用者是否離開螢幕等,提醒使用者返回學習狀態。

彈幕場景下:檢測人臉,解決彈幕遮擋問題

。。。(歡迎補充)

❤️ 謝謝支援

以上便是本次分享的全部內容,希望對你有所幫助^_^

喜歡的話別忘了 分享、點贊、收藏 三連哦~。

歡迎關注公眾號

ELab團隊

收貨大廠一手好文章~

位元組跳動校/社招內推碼:

5UJF23C

投遞連結:

https://job.toutiao.com/s/YSqdt8q

可憑內推碼投遞

位元組教育-成人與創新前端團隊

相關崗位哦~

參考資料

[1]

MediaTrackConstraints - Web APIs | MDN:

https://developer。mozilla。org/en-US/docs/Web/API/MediaTrackConstraints

[2]

captureStream:

https://developer。mozilla。org/zh-CN/docs/Web/API/HTMLCanvasElement/captureStream

[3]

一文看懂卷積神經網路-CNN(基本原理+獨特價值+實際應用)- 產品經理的人工智慧學習庫:

https://easyai。tech/ai-definition/cnn/

[4]

基於face-api。js實現人臉識別的實踐和總結:

https://zhuanlan。zhihu。com/p/330540757

[5]

face-api。js:在瀏覽器中進行人臉識別的JS介面:

https://zhuanlan。zhihu。com/p/39918438

[6]

卷積神經網路:

https://github。com/bighuang624/Andrew-Ng-Deep-Learning-notes/blob/master/docs/Convolutional_Neural_Networks/%E5%8D%B7%E7%A7%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C。md

[7]

CNN Explainer:

https://poloclub。github。io/cnn-explainer/

[8]

face-api。js:

https://github。com/justadudewhohacks/face-api。js/

[9]

卷積神經網路 (Convolutional Neural Network, CNN) - Leo Van | 範葉亮:

https://leovan。me/cn/2018/08/cnn/

- END -