[开源]基于姿态估计的运动计数APP开发(二)
1、先展示一下當前的效果
從keep上扒了一段仰臥起坐的視頻教程進行計數測試:
(CSDN放不了視頻,有興趣的下方評論區留言)
2、回顧:
在上一期的內容中([開源]基于姿態估計的運動計數APP開發(一)),通過使用shufflenet輕量級網絡+上采樣輸出關鍵點的heatmap已經可以在coco數據集中進行訓練,并能夠進行關鍵點識別。但是也存在一個問題,就是針對仰臥起坐這種動作,識別準確率非常低。通過分析原因,主要有兩方面。一是開源的數據集中人的姿態是一些比較生活化的姿態,很少有仰臥起坐之類的姿態。另外一方面是網絡本身比較小,提取特征能力有限,且為了在移動端實現實時檢測,輸出分辨率被限制在224*224,這些都會限制精度。本期主要是基于這些問題展開一些優化,初步實現一個可以進行實時計數的demo。
3、重新思考數據:
前面已經分析了coco以及mpii這些數據集對于我們訓練仰臥起坐計數器這樣一個APP來說并不是非常合適。并且對于這個任務來說,其實并不需要識別那么多的關鍵點。只要識別兩個關鍵點就可以了,一個是頭部關鍵點,一個是膝蓋關鍵點。這樣的話既不會影響我們最終計數APP的功能,又使得網絡的任務減輕,可以專注與識別這兩個關鍵點,從而提升精度。如下圖所示。
由于沒有現成的仰臥起坐數據集,只能自己動手,豐衣足食。好在對于仰臥起坐這樣常規的運動,網上還是有很多相關資源的。這里我采用下載視頻和圖片兩種方式。先從網上搜索“仰臥起坐”的視頻,下載了10個左右的視頻片段,然后通過抽幀的方式,從每個視頻中抽取一部分幀作為訓練用的數據。如下圖所示為從視頻中抽取的關鍵幀。
僅僅使用視頻中抽取的幀會有一個比較嚴重的問題,就是背景過于單一,很容易造成過擬合。于是我從網上進行圖片搜索,得到一分部背景較為豐富的圖片,如下圖所示:
收集完數據,就是進行標注了,這里我為了方便,自己開發了一款關鍵點標注工具,畢竟自己開發的,用著順手。鼠標左鍵進行標注,右鍵取消上一次標注。不得不說,用python+qt開發一些基于UI的工具非常方便!與C++相比,解放了太多的生產力!
4、解決過擬合:
????通過前面搜集的大約1K張圖片,訓練完之后會發現泛化性能并不好,很容易出現誤識別。其實不難發現,由于數據量太少,網絡已經出現了過擬合,訓練到最后的loss非常小。解決過擬合最好的辦法是增加數據量,但是時間有限,真的不想再去收集,標注數據,簡直是浪費青春啊。于是就得考慮用一些數據增強的方法。我之前已經使用了一些光照增強的方法,例如隨機改變亮度,隨機調整HSV,這里主要增加一些幾何上的變換。由于需要修改標簽值,因此會麻煩一些。這里我主要考慮crop,padding,以及flip。
用來上述數據增強方法之后,效果顯著改善了一些,過擬合沒有那么嚴重,但是會出現一些錯誤的召回,會把一些書包或者衣服之類的當作關鍵點。那么主要原始還是訓練數據的背景不夠豐富,這里采用mixup的方法,從coco數據集中挑選一部分沒有人的圖片作為背景,隨機的與訓練圖片進行重疊,從而有利于解決這種問題。
最終經過這些數據增強之后效果還不錯。下面是相關代碼,crop和padding合在一起實現。
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、在線難例挖掘
????通過上面的數據增強,訓練出來的模型已經具備了一定的能力,但是通過大量的測試圖片發現網絡對于膝蓋和頭部的檢測能力是不一樣的。膝蓋的檢測較為穩定,而頭部的檢測經常會出現錯誤。分析原因,可能在于頭部的變化比膝蓋大的多,頭部可能是正對相機,背對相機,也有可能被手臂擋住,因為網絡很難學習到真正的頭部特征。這里可以通過兩種方法來改善,一種是簡單的通過loss權重來賦予頭部更大的權值,使得頭部的梯度信息比膝蓋的大,強迫網絡更關注頭部信息,還有一種就是用到了online hard keypoint mining。這是我在查看曠世的cpn人體姿態估計網絡時候看到的,作者有視頻進行介紹。其實實現起來很簡單,就是對不同的關鍵點分別求loss,然后對loss排序,只返回一定比例的loss求梯度,作者給出的比例是0.5,即返回一半的關鍵點loss來求梯度。
6、總結
????這個階段主要是對網絡精度的提升,通過精簡關鍵點數目,重新收集,標注數據,增加padding,crop,flip數據增強,并引入mixup和在線難例挖掘等措施,來逐步提升網絡泛化性能。并實現了一個python的demo(見文章開頭),下一期主要是將該demo的功能實現為APP。
@end
總結
以上是生活随笔為你收集整理的[开源]基于姿态估计的运动计数APP开发(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [开源]基于姿态估计的运动计数APP开发
- 下一篇: [开源]基于姿态估计的运动计数APP开发