首先,这是一篇关于CV领域中目标检测的文章,本文只是记录与分析其中的特点,若是对目标检测没有基本了解的同学,可出门右转。
原论文地址:https://arxiv.org/pdf/1904.10633.pdf
代码地址:https://github.com/YonghaoHe/A-Light-and-Fast-Face-Detector-for-Edge-Devices
这篇文章有几个特点:
1 Anchor Free
用感受野(receptive field,RF)来代替anchor based的anchor,其实完全可以看成把anchor的大小设置成和RF的大小一致。 详细原因看下面
2 对于检测层的设计:
1 小目标需要更多的周边信息。
2 对于中的目标,只需要部分的周边信息。
3 对于大目标,可以不需要周边信息。
以此为基础,去根据感受野定义不同的检测层
当前层感受野的计算公式为下:
R
F
i
=
(
R
F
i
−
1
−
1
)
∗
s
t
r
i
d
e
+
K
e
r
n
e
l
S
i
z
e
RF_{i}=(RF_{i-1}-1)* stride + KernelSize
RFi=(RFi−1−1)∗stride+KernelSize
其中
R
F
i
−
1
RF_{i-1}
RFi−1是前一层的感受野的大小, kernelSize是卷积核大小 stride 是步长
举个例子:
layer1 : conv1 stride 2 kernelSize 为3 那么 layer1的感受野大小为
(
1
−
1
)
∗
2
+
3
=
3
(1-1)*2 + 3 = 3
(1−1)∗2+3=3
因为输入是像素,其感受野是1
layer2 : conv2 stride2 kernelSize:3 那么layer2的感受野大小为:
(
3
−
1
)
∗
2
∗
2
+
3
=
11
(3-1)*2*2+3 = 11
(3−1)∗2∗2+3=11
因为当前层相对于原图的stride为4
在根据之前对不同目标所需周围信息的不同,计算target的大小和RF之间的比例,尽量保证,需要的信息都在有效感受野里面,才会出现文章中的table2.
3 data augmentation
Color distort:随机调整图片的亮度,增加noise,固定值调整像素量等等。
Random sampling for each scale:
random image contrast: 计算图像的整体均值,再通过一个factor与原图像的像素相结合。
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray_mean = numpy.mean(image_gray)
temp = numpy.ones((image.shape[0], image.shape[1]), dtype=numpy.float32) * gray_mean
factor = numpy.random.uniform(min_factor, max_factor)
result = numpy.zeros(image.shape, dtype=numpy.float32)
result[:, :, 0] = image[:, :, 0] * factor + temp * (1 - factor)
result[:, :, 1] = image[:, :, 1] * factor + temp * (1 - factor)
result[:, :, 2] = image[:, :, 2] * factor + temp * (1 - factor)
result[result > 255] = 255
result[result < 0] = 0
result = result.astype(numpy.uint8)
random brightness:对图片整体乘以一个系数,最终值大于255则取255,之后再对图片取整数
factor = numpy.random.uniform(min_factor, max_factor)
result = image * factor
if factor > 1:
result[result > 255] = 255
result = result.astype(numpy.uint8)
random saturation:随机改变颜色饱和度,通过factor乘以灰度图来影响饱和度
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
factor = numpy.random.uniform(min_factor, max_factor)
result = numpy.zeros(image.shape, dtype=numpy.float32)
result[:, :, 0] = image[:, :, 0] * factor + image_gray * (1 - factor)
result[:, :, 1] = image[:, :, 1] * factor + image_gray * (1 - factor)
result[:, :, 2] = image[:, :, 2] * factor + image_gray * (1 - factor)
result[result > 255] = 255
result[result < 0] = 0
result = result.astype(numpy.uint8)
horizon_flip:直接调用cv的flip
if orientation == 'h':
return cv2.flip(image, 1)
elif orientation == 'v':
return cv2.flip(image, 0)
else:
print('Unsupported orientation: %s.' % orientation)
return image
random sample: 在文章中提及的随机裁剪好像并没有在代码中使用
4 Loss function
在分类的loss中采用的常用的交叉熵的损失函数,因为采用感受野代替anchor,所以每个感受野中 只能存在一个人脸,若在感受野内存在多个人脸时,则忽略掉,同时设置了gray scale,人脸大小在gray scale的范围,也同样忽略掉,以branch3为例:预定检测的人脸为[20, 40]这个范围, 则lower bounds
为180.9 upper bounds 为401.1 即 人脸大小在[18, 20] 以及 [40, 44]的人脸都忽略掉
回归loss采用的L2loss,回归的groundtruth设置如下:
其中
R
F
x
RF_x
RFx和
R
F
y
RF_y
RFy 为感受野的中心坐标在图像中的位置,
b
x
t
l
b^{tl}_x
bxtl和
b
y
t
l
b^{tl}_y
bytl为groundtruth的左上角的坐标,
b
x
b
r
b^{br}_x
bxbr和
b
y
b
r
b^{br}_y
bybr为右下角的坐标,
R
F
s
RF_s
RFs为感受野的size 在论文中 这里的
R
F
s
RF_s
RFs采用的是定语的每一个感受野所检测的最大人脸框的大小
5 复现结果
模型 | Easy Set | Medium Set | Hard Set |
---|---|---|---|
v1 _作者结果 | 0.91 | 0.88 | 0.78 |
自己复现 | 0.897 | 0.872 | 0.761 |
复现基本一致