目标检测之选择性搜索-Selective Search
?
在基于深度學習的目標檢測算法的綜述?那一節中我們提到基于區域提名的目標檢測中廣泛使用的選擇性搜索算法。并且該算法后來被應用到了R-CNN,SPP-Net,Fast R-CNN中。因此我認為還是有研究的必要。
傳統的目標檢測算法大多數以圖像識別為基礎。一般可以在圖片上使用窮舉法或者滑動窗口選出所有物體可能出現的區域框,對這些區域框提取特征并進行使用圖像識別分類方法,得到所有分類成功的區域后,通過非極大值抑制輸出結果。
在圖片上使用窮舉法或者滑動窗口選出所有物體可能出現的區域框,就是在原始圖片上進行不同尺度不同大小的滑窗,獲取每個可能的位置。而這樣做的缺點也顯而易見,復雜度太高,產生了很多的冗余候選區域,而且由于不可能每個尺度都兼顧到,因此得到的目標位置也不可能那么準,在現實當中不可行。而選擇性搜索有效地去除冗余候選區域,使得計算量大大的減小。
我們先來看一組圖片,由于我們事先不知道需要檢測哪個類別,因此第一張圖的桌子、瓶子、餐具都是一個個候選目標,而餐具包含在桌子這個目標內,勺子又包含在碗內。這張圖展示了目標檢測的層級關系以及尺度關系,那我們如何去獲得這些可能目標的位置呢。我們能不能通過視覺特征去減少候選框的數量并提高精確度呢。
可用的特征有很多,到底什么特征是有用的呢?我們看第二副圖片的兩只貓咪,他們的紋理是一樣的,因此紋理特征肯定不行了。而如果通過顏色則能很好區分。但是第三幅圖變色龍可就不行了,這時候邊緣特征、紋理特征又顯得比較有用。而在最后一幅圖中,我們很容易把車和輪胎看作是一個整體,但是其實這兩者的特征差距真的很明顯啊,無論是顏色還是紋理或是邊緣都差的太遠了。而這這是幾種情況,自然圖像那么多,我們通過什么特征去區分?應該區分到什么尺度?
selective search的策略是,既然是不知道尺度是怎樣的,那我們就盡可能遍歷所有的尺度好了,但是不同于暴力窮舉,我們可以先利用基于圖的圖像分割的方法得到小尺度的區域,然后一次次合并得到大的尺寸就好了,這樣也符合人類的視覺認知。既然特征很多,那就把我們知道的特征都用上,但是同時也要照顧下計算復雜度,不然和窮舉法也沒啥區別了。最后還要做的是能夠對每個區域進行排序,這樣你想要多少個候選我就產生多少個,不然總是產生那么多你也用不完不是嗎?
在深入介紹Selective Search之前,先說說其需要考慮的幾個問題:
- ?適應不同尺度(Capture All Scales):窮舉搜索(Exhaustive Selective)通過改變窗口大小來適應物體的不同尺度,選擇搜索(Selective Search)同樣無法避免這個問題。算法采用了圖像分割(Image Segmentation)以及使用一種層次算法(Hierarchical Algorithm)有效地解決了這個問題。
- 多樣化(Diversification):單一的策略無法應對多種類別的圖像。使用顏色(color)、紋理(texture)、大小(size)等多種策略對分割好的區域(region)進行合并。
- 速度快(Fast to Compute):算法,就像功夫一樣,唯快不破!
回到頂部
一 選擇性搜索的具體算法(區域合并算法)
輸入: 一張圖片 輸出:候選的目標位置集合L算法: 1: 利用切分方法得到候選的區域集合R = {r1,r2,…,rn} 2: 初始化相似集合S = ? 3: foreach 遍歷鄰居區域對(ri,rj) do 4: 計算相似度s(ri,rj) 5: S = S ∪ s(ri,rj) 6: while S not=? do 7: 從S中得到最大的相似度s(ri,rj)=max(S) 8: 合并對應的區域rt = ri ∪ rj 9: 移除ri對應的所有相似度:S = S\s(ri,r*) 10: 移除rj對應的所有相似度:S = S\s(r*,rj) 11: 計算rt對應的相似度集合St 12: S = S ∪ St 13: R = R ∪ rt 14: L = R中所有區域對應的邊框首先通過基于圖的圖像分割方法初始化原始區域,就是將圖像分割成很多很多的小塊。然后我們使用貪心策略,計算每兩個相鄰的區域的相似度,然后每次合并最相似的兩塊,直到最終只剩下一塊完整的圖片。然后這其中每次產生的圖像塊包括合并的圖像塊我們都保存下來,這樣就得到圖像的分層表示了呢。那我們如何計算兩個圖像塊的相似度呢?
?
二 保持多樣性的策略
區域合并采用了多樣性的策略,如果簡單采用一種策略很容易錯誤合并不相似的區域,比如只考慮紋理時,不同顏色的區域很容易被誤合并。選擇性搜索采用三種多樣性策略來增加候選區域以保證召回:
- 多種顏色空間,考慮RGB、灰度、HSV及其變種等
- 多種相似度度量標準,既考慮顏色相似度,又考慮紋理、大小、重疊情況等。
- 通過改變閾值初始化原始區域,閾值越大,分割的區域越少。
1、顏色空間變換
通過色彩空間變換,將原始色彩空間轉換到多達八中的色彩空間。作者采用了8中不同的顏色方式,主要是為了考慮場景以及光照條件等。這個策略主要應用于中圖像分割算法中原始區域的生成(兩個像素點的相似度計算時,計算不同顏色空間下的兩點距離)。主要使用的顏色空間有:(1)RGB,(2)灰度I,(3)Lab,(4)rgI(歸一化的rg通道加上灰度),(5)HSV,(6)rgb(歸一化的RGB),(7)C,(8)H(HSV的H通道)
?
2、區域相似度計算
我們在計算多種相似度的時候,都是把單一相似度的值歸一化到[0,1]之間,1表示兩個區域之間相似度最大。
-
顏色相似度
使用L1-norm歸一化獲取圖像每個顏色通道的25 bins的直方圖,這樣每個區域都可以得到一個75維的向量,區域之間顏色相似度通過下面的公式計算:
上面這個公式可能你第一眼看過去看不懂,那咱們打個比方,由于是歸一化后值,每一個顏色通道的直方圖累加和為1.0,三個通道的累加和就為3.0,如果區域ci和區域cj直方圖完全一樣,則此時顏色相似度最大為3.0,如果不一樣,由于累加取兩個區域bin的最小值進行累加,當直方圖差距越大,累加的和就會越小,即顏色相似度越小。
在區域合并過程中使用需要對新的區域進行計算其直方圖,計算方法:
- 紋理相似度
這里的紋理采用SIFT-Like特征。具體做法是對每個顏色通道的8個不同方向計算方差σ=1的高斯微分(Gaussian Derivative),使用L1-norm歸一化獲取圖像每個顏色通道的每個方向的10 bins的直方圖,這樣就可以獲取到一個240(10x8x3)維的向量,區域之間紋理相似度計算方式和顏色相似度計算方式類似,合并之后新區域的紋理特征計算方式和顏色特征計算相同:
?
- 優先合并小的區域
如果僅僅是通過顏色和紋理特征合并的話,很容易使得合并后的區域不斷吞并周圍的區域,后果就是多尺度只應用在了那個局部,而不是全局的多尺度。因此我們給小的區域更多的權重,這樣保證在圖像每個位置都是多尺度的在合并。
上面的公式表示,兩個區域越小,其相似度越大,越接近1。
- 區域的合適度距離
如果區域ri包含在rj內,我們首先應該合并,另一方面,如果ri很難與rj相接,他們之間會形成斷崖,不應該合并在一塊。這里定義區域的合適度距離主要是為了衡量兩個區域是否更加“吻合”,其指標是合并后的區域的Bounding Box(能夠框住區域的最小矩形BBij)越小,其吻合度越高,即相似度越接近1。其計算方式:
- 合并上面四種相似度
其中
?
三 給區域打分
通過上述的步驟我們能夠得到很多很多的區域,但是顯然不是每個區域作為目標的可能性都是相同的,因此我們需要衡量這個可能性,這樣就可以根據我們的需要篩選區域建議個數啦。
這篇文章做法是,給予最先合并的圖片塊較大的權重,比如最后一塊完整圖像權重為1,倒數第二次合并的區域權重為2以此類推。但是當我們策略很多,多樣性很多的時候呢,這個權重就會有太多的重合了,排序不好搞啊。文章做法是給他們乘以一個隨機數,畢竟3分看運氣嘛,然后對于相同的區域多次出現的也疊加下權重,畢竟多個方法都說你是目標,也是有理由的嘛。這樣我就得到了所有區域的目標分數,也就可以根據自己的需要選擇需要多少個區域了。
?
四 選擇性搜索性能評估
自然地,通過算法計算得到的包含物體的Bounding Boxes與真實情況(ground truth)的窗口重疊越多,那么算法性能就越好。這是使用的指標是平均最高重疊率ABO(Average Best Overlap)。對于每個固定的類別 c,每個真實情況(ground truth)表示為?,令計算得到的位置假設L中的每個值lj,那么 ABO的公式表達為:
重疊率的計算方式:
?
上面結果給出的是一個類別的ABO,對于所有類別下的性能評價,很自然就是使用所有類別的ABO的平均值MABO(Mean Average Best Overlap)來評價。
?1、單一策略評估
我們可以通過改變多樣性策略中的任何一種,評估選擇性搜索的MABO性能指標。論文中采取的策略如下:
- 使用RGB色彩空間(基于圖的圖像分割會利用不同的色彩進行圖像區域分割)
- 采用四種相似度計算的組合方式
- 設置圖像分割的閾值k=50
然后通過改變其中一個策略參數,獲取MABO性能指標如下表(第一列為改變的參數,第二列為MABO值,第三列為獲取的候選區的個數):
表中左側為不同的相似度組合,單獨的,我們可以看到紋理相似度表現最差,MABO為0.581,其他的MABO值介于0.63和0.64之間。當使用多種相似度組合時MABO性能優于單種相似度。表的右上角表名使用HSV顏色空間,有463個候選區域,而且MABO值最大為0.693。表的右下角表名使用較小的閾值,會得到更多的候選區和較高的MABO值。
2、多樣性策略組合
我們使用貪婪的搜索算法,把單一策略進行組合,會獲得較高的MABO,但是也會造成計算成本的增加。下表給出了三種組合的MABO性能指標:
上圖中的綠色邊框為對象的標記邊框,紅色邊框為我們使用 'Quality' Selective Search算法獲得的Overlap最高的候選框。可以看到我們這個候選框和真實標記非常接近。
下表為和其它算法在VOC 2007測試集上的比較結果:
下圖為各個算法在選取不同候選區數量,Recall和MABO性能的曲線圖,從計算成本、以及性能考慮,Selective Search Fast算法在2000個候選區時,效果較好。
回到頂部
?五、代碼實現
?我們可以通過下面命令直接安裝Selective Search包。
pip install selectivesearch然后從https://github.com/AlpacaDB/selectivesearch下載源碼,運行example\example.py文件。效果如下:
?
# -*- coding: utf-8 -*- from __future__ import (division,print_function, )import skimage.data import matplotlib.pyplot as plt import matplotlib.patches as mpatches import selectivesearch import numpy as npdef main():# 加載圖片數據img = skimage.data.astronaut() '''執行selective search,regions格式如下[{'rect': (left, top, width, height),'labels': [...],'size': component_size},...]'''img_lbl, regions = selectivesearch.selective_search(img, scale=500, sigma=0.9, min_size=10)#計算一共分割了多少個原始候選區域temp = set()for i in range(img_lbl.shape[0]):for j in range(img_lbl.shape[1]): temp.add(img_lbl[i,j,3]) print(len(temp)) #286#計算利用Selective Search算法得到了多少個候選區域print(len(regions)) #570#創建一個集合 元素不會重復,每一個元素都是一個list(左上角x,左上角y,寬,高),表示一個候選區域的邊框candidates = set()for r in regions:#排除重復的候選區if r['rect'] in candidates:continue#排除小于 2000 pixels的候選區域(并不是bounding box中的區域大小) if r['size'] < 2000:continue#排除扭曲的候選區域邊框 即只保留近似正方形的x, y, w, h = r['rect']if w / h > 1.2 or h / w > 1.2:continuecandidates.add(r['rect'])#在原始圖像上繪制候選區域邊框fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))ax.imshow(img)for x, y, w, h in candidates:print(x, y, w, h)rect = mpatches.Rectangle((x, y), w, h, fill=False, edgecolor='red', linewidth=1)ax.add_patch(rect)plt.show()if __name__ == "__main__":main()?
selective_search函數的定義如下:
def selective_search(im_orig, scale=1.0, sigma=0.8, min_size=50):'''Selective Search首先通過基于圖的圖像分割方法初始化原始區域,就是將圖像分割成很多很多的小塊然后我們使用貪心策略,計算每兩個相鄰的區域的相似度然后每次合并最相似的兩塊,直到最終只剩下一塊完整的圖片然后這其中每次產生的圖像塊包括合并的圖像塊我們都保存下來Parameters----------im_orig : ndarrayInput imagescale : intFree parameter. Higher means larger clusters in felzenszwalb segmentation.sigma : floatWidth of Gaussian kernel for felzenszwalb segmentation.min_size : intMinimum component size for felzenszwalb segmentation.Returns-------img : ndarrayimage with region labelregion label is stored in the 4th value of each pixel [r,g,b,(region)]regions : array of dict[{'rect': (left, top, width, height),'labels': [...],'size': component_size 候選區域大小,并不是邊框的大小},...]'''assert im_orig.shape[2] == 3, "3ch image is expected"# load image and get smallest regions# region label is stored in the 4th value of each pixel [r,g,b,(region)] #圖片分割 把候選區域標簽合并到最后一個通道上 height x width x 4 每一個像素的值為[r,g,b,(region)] img = _generate_segments(im_orig, scale, sigma, min_size)if img is None:return None, {}#計算圖像大小imsize = img.shape[0] * img.shape[1]#dict類型,鍵值為候選區域的標簽 值為候選區域的信息,包括候選區域的邊框,以及區域的大小,顏色直方圖,紋理特征直方圖等信息R = _extract_regions(img)#list類型 每一個元素都是鄰居候選區域對(ri,rj) (即兩兩相交的候選區域)neighbours = _extract_neighbours(R)# calculate initial similarities 初始化相似集合S = ?S = {}#計算每一個鄰居候選區域對的相似度s(ri,rj)for (ai, ar), (bi, br) in neighbours: #S=S∪s(ri,rj) ai表示候選區域ar的標簽 比如當ai=1 bi=2 S[(1,2)就表示候選區域1和候選區域2的相似度S[(ai, bi)] = _calc_sim(ar, br, imsize)# hierarchal search 層次搜索 直至相似度集合為空while S != {}:# get highest similarity 獲取相似度最高的兩個候選區域 i,j表示候選區域標簽i, j = sorted(S.items(), key=lambda i: i[1])[-1][0] #按照相似度排序# merge corresponding regions 合并相似度最高的兩個鄰居候選區域 rt = ri∪rj ,R = R∪rtt = max(R.keys()) + 1.0R[t] = _merge_regions(R[i], R[j])# mark similarities for regions to be removed 獲取需要刪除的元素的鍵值 key_to_delete = [] for k, v in S.items(): #k表示鄰居候選區域對(i,j) v表示候選區域(i,j)表示相似度if (i in k) or (j in k):key_to_delete.append(k)# remove old similarities of related regions 移除候選區域ri對應的所有相似度:S = S\s(ri,r*) 移除候選區域rj對應的所有相似度:S = S\s(r*,rj)for k in key_to_delete:del S[k]# calculate similarity set with the new region 計算候選區域rt對應的相似度集合St,S = S∪Stfor k in filter(lambda a: a != (i, j), key_to_delete):n = k[1] if k[0] in (i, j) else k[0]S[(t, n)] = _calc_sim(R[t], R[n], imsize)#獲取每一個候選區域的的信息 邊框、以及候選區域size,標簽regions = []for k, r in R.items():regions.append({'rect': (r['min_x'], r['min_y'],r['max_x'] - r['min_x'], r['max_y'] - r['min_y']),'size': r['size'],'labels': r['labels']})#img:基于圖的圖像分割得到的候選區域 regions:Selective Search算法得到的候選區域return img, regions?
該函數是按照Selective Search算法實現的,算法的每一步都有相對應的代碼,并且把初始化候選區域,鄰居候選區域對的遍歷以及相似度計算,候選區域的合并都單獨封裝成了一個函數,由于代碼比較長,就不一一介紹了,下面我把代碼附上,并且做了詳細的介紹,有興趣研究的童鞋看一下:
# -*- coding: utf-8 -*- import skimage.io import skimage.feature import skimage.color import skimage.transform import skimage.util import skimage.segmentation import numpy# "Selective Search for Object Recognition" by J.R.R. Uijlings et al. # # - Modified version with LBP extractor for texture vectorizationdef _generate_segments(im_orig, scale, sigma, min_size):"""segment smallest regions by the algorithm of Felzenswalb andHuttenlocher"""# open the Image segment_mask : (width, height) ndarray Integer mask indicating segment labels.im_mask = skimage.segmentation.felzenszwalb(skimage.util.img_as_float(im_orig), scale=scale, sigma=sigma,min_size=min_size)# merge mask channel to the image as a 4th channel 把類別合并到最后一個通道上 height x width x 4im_orig = numpy.append(im_orig, numpy.zeros(im_orig.shape[:2])[:, :, numpy.newaxis], axis=2)im_orig[:, :, 3] = im_maskreturn im_origdef _sim_colour(r1, r2):"""計算顏色相似度calculate the sum of histogram intersection of colourargs:r1:候選區域r1r2:候選區域r2return:[0,3]之間的數值"""return sum([min(a, b) for a, b in zip(r1["hist_c"], r2["hist_c"])])def _sim_texture(r1, r2):"""計算紋理特征相似度calculate the sum of histogram intersection of textureargs:r1:候選區域r1r2:候選區域r2return:[0,3]之間的數值"""return sum([min(a, b) for a, b in zip(r1["hist_t"], r2["hist_t"])])def _sim_size(r1, r2, imsize):"""計算候選區域大小相似度calculate the size similarity over the imageargs:r1:候選區域r1r2:候選區域r2return:[0,1]之間的數值"""return 1.0 - (r1["size"] + r2["size"]) / imsizedef _sim_fill(r1, r2, imsize):"""計算候選區域的距離合適度相似度calculate the fill similarity over the imageargs:r1:候選區域r1r2:候選區域r2imsize:原圖像像素數return:[0,1]之間的數值"""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"]) / imsizedef _calc_sim(r1, r2, imsize):'''計算兩個候選區域的相似度,權重系數默認都是1args:r1:候選區域r1r2:候選區域r2imsize:原圖片像素數'''return (_sim_colour(r1, r2) + _sim_texture(r1, r2)+ _sim_size(r1, r2, imsize) + _sim_fill(r1, r2, imsize))def _calc_colour_hist(img):"""使用L1-norm歸一化獲取圖像每個顏色通道的25 bins的直方圖,這樣每個區域都可以得到一個75維的向量calculate colour histogram for each regionthe size of output histogram will be BINS * COLOUR_CHANNELS(3)number of bins is 25 as same as [uijlings_ijcv2013_draft.pdf]extract HSVargs:img:ndarray類型, 形狀為候選區域像素數 x 3(h,s,v)return:一維的ndarray類型,長度為75"""BINS = 25hist = numpy.array([])for colour_channel in (0, 1, 2):# extracting one colour channelc = img[:, colour_channel]# calculate histogram for each colour and join to the result #計算每一個顏色通道的25 bins的直方圖 然后合并到一個一維數組中hist = numpy.concatenate([hist] + [numpy.histogram(c, BINS, (0.0, 255.0))[0]])# L1 normalize len(img):候選區域像素數hist = hist / len(img)return histdef _calc_texture_gradient(img):"""原文:對每個顏色通道的8個不同方向計算方差σ=1的高斯微分(Gaussian Derivative,這里使用LBP替代calculate texture gradient for entire imageThe original SelectiveSearch algorithm proposed Gaussian derivativefor 8 orientations, but we use LBP instead.output will be [height(*)][width(*)]args:img: ndarray類型,形狀為height x width x 4,每一個像素的值為 [r,g,b,(region)]return:紋理特征,形狀為height x width x 4"""ret = numpy.zeros((img.shape[0], img.shape[1], img.shape[2]))for colour_channel in (0, 1, 2):ret[:, :, colour_channel] = skimage.feature.local_binary_pattern(img[:, :, colour_channel], 8, 1.0)return retdef _calc_texture_hist(img):"""使用L1-norm歸一化獲取圖像每個顏色通道的每個方向的10 bins的直方圖,這樣就可以獲取到一個240(10x8x3)維的向量calculate texture histogram for each regioncalculate the histogram of gradient for each coloursthe size of output histogram will beBINS * ORIENTATIONS * COLOUR_CHANNELS(3)args:img:候選區域紋理特征 形狀為候選區域像素數 x 4(r,g,b,(region))return:一維的ndarray類型,長度為240"""BINS = 10hist = numpy.array([])for colour_channel in (0, 1, 2):# mask by the colour channelfd = img[:, colour_channel]# calculate histogram for each orientation and concatenate them all# and join to the resulthist = numpy.concatenate([hist] + [numpy.histogram(fd, BINS, (0.0, 1.0))[0]])# L1 Normalize len(img):候選區域像素數hist = hist / len(img)return histdef _extract_regions(img):'''提取每一個候選區域的信息 比如類別(region)為5的區域表示的是一只貓的選區,這里就是提取這只貓的邊界框,左上角后右下角坐標args:img: ndarray類型,形狀為height x width x 4,每一個像素的值為 [r,g,b,(region)]return : R:dict 每一個元素對應一個候選區域, 每個元素也是一個dict類型{min_x:邊界框的左上角x坐標,min_y:邊界框的左上角y坐標,max_x:邊界框的右下角x坐標,max_y:邊界框的右下角y坐標,size:像素個數,hist_c:顏色的直方圖,hist_t:紋理特征的直方圖,} '''#保存所有候選區域的bounding box 每一個元素都是一個dict {最小x坐標值,最小y坐標值,最大x坐標值,最大y坐標值,類別}# 通過上面四個參數確定一個邊界框R = {}# get hsv image RGB轉換為HSV色彩空間 height x width x 3hsv = skimage.color.rgb2hsv(img[:, :, :3])# pass 1: count pixel positions 遍歷每一個像素for y, i in enumerate(img): #y = 0 -> height - 1for x, (r, g, b, l) in enumerate(i): # x = 0 -> height - 1# initialize a new regionif l not in R:R[l] = {"min_x": 0xffff, "min_y": 0xffff,"max_x": 0, "max_y": 0, "labels": [l]}# bounding boxif R[l]["min_x"] > x:R[l]["min_x"] = xif R[l]["min_y"] > y:R[l]["min_y"] = yif R[l]["max_x"] < x:R[l]["max_x"] = xif R[l]["max_y"] < y:R[l]["max_y"] = y# pass 2: calculate texture gradient 紋理特征提取 利用LBP算子 height x width x 4tex_grad = _calc_texture_gradient(img)# pass 3: calculate colour histogram of each region 計算每一個候選區域(注意不是bounding box圈住的區域)的直方圖for k, v in R.items():# colour histogram height x width x 3 -> 候選區域k像素數 x 3(img[:, :, 3] == k返回的是一個二維坐標的集合)masked_pixels = hsv[:, :, :][img[:, :, 3] == k]#print(type(masked_pixels),masked_pixels.shape)R[k]["size"] = len(masked_pixels / 4) #候選區域k像素數#在hsv色彩空間下,使用L1-norm歸一化獲取圖像每個顏色通道的25 bins的直方圖,這樣每個區域都可以得到一個75維的向量R[k]["hist_c"] = _calc_colour_hist(masked_pixels) #在rgb色彩空間下,使用L1-norm歸一化獲取圖像每個顏色通道的每個方向的10 bins的直方圖,這樣就可以獲取到一個240(10x8x3)維的向量R[k]["hist_t"] = _calc_texture_hist(tex_grad[:, :][img[:, :, 3] == k]) #tex_grad[:, :][img[:, :, 3] == k]形狀為候選區域像素數 x 4return Rdef _extract_neighbours(regions):'''提取 鄰居候選區域對(ri,rj)(即兩兩相交)args:regions:dict 每一個元素都對應一個候選區域return:返回一個list,每一個元素都對應一個鄰居候選區域對'''#判斷兩個候選區域是否相交def intersect(a, b):if (a["min_x"] < b["min_x"] < a["max_x"]and a["min_y"] < b["min_y"] < a["max_y"]) or (a["min_x"] < b["max_x"] < a["max_x"]and a["min_y"] < b["max_y"] < a["max_y"]) or (a["min_x"] < b["min_x"] < a["max_x"]and a["min_y"] < b["max_y"] < a["max_y"]) or (a["min_x"] < b["max_x"] < a["max_x"]and a["min_y"] < b["min_y"] < a["max_y"]):return Truereturn False#轉換為list 每一個元素 (l,regions[l])R = list(regions.items())#保存兩兩相交候選區域對neighbours = []#每次抽取兩個候選區域 兩兩組合,判斷是否相交for cur, a in enumerate(R[:-1]):for b in R[cur + 1:]:if intersect(a[1], b[1]):neighbours.append((a, b))return neighboursdef _merge_regions(r1, r2):'''合并兩個候選區域args:r1:候選區域1r2:候選區域2return:返回合并后的候選區域rt'''new_size = r1["size"] + r2["size"]rt = {"min_x": min(r1["min_x"], r2["min_x"]),"min_y": min(r1["min_y"], r2["min_y"]),"max_x": max(r1["max_x"], r2["max_x"]),"max_y": max(r1["max_y"], r2["max_y"]),"size": new_size,"hist_c": (r1["hist_c"] * r1["size"] + r2["hist_c"] * r2["size"]) / new_size,"hist_t": (r1["hist_t"] * r1["size"] + r2["hist_t"] * r2["size"]) / new_size,"labels": r1["labels"] + r2["labels"]}return rtdef selective_search(im_orig, scale=1.0, sigma=0.8, min_size=50):'''Selective Search首先通過基于圖的圖像分割方法初始化原始區域,就是將圖像分割成很多很多的小塊然后我們使用貪心策略,計算每兩個相鄰的區域的相似度然后每次合并最相似的兩塊,直到最終只剩下一塊完整的圖片然后這其中每次產生的圖像塊包括合并的圖像塊我們都保存下來Parameters----------im_orig : ndarrayInput imagescale : intFree parameter. Higher means larger clusters in felzenszwalb segmentation.sigma : floatWidth of Gaussian kernel for felzenszwalb segmentation.min_size : intMinimum component size for felzenszwalb segmentation.Returns-------img : ndarrayimage with region labelregion label is stored in the 4th value of each pixel [r,g,b,(region)]regions : array of dict[{'rect': (left, top, width, height),'labels': [...],'size': component_size 候選區域大小,并不是邊框的大小},...]'''assert im_orig.shape[2] == 3, "3ch image is expected"# load image and get smallest regions# region label is stored in the 4th value of each pixel [r,g,b,(region)] #圖片分割 把候選區域標簽合并到最后一個通道上 height x width x 4 每一個像素的值為[r,g,b,(region)] img = _generate_segments(im_orig, scale, sigma, min_size)if img is None:return None, {}#計算圖像大小imsize = img.shape[0] * img.shape[1]#dict類型,鍵值為候選區域的標簽 值為候選區域的信息,包括候選區域的邊框,以及區域的大小,顏色直方圖,紋理特征直方圖等信息R = _extract_regions(img)#list類型 每一個元素都是鄰居候選區域對(ri,rj) (即兩兩相交的候選區域)neighbours = _extract_neighbours(R)# calculate initial similarities 初始化相似集合S = ?S = {}#計算每一個鄰居候選區域對的相似度s(ri,rj)for (ai, ar), (bi, br) in neighbours: #S=S∪s(ri,rj) ai表示候選區域ar的標簽 比如當ai=1 bi=2 S[(1,2)就表示候選區域1和候選區域2的相似度S[(ai, bi)] = _calc_sim(ar, br, imsize)# hierarchal search 層次搜索 直至相似度集合為空while S != {}:# get highest similarity 獲取相似度最高的兩個候選區域 i,j表示候選區域標簽i, j = sorted(S.items(), key=lambda i: i[1])[-1][0] #按照相似度排序# merge corresponding regions 合并相似度最高的兩個鄰居候選區域 rt = ri∪rj ,R = R∪rtt = max(R.keys()) + 1.0R[t] = _merge_regions(R[i], R[j])# mark similarities for regions to be removed 獲取需要刪除的元素的鍵值 key_to_delete = [] for k, v in S.items(): #k表示鄰居候選區域對(i,j) v表示候選區域(i,j)表示相似度if (i in k) or (j in k):key_to_delete.append(k)# remove old similarities of related regions 移除候選區域ri對應的所有相似度:S = S\s(ri,r*) 移除候選區域rj對應的所有相似度:S = S\s(r*,rj)for k in key_to_delete:del S[k]# calculate similarity set with the new region 計算新的候選區域rt對應的相似度集合St,S = S∪Stfor k in filter(lambda a: a != (i, j), key_to_delete): #過濾除了(i,j)之外的候選區域n = k[1] if k[0] in (i, j) else k[0]#計算新的候選區域t與候選區域n之間的相似度S[(t, n)] = _calc_sim(R[t], R[n], imsize)#獲取每一個候選區域的的信息 邊框、以及候選區域size,標簽regions = []for k, r in R.items():regions.append({'rect': (r['min_x'], r['min_y'],r['max_x'] - r['min_x'], r['max_y'] - r['min_y']),'size': r['size'],'labels': r['labels']})#img:ndarray 基于圖的圖像分割得到的候選區域 regions:list Selective Search算法得到的候選區域return img, regions?
參考文章:
[1]圖像分割—基于圖的圖像分割(Graph-Based Image Segmentation)(附代碼)
[2]目標檢測(1)-Selective Search
[3]https://github.com/AlpacaDB/selectivesearch(代碼)
[4]Selective Search for Object Recognition(推薦)
[5]J.R. Uijlings, K.E. vandeSande, T. Gevers, and A.W. Smeulders. Selective search for object recognition. IJCV, 2013.
[6]相關源代碼(matlab)
[7]C++簡版代碼
總結
以上是生活随笔為你收集整理的目标检测之选择性搜索-Selective Search的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目标检测技术演进:R-CNN、Fast
- 下一篇: 浅谈数据湖