【計數算法歸納】一、論文學習筆記-LCFCN

本文主要參考:

https://blog.csdn.net/sinat_37532065/article/details/97910470

https://blog.csdn.net/qq_14845119/article/details/99883316

論文標題:《Where are the Blobs: Counting by Localization with Point Supervision》

出處:ECCV2018

原文鏈接:https://arxiv.org/abs/1807.09856v1

相關repo:https://github.com/ElementAI/LCFCN

摘要

    關於對象計數任務,當下最好的辦法都是基於迴歸的思想進行優化。總的來說,這些方法比基於檢測的方法效果好,因爲相比檢測模型,這些方法不需要學習如何定位對象位置、計算對象大小形狀等。 

    原文中這樣描述:“本文提出一種基於檢測的方法LCFCN,但不需要估計對象大小和形狀。”。我認爲LCFCN的思想是借鑑了檢測任務的定位思想,而模型搭建則是完全延用了語義分割模型。

1 介紹

目標計數問題存在的一些難點

(1)模型需要適應目標在形狀、大小、姿勢和外觀方面的可變性;
(2)對象可能以不同的角度和分辨率出現,並且可能存在部分遮擋;
(3)背景、天氣條件、光照等都會影響泛化效果。
    一般來說,基於檢測的方法需要估計對象實例的位置、形狀、大小,這些相對來說是更困難的任務。 

    對於對象計數任務,預測對象實例的確切大小和形狀並不是必需的,並且通常會帶來更困難的優化問題。出於此原因,本文簡單地專注於在場景中定位實例來完成一個比檢測更加容易的任務。

    現在最先進的基於密度估計的方法,通常假定對象有固定的尺寸(由高斯覈定義)或限制環境,這使得其難以計算不同尺寸和形狀的物體。LCFCN通過點監督來指導學習定位場景中出現的對象,可以靈活地爲不同對象實例預測不同大小的區域,這使得其可適用於計算大小和形狀不同的對象。

文章主要貢獻:

(1)論文提出了4個新穎的loss,基於該loss,可以使得僅僅依靠一個點的標註,就使得模型學習到將每一個實例單獨分出的blob。
(2)論文提出了基於直線和基於分水嶺算法的兩種實例分割方法
(3)本文的算法在Pascal VOC和Penguins數據集上取得了state-of-the-art的效果,該算法甚至超過基於強監督的深度特徵的方法(多點回歸,邊框檢測)
 

2 相關工作

(1)基於聚類的計數方法
    提取運動特徵,進行無監督聚類。缺點是隻適用於視頻序列,不適用於靜止圖像。

(2)基於迴歸的計數方法
    基於迴歸的計數方法被證明比基於檢測的方法更快、更準確,其主要包含glance類方法和密度估計兩大類。

a. 密度估計類方法通過將點監督信息與高斯核卷積構造密度分佈圖來進行迴歸,其難點在於最佳高斯核尺寸高度依賴對象尺寸。

b. Glance類方法,也稱子化範圍類方法,優點是隻需要圖像級監督,缺點對畫面中僅對象個數較少時比較有效。

(3)基於檢測的計數方法
    對檢測模型而言,定位被遮擋目標的位置,計算其大小和形狀都是很困難的任務。而這些又是計數任務最需要解決的點,而且檢測任務目標是得到對象的類別、位置、大小,其中大小和位置並不是計數模型所關心的。總而言之,基於檢測計數方法雖然可行,但效果一般。

    LCFCN
    本文提出的LCFCN模型結構類似經典語義分割模型,如U-Net等。但不同之處在於,LCFCN在訓練時只需要點監督信息,而不需要像訓練語義分割模型一樣,需要全部像素點的信息。

3 Localization-based Counting FCN

LC: 本文提出的基於定位的計數損失

FCN:全卷積網絡(此處可以替換成任何語義分割模型)

3.1 損失函數解釋


LC-loss由四部分組成:

    前兩項圖像級別loss和點級loss參考弱監督語義分割模型設計,目的是迫使模型預測圖像中每個像素的語義標籤。但僅使用這兩部分loss是不足以進行對象計數的,因爲語義分割模型並不具有區分不同實例對象的能力,所以可能得到的結果就是一個預測的blob中包含多個instance。

    第三項loss目的是鼓勵模型爲每個instance輸出一個單獨blob。

    第四項loss目的是爲了去除不含有instance的blob。

    最重要的一點,LC-loss僅需要指示對象位置的點級註釋,而不需要諸如大小、形狀之類的信息。

(1)Image-level loss


    Ce表示圖像中出現的對象的類別集合,對於Ce中的每一類,該項loss旨在提高其出現至少一個像素點的概率;C-e表示圖像中沒有出現過的對象的類別集合,對於C-e中的每一類,該項loss旨在降低其出現任意一個像素點的概率。和都是從Ground Truth中得到的。

    直觀來說,對於圖像中出現的每一個類別,畫面中應該至少有一個像素點被預測爲該類。而對於畫面中沒有出現的任一類別,畫面中不應該有任何一個像素點被預測爲該類。本文中假定每幅圖像中均存在背景像素,所以背景像素被歸在Ce當中。

def compute_image_loss(S, Counts):
    n,k,h,w = S.size()#input[1, 3, height, width],S:[1, 2, height, width],counts:[1,1]
 
    # GET TARGET
    ones = torch.ones(Counts.size(0), 1).long().cuda()#[1,1]
    BgFgCounts = torch.cat([ones, Counts], 1)#[1,2]
    Target = (BgFgCounts.view(n*k).view(-1) > 0).view(-1).float()#[2]
 
    # GET INPUT
    Smax = S.view(n, k, h*w).max(2)[0].view(-1)#[2]
 
    loss = F.binary_cross_entropy(Smax, Target, reduction='sum')
 
return loss

(2)Point-level loss

該項loss(僅)鼓勵模型正確地標記Ground Truth中一小組受監督的像素點。其中,Is表示Ground Truth中受監督的點集,也代表了對象在畫面中的位置信息。注意,該項loss忽略了所以未標註的點。

loss += F.nll_loss(S_log, points,
                       ignore_index=0,
                       reduction='sum')

(3)Split-level loss
    該項loss阻止模型輸出包含大於等於兩個註釋點的blob。大體做法是先找出包含兩個以上對象的blob,然後找出兩個(或多個)對象的邊界,最後引導模型學習如何將邊界上的點預測爲背景,這樣就達到了拆分多個對象的目的。

    a. 首先,對模型輸出的feature map進行處理,根據是否是前景/背景進行二值化,得到一個二進制前景掩碼;
    b. 通過連通域查找算法,得到中的blob集合;
    c .進而得到集合中包含有兩個及兩個以上對象的blob集合,最後運行分割blob算法。

本文提出了兩種分割blob的方法:

(1)線分割法

    考慮B中的任意一個點對,通過一個評分函數來計算這一個點對所構成的分割線是否是最優的劃分界限。評分函數被定義爲分割線上所有點屬於背景的平均概率:

    最佳分界線被定義爲在所有線中具有最高背景概率的那條,即兩個對象之間最有可能的分離界線。 

(2)漫水分割法

漫水分割法由全局和局部兩個分割步驟組成。

global step:將GT-points設爲種子點,在整副圖像上應用漫水法分割。基於分割結果,在前景概率矩陣上進行距離變換,最終可以得到k個分割區域,k是Ground  Truth中的對象個數。
local step:對中的每一個blob,基於其包含的GT-points作爲種子點,進行漫水分割。
最後,將兩步分割結果定義爲邊界。

    上圖中解釋了兩種分割方法的分割效果(黃色線)。基於兩種方法任意其一,可以得到分割邊界集合Tb.這樣就可以按照下式計算分割loss:

其中,Si0表示表示像素屬於背景的概率,ai表示像素所屬的blob中的註釋點的個數。該項loss鼓勵模型專注於拆分包含有更多GT-points的blob,其動機是通過學習如何預測不同對象之間的邊界來使得模型更容易區分它們,因而通過該懲罰措施使得每一個blob中只包含一個註釋點。

對計數任務,獲取正確的分割邊界是沒有必要的。我們只需要保證每個對象身上有一個正的響應區域,且在對象之間有一個負的響應區域即可。這些loss都是輔助訓練使用的,在推理時完全基於從輸出矩陣中獲取的blobs。

def compute_split_loss(S_log, S, points, blob_dict):
    blobs = blob_dict["blobs"]
    S_numpy = ut.t2n(S[0])
    points_numpy = ut.t2n(points).squeeze()
 
    loss = 0.
 
    for b in blob_dict["blobList"]:
        if b["n_points"] < 2:
            continue
 
        l = b["class"] + 1
        probs = S_numpy[b["class"] + 1]
 
        points_class = (points_numpy==l).astype("int")
        blob_ind = blobs[b["class"] ] == b["label"]
 
        T = watersplit(probs, points_class*blob_ind)*blob_ind
        T = 1 - T
 
        scale = b["n_points"] + 1
        loss += float(scale) * F.nll_loss(S_log, torch.LongTensor(T).cuda()[None],
                        ignore_index=1, reduction='elementwise_mean')
 
    return loss
 
 
    # split_mode loss
    if blob_dict["n_multi"] > 0:
        loss += compute_split_loss(S_log, S, points, blob_dict)
 
    # Global loss 
    S_npy = ut.t2n(S.squeeze())
    points_npy = ut.t2n(points).squeeze()
    for l in range(1, S.shape[1]):
        points_class = (points_npy==l).astype(int)
 
        if points_class.sum() == 0:
            continue
 
        T = watersplit(S_npy[l], points_class)
        T = 1 - T
        scale = float(counts.sum())
        loss += float(scale) * F.nll_loss(S_log, torch.LongTensor(T).cuda()[None],
                        ignore_index=1, reduction='elementwise_mean')

(4)False Positive loss

該項loss抑制模型輸出不包含對象的blob,對於虛警blob集合中的每一個像素點,最大化其屬於背景類的概率。這一點對提高對象計數效果至關重要。

def compute_fp_loss(S_log, blob_dict):
    blobs = blob_dict["blobs"]
    
    scale = 1.
    loss = 0.
 
    for b in blob_dict["blobList"]:
        if b["n_points"] != 0:
            continue
 
        T = np.ones(blobs.shape[-2:])
        T[blobs[b["class"]] == b["label"]] = 0
 
        loss += scale * F.nll_loss(S_log, torch.LongTensor(T).cuda()[None],
                        ignore_index=1, reduction='elementwise_mean')
    return loss 

3.2 LC-FCN模型結構和推理過程

    LC-FCN可以使用任何FCN結構,如FCN-8s、Deeplab、PSPNet等。主幹網絡部分可以使用ResNet、VGGNet等抽取特徵,上採樣部分旨在爲每個像素點輸出一個得分。

    LC-FCN的推理過程:

(1)上採樣路徑輸出矩陣Z,每個點表示對應像素屬於類c的概率;
(2)對每個類別,如果某像素點預測爲該類,則置爲1,反之爲0,從而得到每個類別對應的一張二進制mask;
(3)對每個類別,查找其連通域個數,即爲預測出的該類對象個數。

4 實驗
4.1 評價指標
對於單一類別數據集,評價指標使用mean absolute error (MAE)
對於多類別數據集,評價指標使用mean root mean square error (mRMSE).
4.2 實驗結果


4.3 分解研究
loss函數分析


    前兩項loss(Image-level loss 和 Point-level loss)旨在通過點級註釋訓練語義分割模型,結果是如果只用這兩項loss訓練模型,會得到一個巨大的blob,其中包含很多個實例對象。
    第三項split loss旨在鼓勵模型輸出不包含多個對象的blob。這樣的結果導致我們得到很多個blob,且很少有包含多個對象的blob。
    但僅靠上述三項loss,我們沒有抑制模型的虛警,這導致模型預測結果中包含一些沒有對象的blob,即誤報。
最後一項fp-loss旨在阻止模型預測不包含點註釋的blob。通過添加該項,LC-FCN不論是在定性還是定量角度得到了顯著的改善。split-loss和fp-loss配合使用,互補缺點。


分割方法對比

    在split-loss設計中,文章提出了線分割法和漫水填充法兩種方法來分割大的blob。通過實驗對比,表明漫水分割法效果更佳,所以本文實驗都採用了該方法計算split-loss。但分割方法是具有啓發性的,後續這是一個可以重點優化的地方。

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