R-CNN/Selective-Search

Rigion Proposal開山之作,其實網上已經有很多博客了,但是都是就其理論說明,沒有很好的直觀展示,有時候概念性的東西比較不容易腦補,晦澀難懂。  

首先,給出代碼出處:

import selectivesearch
#如果沒有此庫,pip install selectivesearch 安裝
 

SS算法第一步需要進行的是圖像分割,即大名鼎鼎的felzenszwalb算法,下面先就這個算法進行講解:

其實在這個算法裏有這麼幾個可調參數,sigma,k,kernel,min_size,其中,sigma和kernel是GaussianBlur的參數

首先理解無向圖:

圖是若干個頂點(Vertices)和邊(Edges)相互連接組成的。邊僅由兩個頂點連接,並且沒有方向的圖稱爲無向圖。一說到圖就可能涉及到深度優先算法和廣度優先算法,這裏就不做說明,具體的都可以百度到。

假設我給出了兩個圖像(3x3x3),記爲圖像A和B,左A,右B,爲了後面好說明問題,如下圖所示:

我們就對這兩個圖像進行圖像分割,這裏先貼出分割後的結果,左圖C是A的結果,右圖D是B的結果:

如下圖是對建立無向圖的邊的一些簡單說明:

這裏假設了一些數據,這些數據就是上圖A和B的數據,因爲是3x3的矩陣,所以一共生成了24個邊,每個邊標記了連接的像素節點和和像素節點間的距離,圖中已經給出了計算方式,有了這些數據後,開始建立無向圖,先貼出代碼:

#mini_size = 3; 也可以設置成2,可以看下效果
# 構建無向圖(樹)
    class universe(object):
        def __init__(self, n, k):
            self.f = np.array([i for i in range(n)])  # 樹
            # [0 1 2 3 4 5 6 7 8]
            self.r = np.zeros_like(self.f)  # root
            # [0 0 0 0 0 0 0 0 0]
            self.s = np.ones((n))  # 存儲像素點的個數
            # [1. 1. 1. 1. 1. 1. 1. 1. 1.]
            self.t = np.ones((n)) * 60  # 存儲不相似度 這裏50是k值,爲了容易理解,就直接寫60
            # [1. 1. 1. 1. 1. 1. 1. 1. 1.]
            self.k = k
        
        def find(self, x):  # Find root of node x
            if x == self.f[x]:
                return x
            return self.find(self.f[x])
        
        def join(self, a, b):  # Join two trees containing nodes n and m
            if self.r[a] > self.r[b]:
                self.f[b] = a
                self.s[a] += self.s[b]
            else:
                self.f[a] = b
                self.s[b] += self.s[a]
                if self.r[a] == self.r[b]:
                    self.r[b] += 1
    
    u = universe(num, k)
    #第一個循環
    for edge in edges:
        a, b = u.find(int(edge[0])), u.find(int(edge[1]))
        if ((a != b) and (edge[2] <= min(u.t[a], u.t[b]))):
        # 更新類標號:將類a,b標號統一爲的標號a。更新該類的不相似度閾值爲:k / (u.s[a]+u.s[b])
    
            u.join(a, b)
            a = u.find(a)
            u.t[a] = edge[2] + k / u.s[a]
    #第二個循環
    for edge in edges:
        a, b = u.find(int(edge[0])), u.find(int(edge[1]))
        if ((a != b) and ((u.s[a] < min_size) or u.s[b] < min_size)):
            # 分割後會有很多小區域,當區域像素點的個數小於min_size時,選擇與其差異最小的區域合併
            u.join(a, b)

那麼這段代碼具體說了個什麼意思呢?就是我先將這個edges矩陣按照距離方式從小到大排列,那麼我總是按照最小的距離值進行搜索,這裏就用到圖的概念了,下面舉一個比較直觀的例子,定義爲圖E:

我們先要根據像素點(節點)找到它的標號也就是代碼中的a和b值,圖E的例子是 (15, 14, 0),這個距離是最小的,爲0,如果a!=b 且距離<=t中的不相似度閾值,t的更新計算公式如代碼,有個超參數k是可調的,則合併像素點,並且在f中進行標記,那麼這是f的值爲[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 14 16 17],且在14的位置進行root標記,r的結果爲[0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0],存儲該位置的像素點的個數

[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 2. 1. 1. 1.],不相似度矩陣t的結果爲[50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  0.5 50.  50.  50. ],合併14和15像素點。

下一個邊爲(14,15,0),因爲已經被標記過了跳過計算;

下一個邊爲(1,0,50),前提是能從14和15找到1,0嗎,顯然不行,所以要另起爐竈,將1和0像素點合併,f的值爲[ 0  0  2  3  4  5  6  7  8  9 10 11 12 13 14 14 16 17],r的值爲[1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0],s的值爲[2. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 2. 1. 1. 1.],t的值爲[50.5  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  50.  0.5 50.  50.  50. ]

下一個邊爲(2,1,50),因爲此時節點1和0節點是相連的了,所以a和b的值爲(2,0),合併0和1和2像素,這裏我直接給出值:

[ 0  0  0  3  4  5  6  7  8  9 10 11 12 13 14 14 16 17]
[1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]
[3. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 2. 1. 1. 1.]
[50.33333333 50.   50.  50.  50.   50.  50.  50.  50.  50.  50.  50.  50.   50.   0.5   50.   50.    50.  ]

下一個邊爲(3,2,50),這裏我們看一下,3沒有與任何節點相連,2與節點0相連,那麼a和b的值爲(3,0),滿足合併條件繼續合併,也直接給出值:

[ 0  0  0  0  4  5  6  7  8  9 10 11 12 13 14 14 16 17]
[1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]
[4. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 2. 1. 1. 1.]
[50.25 50.   50.   50.   50.   50.   50.   50.   50.   50.   50.   50. 50.   50.    0.5  50.   50.   50.  ]

下一個邊爲(5.  4. 50),這裏5和4顯然沒有與任何節點相連,那麼又要另起爐竈了,滿足和並條件合併,這裏就下不給值了,我們往下看

下一個邊爲(7,6,50),這裏7和6顯然沒有與任何節點相連,那麼又要另起爐竈了,滿足和並條件合併

下一個邊爲(8,7,50),7顯然與6相連,那麼a和b的值爲(8,6),合併6,7,8像素

下一個邊爲(10,9,50),另起爐竈合併

下一個邊爲(13,12,50),另起爐竈合併

[50.25   50.   50.    50.   50.5    50.   50.33333333   50.    50.     50.5     50.  50.   50.5    50.     0.5    50.     50.      50.   ]

下一個邊爲(16,15,50),14與15已經合併,那麼a和b的值爲(16,14),等下貌似符合合併條件,但是,別忘了還有個min(t[a],t[b]),14位置的不相似度閾值已經是0.5了,所以50大於0.5,當然也不滿足合併條件樓

下一個邊爲(17,16,50),這兩個邊都沒有標記過,那麼我們就講究湊合着合併吧

下一個邊是(0,1,50),顯然0和1已經連接過了,a和b的值爲(0,0),那麼不符合a!=b的條件,統統放棄

至此所有的像素合併結束,其實就是按圖索驥。。。,這個例子似乎一直是從左到右的在合併,其實上下也在進行,只不過要看左右上下的距離排序,圖B就是先合併的上下。

#########################################

此時來到第二個循環,一句話理解,分割後會有很多的小區域,當區域像素點的個數小於min_size時,選擇與其差異最小的區域合併,用圖說話,圖E設置了min_size爲2,效果如上,如果將min_size設置爲3呢

設置爲4和100呢,100就成了一個顏色了,設置成1和2的效果是一樣的

至此,圖像分割算法就完成了,接着往下走,這次就拿真實圖片來講,左邊是真實圖片,右邊是分割完的圖片,直接是selectivesearch裏面產生的效果:

下面進入另一個函數,

def _extract_regions(img):

區域合併採取了多樣性的策略,如果簡單採用一種策略很容易錯誤合併不相似區域,比如只考慮紋理,不同顏色的區域很容易被錯誤合併,選擇性搜索採用三種多樣性策略來增加候選區域以保證找回;1.多種顏色空間,考慮RGB,灰度,HSV及其變種;2.多種相似度度量標準,即考慮顏色相似度,又考慮紋理、大小、重疊情況;3.通過改變閾值初始化原始區域,閾值越大,分割的區域越少。

由RBG顏色空間到HSV顏色空間

計算texture_gradient,效果如圖:

區域相似度計算:

顏色相似度:使用L1-normalize歸一化獲取每個顏色通道的25bins的直方圖,這樣每個區域都可以得到一個75維的向量,具體請看代碼

def _calc_colour_hist(img):

紋理相似度計算:這裏紋理採用SIFT-LIKE特徵,具體做法是對每個顏色通道的8個不同方向計算方差 delt = 1的高斯微分,使用L1-normalize歸一化獲取圖像每個顏色通道的每個方向的10bins的直方圖,這樣就可以獲取到一個240(10x8x3)維的向量,區域間紋理相似度與顏色相似度計算類似。具體請看代碼

def _calc_texture_hist(img):

區域合併:

優先合併小的區域,如果僅僅是通過顏色和紋理特徵合併的話,很容易使合併後的區域不斷吞噬周圍的區域,後果就是多尺度只應用在了那個局部,而不是全局的多尺度。因此我們給小的區域更多的權重,這樣保證在圖像每個位置都是多尺度合併。

代碼:

def _sim_size(r1, r2, imsize):
    """
        calculate the size similarity over the image
    """
    return 1.0 - (r1["size"] + r2["size"]) / imsize

如果區域ri包含在rj內,我們首先應該合併,另一方面,如果ri和rj相接,他們之間會形成斷崖,不應該合併在一塊。這裏定義區域的合適度距離主要是爲了衡量兩個區域是都更加吻合,其指標是合併後的bounding box越小,其相似度越近,代碼中是bbsize

代碼:

def _sim_fill(r1, r2, imsize):
    """
        calculate the fill similarity over the image
    """
    bbsize = (
        (max(r1["max_x"], r2["max_x"]) - min(r1["min_x"], r2["min_x"]))
        * (max(r1["max_y"], r2["max_y"]) - min(r1["min_y"], r2["min_y"]))
    )
    return 1.0 - (bbsize - r1["size"] - r2["size"]) / imsize

合併四種相似度:

代碼:

def _calc_sim(r1, r2, imsize):
    return (_sim_colour(r1, r2) + _sim_texture(r1, r2)
            + _sim_size(r1, r2, imsize) + _sim_fill(r1, r2, imsize))

完畢!!關於直方圖可視化後續再加上吧!!!

 

 

 

 

 

 

 

 

 

 

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