class SampleSelector:
#param:class_count, img_data
def __init__:
self.classes = [b for b in class_count.keys() if class_count[b] >0]
self.class_cycle = itertools.cycle(self.classes)
self.curr_class =next(self.class_cycle)#迭代器
def skip_sample_for_balanced_class(self, img_data):
获取img_data中box信息,对于每个bbox,有key['class']表示该box中图像的类别:
for bbox in img_data['bboxes']:
cls_name = bbox['class']
def get_anchor_gt(all_img_data, class_count, C, model='train')
如果model=='train',则采用data_augment;否则,不采用:
img_data_aug, x_img = data_augment
令img_size最小边为C.im_size,即最小边为600,并等比缩放另一条边:
resized_width, resized_height = get_new_img_size
计算rpn,
def calc_rpn(C, ing_data, width, height, resized_width, resized_height)
downscale = float(C.rpn_stride)
anchor_sizes = C.anchor_box_scales
anchor_ratios = C.anchor_box_ratios
num_anchors = len(anchor_sizes) * len(anchor_ratios)
初始化输出目标:
y_rpn_overlap = np.zeros((output_height, output_width, num_anchors))#
y_is_box_valid = np.zeros((output_height, output_width, num_anchors))
y_rpn_regr = np.zeros((output_height, output_width, num_anchors * 4))
获取一张图中的bbox数量:
num_bboxes = len(img_data['bboxes'])
定义best_anchor:best_anchor_for_bbox = -1 * np.ones((num_bboxes, 4)).astype(int)
初始化iou, x, dx:
best_iou_for_bbox = np.zeros(num_bboxes).astype(np.float32)#每一个bbox的IoU
best_x_for_bbox = np.zeros((num_bboxes, 4)).astype(int)#每一个bbox的中心和宽高
best_dx_for_bbox = np.zeros((num_bboxes, 4)).astype(np.float32)#每一个bbox的缩放平移
由于在图片预处理时,将图像最短边定为600,且整个图像做了缩放,所以在这一步获取bbox坐标时,也应作相对应的变化。
for bbox_num, bbox in enumerate(img_data['bboxes']):
'''获取每个bbox的ground truth'''
gta[bbox_num, 0] = bbox['x1'] * (resized_width / float(width))
gta[bbox_num, 1] = bbox['x2'] * (resized_width / float(width))
gta[bbox_num, 2] = bbox['y1'] * (resized_height / float(height))
gta[bbox_num, 3] = bbox['y2'] * (resized_height / float(height))
获取rpn gound truth:
在图片上,从bbox整体不越界的第一个位置开始,每隔rpn_stride=16个像素,取3 x 3个anchor;
for anchor_size_idx in range(len(anchor_sizes)):
for anchor_ratio_idx in range(n_anchratios):
anchor_x = anchor_sizes[anchor_size_idx] * anchor_ratios[anchor_ratio_idx][0]
anchor_y = anchor_sizes[anchor_size_idx] * anchor_ratios[anchor_ratio_idx][1]
for ix in range(output_width):
# x-coordinates of the current anchor box
x1_anc = downscale * (ix +0.5) - anchor_x /2
x2_anc = downscale * (ix +0.5) + anchor_x /2
# ignore boxes that go across image boundaries
if x1_anc <0 or x2_anc > resized_width:
continue
for jy in range(output_height):
# y-coordinates of the current anchor box
y1_anc = downscale * (jy +0.5) - anchor_y /2
y2_anc = downscale * (jy +0.5) + anchor_y /2
# ignore boxes that go across image boundaries
if y1_anc <0 or y2_anc > resized_height:
continue
这一步嵌套在上层for循环中,即对每个bbox做判断。
初始化该bbox是否为target:bbox_type = 'neg'
计算当前bbox和图像中所有ground truth的bbox的IoU(调用def iou),结果赋给curr_iou;对每一个ground truth 的bbox,执行如下操作:
gta中存储的是原图的bbox的ground truth!!!
首先获取当前比较的这个ground truth bbox的中心:
cx = (gta[bbox_num, 0] + gta[bbox_num, 1]) / 2.0
cy = (gta[bbox_num, 2] + gta[bbox_num, 3]) / 2.0
再获取当前bbox中心:
cxa = (x1_anc + x2_anc) / 2.0
cya = (y1_anc + y2_anc) / 2.0
紧接着,比较两个bbox间的差异:
tx = (cx - cxa) / (x2_anc - x1_anc)#中心坐标
ty = (cy - cya) / (y2_anc - y1_anc)#中心坐标
tw = np.log((gta[bbox_num,1] - gta[bbox_num,0]) / (x2_anc - x1_anc))
th = np.log((gta[bbox_num,3] - gta[bbox_num,2]) / (y2_anc - y1_anc))#宽高比
如果当前ground truth bbox的label class不是background,即框住了某个物体:
!!!!必须保证原图中每一个ground truth bbox都能找到至少一个对应的anchor
如果当前这个坐标和尺寸的bbox与当前比较的ground truth bbox的IoU得分大于已有的得分,则更新:
best_anchor_for_bbox[bbox_num] = [jy, ix, anchor_ratio_idx,anchor_size_idx]#参数为当前bbox的中心坐标和长宽比以及整体尺寸;
并更新得分:
best_iou_for_bbox[bbox_num] = curr_iou
更新中心位置,即为当前这个anchor的坐标:
best_x_for_bbox[bbox_num,:] = [x1_anc, x2_anc, y1_anc, y2_anc]
更新当前bbox与ground truth bbox的差异,宽高差异百分比、缩放差异百分比:
best_dx_for_bbox[bbox_num,:] = [tx, ty, tw, th]
与上一个如果(if)是并列:
如果现在这个bbox与当前对比的ground truth bbox的重叠度超过C.rpn_max_overlap=0.7:
则认为当前bbox为正样本:bbox_type = 'pos'
当前ground_truth_bbox对应的anchor数量加一。
####
best_iou_for_loc在每次确定一个anchor对应的bbox后,初始化为0,接下来开始遍历所有原图中的ground truth bbox;目的是,找到和当前bbox匹配度最高的ground truth bbox。
#####
且,如果当前bbox的IoU大于了best_iou_for_loc,即这一步是定最好的回归参数:
best_iou_for_loc = curr_iou
best_regr = (tx, ty, tw, th)
如果当前IoU在0.3到0.7之间,则认为该bbox与当前遍历到的ground truth bbox关系不密切,则设:bbox_type = 'neutral'
对于一个bbox,遍历完原图中所有的ground truth bbox后,
(1)如果bbox_type == 'neg',则表明该bbox是有用的,可以作为负样本,但是没有和rpn重叠;
(2)如果bbox_type == 'neutral',则表明该bbox没有用处;
(3)如果bbox_type == 'pos'(只要该bbox与原图中任何一个ground truth bbox的IoU>0.7,则该bbox可作为正样本),则表明该bbox是有用的,可以作为正样本,并且和rpn有重叠。此时可以将其对应的回归参数best_regr保存下来。
对应的,
与rpn有重叠,y_rpn_overlap=1;bbox有用,y_is_box_valid=1;
y_rpn_overlap, y_is_box_valid:[output_height, output_width, num_anchors],
输出图片的某个位置[x, y]上,第i号尺寸的bbox有overlap或valid,记作:
y_rpn_overlap[x,y,i]=1, y_is_box_valid[x, y, i]=1
对于每一个anchor位置,每一种bbox尺寸都遍历完之后
如果,有某个gound truth bbox没有对应的anchor,即:
在各个尺寸的bbox下,没有一个anchor的bbox和gound truth bbox的IoU>0.7,也就是说
num_anchors_for_bbox = 0
我们必须保证每个ground truth bbox都能找到至少一个对应的anchor,则现在采取如下措施:
找到与其IoU最高的(尽管<0.7),将其坐标和尺寸以及相应的回归系数作为这个ground truth bbox对应的anchor。
定义pos_loc为rpn_overlap和box_valid都为1的位置,定义neg_loc为均为0的位置。
由于RPN存在的问题是,negative远超过positive,故对正负样本做一定的取舍。
y_rpn_regr:[output_height, output_width, num_anchors * 4]
进行transpose以及expand_dim后:
y_rpn_overlap: [0, num_anchors, output_height, output_width]
y_is_box_valid: [0, num_anchors, output_height, output_width]
y_rpn_regr: [0, num_anchors*4, output_height, output_width]
最终,
y_rpn_cls = np.concatenate([y_is_box_valid, y_rpn_overlap],axis=1)
# [0, num_anchors*2, output_height , output_width]
y_rpn_regr = np.concatenate([np.repeat(y_rpn_overlap,4,axis=1), y_rpn_regr],axis=1)
# [0, num_anchors*8, output_height , output_width]
return np.copy(y_rpn_cls), np.copy(y_rpn_regr)
在def get_anchor_gt中,
y_rpn_cls, y_rpn_regr = 上栏中的return()
对于y_rpn_regr[0, num_anchors*8, output_height, output_width]第二个维度后4个anchor参数做std_scaling,即对bbox的偏移做scaling:
y_rpn_regr[:, y_rpn_regr.shape[1]//2:, :, :] *= C.std_scaling