赞
踩
backbone用于特征提取,通常使用的是VGG16或者ResNet网络,其中要经过4个pooling层,且经过多层卷积后层数也发生了变化,但仍要保证在进行下一次池化之前,特征图深度为上一次池化之前深度的两倍。故第一个pooling层的strides=4,第二个的strides=2,第三个的pooling层的strides=2,第四个的pooling层的的strides=2,所以得到的特征图的shape分别为:[1, 304, 304, 256]、[1, 152, 152, 512]、[1, 76, 76, 1024]、[1, 38, 38, 2048]。由图可知此时获取到的是C2、C3、C4、C5层是经过池化得到的。C1层未经过池化。
- C2,C3,C4,C5 = model.backbone(image,training=False)
- #此处的model直接调用backbone()函数,返回为上述的4个特征图
经过backbone获取到了4张特征图,为了更好的检测图像,引入FPN概念,由图可知,[1, 38, 38, 2048]的特征图经过两倍放大的上采样后,与[1, 76, 76, 1024]经过1层的kernel_size=1的卷积核进行卷积。
由上图可知,经过FPN层(自上而下的上采样然后与同层相加)后,会获得M5、M4、M3、M2特征图。
P2,P3,P4,P5 = model.neck([C2,C3,C4,C5],training=False)
此时获得到的P2、P3、P4、P5,P6的特征图,是M2、M3、M4、M5特征图经一个3*3的卷积核得到的,且P6特征图直接由P5特征图进行下采样得到。5张特征图的shape为:[1, 304, 304, 256]、[1, 152, 152, 512]、[1, 76, 76, 1024]、[1, 38, 38, 2048]、[1, 19, 19, 256]。获得特征融合后的5张特征图
1、生成一系列的固定参考框anchors,覆盖图像的任意位置,然后送入后续网络中进行分类和回归
2、分类分支:通过softmax分类判断anchor中是否包含目标
3、回归分支:计算目标框对于anchors的偏移量,以获得精确的候选区域
4、最后的Proposal层则负责综合含有目标的anchors和对应bbox回归偏移量获取候选区域,同时剔除太小和超出边界的候选区域。
(1)anchors,多尺度、多长宽比,例如:尺度为32、64、128,长宽比为:1:1,1:2,2:1的一组anchors。一般有RPN的情况下,每个特征图上生成一个尺度,三个长宽比的anchors即3个anchors。没有RPN的情况下则生成如下图的9个anchors
故,因为使用了FPN,所以每个像素点生成3个anchors,对应于上述经FPN生成的5张特征图上生成anchors,anchors的数量为:3043043+1521523+76763+38383+19193=369303,可通过以下代码获得,其中所传参数为imagemeta是图像的元信息。此处根据generate_pyramid_anchors()函数可知,需要传递图像的元信息,通过5张特征图获取到了所有的anchors。返回的anchors为所有的anchor在原图的坐标,shape为[369303, 4]。
- # 产生anchor:输入图像元信息即可,输出anchor对应于原图的坐标值
- anchors,valid_flags = model.rpn_head.generator.generate_pyramid_anchors(imagemeta)
可以通过visualize模块进行图像绘制,绘制在原图上的所有anchors
- visualize.draw_boxes(rgd_image[0],boxes=anchors[:10000,:4])
- plt.show()
(2)在5张特征图上分别都声称了anchors后,然后每张特征图再经先做一个1x1的卷积,得到[batchsize,H,W,18]的特征图,然后进行变形,将特征图转换为[batchsize,9xH,W,2]的特征图后,送入softmax中进行分类,得到分类结果(判断)后,再进行reshape最终得到[batchsize,H,W,18]大小的结果,18表示k=9个anchor是否包含目标的概率值。
举例子:P6特征图输出后的shape是[1, 19, 19, 256],此时经过下图的1*1的卷积核后,shape变为[1,19,19,18],此时经过Reshape模块,特征图的shape变为[1,9X19,9X19,2],此时可以理解为特征图的H变为了9倍,即特征图像素变为了原来的9倍。每个像素点都对应了一个anchor。之所以第四维是2,是因为经过softmax后,判断是否存在物体,给出对应的概率。然后在经过Reshape模块,特征图又变为[1,19,19,18]。此时,特征图上9X19X19个anchors均经过softmax后给出了是否存在物体的概率,例如:存在0.6,不存在:0.4,对应的anchors的坐标值都已记录下来,后映射到真实的图像上。所以此时真实图像上的anchors中是否含有物体已经判断出来了。
(3)由综合过程图可知,经过FPN后的模型,返回的5张不同尺寸的特征值,又经过下图中的1*1卷积,使得特征图的深度变为36,即为4x9的深度,其中9代表的是每个像素点有9个anchors,4代表的是经过回归后的尺寸变化,即[dx(A),dy(A),dw(A),dh(A)],其中d表示变化率。
该数值进行结合,求出预测的是anchor与真实值之间的平移因子和尺度因子。其中[dx(A),dy(A),dw(A),dh(A)]分别等于下图的tx、ty、tw、th,其中xa、ya、wa、ha表示anchor框的中心点x、y、w、h的值。
可以通过以下代码进行获取:其中model.rpn_head()出入的是特征图金字塔,返回:rpn_class_logits.shape=[1, 369303, 2]、rpn_probs.shape=[1, 369303, 2]、rpn_deltas.shape=[1, 369303, 4]
- # RPN网络的输入:FPN网络获取的特征图
- rpn_feature_maps = [P2,P3,P4,P5,P6]
- # RPN网络预测,返回:logits送入softmax之前的分数,包含目标的概率,对框的修正结果
- rpn_class_logits,rpn_probs,rpn_deltas = model.rpn_head(rpn_feature_maps,training = False)
其中rpn_class_logits表示每个anchor进行分类处理后的分类之后结果,即样本标签。rpn_probs为经过softmax后输出的概率,同时也反映了anchor中存在检测物体的置信度,其中第一列为存在物体的置信度,第二列为不存在物体的置信度,rpn_deltas返回的是上述的[dx(A),dy(A),dw(A),dh(A)]回归后的结果
经过回归函数,此时能够获得每个anchor的置信度,我们可以通过代码,将置信度高的anchor绘制在图像上。
- # 获取分类结果中包含目标的概率值
- rpn_probs_tmp = rpn_probs[0,:,1]
- # 获取前100个较高的anchor
- limit = 100
- ix = tf.nn.top_k(rpn_probs_tmp,k=limit).indices[::-1]
- # 获取对应的anchor绘制图像上,那这些anchor就有很大概率生成候选区域
- visualize.draw_boxes(rgd_image[0],tf.gather(anchors,ix).numpy())
(4)Proposal层,经过了RPN的分类和回归,我们获得了如下的信息:所有的anchors是否存在物体的置信度,及[dx(A),dy(A),dw(A),dh(A)]的平移因子和尺度因子。通过model.rpn_head()函数获得了上述的两个结果。然后图像及特征图要经过Proposal层,进行anchor的修正获得最终的候选框。
Proposal层的处理流程如下:model.rpn_head.get_proposals(rpn_probs,rpn_deltas,imagemeta)
利用RPN网络回归的结果对所有的anchors进行修正,得到修正后的检测框
根据RPN网络分类的softmax输出的概率值由大到小对检测框进行排序,提取前6000个结果,即提取修正位置后的检测框
限定超出图像边界的检测框为图像边界,防止后续roi pooling时候选区域超出图像边界。
代码表示:
- # 获取候选区域
- proposals_list = model.rpn_head.get_proposals(rpn_probs,rpn_deltas,imagemeta)
- #结果
- proposals_list
- [<tf.Tensor: shape=(1533, 4), dtype=float32, numpy=
- array([[0.20729761, 0.00852748, 0.748096 , 0.46975034],
- [0.42213044, 0.5887971 , 0.7810232 , 0.9806169 ],
- [0.40125194, 0.4384725 , 0.48458642, 0.47913405],
- ...,
- [0.25977597, 0.435113 , 0.27290097, 0.4483906 ],
- [0.38884488, 0.41798416, 0.41393432, 0.4339822 ],
- [0.5885266 , 0.65331775, 0.62330776, 0.6913476 ]], dtype=float32)>]
返回的proposals_list就是经过回归处理后的候选框坐标。但是此时的坐标是经过了归一化处理了 所以绘制图像的时候 要乘以像素值1216。
- visualize.draw_boxes(rgd_image[0],boxes=proposals_list[0].numpy()[:,:4]*1216)
- plt.show()
RoI Pooling层则负责收集RPN网络生成的候选区域,并将其映射到特征图中并固定维度,送入后续网络中进行分类和回归。
RoI Pooling 的作用过程,如下图所示:
Proposal层给出了候选框,此时的候选框中存在物体,且具有1533个,且类似于抠图的操作,相当于在原图上接触1533个图片。图片的H和W各不相同。ROIPooling层的作用就是将这些不同尺寸的图片,转化为统一尺寸的pool_H和pool_W。此处的尺寸改变是通过池化的方式改变的,如下图所示,所有的候选框都转变为7*7(该参数是超参数,可以改变)的格式,且每个小格内部取最大值。
此时要思考一个问题,FPN层给出了多种尺寸的特征图,Proposal层得到了多种尺寸的候选框,请问不同尺寸的候选框分别映射到了哪个特征图上了呢?
在这里,不同尺度的ROI使用不同特征层作为ROI pooling层的输入,大尺度ROI就用后面一些的金字塔层,比如P5;小尺度ROI就用前面一点的特征层,比如P3,我们使用下面的公式确定ROI所在的特征层:
其中,224是ImageNet的标准输入,k0是基准值,设置为4,w和h是ROI区域的长和宽,假设ROI是112x112的大小,那么k = k0-1 = 4-1 = 3,意味着该ROI应该使用P3的特征层。k值会做取整处理,防止结果不是整数,而且为了保证k值在2-5之间,还会做截断处理。由此可知根据ROI候选区域的长和宽确定映射到的特征图。
- # ROI Pooling层实现:输入是候选区域,特征图,图像的元信息
- pool_region_list = model.roi_align((proposals_list,rcnn_feature_maps,imagemeta),training = False)
- #输出结果为:每一个候选区域都被固定为7x7大小
- [<tf.Tensor: shape=(1533, 7, 7, 256), dtype=float32, numpy=
- array([[[[-6.26428795e+00, 3.55317879e+00, 3.37260556e+00, ...,
- 6.22574663e+00, 3.75851846e+00, -2.49103808e+00],
- [-9.01443863e+00, 7.67611027e-01, 7.18744850e+00, ...,
- 6.20492172e+00, 4.09835625e+00, 6.05924249e-01],
- [-7.43907213e+00, -3.76329374e+00, 5.01457691e+00, ...,
- 6.22656918e+00, 1.19414163e+00, 3.06410480e+00],
- ...,
- [ 1.39127302e+00, -1.71078873e+00, 4.01916075e+00, ...,
- 5.94641972e+00, 3.63194764e-01, 2.91014194e+00],
- [-5.21681070e+00, 2.39917469e+00, 2.49682212e+00, ...,
- 5.92232943e+00, 3.01222801e+00, 1.63518691e+00],
- [-1.26697767e+00, -6.90211892e-01, 4.50919747e-01, ...,
- 1.97156405e+00, -1.07467103e+00, 4.54943466e+00]]
经过了ROIPooling层后,获得了统一H*W的特征图,此时经过后续的分类得到每个特征图都对应于哪个类别,该部分利用获得的候选区域的特征图,通过全连接层与softmax计算每个候选区域具体属于的类别(如人,车,电视等),输出概率值;同时再次利用回归方法获得每个候选区域的位置偏移量,用于回归更加精确的目标检测框。该部分网络结构如下所示:
从RoI Pooling层获取到固定大小的特征图后,送入后续网络,可以看到做了如下2件事:
通过全连接和softmax对候选区域进行分类
再次对候选区域进行回归修正,获取更高精度的检测框
实现流程如下:
首先获取网络分类和回归的结果:
- # RCNN网络的预测:输入是ROIPooling层的特征,输出:类别的score,类别的概率值,回归结果
- rcnn_class_logits,rcnn_class_probs,rcnn_deltas_list=model.bbox_head(pool_region_list,training=False)
利用结果对候选区域进行修正:
# 获取预测结果:输入:rcnn返回的分类和回归结果,候选区域,图像元信息,输出:目标检测结果detection_list = model.bbox_head.get_bboxes(rcnn_class_probs,rcnn_deltas_list,proposals_list,imagemeta)
结果为:一共检测出17个目标,前四个数据为每个目标的坐标位置,第五个数据为目标类别id,第六个数据为目标类别置信度。6个值构成。此处对生成的检测框做了NMS
- [<tf.Tensor: shape=(17, 6), dtype=float32, numpy=
- array([[2.3262584e+02, 2.1799168e+01, 9.0194098e+02, 5.4503723e+02,
- 1.0000000e+00, 9.9917287e-01],
- [1.4013255e+02, 5.5109363e+02, 3.8764392e+02, 7.5518970e+02,
- 1.0000000e+00, 9.9226898e-01],
- [2.0952664e+02, 7.8792090e+02, 8.9771838e+02, 1.2104146e+03,
- 1.0000000e+00, 9.9193186e-01],
- [2.0348978e+02, 4.1579453e+02, 3.3001547e+02, 5.3761450e+02,
- 1.0000000e+00, 9.8929125e-01],
- [1.8087936e+02, 7.9734338e+02, 5.1281873e+02, 1.0274907e+03,
- 1.0000000e+00, 9.8689401e-01],
- [1.7813437e+02, 2.3680782e+00, 5.0309012e+02, 1.1671781e+02,
- 1.0000000e+00, 9.8671734e-01],
- [1.5557167e+02, 6.2398212e+02, 4.6821997e+02, 8.8862134e+02,
- 1.0000000e+00, 9.8594207e-01],
- [1.6307811e+02, 2.1531593e+02, 3.3396735e+02, 3.1797446e+02,
- 1.0000000e+00, 9.7716457e-01],
- [5.5404950e+02, 7.0997412e+02, 9.0215717e+02, 1.0564817e+03,
- 1.0000000e+00, 9.7271395e-01],
- [3.5928052e+02, 5.3055298e+02, 4.3132263e+02, 6.3369983e+02,
- 4.6000000e+01, 9.7136974e-01],
- [2.0050583e+02, 9.7621101e+01, 3.2383597e+02, 2.6199030e+02,
- 1.0000000e+00, 9.6375221e-01],
- [2.9822769e+02, 5.8259045e+02, 3.4338364e+02, 6.6165851e+02,
- 4.6000000e+01, 9.5854193e-01],
- [3.7460797e+02, 2.8190384e+02, 9.0596057e+02, 1.0374227e+03,
- 6.1000000e+01, 9.2184818e-01],
- [5.3237848e+02, 8.8739655e+02, 6.0120386e+02, 1.0191014e+03,
- 4.6000000e+01, 8.9205891e-01],
- [6.0350385e+02, 9.9131537e+02, 9.0866974e+02, 1.1663280e+03,
- 5.7000000e+01, 8.5597926e-01],
- [3.3973947e+02, 6.0475940e+02, 3.7579034e+02, 6.4243842e+02,
- 4.5000000e+01, 8.1343234e-01],
- [5.1774200e+02, 4.7480432e+02, 5.7942987e+02, 5.0882794e+02,
- 4.0000000e+01, 7.8660023e-01]], dtype=float32)>]
可以将其绘制在图像上:
- # 绘制在图像上
- visualize.draw_boxes(rgd_image[0],boxes=detection_list[0][:,:4])
- plt.show()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。