OpenSitUp開源項(xiàng)目:零基礎(chǔ)開發(fā)基于姿態(tài)估計(jì)的運(yùn)動健身APP

更多深度學(xué)習(xí)工程實(shí)踐項(xiàng)目,請關(guān)注公眾號:DL工程實(shí)踐

1.項(xiàng)目開源地址
https://github.com/DL-Practise/OpenSitUp
?
2.項(xiàng)目簡介
計(jì)算機(jī)視覺中有一個(gè)應(yīng)用分支叫做姿態(tài)估計(jì),通過人體關(guān)鍵點(diǎn)的方式來估計(jì)出一個(gè)/多個(gè)人的姿態(tài)信息。如下圖所示:

OpenSitUp是一個(gè)基于姿態(tài)估計(jì)的開源項(xiàng)目,旨在幫助對姿態(tài)估計(jì)感興趣的朋友,能夠從零開始搭建一個(gè)在android手機(jī)上運(yùn)行的仰臥起坐計(jì)數(shù)APP。主要的技術(shù)難點(diǎn)為如何讓計(jì)算量較大的人體姿態(tài)估計(jì)網(wǎng)絡(luò)流暢的運(yùn)行在手機(jī)端,并且實(shí)現(xiàn)仰臥起坐的計(jì)數(shù)功能。掌握了這個(gè)項(xiàng)目的原理之后,可以很方便的遷移到類似的運(yùn)動,健身APP當(dāng)中。
?
3.項(xiàng)目成果展示
如下展示的是這個(gè)項(xiàng)目最后的APP效果,在人潮涌動的西湖景區(qū),我當(dāng)眾躺下做仰臥起坐,羞煞老夫也!

4.項(xiàng)目目錄
由于需要從零開始開發(fā)仰臥起坐計(jì)數(shù)APP,因此整個(gè)項(xiàng)目需要包含多個(gè)工程,包括數(shù)據(jù)采集,標(biāo)注,訓(xùn)練,部署,APP開發(fā)等,整體目錄結(jié)構(gòu)如下圖所示:
4.1 DataSet
數(shù)據(jù)集存放目錄,這里我預(yù)先放置了300多張標(biāo)注好的圖片,用這些圖片已經(jīng)可以訓(xùn)練出“項(xiàng)目成果展示”中展示的效果。但是為了獲得更好的性能,您可以采集更多的仰臥起坐圖片。
4.2 LabelTool
這里為您準(zhǔn)備了一個(gè)適用于該項(xiàng)目的標(biāo)注工具,主要是標(biāo)注人體的一些關(guān)鍵點(diǎn)。當(dāng)您采集了很多仰臥起坐的圖片之后,可以使用該工具進(jìn)行標(biāo)注,生成相應(yīng)的標(biāo)簽。
4.3 Trainer
這是一個(gè)基于pytorch的關(guān)鍵點(diǎn)訓(xùn)練工具,里面包含針對手機(jī)設(shè)計(jì)的輕量級關(guān)鍵點(diǎn)檢測網(wǎng)絡(luò)。
4.4 SiteUpAndroid
Android上的仰臥起坐計(jì)數(shù)APP。
?
5.項(xiàng)目流程:
5.1 采集圖片
由于沒有現(xiàn)成的仰臥起坐數(shù)據(jù)集,只能自己動手,豐衣足食。好在對于仰臥起坐這樣常規(guī)的運(yùn)動,網(wǎng)上還是有很多相關(guān)資源的。這里我采用下載視頻和圖片兩種方式。先從網(wǎng)上搜索“仰臥起坐”的視頻,下載了10個(gè)左右的視頻片段,然后通過抽幀的方式,從每個(gè)視頻中抽取一部分幀作為訓(xùn)練用的數(shù)據(jù)。如下圖所示為從視頻中抽取的關(guān)鍵幀。

僅僅使用視頻中抽取的幀會有一個(gè)比較嚴(yán)重的問題,就是背景過于單一,很容易造成過擬合。于是我從網(wǎng)上進(jìn)行圖片搜索,得到一分部背景較為豐富的圖片,如下圖所示:

?
5.2 標(biāo)注圖片
收集完數(shù)據(jù),就是進(jìn)行標(biāo)注了,雖然已經(jīng)有一些現(xiàn)成的開源標(biāo)注工具,但是我用的不順手,因此自己開發(fā)了一款關(guān)鍵點(diǎn)標(biāo)注工具,就是上面開源的LabelTool,畢竟自己開發(fā)的,用著順手。注意該工具在win10/python 3.6環(huán)境下做過測試,其他環(huán)境暫時(shí)沒有測試。使用命令python main_widget.py打開界面。初始界面非常簡潔,通過“打開”按鈕來打開收集好的仰臥起坐圖片。

?
在類別中使用0表示標(biāo)注的是頭部,1表示標(biāo)注的是膝蓋,2表示標(biāo)注的是胯部(由于我們需要在手機(jī)上識別仰臥起坐,需要盡可能的減少計(jì)算量,姿態(tài)估計(jì)一般會預(yù)測全身很多的關(guān)鍵點(diǎn),但是對于仰臥起坐,只要能準(zhǔn)確的預(yù)測頭部,膝蓋和胯部,就能較好的進(jìn)行仰臥起坐的動作識別,因此這里只需要標(biāo)注三個(gè)點(diǎn))。單擊鼠標(biāo)左鍵進(jìn)行標(biāo)注,右鍵取消上一次標(biāo)注。不得不說,用python+qt開發(fā)一些基于UI的工具非常方便!與C++相比,解放了太多的生產(chǎn)力!

?
標(biāo)注完圖片之后,會在圖片目錄下面生成一個(gè)標(biāo)簽文件label.txt,里面的內(nèi)容如下:

?? ?
5.3 算法原理
我先簡單的介紹一下仰臥起坐的算法原理。在姿態(tài)估計(jì)(關(guān)鍵點(diǎn)檢測)領(lǐng)域,一般很少采用回歸的方式來預(yù)測關(guān)鍵點(diǎn)位置,取而代之的是采用heatmap輸出關(guān)鍵點(diǎn)的位置。這和anchor free的目標(biāo)檢測中的centness之類的做法差不多,即通過查找heatmap中響應(yīng)值最大的點(diǎn)來確定關(guān)鍵點(diǎn)的坐標(biāo)。如下圖所示(只顯示部分heatmap):

?
思考了一下原因,直接回歸坐標(biāo),通常會將最后的featuremap下采樣到很小,這樣才能夠?qū)崿F(xiàn)全局的回歸,但是關(guān)鍵點(diǎn)預(yù)測這種任務(wù)對位置信息非常敏感,過小的特征會極大的丟失空間信息,因而導(dǎo)致預(yù)測位置非常不準(zhǔn)。而heatmap方式一般要求最后的特征圖比較大,通常是輸入圖片的1/2或者1/4,那么就非常適合做一些空間相關(guān)的任務(wù)。其實(shí)如果人為的將特征圖壓縮的很小,heatmap的方式也一樣不太準(zhǔn)。有了上面的思考,便有了最終的方案,就是將shufflenet最后輸出的7*7的特征圖進(jìn)行上采樣到3*56*56大?。紤]到最終的應(yīng)用以及場景,56*56足夠?qū)崿F(xiàn)仰臥起坐動作的識別),3表示的是3個(gè)關(guān)鍵點(diǎn)。然后輸出的特征經(jīng)過sigmoid激活之后便得到了3*56*56的heatmaps。這里多提兩點(diǎn),就是heatmap標(biāo)簽的設(shè)計(jì)和loss的平衡問題。先說說標(biāo)簽的設(shè)計(jì),如果只是簡單的將標(biāo)簽轉(zhuǎn)化成一個(gè)one_hot的heatmap,效果不會太好。因?yàn)闃?biāo)簽點(diǎn)附件的點(diǎn)實(shí)際上對于網(wǎng)絡(luò)來說提取的特征是類似的,那么如果強(qiáng)行把不是標(biāo)簽附近的點(diǎn)設(shè)置為0,表現(xiàn)不會很好,一般會用高斯分布來制作標(biāo)簽heatmap,如下圖所示:

另外要說的就是loss的平衡了,上面的標(biāo)簽heatmap大家也看到了,無論是one-hot的heatmap還是高斯分布的heatmap,大部分的點(diǎn)都是負(fù)樣本點(diǎn),直接使用MSE而不加以區(qū)分,網(wǎng)絡(luò)基本上會訓(xùn)練出一個(gè)輸出全是0的heatmap。主要原因就是訓(xùn)練的梯度被負(fù)樣本壓制,正樣本的梯度實(shí)在太小。因此需要做一個(gè)分區(qū)。我這里把正負(fù)樣本的比重設(shè)置為10:1。
?
5.3 Trainer訓(xùn)練工具
Trainer工具主要包括四個(gè)部分:
cfg:配置文件目錄
data:數(shù)據(jù)讀取目錄
DLEngine:訓(xùn)練引擎
models:網(wǎng)絡(luò)模型目錄
首先在models下的keypoint目錄下,我實(shí)現(xiàn)了上述討論的基于shufflenet的關(guān)鍵點(diǎn)檢測網(wǎng)絡(luò),ShuffleNetV2HeatMap,然后在data目錄下實(shí)現(xiàn)了讀取LabelTool標(biāo)注的標(biāo)簽文件的數(shù)據(jù)集讀取工具:person_keypoint_txt.py。最后在配置文件夾cfgs下的key_point目錄下實(shí)現(xiàn)了針對該項(xiàng)目的配置文件:keypoint_shufflenetv2_heatmap_224_1.0_3kps.py,里面包含的主要字段如下:




啟動訓(xùn)練前,將train.py文件中的CFG_FILE修改成上述配置文件即可:
CFG_FILE='cfgs/key_point/keypoint_shufflenetv2_heatmap_224_1.0_3kps.py'。使用命令 python train.py 啟動訓(xùn)練。
?
5.4 轉(zhuǎn)換模型
在Trainer中完成訓(xùn)練之后,會在save目錄下面生成相應(yīng)的模型文件。但是這些pytorch的模型無法直接部署到手機(jī)中運(yùn)行,需要使用相應(yīng)的推理庫。目前開源的推理庫有很多,例如mnn,ncnn,tnn等。這里我選擇使用ncnn,因?yàn)閚cnn開源的早,使用的人多,網(wǎng)絡(luò)支持,硬件支持都還不錯,關(guān)鍵是很多問題都能搜索到別人的經(jīng)驗(yàn),可以少走很多彎路。但是遺憾的是ncnn并不支持直接將pytorch模型導(dǎo)入,需要先轉(zhuǎn)換成onnx格式,然后再將onnx格式導(dǎo)入到ncnn中。另外注意一點(diǎn),將pytroch的模型到onnx之后有許多膠水op,這在ncnn中是不支持的,需要使用另外一個(gè)開源工具:onnx-simplifier對onnx模型進(jìn)行剪裁,然后再導(dǎo)入到ncnn中。因此整個(gè)過程還有些許繁瑣,為此,我在Trainer工程中,編寫了export_ncnn.py 腳本,可以一鍵將訓(xùn)練出來的pytorch模型轉(zhuǎn)換成ncnn模型。轉(zhuǎn)換成功后,會在save目錄下的pytorch模型文件夾下生成三個(gè)ncnn相關(guān)的文件:model.param; model.bin以及? ncnn.cfg。
?
5.5 APP開發(fā)
android的APP開發(fā)主要包括一個(gè)Activity類,兩個(gè)SurfaceView類,一個(gè)Alg類,一個(gè)Camera類組成。Alg類主要負(fù)責(zé)調(diào)用算法進(jìn)行推理,并返回結(jié)果。這里實(shí)際上是調(diào)用的NCNN庫的推理功能。Camera類主要負(fù)責(zé)攝像頭的打開和關(guān)閉,以及進(jìn)行預(yù)覽回調(diào)。第一個(gè)SurfaceView(DisplayView)主要用于攝像頭預(yù)覽的展示。第二個(gè)SurfaceView(CustomView)主要用于繪制一些關(guān)鍵點(diǎn)信息,計(jì)數(shù)統(tǒng)計(jì)信息等。Activity就是最上層的一個(gè)管理類,負(fù)責(zé)管理整個(gè)APP,包括創(chuàng)建按鈕,創(chuàng)建SurfaceView,創(chuàng)建Alg類,創(chuàng)建Camera類等。

具體的代碼邏輯可以查看SiteUpAndroid源碼。
?