Selective Search算法理解
一、算法简介
(一)前言
在目标检测算法中,RCNN具有较大的影响力。该模型的架构如上图所示。在RCNN中,为了确定目标的具体位置,使用了Selective Search算法,从原始图像中提取2000左右个候选区域,然后再将这些候选区域输入到后续架构中,确定目标的具体位置。
关于RCNN算法的讲解,可参考:
R-CNN(Region-based Convolutional Neural Network)论文解读
(二)算法思路
算法分为如下几个大步:
- 生成原始的区域集R(利用felzenszwalb算法)
- 计算区域集R里每个相邻区域的相似度S={s1,s2,…}
- 找出相似度最高的两个区域,将其合并为新集,添加进R
- 从S中移除所有与第3步中有关的子集
- 计算新集与所有子集的相似度
- 跳至第三步,不断循环,合并,直至S为空(到不能再合并时为止)
(三)Python源码分析
1、主函数:selective_search
(1)作用
- 通过图像分割的方法,从原始图像中提取小区域。
(2)输入
- im_orig
原始图片,数据类型为ndarray。 - scale
图像分割的集群程度。
值越大,意味集群程度越高,分割的越少,获得子区域越大,默认为1。
数据类型为int。 - sigma
图像分割前,会先对原图像进行高斯滤波去噪,sigma即为高斯核的大小,默认为0.8。
数据类型为float。 - min_size
最小的区域像素点个数。当小于此值时,图像分割的计算就停止,默认为20。
数据类型为int。
(3)输出
- img
具有区域标签的图片,数据类型为ndarray。
img数组的形状为(im_orig_height, im_orig_width, 4)
其中存储的像素信息为:[r, g, b, (region)],region是指区域标签。 - regions
区域列表
regions的结构如下:
[
{‘rect’: (x_l, y_d, width, height), ‘size’: size, ‘labels’: labels_list},
…
]
变量名注释:
‘rect’:
x_l:区域左下角的x值;
y_d:区域左下角的y值;
width:区域的宽;
height:区域的高。
通过这四个值,我们就能够确定区域的大小及位置。
‘size’:区域的尺寸。
‘labels’:区域的标签,是列表。
(4)源码
def selective_search(
im_orig, scale=1.0, sigma=0.8, min_size=50):
'''Selective Search
Parameters
----------
im_orig : ndarray
Input image
scale : int
Free parameter. Higher means larger clusters in felzenszwalb segmentation.
sigma : float
Width of Gaussian kernel for felzenszwalb segmentation.
min_size : int
Minimum component size for felzenszwalb segmentation.
Returns
-------
img : ndarray
image with region label
region 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)]
img = _generate_segments(im_orig, scale, sigma, min_size)
if img is None:
return None, {}
imsize = img.shape[0] * img.shape[1]
R = _extract_regions(img)
# extract neighbouring information
neighbours = _extract_neighbours(R)
# calculate initial similarities
S = {}
for (ai, ar), (bi, br) in neighbours:
S[(ai, bi)] = _calc_sim(ar, br, imsize)
# hierarchal search
while S != {}:
# get highest similarity
i, j = sorted(S.items(), key=lambda i: i[1])[-1][0]
# merge corresponding regions
t = max(R.keys()) + 1.0
R[t] = _merge_regions(R[i], R[j])
# mark similarities for regions to be removed
key_to_delete = []
for k, v in list(S.items()):
if (i in k) or (j in k):
key_to_delete.append(k)
# remove old similarities of related regions
for k in key_to_delete:
del S[k]
# calculate similarity set with the new region
for k in [a for a in key_to_delete if a != (i, j)]:
n = k[1] if k[0] in (i, j) else k[0]
S[(t, n)] = _calc_sim(R[t], R[n], imsize)
regions = []
for k, r in list(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']
})
return img, regions
(5)函数理解
算法实现过程:
- 先判断图像的颜色通道数是否为3。
- 使用_generate_segments函数,生成具有区域标签的图片。
- 使用_extract_regions函数,获得原始区域集R(R中的每个元素都包含了该区域的位置,尺寸,颜色特征,纹理特征等信息)。
- 使用_extract_neighbours函数,获得原始区域集R中相交的区域。
- 使用_calc_sim函数计算两个原始区域集R中相交区域的相似度S。
- 从S中找出相似度最大的区域对(设为(a, b)),使用_merge_regions函数将其合并为新的区域(设为c),添加进R。
- 从S中移除所有与上一步中相关的子集,即将所有包含a或b的区域对(设为(a, x),(b, y),(a, b))及对应的相似度,从S中移除。
- 计算新集与所有子集的相似度,即分别计算区域c与x,y的相似度,并将(c, x): similarity_1和(c, y): similarity_2添加到S中。
- 不断循环执行后三步,直至S为空。
我们将后三步放在生成新区域部分进行讲解。 - 完成上述操作后,我们得到了完整的区域集R’。
- 从R’中提取我们所需的信息,存入列表regions中。
此部分我们放入R’信息提取部分进行讲解。 - 将具有区域标签的图片img和区域集列表regions返回。
2、_generate_segments
(1)作用
使用felzenszwalb图像分割算法,生成原始区域集。
(2)输入
- im_orig
原始图片。
数据类型为ndarray。 - scale
图像分割的集群程度。
值越大,意味集群程度越高,分割的越少,获得子区域越大。
数据类型为int。 - sigma
图像分割前,会先对原图像进行高斯滤波去噪,sigma即为高斯核的大小。
数据类型为float。 - min_size
最小的区域像素点个数。
当小于此值时,图像分割的计算就停止。
数据类型为int。
(3)输出
- img
具有区域标签的图片,数据类型为ndarray。
img数组的形状为(im_orig_height, im_orig_width, 4)
其中存储的像素信息为:[r, g, b, (region)],region是指区域标签。
(4)源码
def _generate_segments(im_orig, scale, sigma, min_size):
"""
segment smallest regions by the algorithm of Felzenswalb and
Huttenlocher
"""
img = []
# 分割图片,得到图片中每个像素值对应的区域标签
im_mask = skimage.segmentation.felzenszwalb(
skimage.util.img_as_float(im_orig), scale=scale, sigma=sigma,
min_size=min_size)
# 将每个像素的区域标签与RGB值合并,构成四维张量
# merge mask channel