CVPR2017 | G-RMI_Google大佬構建的姿態估計baseline

CVPR2017 Google | Towards accurate multi-person pose estimation in the wild
Official Code: pytorch

1.文章概述

正如文章中提到的in the wild,本文的目的是利用top-down類姿態估計算法,嘗試解決現實生活中各種實際存在的複雜情況下的人體姿態估計問題。其中最常見的是在人與人彼此靠近時,人體目標框中存在多個人體肢體的場景。文章利用fastrcnn檢測圖片中可能容納人體的目標框位置和大小,並估計每個框中可能包含的人體關鍵點。對於每種關鍵點類型,使用全卷積ResNet預測一個關鍵點熱度圖和兩個關鍵點偏移量(X軸,Y軸)。爲了結合這些輸出,引入了一種新穎的熱圖-偏移聚合方法來獲得精準的關鍵點預測。爲了避免重複關鍵點的預測,通過直接基於OKS指標(OKS-NMS)的新型基於關鍵點的非最大抑制(NMS)機制,而不是較粗糙的基於boundingbox 的IOU NMS。作者還提出了一種新穎的基於關鍵點的置信度估計器,與使用Faster-RCNN檢測框的得分進行結合得到最終姿態置信度,該方法能夠對檢測的AP有極大改善。本文提出的一種image_crop策略也被後續很多文章使用。

綜上所述,本文提出了四種有效提升關鍵點預測精度的Trick:
1.多輸出姿態估計網絡
2.熱圖-偏移解碼器
3.keypoint_rescore
4.keypoint_oks_nms
5.image_crop

2.多輸出姿態估計網絡

如下圖所示,本文提出的人體姿態估計網絡存在兩個輸出,其一與傳統的網絡類似:輸出N個Heatmap。第二個輸出爲:2N個2D偏置向量圖。N表示關鍵點類型個數。其中製作兩者的標籤時:Heatmap圖中關鍵點座標半徑內的值爲1,其餘爲0;2D偏置向量中離關鍵點座標越近的向量模長越小。如下圖所示展示了最終網絡通過整合Heatmap和偏置向量圖得到最終精確的人體關鍵點位置。網絡的backbone爲Resnet101。具體的整合方式在下述解碼器部分講解。需要注意的是下圖只是一種概念上的說明,事實上2D偏置圖是Heatmap圖的兩倍。(因爲包含了x座標和y座標)

如下圖的loss爲多輸出姿態估計網絡的損失函數,從中可以看出結合了2D向量的值和Heatmap的置信度值。

3.熱圖-偏移解碼器

由於基於heatmap的方法,由於網絡降採樣的問題,最終的輸出特徵圖和輸入圖之間存在分辨率的差別,在將關鍵點反算回去時存在固有的偏差。基於此,如下圖所示,本文利用多輸出姿態估計網絡輸出的2D偏移向量結合Heatmap得到更加精準的關鍵點。


得到Heatmap和偏移向量後,最終的解碼過程如下代碼所示:
imgs.shape[0]:表示minibatch數量
self.num_classes:表示關鍵點種類數
offsets_x_pred,offsets_y_pred:表示2D偏移向量的值
maps_pred:表示Heatmap上關鍵點置信度值
從下面的代碼中可以看出,最終通過Heatmap上關鍵點的最高置信度位置,結合2D向量計算的距離得到一個新的score值,最終通過最大化該score值得到最後精確的關鍵點位置。

for i in range(imgs.shape[0]):
    for k in range(self.num_classes):
        offsets_x_ij = self.offset_x_ij + offsets_x_pred[i][k]
        offsets_y_ij = self.offset_y_ij + offsets_y_pred[i][k]
        distances_ij = torch.sqrt(offsets_x_ij * offsets_x_ij + offsets_y_ij * offsets_y_ij)

        distances_ij[distances_ij > 1] = 1
        distances_ij = 1 - distances_ij
        score_ij = (distances_ij * maps_pred[i][k]).sum(3).sum(2)

        v1,index_y = score_ij.max(0)
        v2,index_x = v1.max(0)
                
        keypoints[i][k][0] = index_y[index_x]
        keypoints[i][k][1] = index_x
4.keypoint_rescore

我們對位置進行最大化處理,對關鍵點進行平均處理,從而得到最終的實例級別的姿態檢測分數,具體實現代碼如下所示:

        for img in kpts.keys():
            img_kpts = kpts[img]
            for n_p in img_kpts:
                box_score = n_p['score']
                kpt_score = 0
                valid_num = 0
                for n_jt in range(0, num_joints):
                    t_s = n_p['keypoints'][n_jt][2]
                    if t_s > in_vis_thre:
                        kpt_score = kpt_score + t_s
                        valid_num = valid_num + 1
                if valid_num != 0:
                    kpt_score = kpt_score / valid_num
                # rescoring
                n_p['score'] = kpt_score * box_score
5.keypoint_oks_nms

標準NMS基於目標框的交疊比(IoU)來測量重疊率。本文提出了一種考慮關鍵點的更精確的變體。使用關鍵點相似度(OKS)來測量兩個候選整體檢測的重疊。通常,在人體目標檢測器的輸出處使用一個相對較高的iou 閾值來過濾高度重疊的框。姿態估計器輸出的更合適的oks閾值,更適合於確定兩個候選檢測姿態之間的重疊。實現代碼如下所示:

def oks_iou(g, d, a_g, a_d, sigmas=None, in_vis_thre=None):
    if not isinstance(sigmas, np.ndarray):
        sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62, .62, 1.07, 1.07, .87, .87, .89, .89]) / 10.0
    vars = (sigmas * 2) ** 2
    xg = g[0::3]
    yg = g[1::3]
    vg = g[2::3]
    ious = np.zeros((d.shape[0]))
    for n_d in range(0, d.shape[0]):
        xd = d[n_d, 0::3]
        yd = d[n_d, 1::3]
        vd = d[n_d, 2::3]
        dx = xd - xg
        dy = yd - yg
        e = (dx ** 2 + dy ** 2) / vars / ((a_g + a_d[n_d]) / 2 + np.spacing(1)) / 2
        if in_vis_thre is not None:
            ind = list(vg > in_vis_thre) and list(vd > in_vis_thre)
            e = e[ind]
        ious[n_d] = np.sum(np.exp(-e)) / e.shape[0] if e.shape[0] != 0 else 0.0
    return ious


def oks_nms(kpts_db, thresh, sigmas=None, in_vis_thre=None):
    """
    greedily select boxes with high confidence and overlap with current maximum <= thresh
    rule out overlap >= thresh, overlap = oks
    :param kpts_db
    :param thresh: retain overlap < thresh
    :return: indexes to keep
    """
    if len(kpts_db) == 0:
        return []

    scores = np.array([kpts_db[i]['score'] for i in range(len(kpts_db))])
    kpts = np.array([kpts_db[i]['keypoints'].flatten() for i in range(len(kpts_db))])
    areas = np.array([kpts_db[i]['area'] for i in range(len(kpts_db))])

    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)

        oks_ovr = oks_iou(kpts[i], kpts[order[1:]], areas[i], areas[order[1:]], sigmas, in_vis_thre)

        inds = np.where(oks_ovr <= thresh)[0]
        order = order[inds + 1]

    return keep
6.image_crop

作者首先通過目標框的高度和寬度,使所有的框具有相同的固定長寬比,而不扭曲圖像的長寬比。在此之後,進一步放大了方框,以包含額外的圖像上下文,該擴大比例在訓練時隨機爲1–1.5,在測試時定義爲1.25。最後將得到的crop框resize成網絡的輸入大小。實現代碼如下所示:

    def _xywh2cs(self, x, y, w, h):
        center = np.zeros((2), dtype=np.float32)
        center[0] = x + w * 0.5
        center[1] = y + h * 0.5

        if w > self.aspect_ratio * h:
            h = w * 1.0 / self.aspect_ratio
        elif w < self.aspect_ratio * h:
            w = h * self.aspect_ratio
        scale = np.array(
            [w * 1.0 / self.pixel_std, h * 1.0 / self.pixel_std],
            dtype=np.float32)
        if center[0] != -1:
            scale = scale * 1.25

        return center, scale
7.結果展示

如下圖所示,本文提出的方法達到了當時的SOTA。需要注意的是本文提出的很多Trick你在現在的SOTA算法中都能看到被使用,包括keypoint_rescore,keypoint_oks_nms,image_crop,總之谷歌出品必屬精品。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章