- 原文地址:Real-time Human Pose Estimation in the Browser with TensorFlow.js
- 原文作者:Dan Oved
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:NoName4Me
- 校對者:luochen1992、isiyin
釋出者:Dan Oved,谷歌創意實驗室自由創意技術專家,紐約大學 ITP 研究生。 編輯和插圖:創意技術專家 Irene Alvarado 和谷歌創意實驗室自由平面設計師 Alexis Gallo。
在與谷歌創意實驗室的合作中,我很高興地宣佈 TensorFlow.js 版 PoseNet¹ 的發行,² 它是一個能夠在瀏覽器裡對人體姿態進行實時估計的機器學習模型。點選這裡線上體驗。
PoseNet 使用單人姿態或多人姿態演算法可以檢測影象和視訊中的人物形象 —— 全部在瀏覽器中完成。
那麼,姿態估計究竟是什麼呢?姿態估計是指在影象和視訊中檢測人物的計算機視覺技術,比如,可以確定某個人的肘部在影象中的位置。需要澄清一點,這項技術並不是識別影象中是誰 —— 姿態估計不涉及任何個人身份資訊。該演算法僅僅是估計身體關鍵關節的位置。
好吧,為什麼這是令人興奮的開始?姿態估計有很多用途,從互動裝置反饋給身體,到擴增實境,動畫,健身用途等等。我們希望藉助此模型激發更多的開發人員和製造商嘗試將姿態檢測應用到他們自己的獨特專案中。雖然許多同類姿態檢測系統也已經開源,但它們都需要專門的硬體和/或相機,以及相當多的系統安裝。 藉助執行在 TensorFlow.js 上的 PoseNet,任何人只需擁有帶攝像頭的桌上型電腦或者手機即可在瀏覽器中體驗這項技術。而且由於我們已經開源了這個模型,JavaScript 開發人員可以用幾行程式碼來修改和使用這個技術。更重要的是,這實際上可以幫助保護使用者隱私。因為基於 TensorFlow.js 的 PoseNet 執行在瀏覽器中,任何姿態資料都不會離開使用者的計算機。
在我們深入探討如何使用這個模型的細節之前,先向所有讓這個專案成為可能的人們致意:論文對戶外多人姿態的精確估計 和 PersonLab: 自下而上的區域性幾何嵌入模型的人體姿態估計和例項分割的作者 George Papandreou 和 Tyler Zhu,以及 TensorFlow.js 庫背後的 Google Brain 小組的工程師 Nikhil Thorat 和 Daniel Smilkov。
PoseNet 入門
PoseNet 可以被用來估計單人姿態或多人姿態,這意味著演算法有一個檢測影象/視訊中只有一個人的版本,和檢測多人的版本。為什麼會有兩個版本?因為單人姿態檢測更快,更簡單,但要求影象中只能有一個主體(後面會詳細說明)。我們先說單體姿態,因為它更簡單易懂。
姿態估計整體來看主要有兩個階段:
- 輸入一個通過卷積神經網路反饋的 RGB 影象。
- 使用單人姿態或多人姿態解碼演算法從模型輸出中解碼姿態,姿態置信得分,關鍵點位置,以及關鍵點置信得分。
等一下,所有這些關鍵詞指的是什麼?我們看看一下最重要的幾個:
- 姿態 - 從最上層來看,PoseNet 將返回一個姿態物件,其中包含每個檢測到的人物的關鍵點列表和例項級別的置信度分數。
PoseNet 返回檢測到的每個人的置信度值以及檢測到的每個姿態關鍵點。圖片來源:「Microsoft Coco:上下文資料集中的通用物件」,cocodataset.org。
- 姿態置信度分數 - 決定了對姿態估計的整體置信度。它介於 0.0 和 1.0 之間。它可以用來隱藏分數不夠高的姿態。
- 關鍵點 —— 估計的人體姿態的一部分,例如鼻子,右耳,左膝,右腳等。 它包含位置和關鍵點置信度分數。PoseNet 目前檢測到下圖所示的 17 個關鍵點:
PosNet 檢測到17個姿態關鍵點。
- 關鍵點置信度得分 - 決定了估計關鍵點位置準確的置信度。它介於 0.0 和 1.0 之間。它可以用來隱藏分數不夠高的關鍵點。
- 關鍵點位置 —— 檢測到關鍵點的原始輸入影象中的二維 x 和 y 座標。
第 1 部分:匯入 TensorFlow.js 和 PoseNet 庫
很多工作都是將模型的複雜性抽象化並將功能封裝為易於使用的方法。我們看一下構建 PoseNet 專案的基礎知識。
該庫可以通過 npm 安裝:
npm install @[tensorflow-models/posenet](https://www.npmjs.com/package/@tensorflow-models/posenet)
複製程式碼
然後使用 es6 模組匯入:
import * as posenet from '@[tensorflow-models/posenet](https://www.npmjs.com/package/@tensorflow-models/posenet)';
const net = await posenet.load();
複製程式碼
或通過頁面中的一個包:
<html>
<body>
<!-- Load TensorFlow.js -->
<script src="[https://unpkg.com/@tensorflow/tfjs](https://unpkg.com/@tensorflow/tfjs)"></script>
<!-- Load Posenet -->
<script src="[https://unpkg.com/@tensorflow-models/posenet](https://unpkg.com/@tensorflow-models/posenet)">
</script>
<script type="text/javascript">
posenet.load().then(function(net) {
// posenet 模組載入成功
});
</script>
</body>
</html>
複製程式碼
第 2a 部分:單人姿態估計
單人姿態估計演算法用於影象的示例。圖片來源:「Microsoft Coco:上下文資料集中的通用物件」,cocodataset.org。
如前所述,單人姿態估計演算法更簡單,速度更快。它的理想使用場景是當輸入影象或視訊中心只有一個人。缺點是,如果影象中有多個人,那麼來自兩個人的關鍵點可能會被估計成同一個人的姿態的一部分 —— 舉個例子,路人甲的左臂和路人乙的右膝可能會由該演算法確定為屬於相同姿態而被合併。如果輸入影象可能包含多人,則應該使用多人姿態估計演算法。
我們來看看單人姿態估計演算法的輸入:
- 輸入影象元素 —— 包含要預測影象的 html 元素,例如視訊或影象標記。重要的是,輸入的影象或視訊元素應該是正方形的。
- 影象比例因子 —— 在 0.2 和 1 之間的數字。預設為 0.50。在送入神經網路之前如何縮放影象。將此數字設定得較低以縮小影象,並以精度為代價增加通過網路的速度。
- 水平翻轉 —— 預設為 false。表示是否姿態應該水平/垂直映象。對於視訊預設水平翻轉(比如網路攝像頭)的視訊,應該設定為 true,因為你希望姿態能以正確的方向返回。
- 輸出步幅 —— 必須為 32,16 或 8。預設 16。在內部,此引數會影響神經網路中圖層的高度和寬度。在上層看來,它會影響姿態估計的精度和速度。值越小精度越高,但速度更慢,值越大速度越快,但精度更低。檢視輸出步幅對輸出質量的影響的最好方法是體驗這個單人姿態估計演示。
下面讓我們看一下單人姿態估計演算法的輸出:
- 一個包含姿態置信度得分和 17 個關鍵點陣列的姿態。
- 每個關鍵點都包含關鍵點位置和關鍵點置信度得分。同樣,所有關鍵點位置在輸入影象的座標空間中都有 x 和 y 座標,並且可以直接對映到影象上。
這個段程式碼塊顯示瞭如何使用單人姿態估計演算法:
const imageScaleFactor = 0.50;
const flipHorizontal = false;
const outputStride = 16;
const imageElement = document.getElementById('cat');
// 載入 posenet 模型
const net = await posenet.load();
const pose = await net.estimateSinglePose(imageElement, scaleFactor, flipHorizontal, outputStride);
複製程式碼
輸出姿態示例如下:
{
"score": 0.32371445304906,
"keypoints": [
{ // 鼻子
"position": {
"x": 301.42237830162,
"y": 177.69162777066
},
"score": 0.99799561500549
},
{ // 左眼
"position": {
"x": 326.05302262306,
"y": 122.9596464932
},
"score": 0.99766051769257
},
{ // 右眼
"position": {
"x": 258.72196650505,
"y": 127.51624706388
},
"score": 0.99926537275314
},
...
]
}
複製程式碼
第 2b 部分:多人姿態估計
多人姿態估計演算法應用於影象的例子。圖片來源:「Microsoft Coco:上下文資料集中的通用物件」,cocodataset.org。
多人姿態估計演算法可以估計影象中的多個姿態/人物。它比單人姿態演算法更復雜並且稍慢,但它的優點是,如果圖片中出現多個人,他們檢測到的關鍵點不太可能與錯誤的姿態相關聯。出於這個原因,即使用例是用來檢測單人的姿態,該演算法也可能更合乎需要。
此外,該演算法吸引人的特性是效能不受輸入影象中人數的影響。無論是 15 人還是 5 人,計算時間都是一樣的。
讓我們看一下它的輸入:
- 輸入影象元素 —— 與單人姿態估計相同
- 影象比例因子 —— 與單人姿態估計相同
- 水平翻轉 —— 與單人姿態估計相同
- 輸出步幅 —— 與單人姿態估計相同
- 最大檢測姿態 —— 一個整數。預設為 5,表示要檢測的姿態的最大數量。
- 姿態置信分數閾值 —— 0.0 至 1.0。預設為 0.5。在更深層次上,這將控制返回姿態的最低置信度分數。
- 非最大抑制(NMS,Non-maximum suppression)半徑 —— 以畫素為單位的數字。在更深層次上,這控制了返回姿態之間的最小距離。這個值預設為 20,這在大多數情況下可能是好的。可以通過增加/減少,以濾除不太準確的姿態,但只有在調整姿態置信度分數無法滿足時才調整它。
檢視這些引數有什麼影響的最好方法是體驗這個多人姿態估計演示。
讓我們看一下它的輸出:
- 以一系列姿態為
resolve
的promise
。 - 每個姿態包含與單人姿態估計演算法中相同的資訊。
這段程式碼塊顯示瞭如何使用多人姿態估計演算法:
const imageScaleFactor = 0.50;
const flipHorizontal = false;
const outputStride = 16;
// 最多 5 個姿態
const maxPoseDetections = 5;
// 姿態的最小置信度
const scoreThreshold = 0.5;
// 兩個姿態之間的最小畫素距離
const nmsRadius = 20;
const imageElement = document.getElementById('cat');
// 載入 posenet
const net = await posenet.load();
const poses = await net.estimateMultiplePoses(
imageElement, imageScaleFactor, flipHorizontal, outputStride,
maxPoseDetections, scoreThreshold, nmsRadius);
複製程式碼
輸出陣列的示例如下所示:
// 姿態/人的陣列
[
{ // pose #1
"score": 0.42985695206067,
"keypoints": [
{ // nose
"position": {
"x": 126.09371757507,
"y": 97.861720561981
},
"score": 0.99710708856583
},
...
]
},
{ // pose #2
"score": 0.13461434583673,
"keypositions": [
{ // nose
"position": {
"x": 116.58444058895,
"y": 99.772533416748
},
"score": 0.9978438615799
},
...
]
},
...
]
複製程式碼
如果你已經讀到了這裡,你應該瞭解的足夠多,可以開始 PoseNet 演示了。這可能是一個很好的停止點。如果你想了解更多關於該模型和實現的技術細節,我們邀請你繼續閱讀下文。
寫給好奇的人:技術深探
在本節中,我們將介紹關於單人姿態估計演算法的更多技術細節。在上層來看,這個過程如下所示:
使用 PoseNet 的單人姿態檢測器流程。
需要注意的一個重要細節是研究人員訓練了 PoseNet 的 ResNet 模型和 MobileNet 模型。雖然 ResNet 模型具有更高的準確性,但其大尺寸和多層的特點會使頁面載入時間和推導時間對於任何實時應用程式都不理想。我們使用 MobileNet 模型,因為它是為移動裝置而設計的。
重新審視單人姿態估計演算法
處理模型輸入:輸出步幅的解釋
首先,我們將說明如何通過討論輸出步幅來獲得 PoseNet 模型輸出(主要是熱圖和偏移向量)。
比較方便地是,PoseNet 模型是影象大小不變的,這意味著它能以與原始影象相同的比例預測姿態位置,而不管影象是否縮小。這意味著 PoseNet 可以通過設定上面我們在執行時提到的輸出步幅,以犧牲效能獲得更高的精度。
輸出步幅決定了我們相對於輸入影象大小縮小輸出的程度。它會影響圖層和模型輸出的大小。輸出步幅越大,網路中的層和輸出的解析度越小,準確性也越低。在此實現中,輸出步幅可以為 8,16 或 32。換句話說,輸出步幅為 32 將輸出最快但是精度最低,而 8 的精度最高但是最慢。我們建議從 16 開始。
輸出步幅決定了輸出相對於輸入影象的縮小程度。較高的輸出步幅會更快,但會導致精度較低。
本質是,當輸出步幅設定為 8 或 16 時,層中的輸入量減少使得可以建立更大的輸出解析度。然後使用 Atrous 卷積來使後續圖層中的卷積濾波器具有更寬的視野(當輸出步幅為 32 時,不會應用 atrous 卷積)。雖然 Tensorflow 支援 atrous 卷積,但 TensorFlow.js 不支援,所以我們新增了一個 PR 來包含這個。
模型輸出:熱圖和偏移向量
當 PoseNet 處理影象時,事實上返回的是熱圖以及偏移向量,可以解碼以找到影象中與姿態關鍵點對應的高置信度區域。我們將在一分鐘內討論它們各自的含義,但現在下面的插圖以高階方式捕獲每個姿態關鍵點與一個熱圖張量和一個偏移向量張量的關聯。
PoseNet 返回的 17 個姿態關鍵點中的每一個都與一個熱圖張量和一個偏移向量張量相關聯,用於確定關鍵點的確切位置。
這兩個輸出都是具有高度和寬度的 3D 張量,我們將其稱為解析度。解析度由輸入影象大小和輸出步幅根據以下公式確定:
Resolution = ((InputImageSize - 1) / OutputStride) + 1
// 示例:寬度為 225 畫素且輸出步幅為 16 的輸入影象產生輸出解析度為 15
// 15 = ((225 - 1) / 16) + 1
複製程式碼
熱圖
每個熱圖是尺寸 resolution x resolution x 17 的 3D 張量,因為 17 是 PoseNet 檢測到的關鍵點的數量。例如,影象大小為 225,輸出步幅為 16,那麼就是 15 x 15 x 17。第三維(17)中的每個切片對應於特定關鍵點的熱圖。該熱圖中的每個位置都有一個置信度分數,這是該型別關鍵點的一部分在該位置的概率。它可以被認為是原始影象被分解成 15 x 15 網格,其中熱圖分數提供了每個網格中每個關鍵點存在概率的等級。
偏移向量
每個偏移向量都是尺寸 resolution x resolution x 34 的 3D 張量,其中 34 是關鍵點數* 2。影象大小為 225,輸出步幅為 16 時,它是 15 x 15 x 34。由於熱圖是關鍵點位置的近似值,所以偏移向量在位置上對應於熱圖中的點,用於遍歷熱圖點對應的向量,從而預測關鍵點的確切位置。偏移向量的前 17 個切片包含向量的 x 座標和後 17 個包含 y 座標。偏移向量大小與原始影象具有相同的比例。
根據模型的輸出估計姿態
影象通過模型饋送後,我們執行一些計算來從輸出中估計姿態。例如,單人姿態估計演算法返回姿態置信度分數,它包含關鍵點陣列(以各部分的 ID 作索引),每個關鍵點具有置信度分數和 x,y 位置。
為了獲得姿態的關鍵點:
- 呼叫熱圖的 sigmoid 方法,以獲得分數。
scores = heatmap.sigmoid()
- argmax2d 是根據關鍵點置信度得分來獲得熱圖中 x 和 y 索引,取每個部分的得分最高者,一般也是該部分最有可能存在的地方。這會產生一個尺寸為 17 x 2 的張量,每一行都是每部分得分最高的熱圖中的 y 和 x 索引。
heatmapPositions = scores.argmax(y, x)
- 每部分的偏移向量通過從該部分的熱圖中對應 x 和 y 索引的偏移量中獲取 x 和 y。這會產生 17 x 2 的張量,每行都是相應關鍵點的偏移向量。例如,對於索引為 k 的部分,當熱圖位置為 y 和 x 時,偏移向量為:
offsetVector = [offsets.get(y, x, k), offsets.get(y, x, 17 + k)]
- 為了獲得關鍵點,將每部分的熱圖 x 和 y 乘以輸出步幅,然後將其新增到它們對應的偏移向量中,該向量與原始影象具有相同的比例。
keypointPositions = heatmapPositions * outputStride + offsetVectors
- 最後,每個關鍵點置信度分數是其熱圖位置的置信度分數。該姿態的置信度得分是關鍵點的評分的平均值。
多人姿態估計
多人姿態估計演算法的細節超出了本文的範圍。該演算法的主要不同之處在於它使用貪婪過程通過沿著基於部分圖的位移向量將關鍵點分組為姿態。具體來說,它使用研究論文 PersonLab: 自下而上的區域性幾何嵌入模型的人體姿態估計和例項分割中的快速貪婪解碼演算法。有關多人姿態演算法的更多資訊,請閱讀完整的研究論文或檢視程式碼。
我們希望隨著越來越多的模型被移植到 TensorFlow.js,機器學習的世界變得對更容易被新的開發者和製造者接受,更受歡迎和更有趣。基於 TensorFlow.js 的 PoseNet 是實現這一目標的一個小小嚐試。我們很樂意看到你做出了什麼 —— 不要忘記使用 #tensorflowjs 和 #posenet 分享您的精彩專案!
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。