FasterRcnn-Resnet50-FPN对图像的处理过程

如题。感觉物体检测框架还是比较复杂的,在这里理一下,一张图像从输入到输出,究竟被做了哪些操作。

警告:可能存在大量不知道我在说啥的状况,这个博客针对自己的初步理解,还是不够细致和准确,我只是记录一下,防止自己忘记,并无科普目的。

那么首先肯定是图像的预处理和增强。这个不必多说。假设处理完之后,图像的大小为3*800*1216。

FasterRcnn-Resnet50-FPN由backbone,proposal_generator和roi_head三大部分组成。

backbone先介绍Resnet50。resnet50由一个预处理模块和4个卷积模块构成。4个卷积模块分别命名为res2, res3, res4, res5。预处理模块包括一个卷积层和一个池化层,分别会使图像的大小缩小两倍。3*800*1216的图像经过预处理模块之后,大小会变成64*200*304,然后再送入后续4个block。FPN需要resnet的4个模块输出的特征图,其大小分别是:[res2: 256*200*304, res3: 512*100*152, res4: 1024*50*76, res5: 2048*25*38]。

接下来是FPN部分。FPN取res2, res3, res4, res5的输出特征作为输入,然后对这些特征图进行不同尺度之间的交互。具体就是这样的:先对res5来个1*1卷积把channel数量降到256,然后再来个3*3卷积得到特征p5: 256*25*38;然后把p5上采样,得到256*50*76,这个p5上采样结果加上res4的1*1卷积结果,得到256*50*76,再来个3*3卷积得到p4。然后p4上采样,res3 1*1卷积,相加,3*3卷积得到p3,然后同理得到p2。最终p5被搞了一个MaxPooling得到p6。如此一来,FPN的输出即为:[p2: 256*200*304, p3: 256*100*152, p4: 256*50*76, p5: 256*25*38, p6: 256*13*19]。

图像经过了resnet50-FPN,变成了不同尺度的特征图p2--p6。接下来这些特征图送入到proposal-generator中来生成初步的proposal。这里的proposal_generator即为RPN。RPN的输入为image,特征图以及图像的label,如果是测试等不需要计算loss的时候,不输入label也可以。然后第一步:根据输入的特征图生成anchors。生成anchors的话,首先要确定每个特征图对应的anchor size(可以理解为正方形框的边长)以及anchor的长宽比。假设此时的anchor size为[32,64,128,256,512],长宽比为[0.5, 1, 2]。然后根据不同的size和不同长宽比的排列组合,可以得到15 组个base anchor,这15个base anchor都假设中心点为(0,0)。接下来根据size和stride(stride为每个特征图对应的缩小系数,如p2对应的缩小系数为4.)生成偏移,将这些偏移应用于base anchor,就得到了每个特征图对应的anchors。再具体一点,以特征图p2为例:p2对应的stride为4。首先根据[32,64,128,256,512]以及纵横比得到了15个base anchor,这15个base anchor的中心都是(0,0)。然后又根据特征图size (200,304)和stride 4,在原图[(200*4)*(304*4)]上以步长为4生成了200*304个中心点。这200*304个中心点与那3个base anchor组合,得到了200*304*15个anchor。然后p3上得到100*152*15个anchor,p4得到50*76*15个,依次类推。

生成了anchors之后,接下来是rpn_head。rpn_head将输入的特征图映射为每个anchor的正负类以及对应的4个顶点的offset。还是拿p2为例子。rpn_head首先用一个3*3卷积卷一下p2,输出的大小还是256*200*304。然后再分别用两个1*1卷积,分别输出p2对应的15*300*304的logits_score和60*300*304的offsets。p3--p6同理。

接下来就是计算loss和predict proposals。先来讲计算loss。首先,要对anchor_generator在各个特征图上生成的anchors做一个匹配,看看哪个anchor和gt(ground truth)对的上。这个匹配非常的消耗内存,需要计算每个anchor和每个gt之间的IoU。这里是通过IOU阈值来判断的,label有3种:-1:ignore,0:negative,1:positive。IOU的阈值需要两个,假设为[0.3, 0.7]。那么低于0.3的部分,label会被标注为0。高于0.7的部分,label会被标注为1。0.3--0.7之间的部分会被标注为-1。匹配完事之后会得到每个anchor的label以及每个anchor匹配的gt。得到了匹配结果之后,需要对匹配结果再进行采样。采样256个正负anchor,其中正anchor的数量要尽可能的多,但是不能超过256*1/4。采样正anchor和负anchor的过程是随机的。采样完了256个anchor之后,把剩下的所以的anchor的label变为-1。再然后需要得到每个正anchor所匹配的gt的bbox。在进行了anchor的匹配和采样之后,就可以计算loss了。之前计算的logits和offsets就派上用场了。

计算完了loss,rpn还要输出predict proposals。之前的loss都是直接在生成的anchor的基础上进行匹配和采样的。到了这一步,就需要把模型预测的offset给用上了。这里是在所有的anchor上选logits比较大的1000个anchor传递给roi_head。具体的选取过程为:对于每张特征图,select the `pre_nms_topk` highest scoring proposals, apply NMS, clip proposals, and remove small boxes. Return the `post_nms_topk` highest scoring proposals among all the feature maps for each image. 选除了post_nms_topk(1000)个proposal之后,把这1000个proposal送到roi_head那里去。rpn整体的输出为1000个proposals以及losses。

接下来终于进入到了roi_head的部分。roi_head取images,feature maps,proposals,gt作为输入,输出预测结果和loss。如果gt不输入的话,那就不算loss了。roi_head又分为3个子模块,分别为ROIPooler,box_head和box_predictor。

在训练过程中,首先要对rpn送来的1000个proposals进行gt匹配和采样。rpn中有个batchsize_per_image,在roi_head这里,也有一个batchsize_per_image,其值为512。采样过程中,首先把gt添加进了proposals。然后计算图像中的gt和(1000+num(gt))个proposals之间的IOU,根据阈值0.5进行匹配。大于0.5的proposal标记为1,否则标记为0。然后就可以得到这(1000+num(gt))个proposals所匹配的gt和实际的标签。然后从这些里面采样512个样本,其中正样本的数量越多越好,但是不要超过512*1/4。采样完之后,记录下这512个proposal以及他们对应的物体类别。

采样出512个proposals之后,需要进一步对这些proposal进行类别预测和边界框回归。过程分3步:首先对输入的特征图进行ROIPooling,然后将pooling得到的结果送入box_head进行编码,最后将编码得到的特征送入box_predictor输出物体的类别和框框的offset。

首先来看POIPooling。这里只对p2--p5进行了pooling,因为到了p6特征图就太小了。pooler的输入是特征图list和proposal list。特征图list装着4套特征图,每套的大小为B*C*H*W。这里我们只考虑输入了一张图像,所以这里的B为1。那么输出的特征图list就是这样图像的不同尺度的特征图p2--p5。然后需要对512个proposal进行转化,变成512*5的形式,第一列代表着这个proposal是在那个特征图上的。proposal可以经过一个level mapper的东西,根据proposal的面积来决定这个proposal来自于哪层特征。再接下里,就是把proposal面积缩小(1/stride),然后在对应的特征图上把proposal框住的部分抠出来,再将抠出来的部分平均分成7*7的网格,每个channel上的每个网格内进行max pooling。完事之后,一个proposal的特征就会变成256*7*7。ROIPooling最终的输出为512*256*7*7.

pooling完之后就是box_head进行编码。这个简单,直接flatten,变成512*12544,然后两个全连接,变成512*1024。然后再送入最后的box_predictor。

box_predictor的输入即为proposal特征:N*1024,输出为类别:N*(K+1),offset:N*4。这两个结果都是分别再N*1024上添加1个全连接得到的。如果是训练的话,拿着这些最终结果,去和每个proposal匹配的gt去计算loss就可以了。

如果不是训练的话,那还需要对这些proposal进行进一步的处理。首先,如果不是在train的话,那么就不需要对RPN送来的1000个proposal进行采样。经过同样的处理之后,会得到的这1000个proposal的类别logits和offset。然后再进行inference:这里的inference需要roi_head对这1000个proposal的处理结果,以及1000个proposal本身作为输入。首先将offset应用于proposal,得到1000个框框。然后再将1000proposal的类别logits进行softmax得到score。最后,clip proposals,移除背景类,然后根据阈值进行筛选,先将score的前K列取出来,然后选取分值大于0.05的部分,再移除空的proposal,最后对选出的proposal进行逐类别NMS,得到最终的输出。

你可能感兴趣的:(目标检测)