1. 程式人生 > >用 Python 實現抖音尬舞機

用 Python 實現抖音尬舞機

如今說到體感遊戲,大家一定都不陌生,比如微軟的 Kinect、任天堂的 Switch,都曾是遊戲業的革命性產品。而另一款網紅產品—抖音,也在去年底上線過一個“尬舞機”的音樂體感遊戲(現在成了隱藏功能):

遊戲開始後,隨著音樂會給出不同的動作提示,使用者按照提示擺出正確動作即可得分。援引官方說法,“尬舞機”主要應用了今日頭條 AI Lab 自主開發的“人體關鍵點檢測技術”,依靠這項技術,抖音能夠檢測到影象中所包含人體的各個關鍵點的位置,從而實現從使用者姿態到目標姿態的準確匹配。

以上這些體感遊戲,都牽涉到計算機視覺中的一個細分領域:人體姿態估計(pose estimation),即識別影象中的人體關鍵點(人體上有一定自由度的關節,如頭、頸、肩、肘、腕、腰、膝、踝等)並正確的聯絡起來,通過對人體關鍵點在三維空間相對位置的計算,來估計人體當前的姿態。

人體姿態估計有不少難點,比如:如何從圖片中區分出人和背景;如何定位人體的關鍵點;如何根據二維的關鍵點座標計算出三維中的姿態;如何處理四肢交叉或遮擋的情況;如何定位多人;如何提升計算速度等等。而相關技術在遊戲、安防、人機互動、行為分析等方面都有應用前景。因此,這是計算機視覺甚至人工智慧領域中極具挑戰的一個課題。(小聲說句,我的碩士畢業論文就是這個方向)

不過,因為前人的貢獻,現在你只需通過少量的 Python 程式碼,也可以實現從照片或視訊中進行人體姿態估計。這都要仰賴於 CMU 的開源專案:Openpose

OpenPose 是基於卷積神經網路和監督學習並以 caffe 為框架寫成的開源庫,可以實現人的面部表情、軀幹和四肢甚至手指的跟蹤,適用多人且具有較好的魯棒性。是世界上第一個基於深度學習的實時多人二維姿態估計,為機器理解人類提供了一個高質量的資訊維度。其理論基礎來自《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》,是 CVPR 2017 的一篇論文,作者是來自 CMU 感知計算實驗室的曹哲、Tomas Simon、Shih-En Wei、Yaser Sheikh。專案地址:
github.com/ZheC/Realtim
(摘自網路)

論文演示效果:

此方法可以達到對視訊流的實時多人檢測。要知道,Kinect 可是加了一個額外的紅外深度攝像頭才做到如此準確地識別(還不能是這麼多人)。

詳細的原理,我在這裡就不冒充大牛強行解釋了。但通俗地說幾點,為什麼 Openpose 有如此突破性地效果:

  1. 以往的識別思路是自上而下:先找人,找到人了再進一步區分身體不同部分。Openpose 則是自下而上:先找手腳關節等特徵部位,再組合人體
  2. Openpose 團隊將人臉識別手部識別的已有成果整合到了姿態識別中,取得了更好的效果;
  3. 有了大資料的支援,這是過去的研究所沒有的。看看這個 CMU 為採集人體資料所搭建的裝置,你就會有所體會:

之前的文章 Python OpenCV 十幾行程式碼模仿世界名畫 中,我們提到 OpenCV-Python 在 3.3 版本中加入了深度神經網路(DNN)的支援。同樣在專案 Samples 中,提供 Openpose 的一個 Python 簡單實現版本。(只支援影象中有單個人)

官方程式碼:

使用方法,命令列進入程式碼所在目錄執行:

python openpose.py --model pose.caffemodel --proto pose.prototxt --dataset MPI

--model 引數和 --proto 引數分別是預先訓練好的人體姿態模型和配置檔案。因為模型檔案很大,並不包括在 OpenCV 程式碼庫中,可以在 Openpose 專案(github.com/CMU-Perceptu )找到下載地址。

另外可以通過 --input 引數指定識別的圖片或視訊地址,預設則使用攝像頭實時採集。

執行後效果:

核心程式碼:

net = cv.dnn.readNetFromCaffe(args.proto, args.model)
inp = cv.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), (0, 0, 0), swapRB=False, crop=False)
net.setInput(inp)
out = net.forward()

和之前 fast-neural-style 的程式碼類似,大部分的工作都是 Openpose 做好的,OpenCV 這裡只是使用訓練好的神經網路進行計算。所以核心程式碼其實沒有幾行,而且跟上次的例子幾乎一致。剩下一半的程式碼都是在把獲取到的關鍵點座標繪製成人體的骨架結構。

這裡順帶提醒一下,我發現程式碼中的一個斷言 assert(len(BODY_PARTS) == out.shape[1]) 無法滿足,會導致程式終止。如果出現這樣的問題,就把這句註釋掉,並不會對結果有影響。

拿到人體關鍵點資料後,我們就可以做進一步的判斷。比如我們加一個很簡單的判斷:

neck = points[BODY_PARTS['Neck']]
left_wrist = points[BODY_PARTS['LWrist']]
right_wrist = points[BODY_PARTS['RWrist']]
print(neck, left_wrist, right_wrist)if neck and left_wrist and right_wrist and left_wrist[1] < neck[1] and right_wrist[1] < neck[1]:
    cv.putText(frame, 'HANDS UP!', (10, 100), cv.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 2)

如果左手腕和右手腕的高度都超過脖子的高度,就認為是一個擡手的動作,在螢幕上輸出“HANDS UP!”。注意在 OpenCV 座標系裡,影象的座標原點是左上角。

效果:

如此,一個簡單的動作識別程式就有了。雖然很粗糙,但已經可以附加在很多應用上,比如:商場、科技館裡的互動遊戲、互動式的視覺藝術作品等等。感興趣的同學不妨親自試一試,期待看到你們藉此做出更有意思的專案。

獲取文中相關程式碼和模型下載地址,請在公眾號(Crossin的程式設計教室)對話裡回覆關鍵字 姿態

════其他文章及回答:

歡迎微信搜尋及關注:Crossin的程式設計教室