Faster RCNN学习记录(从维度的角度来理解)
https://www.bilibili.com/video/BV1af4y1m7iL faster rcnn理论讲解(视频)
https://www.bilibili.com/video/BV1of4y1m7nj faster rcnn代码讲解(视频)
https://zhuanlan.zhihu.com/p/31426458 faster rcnn理论讲解
https://zhuanlan.zhihu.com/p/145842317 faster rcnn代码讲解
一 Faster RCNN原理
Faster RCNN的具体步骤可以总结如下:
(1)对图像进行预处理
(2)将处理好的图像输入至特征提取网络中(backbone)得到相应的特征图(feature maps)
(3)使用RPN(Region Proposal Networks)结构生成候选框(proposals),接着将生成的候选框投影到特征图中得到相应的特征矩阵
(4)将每个特征矩阵通过roi pooling层缩放至7×7大小的特征图,接着将特征图展平后通过一系列全连接层得到预测结果
二 Faster RCNN的结构
来自https://www.bilibili.com/video/BV1of4y1m7nj?p=9
三 Faster RCNN的算法流程
1 获取数据集(读取pascal voc数据集)
pascal voc数据集是人工标注的一些图片(xml格式),其中包含了图片的宽高、ground_truth值(xmin,ymin,xmax,ymax)的形式,标签值等。
通过读取pascal voc数据集数据集我们获得以下值:
(1)image:即具体的图片信息
(2)targets:包含boxes、labels、image_id、area、iscrowed五个索引的字典
其中boxes是[xmin,ymin,xmax,ymax]的形式,lables中存储的是labels对应的标签值
<annotation>
<folder>VOC2012</folder>
<filename>2007_000033.jpg</filename>
<source>
<database>The VOC2007 Database</database>
<annotation>PASCAL VOC2007</annotation>
<image>flickr</image>
</source>
<size>
<width>500</width>
<height>366</height>
<depth>3</depth>
</size>
<segmented>1</segmented>
<object>
<name>aeroplane</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>9</xmin>
<ymin>107</ymin>
<xmax>499</xmax>
<ymax>263</ymax>
</bndbox>
</object>
</annotation>
2 图片的预处理(GeneralizedRCNNTransform类)
这里的对图片的处理主要包括Normalize、Resize
在初始化函数中传入参数min_size、max_size、image_mean、image_std
在forward函数中传入参数images,targets
注:在训练脚本中,dataloader传入的batch_size即为一次选择多少图片进行训练,此时dataloader会调用VOC2012DataSet中的____getitem____()方法,一次可以获取一个batch_size张图片并进行返回。
- 首先获取一个batch_size中所有的图片信息
对于res_50_fpn训练脚本:batch_size设置为2,返回的images为
[torch.size([3,h1,w1]),
torch.size([3,h2,w2])]
targets信息为
[{
"boxes":torch.size([3,4]),
"labels":torch.size([3]),
"image_id":torch.size([1]),
"area":torch.size([3]),
"iscrowed":torch.size([3])},
{
"boxes":torch.size([2,4]),
"labels":torch.size([2]),
"image_id":torch.size([1]),
"area":torch.size([2]),
"iscrowed":torch.size([2])}]
- 遍历一个batch_size中的所有图片,得到每一张图片的image和target_index,分别对每一张图片进行标准化处理(减去均值,除以方差)
(image - mean[:, None, None]) / std[:, None, None]
注:这里的mean[:, None, None]将mean增加两个维度,None的位置维度为1,之后采用广播计算
- 将图片进行Resize处理
resize(image, target_index)
-
获取输入图片(image)的高度和宽度,并找到宽度和高度中的最大值(max_size)和最小值(min_size)
-
用size / min_size计算缩放比例(scale_factor)
-
如果max_size * scale_factor > self.max_size,则scale_factor 换为self.max_size / max_size
-
采用双线性插值的方法将图片以scale_factor为缩放因子对图片进行Resize
-
根据图片的缩放比例对boxes进行缩放(根据resize后的图像和resize前的图像计算在宽度和高度上的缩放比例,得到[ratios_hight,ratios_width],将xmin和xmax乘ratios_width,ymin和ymax乘ratios_hight即可得到resize后的boxes值)
-
用resize后的图片和boxes替换原来的值
-
记录resize之后的图像的尺寸,此时的images仍是list2
image_sizes = [img.shape[-2:] for img in images]
注:image_sizes的shape为[torch.size([h1,w1]),torch.size([h2,w2])]
- 将一个batch中的图像打包成一个batch
batch_images(images)
-
找到一个batch中所有图像最大的高度和宽度max_size[c,h_max,w_max]
-
将h_max,w_max向上调整至32的整数倍得到ceil_h_max,ceil_w_max(这是因为经过backbone后feature map为原来的1/32)
-
设定batch_shape = [len(images)] + max_size([batch, channel, height, width])
-
创建shape为batch_shape且值全部为0的tensor
-
将输入images中的每张图片复制到新的batched_imgs的每张图片中,对齐左上角,保证bboxes的坐标不变
-
返回batched_imgs的shape为torch.size([2,3,h_max,w_max])
-
将image_sizes由List[Tensor,Tensor]变换为List[tuple,tuple],即 [(h1,w1),(h2,w2)]
-
GeneralizedRCNNTransform类的返回值:
(1) image_list:Imagelist类型,
image_list.image_sizes,对应resize后的图像尺寸 [(h1,w1),(h2,w2)]的形式;
image_list.tensors,表示一个batch中的所有图片信息torch.size([2,3,ceil_h_max,ceil_w_max])的形式
(2) targets:List[dict]类型
targets = [{
"boxes":torch.size([3,4]),
"labels":torch.size([3]),
"image_id":torch.size([1]),
"area":torch.size([3]),
"iscrowed":torch.size([3])},
{
"boxes":torch.size([2,4]),
"labels":torch.size([2]),
"image_id":torch.size([1]),
"area":torch.size([2]),
"iscrowed":torch.size([2])}]
3 输入到backbone中进行特征提取
对于res_50_fpn:获得的features为一个有序字典(OrderedDict类型)
OrderedDict([('0':torch.size([2,256,192,256])),
'1':torch.size([2,256,96,128]),
'2':torch.size([2,256,48,64]),
'3':torch.size([2,256,24,32]),
'pool':torch.size([2,256,12,16])])
4 RPN(Region Proposal Networks)
初始化函数中设定:
anchor_generator:用来在原图上生成一系列的anchors
head:rpn_head部分,用来计算feature map中每个点为前景的概率以及边界框回归值
box_coder:用来计算边界框回归参数以及计算边界框坐标值
box_similarity:用来计算iou值
proposal_matcher:根据iou值设定正样本、负样本、丢弃的样本
fg_bg_sampler:计算损失时采样正负样本
self._pre_nms_top_n = pre_nms_top_n 选取每个feature map中前2000个score
self._post_nms_top_n = post_nms_top_n 在选取的约8000个proposals中选取前2000个
self.nms_thresh = nms_thresh nms的阈值
self.min_size = 1.
forward函数中传入:images、features、targets
images:image_list.image_sizes,对应resize后的图像尺寸 [(h1,w1),(h2,w2)]的形式;
image_list.tensors,表示一个batch中的所有图片信息torch.size([2,3,ceil_h_max,ceil_w_max])的形式
features:为一个有序字典(OrderedDict类型)
OrderedDict([('0':torch.size([2,256,192,256])),
'1':torch.size([2,256,96,128]),
'2':torch.size([2,256,48,64]),
'3':torch.size([2,256,24,32]),
'pool':torch.size([2,256,12,16])])
在使用的时候,features将变为list5类型
features.shape = [torch.size([2,256,192,256]),
torch.size([2,256,96,128]),
torch.size([2,256,48,64]),
torch.size([2,256,24,32]),
torch.size([2,256,12,16])]
targets:list2
targets = [{
"boxes":torch.size([3,4]),
"labels":torch.size([3]),
"image_id":torch.size([1]),
"area":torch.size([3]),
"iscrowed":torch.size([3])},
{
"boxes":torch.size([2,4]),
"labels":torch.size([2]),
"image_id":torch.size([1]),
"area":torch.size([2]),
"iscrowed":torch.size([2])}]
4.1 AnchorsGenerator类
- 首先获取每个预测特征层的尺寸,得到grid_sizes,由于有5个预测特征层,因此grid_sizes的shape为List5,
grid_sizes.shape=[torch.size([168,256]),
torch.size([84,128]),
torch.size([42,64]),
torch.size([21,32]),
torch.size([11,16])] #feature map的shape不一定和这些相同
- 获取输入图像的height和width,这里的height和width已经调整为32的整数倍,torch.size([672,1024])
image_size.shape = torch.size([672,1024])
- 计算特征图上的一步相当于原图上的多少步,strides为List5
strides.shape = [[torch.size(4),torch.size(4)],
[torch.size(8),torch.size(8)],
[torch.size(16),torch.size(16)],
[torch.size(32),torch.size(32)],
[torch.size(61),torch.size(64)]]
- 根据提供的scales=((32,), (64,), (128,), (256,), (512,))和aspect_ratios=((0.5, 1.0, 2.0),) * len(anchor_sizes)生成anchors模板,这里针对5个预测特征层,在每个预测特征层用1中尺度,三种比例生成模板,得到cell_anchors为List5
cell_anchors.shape = [torch.size([3,4]),
torch.size([3,4]),
torch.size([3,4]),
torch.size([3