[开源]基于姿态估计的运动计数APP开发(二)
1、先展示一下當(dāng)前的效果
從keep上扒了一段仰臥起坐的視頻教程進(jìn)行計(jì)數(shù)測(cè)試:
(CSDN放不了視頻,有興趣的下方評(píng)論區(qū)留言)
2、回顧:
在上一期的內(nèi)容中([開源]基于姿態(tài)估計(jì)的運(yùn)動(dòng)計(jì)數(shù)APP開發(fā)(一)),通過使用shufflenet輕量級(jí)網(wǎng)絡(luò)+上采樣輸出關(guān)鍵點(diǎn)的heatmap已經(jīng)可以在coco數(shù)據(jù)集中進(jìn)行訓(xùn)練,并能夠進(jìn)行關(guān)鍵點(diǎn)識(shí)別。但是也存在一個(gè)問題,就是針對(duì)仰臥起坐這種動(dòng)作,識(shí)別準(zhǔn)確率非常低。通過分析原因,主要有兩方面。一是開源的數(shù)據(jù)集中人的姿態(tài)是一些比較生活化的姿態(tài),很少有仰臥起坐之類的姿態(tài)。另外一方面是網(wǎng)絡(luò)本身比較小,提取特征能力有限,且為了在移動(dòng)端實(shí)現(xiàn)實(shí)時(shí)檢測(cè),輸出分辨率被限制在224*224,這些都會(huì)限制精度。本期主要是基于這些問題展開一些優(yōu)化,初步實(shí)現(xiàn)一個(gè)可以進(jìn)行實(shí)時(shí)計(jì)數(shù)的demo。
3、重新思考數(shù)據(jù):
前面已經(jīng)分析了coco以及mpii這些數(shù)據(jù)集對(duì)于我們訓(xùn)練仰臥起坐計(jì)數(shù)器這樣一個(gè)APP來說并不是非常合適。并且對(duì)于這個(gè)任務(wù)來說,其實(shí)并不需要識(shí)別那么多的關(guān)鍵點(diǎn)。只要識(shí)別兩個(gè)關(guān)鍵點(diǎn)就可以了,一個(gè)是頭部關(guān)鍵點(diǎn),一個(gè)是膝蓋關(guān)鍵點(diǎn)。這樣的話既不會(huì)影響我們最終計(jì)數(shù)APP的功能,又使得網(wǎng)絡(luò)的任務(wù)減輕,可以專注與識(shí)別這兩個(gè)關(guān)鍵點(diǎn),從而提升精度。如下圖所示。
由于沒有現(xiàn)成的仰臥起坐數(shù)據(jù)集,只能自己動(dòng)手,豐衣足食。好在對(duì)于仰臥起坐這樣常規(guī)的運(yùn)動(dòng),網(wǎng)上還是有很多相關(guān)資源的。這里我采用下載視頻和圖片兩種方式。先從網(wǎng)上搜索“仰臥起坐”的視頻,下載了10個(gè)左右的視頻片段,然后通過抽幀的方式,從每個(gè)視頻中抽取一部分幀作為訓(xùn)練用的數(shù)據(jù)。如下圖所示為從視頻中抽取的關(guān)鍵幀。
僅僅使用視頻中抽取的幀會(huì)有一個(gè)比較嚴(yán)重的問題,就是背景過于單一,很容易造成過擬合。于是我從網(wǎng)上進(jìn)行圖片搜索,得到一分部背景較為豐富的圖片,如下圖所示:
收集完數(shù)據(jù),就是進(jìn)行標(biāo)注了,這里我為了方便,自己開發(fā)了一款關(guān)鍵點(diǎn)標(biāo)注工具,畢竟自己開發(fā)的,用著順手。鼠標(biāo)左鍵進(jìn)行標(biāo)注,右鍵取消上一次標(biāo)注。不得不說,用python+qt開發(fā)一些基于UI的工具非常方便!與C++相比,解放了太多的生產(chǎn)力!
4、解決過擬合:
????通過前面搜集的大約1K張圖片,訓(xùn)練完之后會(huì)發(fā)現(xiàn)泛化性能并不好,很容易出現(xiàn)誤識(shí)別。其實(shí)不難發(fā)現(xiàn),由于數(shù)據(jù)量太少,網(wǎng)絡(luò)已經(jīng)出現(xiàn)了過擬合,訓(xùn)練到最后的loss非常小。解決過擬合最好的辦法是增加數(shù)據(jù)量,但是時(shí)間有限,真的不想再去收集,標(biāo)注數(shù)據(jù),簡(jiǎn)直是浪費(fèi)青春啊。于是就得考慮用一些數(shù)據(jù)增強(qiáng)的方法。我之前已經(jīng)使用了一些光照增強(qiáng)的方法,例如隨機(jī)改變亮度,隨機(jī)調(diào)整HSV,這里主要增加一些幾何上的變換。由于需要修改標(biāo)簽值,因此會(huì)麻煩一些。這里我主要考慮crop,padding,以及flip。
用來上述數(shù)據(jù)增強(qiáng)方法之后,效果顯著改善了一些,過擬合沒有那么嚴(yán)重,但是會(huì)出現(xiàn)一些錯(cuò)誤的召回,會(huì)把一些書包或者衣服之類的當(dāng)作關(guān)鍵點(diǎn)。那么主要原始還是訓(xùn)練數(shù)據(jù)的背景不夠豐富,這里采用mixup的方法,從coco數(shù)據(jù)集中挑選一部分沒有人的圖片作為背景,隨機(jī)的與訓(xùn)練圖片進(jìn)行重疊,從而有利于解決這種問題。
最終經(jīng)過這些數(shù)據(jù)增強(qiáng)之后效果還不錯(cuò)。下面是相關(guān)代碼,crop和padding合在一起實(shí)現(xiàn)。
class KPRandomPadCrop(object):def __init__(self, ratio=0.25, pad_value=[128, 128, 128]):assert (ratio > 0 and ratio <= 1)self.ratio = ratioself.pad_value = pad_valuedef __call__(self, image, labels=None):if random.randint(0,1):h, w = image.shape[:2]top_offset = int(h * random.uniform(0, self.ratio))bottom_offset = int(h * random.uniform(0, self.ratio))left_offset = int(w * random.uniform(0, self.ratio))right_offset = int(w * random.uniform(0, self.ratio))# padif random.randint(0,1):image = cv2.copyMakeBorder(image, top_offset, bottom_offset, left_offset, right_offset, cv2.BORDER_CONSTANT, value=self.pad_value)if labels is not None and len(labels) > 0:labels[:, 0] = (labels[:, 0] * w + left_offset) / (w + left_offset + right_offset)labels[:, 1] = (labels[:, 1] * h + top_offset) / (h + top_offset + bottom_offset)# cropelse:image = image[top_offset:h - bottom_offset, left_offset:w-right_offset]if labels is not None and len(labels) > 0:labels[:, 0] = (labels[:, 0] * w - left_offset) / (w - left_offset - right_offset)labels[:, 1] = (labels[:, 1] * h - top_offset) / (h - top_offset - bottom_offset)return image, labelsclass KPRandomHorizontalFlip(object):def __init__(self):passdef __call__(self, image, labels=None):if random.randint(0, 1):image = cv2.flip(image, 1)h, w = image.shape[:2]if labels is not None and len(labels) > 0:labels[:, 0] = 1.0 - labels[:, 0]return image, labelsclass KPRandomNegMixUp(object):def __init__(self, ratio=0.5, neg_dir='./coco_neg'):self.ratio = ratioself.neg_dir = neg_dirself.neg_images = []files = os.listdir(self.neg_dir)for file in files:if str(file).endswith('.jpg') or str(file).endswith('.png'):self.neg_images.append(str(file))def __call__(self, image, labels):if random.randint(0, 1):h, w = image.shape[:2]neg_name = random.choice(self.neg_images)neg_path = self.neg_dir + '/' + neg_nameneg_img = cv2.imread(neg_path)neg_img = cv2.resize(neg_img, (w, h)).astype(np.float32)neg_alpha = random.uniform(0, self.ratio)ori_alpha = 1 - neg_alphagamma = 0img_add = cv2.addWeighted(image, ori_alpha, neg_img, neg_alpha, gamma)return image, labelselse:return image, labels5、在線難例挖掘
????通過上面的數(shù)據(jù)增強(qiáng),訓(xùn)練出來的模型已經(jīng)具備了一定的能力,但是通過大量的測(cè)試圖片發(fā)現(xiàn)網(wǎng)絡(luò)對(duì)于膝蓋和頭部的檢測(cè)能力是不一樣的。膝蓋的檢測(cè)較為穩(wěn)定,而頭部的檢測(cè)經(jīng)常會(huì)出現(xiàn)錯(cuò)誤。分析原因,可能在于頭部的變化比膝蓋大的多,頭部可能是正對(duì)相機(jī),背對(duì)相機(jī),也有可能被手臂擋住,因?yàn)榫W(wǎng)絡(luò)很難學(xué)習(xí)到真正的頭部特征。這里可以通過兩種方法來改善,一種是簡(jiǎn)單的通過loss權(quán)重來賦予頭部更大的權(quán)值,使得頭部的梯度信息比膝蓋的大,強(qiáng)迫網(wǎng)絡(luò)更關(guān)注頭部信息,還有一種就是用到了online hard keypoint mining。這是我在查看曠世的cpn人體姿態(tài)估計(jì)網(wǎng)絡(luò)時(shí)候看到的,作者有視頻進(jìn)行介紹。其實(shí)實(shí)現(xiàn)起來很簡(jiǎn)單,就是對(duì)不同的關(guān)鍵點(diǎn)分別求loss,然后對(duì)loss排序,只返回一定比例的loss求梯度,作者給出的比例是0.5,即返回一半的關(guān)鍵點(diǎn)loss來求梯度。
6、總結(jié)
????這個(gè)階段主要是對(duì)網(wǎng)絡(luò)精度的提升,通過精簡(jiǎn)關(guān)鍵點(diǎn)數(shù)目,重新收集,標(biāo)注數(shù)據(jù),增加padding,crop,flip數(shù)據(jù)增強(qiáng),并引入mixup和在線難例挖掘等措施,來逐步提升網(wǎng)絡(luò)泛化性能。并實(shí)現(xiàn)了一個(gè)python的demo(見文章開頭),下一期主要是將該demo的功能實(shí)現(xiàn)為APP。
@end
總結(jié)
以上是生活随笔為你收集整理的[开源]基于姿态估计的运动计数APP开发(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [开源]基于姿态估计的运动计数APP开发
- 下一篇: 2个月没人管!AMD老显卡终于要有新驱动