-
数据增强:SSD在数据部分做了充分的数据增强工作,包括光学变换与几何变换等,极大限度地扩充了数据集的丰富性,从而有效提升了模型的检测精度。
-
网络骨架:SSD在原始VGGNet的基础上,进一步延伸了4个卷积模块,最深处的特征图大小为1×1,这些特征图具有不同的尺度与感受野,可以负责检测不同尺度的物体。
-
PriorBox与多层特征图:与Faster RCNN类似,SSD利用了固定大小与宽高的PriorBox作为区域生成,但与Faster RCNN不同的是,SSD不是只在一个特征图上设定预选框,而是在6个不同尺度上都设立预选框,并且在浅层特征图上设立较小的PriorBox来负责检测小物体,在深层特征图上设立较大的PriorBox来负责检测大物体。(原因的是浅层特征层的感受野小,分辨率高,深层特征层感受野大,分辨率低)
-
正、负样本的选取与损失计算:利用3×3的卷积在6个特征图上进行特征的提取,并分为分类与回归两个分支,代表所有预选框的预测值,随后进行预选框与真实框的匹配,利用IoU筛选出正样本与负样本,最终计算出分类损失与回归损失。
由整个过程可以看出,SSD只进行了一次框的预测与损失计算,属于一阶网络。由于利用了多个特征图,SSD实现了较好的检测精度。接下来分点介绍几个部分。
数据增强:
SSD做了丰富的数据增强策略,这部分为模型的mAP带来了8.8%的提升,尤其是对于小物体和遮挡物体等难点,数据增强起到了非常重要的作用。
SSD的数据增强 整体流程如图所示,总体上包括光学变换与几何变换两个过程。光学变换包括亮度和对比度等随机调整,可以调整图像像素值的大小,并不会改变图像尺寸; 几何变换包括扩展、裁剪和镜像等操作,主要负责进行尺度上的变化,最后再进行去均值操作。大部分操作都是随机的过程,尽可能保证数据的丰富性。
数据增强的总体流程代码如下:
class SSDAugmentation(object):
def init(self, size=300, mean=(104, 117, 123)):
self.mean = mean
self.size = size
self.augment = Compose([
# 首先将图像像素值从整型变成浮点型
ConvertFromInts(),
# 将标签中的边框从比例坐标变换为真实坐标
ToAbsoluteCoords(),
# 进行亮度、对比度、色相与饱和度的随机调整,然后随机调换通道
PhotometricDistort(),
Expand(self.mean), # 随机扩展图像大小,图像仅靠右下方
RandomSampleCrop(), # 随机裁剪图像
RandomMirror(), # 随机左右镜像
ToPercentCoords(), # 从真实坐标变回比例坐标
Resize(self.size), # 缩放到固定的300×300大小
SubtractMeans(self.mean) # 最后进行去均值
])
将像素值从整型变到浮点型,边框从比例坐标变换到真实坐标,这两种变换较为简单,这里不展开叙述。接下来重点介绍光学变换与几何变换这两个重要的变换方法。
- 光学变换
首先是进行亮度调整,具体方法是以0.5的概率为图像中的每一个点加一个实数,该实数随机选取于[-32,32)区间中。具体可见如下RandomBrightness
类的实现。
class RandomBrightness(object):
def init(self, delta=32):
self.delta = delta
def call(self, image, boxes=None, labels=None):
if random.randint(2):
# 随机选取一个位于[-32, 32)区间的数,相加到图像上
delta = random.uniform(-self.delta, self.delta)
image += delta
return image, boxes, labels
接下来是对比度、色相与饱和度的随机调整。色相的随机调整与亮度很类似,都是随机地加一个数,而对比度与饱和度则是随机乘一个数。另外,对色相与饱和度的调整是在HSV色域空间进行的。由于此三者与亮度很相似,代码不再单独给出。 关于以上三者的调整顺序,SSD也给了一个随机处理,即有一半的概率对比度在另外两者之前,另一半概率则是对比度在另外两者之后。 光学变换的最后一项是添加随机的光照噪声,具体做法是随机交换RGB三个通道的值,具体实现如RandomLightingNoise()
类所示。
class RandomLightingNoise(object):
def init(self):
self.perms = ((0, 1, 2), (0, 2, 1),
(1, 0, 2), (1, 2, 0),
(2, 0, 1), (2, 1, 0))
def call(self, image, boxes=None, labels=None):
if random.randint(2):
# 随机选取一个通道的交换顺序,交换图像三个通道的值
swap = self.perms[random.randint(len(self.perms))]
shuffle = SwapChannels(swap) # shuffle channels
image = shuffle(image)
return image, boxes, labels
- 几何变换
在几何变换中,首先进行的是尺度的随机扩展。扩展的具体过程是随机选择一个在[1,4)区间的数作为扩展比例,将原图像放在扩展后图像的右下角,其他区域填入每个通道的均值&#x