引言
計算機視覺領域有幾個基本的任務:
- object recognition:物體識別,即在給定的圖片中辨認出其中的物體種類,例如貓,飛機,行人等。這里的輸入是整個圖片,輸出是類別標簽以及相應的概率(自信度)。
- image classification = object recognition
- object detection: 物體檢測,不僅要識別圖片中的物體,還要定位 (localization),即用 bounding box 將物體圈出來。如果圖片中有多個物體,則每個物體都要有對應的 bounding box 和識別出的類型標簽。
object detection 的基礎是 object recognition,只不過要先將圖片進行分割,對每個分割之后的子圖區域 region (也稱為 patch) 進行 object recognition.
由于事先并不知道物體在圖片的哪個位置,為了避免漏檢,我們應該對圖片中盡量多的 region 進行搜索。理論上來說,可以有無窮多個 region。這里就需要一種 region proposal 的算法,以比較高效的方式提出圖片劃分 region 的方式,從而加速整個 object detection 的過程并且提高準確率。
本文將要介紹的 selective search 算法,是比較經典的,也是 R-CNN 中使用的 region proposal 算法。
參考文獻:
- original paper by Uijlings et al
- https://www.learnopencv.com/selective-search-for-object-detection-cpp-python/
基本思想
為了避免蠻力搜索,selective search 算法首先需要一個基于像素的圖像分割。這里用的是 Felzenszwalb and Huttenlocher 算法 (因為是當時速度最快的算法,而且是公開的),得到一個 oversegmented 的圖像分割。例如:
這里之所以用 oversegmented 圖像,是為了得到盡可能細分的區域,再以此為基礎逐步合并,形成更大的區域。
image segmentation 可以用作 region proposal 的基礎,每個分割的區域都可以看作一個潛在的 region,但是一個 object 往往包含了多個分割的區域,例如盛有咖啡的杯子,咖啡和杯子應該作為一個整體來看待。因此,還要根據某種相似性原則進行分割區域的合并,得到更大范圍的 region。
Selective search 算法考慮了 4 種相似性度量,取值都在 [0,1] 之間,越大越相似。
- 顏色相似性
- 紋理相似性
- size 相似性
,促使小的區域之間優先合并
- shape 相似性
,合并只能在緊鄰的兩個區域間進行,遠離的兩個區域不能合并
最終的相似性度量是上述四個度量的組合:
其中 取 0 或 1.
總結起來,selective search 的算法步驟非常簡單:
- 基于 oversegmented 得到細分的區域,作為初始的 region 集合。
- 計算 region 兩兩之間的相似性,合并具有最大相似性的兩個 region,得到新的更大的 region,加入 region 集合中。
- 重復 step 2,直到整幅圖只剩一個 region。至此,得到的 region 集合就是算法的輸出。
實例程序
環境配置:
- opencv >= 3.3,本機用的 4.1.0 版本
- 安裝
opencv-contrib-python
packagepip install opencv-contrib-python --user
-
測試用的圖片如下:
from https://people.com/pets/dog-breeds-diversity-verses-cats-breeds-explainer/
具體程序:
import sys
import cv2
# 讀取照片,這里要根據具體情況設置路徑
im = cv2.imread("./pic/cats-dogs.jpg")
# 重置圖片大小,高設置為 400,保持高、寬比例
newHeight = 400
newWidth = int(im.shape[1]*400/im.shape[0])
im = cv2.resize(im, (newWidth, newHeight))
# 創建 Selective Search Segmentation 對象
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
# 添加待處理的圖片
ss.setBaseImage(im)
# 可以選擇快速但是低 recall 的方式
# 這里的 recall 指的是選擇出來的 region 是否包含了所有應該包含的區域。recall 越高越好
#ss.switchToSelectiveSearchFast()
# 也可以選擇慢速但是高 recall 的方式
ss.switchToSelectiveSearchQuality()
# 進行 region 劃分,輸出得到的 region 數目
rects = ss.process()
print('Total Number of Region Proposals: {}'.format(len(rects)))
# 設定要顯示的 region 數目
numShowRects = 100
# 可以通過按鍵逐步增加或者減少顯示的 region 數目
increment = 50
while True:
# 不要在原圖上畫 bounding box,而是復制一個新圖
imOut = im.copy()
# 遍歷 regions
for i, rect in enumerate(rects):
# 通過 bounding box 顯示出指定數量的 region
if (i < numShowRects):
x, y, w, h = rect # bounding box 左上角坐標 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 數目
numShowRects += increment
# “l” 鍵 is pressed
elif k == 108 and numShowRects > increment:
# 減少顯示的 bbox 數目
numShowRects -= increment
# “q” 鍵 is pressed
elif k == 113:
break
# close image show window
cv2.destroyAllWindows()
最后效果如下:
顯示的 100 個 region 已經包含了我們感興趣的待檢測區域,說明了 selective search 算法的高效。