目录
前言
此处只是叙述ss(selective search method)
主体功能, 并且使用网上的简易实现版本-python代码,方便讲解.
官方MATLAB版本和<Selective search for object recognition>
具体论文可以参见文末论文地址 reference 1
1 图像分割和目标识别过程:
1.1 训练过程
(1) selective search
提取候选框:
step1: SS Method
:
先过分割形成不同尺度的初始模块区域(region, 初始化方法即Felzenszwalb方法
-具体参见<Efficient Graph-Based Image Segmentation>
) ---------> 然后通过颜色(color)、纹理(texture)、大小(size)等相似度判断策略, 对相邻区域(region)进行合并 ---------> 不断迭代直到没有可合并区域.---------> 得到不同目标物体的分割图像, 其所在min bounding box即为候选区域
tips:
绿色->正样本
蓝色-> 负样本
step2: 区分正负样本
:
- 正样本:
ground-truth(GT,真实标记区域)作为正样本 - 负样本:
计算每个候选区域与GT之间的重合度,如果区域A与GT的重合度在20-50%之间,而且A与其他的任何一个已生成的负样本之间的重合度不大于70%,则A被采纳为负样本,否则丢弃A,继续判别下一个区域
(2) 提取特征层:
第一部分中将正样本区域和负样本区域都提取出来了,现在就需要提取每个区域的特征了.
paper在实现过程中,主要使用bag-of-words
的方法用于目标表识别. 相比于之前的实现方法,主要的创新点在: 使用多样的color-SIFT
特征以及精细的spatial pyramid divsion
方法。
- 使用单尺度(σ=1.2)特征金字塔。
- 使用
SIFT、2个colour SIFT、 Extended OpponentSIFT、RGB-SIFT
特征,在四层金字塔模型 1×1、2×2、3×3、4×4,提取特征,最终可以得到一个维的特征向量.
(3) SVM
分类训练:
- 上一步中,每个区域的特征提取出来了,真实类别标签也知道,那这就是一个2分类问题;
- 分类器这里采用了带有Histogram Intersection Kernel的SVM分类器进行分类
(4) 训练反馈:
第三部分将分类器训练好了,训练好了就完了吗? NO! 现在流行一种反馈机制,SVM训练完成了,将得到每个训练图像每个候选区域的软分类结果(每个区域都会得到一个属于正样本的概率),一般如果概率大于0.5将被认为是目标,否则被认为是非目标,如果完全分类正确,所有的正样本的SVM输出概率都大于0.5,所有负样本的SVM输出概率都小于0.5,但是最常见的情况是有一部分的负样本的输入概率也是大于0.5的,我们会错误地将这样样本认为是目标,这些样本就称之为"False Positives".
我们这里就是想把这些"False Positives"收集起来,以刚才训练得到的SVM的权值作为其初始权值,对SVM进行二次训练,经过二次训练的SVM的分类准确度一般会有一定的提升
1.2 测试过程
测试的过程基本和训练过程相同:
首先用SS方法得到测试图像上候选区域
–> 然后提取每个区域的特征向量
–> 送入已训练好的SVM进行软分类
–> 将这些区域按照概率值进行排序
–> 把概率值小于0.5的区域去除
–> 对那些概率值大于0.5的,计算每个区域与比它分数更高的区域之间的重叠程度,如果重叠程度大于30%,则把这个区域也去除了
–> 最后剩下的区域为目标区域.
2 论文最大亮点 - selective search method
2.1 主要思路:
输入一张图片,首先通过图像分割的方法(如大名鼎鼎的felzenszwalb算法
, 参见reference 2)获得很多小的区域,然后对这些小的区域不断进行合并(通过相似性公式计算, 相邻区域相似度高的合并),一直到无法合并为止。此时这些原始的小区域和合并得到的区域的就是我们得到的bounding box.
算法分为如下几个大步:
- 生成原始的区域集R(利用felzenszwalb算法)
- 计算区域集R里每个相邻区域的相似度S={s1,s2,…}
- 找出相似度最高的两个区域,将其合并为新集,添加进R
- 从S中移除所有与第3步中有关的子集
- 计算新集与所有子集的相似度
- 跳至第三步,不断循环,合并,直至S为空(到不能再合并时为止)
整体计算过程 已经在1-(1)-step1中描述, 相关流程伪代码参见如下:
2.2 核心实现
2.2.1 分析了颜色空间
paper要考虑不同的场景和照明条件。因此,paper在具有一系列不变性的颜色空间中执行分层分组算法。
具体地说,以递增的不变性来描述以下颜色空间:
(1)RGB,
(2)强度(灰度图像)I,
(3)Lab,
(4)归一化RGB中的rg通道 + 强度通道I,表示为rgI,
(5)HSV,
(6)标准化的RGB表示为rgb,
(7)C , 这是一个对比色空间,其中强度通道被划分出去,
(8)来自HSV的色调通道H。
下表中列出了在 光线强度/阴影/光线明亮不同条件下, 具体的不变性特性结果:
注:
“+”:具有不变性
“-”:不具有不变性
“+/-”: 在此空间或者通道具有部分不变性
“1/3”: 1/3 通道可以说的上具有不变性
这一节提供了调参的一些思路,分析了不同颜色空间各通道的特点,这有利于我们在面对自己的实际任务时明白要尝试的方向. 但是paper中算法的实现是使用的是单一颜色空间和通道的(并不是所有颜色空间的组合).
2.2.2 相似性判断标准的多样性
关于两张图片相似度计算:
论文中考虑了四种相似度 – 颜色,纹理,尺寸,以及交叠。其中所有的标准的取值输入范围都是在[0, 1] 之间. 其中颜色和纹理相似度,通过获取两个区域的直方图的交集,来判断相似度。
- 颜色相似度计算公式:
说明:
step1: 每一个颜色通道通过25个bins(柱状条)计算colour histograms
(颜色分布直方图). 如果一个region ri
是3通道(例如说RGB)的, 则共有75个bins, 即下面面的Ci中 n = 75.
Ci表示:region ri
的colour histogram
step2: 计算相邻region的ri, rj
颜色相似度: 就是求和Ci, Cj两个合集中各个bins的最小值的和.
step3: 合并ri, rj
之后的新的region rt
的colour histogram
, 可以通过下式子更新:
size(ri) 表示ri 的bounding box面积
size(rj) 表示rj 的bounding box面积
- 纹理相似度:
step1: 选择同一个region ri
的单通道图像, 令σ = 1并计算在8个方向的高斯导数, 从而形成texture histogram
(纹理分布直方图). 从直方图中每个方向各选择10个bins, 组成集合. 如果, 使用的是3通道的图像, 则共有n = 8x10x3=240 (个bins). 即为:
step2: 与色彩相似度类似, 计算纹理相似度的公式表示如下:
step3: 用于更新合并 ri, rj形成新的区域的rt的文理直方图方式, 和颜色相似度类似, 不再赘述.
- 尺寸相似度:
计量标准的作用,就是促使更多的小区域尽快合并(面积越小, 相似度越高; 面积越大或者差别越大, 相似度相对会小)
size(ri) 表示ri 的bounding box面积
size(rj) 表示rj 的bounding box面积
size(im) 表示的是整幅图像的面积大小.
- 交叠相似度:
其中
表示ri, rj
交集的bounding box面积
- 最后的相似度计算总公式, 是四种相似度的加和。
ai 表示各个部分的权重. paper并未使用
2.2.3 初始region选择的多样性
初始化分割区域的方法有多种, paper中说当时最好的还是(paper毕竟还是2011-2012年, Felzenszwalb的实现和提出恐怕要追溯到2004年了 ),
P. F. Felzenszwalb and D. P. Huttenlocher. Efficient GraphBased Image Segmentation. IJCV, 59:167–181, 2004
, 即Felzenszwalb method
, 源码参见 链接…
另外, 其实skimage
有对应的具体实现, 即:
from skimage.segmentation import felzenszwalb
python实现版 - 可以参见 链接
3 python源码说明
3.1 相似度的实现
def _sim_colour(r1, r2):
"""
calculate the sum of histogram intersection of colour
"""
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 texture
"""
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 image
"""
return 1.0 - (r1["size"] + r2["size"]) / imsize
def _sim_fill(r1, r2, imsize):
"""
calculate the fill similarity over the image
"""
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"]) / imsize
3.2 主函数
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
说明:
( 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’:区域的标签,是列表。
Reference
- [SS-Method]论文/项目 Selective search for object recognition. | [IJCV, 2013] |
[pdf]
|[matlab code]
- Felzenszwalb图像分割
- selective search - python源码分析
- selective search - python源码实现地址