小總結-特征點檢測的兩種方法-FC回歸和heat map


最近研究了一下特征點檢測最后一步的的兩種主流方法,整理了分享出來。其實說的是經過一系列網絡運算之后,最后一步如何由網絡的feature map直接預測出特征點坐標位置。

---------Agenda-------

  • overview
  • 基本的heat map是怎么實現的
  • fc VS heat map
  • heat map v2.0

----------------------------

 

-------------------------------------------------------------------開始吧------------------------------------------------------------------

主流的做關鍵點檢測有兩大流派

  • 一種是用FC fully connection layer 去在最后一層去回歸出每個點的坐標值, 比如要輸出68個關鍵點,那么就是回歸出68x2個坐標值
  • 另一種是用heatmap去得到每個關鍵點的熱力圖,如下圖所示,每個關鍵點一個channel, 直接用圖的形式去得到最后的68個channel的output, 然后通過argmax去得到關鍵點坐標

 

 

 

 基本的heat map是怎么實現的

FC的方法比較好理解,下面着重介紹下最基本的heatmap的方法怎么去做的

def get_preds_fromhm(hm, center=None, scale=None):

    """Obtain (x,y) coordinates given a set of N heatmaps. If the center

    and the scale is provided the function will return the points also in

    the original coordinate frame.

 

    Arguments:

        hm {torch.tensor} -- the predicted heatmaps, of shape [B, N, W, H]

 

    Keyword Arguments:

        center {torch.tensor} -- the center of the bounding box (default: {None})

        scale {float} -- face scale (default: {None})

    """

    B, C, H, W = hm.shape

    hm_reshape = hm.reshape(B, C, H * W)

    idx = np.argmax(hm_reshape, axis=-1) # 首先通過argmax得到每個channel最大的相應的位置

    scores = np.take_along_axis(hm_reshape, np.expand_dims(idx, axis=-1), axis=-1).squeeze(-1)

    preds, preds_orig = _get_preds_fromhm(hm, idx, center, scale)

 

    return preds, preds_orig, scores

@jit(nopython=True)

def _get_preds_fromhm(hm, idx, center=None, scale=None):

    """Obtain (x,y) coordinates given a set of N heatmaps and the

    coresponding locations of the maximums. If the center

    and the scale is provided the function will return the points also in

    the original coordinate frame.

    Arguments:

        hm {torch.tensor} -- the predicted heatmaps, of shape [B, N, W, H]

 

    Keyword Arguments:

        center {torch.tensor} -- the center of the bounding box (default: {None})

        scale {float} -- face scale (default: {None})

    """

    B, C, H, W = hm.shape

    idx += 1

    preds = idx.repeat(2).reshape(B, C, 2).astype(np.float32)

    preds[:, :, 0] = (preds[:, :, 0] - 1) % W + 1

    preds[:, :, 1] = np.floor((preds[:, :, 1] - 1) / H) + 1 #然后得到每個熱力圖最熱的部位的x和y的坐標,分別存在preds[:,:,0]和preds[:,:,1]中

 

    for i in range(B):

        for j in range(C):

            hm_ = hm[i, j, :]

            pX, pY = int(preds[i, j, 0]) - 1, int(preds[i, j, 1]) - 1

            if pX > 0 and pX < 63 and pY > 0 and pY < 63:

                diff = np.array(

                    [hm_[pY, pX + 1] - hm_[pY, pX - 1],

                     hm_[pY + 1, pX] - hm_[pY - 1, pX]])

                preds[i, j] += np.sign(diff) * 0.25 #  去計算熱力圖上最熱部位的左右兩邊的熱力值,如果右邊的大,那么熱力圖的最熱點(preds)的值就往右邊移1/4個pixel,反之亦然。同樣適用於上下兩個像素的值,通過這種方式去得到一個接近亞像素的坐標值

 

    preds -= 0.5 #取中心的坐標

 

    preds_orig = np.zeros_like(preds)

    if center is not None and scale is not None:

        for i in range(B):

            for j in range(C):

                preds_orig[i, j] = transform_np(

                    preds[i, j], center, scale, H, True)

 

    return preds, preds_orig

就比較簡單了,咱們多討論一下

這種方法顯然不是特別合理,首先說如果就用argmax肯定是不行的,因為可能邊上的點其實跟你幾乎一模一樣就小一點點,那這樣像素的精度只能達到1個pixel,其實很多關鍵點檢測是縮小了去做的,那么達到的關鍵點坐標放大回去就會有幾個像素的偏差,很多任務中會造成抖動,顯然是不可接受的。這就是很多文章中會提及的heatmap的方法存在一個理論誤差下值。

那么左邊這段代碼的方法方法可以稍微考慮下左右像素,實現亞像素精度。但是如果左右兩個像素值其實比中心小很多,然后你還去區分左邊多一點還是右邊多一點,然后硬往那邊挪1/4個像素,其實也是不合理的。

 

 

Hm[py-1,px]

 

Hm[py,px-1]

Hm[py,px]

Hm[py,px-1]

 

Hm[py+1,px]

 

在訓練標簽的准備中,需要由數據標簽坐標值去准備高斯熱圖

在訓練的時候,所有的像素點都對Loss有貢獻,但是在實際Inference的時候,只有5個像素點能決定最終的坐標,輸出和坐標之間不能完全對應

像右邊這種圖,在算Loss的時候,中間的是5x5x4 = 100, 而右邊的是(9-5)^2=16,似乎是右邊的更好,但是在取了argmax之后,其實是中間的結果更好,就是上面所說的訓練的時候計算的loss不能完全對應到輸出坐標。

 

 

 

 

 

 

 

FC vs heat map

 

“”實戰中發現fc更傾向於預測一個合理得模板,heatmap方法相對關注局部一點得特征,相對來說,heatmap方法更容易出現點位錯亂的情況,而fc更多的是單個點精度不足導致偏差

 

regression模型很容易訓練,甚至即是很少的數據也能夠訓練。當然與此帶來的就是極度的容易過擬合和泛化問題(如果說數據能夠覺得那當我沒說)。key points之間的能夠保持某種潛在的“秩序”(比如face alignment中雙眼+鼻子+兩嘴角構成的5個點,這5個點總是會保持方位上的秩序)。這種秩序性更體現在,如果輸入是一張完全不相關的圖像,regression依然可以給出一個形狀上非常合理的結果(反觀heatmap所給的key points則會完全亂了套)”

 

From <https://zhuanlan.zhihu.com/p/374842773>

 

Numerical Coordinate Regression with Convolutional Neural Networks這篇文章中有詳細比較

 

FC的方法直接得到一個坐標點是不靠譜的,因為坐標點這個東西太隱晦了,比如說在目標檢測的時候,為什么不直接FC輸出4個值呢?因為這樣效果不會好,所以要有什么maskRcnn還有yolo還有SSD的方法。用FC在小數據集上很容易出現過擬合。

高斯熱圖的方法就對CNN友好很多

就人臉關鍵點檢測這個任務來說,FC的方法又有其優越性,畢竟人臉其實是一個相對人體更加剛性的結構,FC的方方能八九不離十的猜出一個好不錯的特征點。而且人臉的很多特征點其實並不是角點,比如眼睛上部的那個點,還有臉頰上的點,在標注的時候都有猜的成分在。高斯熱圖想要在這里有一個高響應比較困難。

 

但是高斯熱圖的方法計算量比較大,畢竟最后幾層需要輸出與輸入等分辨率的而且channel數很多的熱圖。

 

 

 

 

 heat map v2.0

那么有一種改進后的方法就來了,叫做DSNT differentiable spatial to numerical transform (DSNT) layer,他在heat map方法上進行了改進。paper link Numerical Coordinate Regression with Convolutional Neural Networks

怎么做的呢?其實就是把所有的取值做了softmax, 然后得到下圖第一張,然后生成一下每個點x的坐標,每個點y的坐標,即下面圖的中間和右邊部分。

然后去計算x的期望和y的期望,用這個期望作為預測出的x和y。就是下面兩個式子算的。這樣可得亞像素的精度,而且是任意精度,不是上面基本的heat map方法的0.25像素大小的精度。非常合理。

 

 

 

 

說完合理的部分,下面說一說可能會有的問題,那就是如果光用跟這個去約束,可能得到的heat map會很奇怪,比如我們在GT的坐標的左右對稱有兩個高響應點也可以得到很小的loss,其實這個是會有問題的,就像下面幾種方法得到的坐標位置都不差,但是還是在目標點處的一個高響應是最理想的,實驗證明,如果對heat map的形狀進行約束,這樣可以指導網絡得到更好的效果。所以loss上還會加一些約束,得到一個盡可能接近高斯相應的heat map。

 

 

 

 

具體怎么做的呢?

就是用KL散度去計算得到的heat map響應和高斯圖之間的相似度即可。加一個Loss。

 

----------------------------------------------------------END-----------------------------------------------------------------------------------------------------

Related paper

Numerical Coordinate Regression with Convolutional Neural Networks https://arxiv.org/pdf/1801.07372.pdf

Learning Feature Pyramids for Human Pose Estimation https://arxiv.org/pdf/1708.01101.pdf

Human pose estimation via Convolutional Part Heatmap Regression https://arxiv.org/pdf/1609.01743.pdf

How far are we from solving the 2D & 3D Face Alignment problem? (and a dataset of 230,000 3D facial landmarks) https://arxiv.org/pdf/1703.07332.pdf

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM