1 概述
本文牽涉的概念是候選區域(Region Proposal ),用於物體檢測算法的輸入。無論是機器學習算法還是深度學習算法,候選區域都有用武之地。
2 物體檢測和物體識別
物體識別是要分辨出圖片中有什么物體,輸入是圖片,輸出是類別標簽和概率。物體檢測算法不僅要檢測圖片中有什么物體,還要輸出物體的外框(x, y, width, height)來定位物體的位置。
物體檢測的核心就是物體識別。
為了定位物體,我們需要選擇一些子區域並在子區域上運行物體識別算法。物體的位置就是物體識別算法返回最高概率的子區域內。
產生候選子區域的最直接的方法就是滑窗法,但是這種辦法效率比較低,一般使用‘候選區域’算法,而擇性搜索(Selective Search)就是最流行的候選區域產生算法之一(個人理解:這個最流行可能是針對論文那兩年說的,現在深度學習都是使用網絡產生候選區域,不用算法生成了)。
2.1 滑窗法
在滑窗方案中,我們要使用一個小窗口遍歷搜索整張圖片,在每個位置上對滑窗內的圖片做物體識別算法。不僅要搜索不同的位置,還要遍歷不同的大小,工作量可想而知。問題還沒完,對於人臉和人體這種長寬比基本固定的物體還好,對於長寬不固定的物體,搜索起來簡直就是噩夢,計算量直接飆升。
2.2 候選區域(Region Proposal)算法
滑窗法的問題可以使用候選區域產生算法解決。這些算法輸入整張圖片,然后輸出可能有物體的候選區域位置,這些候選區域可以有噪聲或者重疊,或者和物體的重合度不是很好,這都不要緊,只要這些區域里有一個和實際物體的位置足夠接近就行。因為不好的候選區域會被物體識別算法過濾掉。
候選區域算法用分割不同區域的辦法來識別潛在的物體。在分割的時候,我們要合並那些在某些方面(如顏色、紋理)類似的小區域。相比滑窗法在不同位置和大小的窮舉,候選區域算法將像素分配到少數的分割區域中。所以最終候選區域算法產生的數量比滑窗法少的多,從而大大減少運行物體識別算法的次數。同時候選區域算法所選定的范圍天然兼顧了不同的大小和長寬比。
候選區域算法比較重要的特征就是要有較高的召回率。我們要通過這種方法保證擁有物體的區域就在候選區域列表里。所以我們不介意有很多區域什么都有,這都沒關系,物體檢測算法會過濾掉他們,雖然會浪費一點時間。
目前已有不少成熟的后續區域產生算法:
- Objectness
- Constrained Parametric Min-Cuts for Automatic Object Segmentation
- Category Independent Object Proposals
- Randomized Prim
- Selective Search
由於Selective Search又快召回率又高,這個方法是最常用的。說了這么多,終於牽出本文的主角了。
2.3 物體檢測之選擇性搜索(Selective Search)
選擇性搜索算法用於為物體檢測算法提供候選區域,它速度快,召回率高。選擇性搜索算法需要先使用《Efficient Graph-Based Image Segmentation》論文里的方法產生初始的分割區域,然后使用相似度計算方法合並一些小的區域。 下列兩張圖分別是原圖和原始分割圖:
我們不能使用原始分割圖的區域作為候選區域,原因如下:
- 大部分物體在原始分割圖里都被分為多個區域
- 原始分割圖無法體現物體之間的遮擋和包含。
如果我們試圖通過進一步合並相鄰的區域來解決第一個問題,我們最終會得到一個包含兩個對象的分段區域。我們不要需要完美的的分割區域,我們只想要和實際物體高度重合的區域就行了。選擇性搜索算法使用《Efficient Graph-Based Image Segmentation》論文里的方法產生初始的分割區域作為輸入,通過下面的步驟進行合並:
- 首先將所有分割區域的外框加到候選區域列表中
- 基於相似度合並一些區域
- 將合並后的分割區域作為一個整體,跳到步驟1
通過不停的迭代,候選區域列表中的區域越來越大。可以說,我們通過自底向下的方法創建了越來越大的候選區域。表示效果如下:
相似度
選擇性搜索算法如何計算兩個區域的像素度的呢? 主要是通過以下四個方面:顏色、紋理、大小和形狀交疊 ,最終的相似度是這四個值取不同的權重相加
效果
opencv實現了選擇性搜索算法,可以給出上千個根據有物體的可能性降序排列的候選區域。下圖是畫出了前面200~250個候選區域的效果。一般來說。1000~1200個候選區域基本能勝任物體檢測的任務了。 python代碼實現

#!/usr/bin/env python import cv2 if __name__ == '__main__': # speed-up using multithreads cv2.setUseOptimized( True ); cv2.setNumThreads( 4 ); # read image im = cv2.imread( 'test.jpg' ) # resize image newHeight = 200 newWidth = int( im.shape[1] * 200 / im.shape[0] ) im = cv2.resize( im, (newWidth, newHeight) ) # create Selective Search Segmentation Object using default parameters ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation() # set input image on which we will run segmentation ss.setBaseImage( im ) if 1: # Switch to fast but low recall Selective Search method ss.switchToSelectiveSearchFast() else: # Switch to high recall but slow Selective Search method ss.switchToSelectiveSearchQuality() # run selective search segmentation on input image rects = ss.process() print( 'Total Number of Region Proposals: {}'.format( len( rects ) ) ) # number of region proposals to show numShowRects = 100 # increment to increase/decrease total number # of reason proposals to be shown increment = 50 while True: # create a copy of original image imOut = im.copy() # itereate over all the region proposals for i, rect in enumerate( rects ): # draw rectangle for region proposal till numShowRects if (i < numShowRects): x, y, w, h = rect cv2.rectangle( imOut, (x, y), (x + w, y + h), (0, 255, 0), 1, cv2.LINE_AA ) else: break # show output cv2.imshow( "Output", imOut ) # record key press k = cv2.waitKey( 0 ) & 0xFF # m is pressed if k == 109: # increase total number of rectangles to show by increment numShowRects += increment # l is pressed elif k == 108 and numShowRects > increment: # decrease total number of rectangles to show by increment numShowRects -= increment # q is pressed elif k == 113: break # close image show window cv2.destroyAllWindows()
結果如下所示:
效果不是很好,現在都是用深度!!!
參考:yuanlulu