引言
計(jì)算機(jī)視覺領(lǐng)域有幾個(gè)基本的任務(wù):
- object recognition:物體識(shí)別,即在給定的圖片中辨認(rèn)出其中的物體種類,例如貓,飛機(jī),行人等。這里的輸入是整個(gè)圖片,輸出是類別標(biāo)簽以及相應(yīng)的概率(自信度)。
- image classification = object recognition

- object detection: 物體檢測(cè),不僅要識(shí)別圖片中的物體,還要定位 (localization),即用 bounding box 將物體圈出來。如果圖片中有多個(gè)物體,則每個(gè)物體都要有對(duì)應(yīng)的 bounding box 和識(shí)別出的類型標(biāo)簽。

object detection 的基礎(chǔ)是 object recognition,只不過要先將圖片進(jìn)行分割,對(duì)每個(gè)分割之后的子圖區(qū)域 region (也稱為 patch) 進(jìn)行 object recognition.
由于事先并不知道物體在圖片的哪個(gè)位置,為了避免漏檢,我們應(yīng)該對(duì)圖片中盡量多的 region 進(jìn)行搜索。理論上來說,可以有無窮多個(gè) region。這里就需要一種 region proposal 的算法,以比較高效的方式提出圖片劃分 region 的方式,從而加速整個(gè) object detection 的過程并且提高準(zhǔn)確率。
本文將要介紹的 selective search 算法,是比較經(jīng)典的,也是 R-CNN 中使用的 region proposal 算法。
參考文獻(xiàn):
- original paper by Uijlings et al
- https://www.learnopencv.com/selective-search-for-object-detection-cpp-python/
基本思想
為了避免蠻力搜索,selective search 算法首先需要一個(gè)基于像素的圖像分割。這里用的是 Felzenszwalb and Huttenlocher 算法 (因?yàn)槭钱?dāng)時(shí)速度最快的算法,而且是公開的),得到一個(gè) oversegmented 的圖像分割。例如:

這里之所以用 oversegmented 圖像,是為了得到盡可能細(xì)分的區(qū)域,再以此為基礎(chǔ)逐步合并,形成更大的區(qū)域。
image segmentation 可以用作 region proposal 的基礎(chǔ),每個(gè)分割的區(qū)域都可以看作一個(gè)潛在的 region,但是一個(gè) object 往往包含了多個(gè)分割的區(qū)域,例如盛有咖啡的杯子,咖啡和杯子應(yīng)該作為一個(gè)整體來看待。因此,還要根據(jù)某種相似性原則進(jìn)行分割區(qū)域的合并,得到更大范圍的 region。
Selective search 算法考慮了 4 種相似性度量,取值都在 [0,1] 之間,越大越相似。
- 顏色相似性
- 紋理相似性
- size 相似性
,促使小的區(qū)域之間優(yōu)先合并
- shape 相似性
,合并只能在緊鄰的兩個(gè)區(qū)域間進(jìn)行,遠(yuǎn)離的兩個(gè)區(qū)域不能合并
最終的相似性度量是上述四個(gè)度量的組合:
其中 取 0 或 1.
總結(jié)起來,selective search 的算法步驟非常簡(jiǎn)單:
- 基于 oversegmented 得到細(xì)分的區(qū)域,作為初始的 region 集合。
- 計(jì)算 region 兩兩之間的相似性,合并具有最大相似性的兩個(gè) region,得到新的更大的 region,加入 region 集合中。
- 重復(fù) step 2,直到整幅圖只剩一個(gè) region。至此,得到的 region 集合就是算法的輸出。

實(shí)例程序
環(huán)境配置:
- opencv >= 3.3,本機(jī)用的 4.1.0 版本
- 安裝
opencv-contrib-pythonpackagepip install opencv-contrib-python --user -
測(cè)試用的圖片如下:
from https://people.com/pets/dog-breeds-diversity-verses-cats-breeds-explainer/
具體程序:
import sys
import cv2
# 讀取照片,這里要根據(jù)具體情況設(shè)置路徑
im = cv2.imread("./pic/cats-dogs.jpg")
# 重置圖片大小,高設(shè)置為 400,保持高、寬比例
newHeight = 400
newWidth = int(im.shape[1]*400/im.shape[0])
im = cv2.resize(im, (newWidth, newHeight))
# 創(chuàng)建 Selective Search Segmentation 對(duì)象
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
# 添加待處理的圖片
ss.setBaseImage(im)
# 可以選擇快速但是低 recall 的方式
# 這里的 recall 指的是選擇出來的 region 是否包含了所有應(yīng)該包含的區(qū)域。recall 越高越好
#ss.switchToSelectiveSearchFast()
# 也可以選擇慢速但是高 recall 的方式
ss.switchToSelectiveSearchQuality()
# 進(jìn)行 region 劃分,輸出得到的 region 數(shù)目
rects = ss.process()
print('Total Number of Region Proposals: {}'.format(len(rects)))
# 設(shè)定要顯示的 region 數(shù)目
numShowRects = 100
# 可以通過按鍵逐步增加或者減少顯示的 region 數(shù)目
increment = 50
while True:
# 不要在原圖上畫 bounding box,而是復(fù)制一個(gè)新圖
imOut = im.copy()
# 遍歷 regions
for i, rect in enumerate(rects):
# 通過 bounding box 顯示出指定數(shù)量的 region
if (i < numShowRects):
x, y, w, h = rect # bounding box 左上角坐標(biāo) x,y, 以及 box 的寬和高
cv2.rectangle(imOut, (x, y), (x+w, y+h), (0, 255, 0), 1) # 綠色 box,線寬為 1
else:
break
# 顯示圖片+bbox
cv2.imshow("Output", imOut)
# 接收按鍵輸入
k = cv2.waitKey(0) & 0xFF
# “m” 鍵 is pressed
if k == 109:
# 增加顯示的 bbox 數(shù)目
numShowRects += increment
# “l(fā)” 鍵 is pressed
elif k == 108 and numShowRects > increment:
# 減少顯示的 bbox 數(shù)目
numShowRects -= increment
# “q” 鍵 is pressed
elif k == 113:
break
# close image show window
cv2.destroyAllWindows()
最后效果如下:

顯示的 100 個(gè) region 已經(jīng)包含了我們感興趣的待檢測(cè)區(qū)域,說明了 selective search 算法的高效。
