目标检测——yolov4预测及后处理
运用训练好的模型进行目标检测,模型输出为中心点对grid的偏移,长宽相对于anchor的缩放比例以及类别
其维度为(b, 13, 13, 3, classes+5)
1. 根据(x, y, h, w)计算出预测框相对于原图像的位置和大小
def yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape): # (b, 13, 13, 3, 2) box_yx = box_xy[..., ::-1] box_hw = box_wh[..., ::-1] input_shape = K.cast(input_shape, K.dtype(box_yx)) image_shape = K.cast(image_shape, K.dtype(box_yx)) new_shape = K.round(image_shape * K.min(input_shape / image_shape)) # 原图相对于input_shape的偏移 offset = (input_shape - new_shape) / 2. / input_shape # 原图相对于input_shape的缩放比例 scale = input_shape / new_shape # 映射回原图 box_yx = (box_yx - offset) * scale box_hw *= scale box_mins = box_yx - (box_hw / 2.) box_maxes = box_yx + (box_hw / 2.) # (b, 13, 13, 3, 4) boxes = K.concatenate([ box_mins[..., 0:1], # y_min box_mins[..., 1:2], # x_min box_maxes[..., 0:1], # y_max box_maxes[..., 1:2] # x_max ]) # 实际大小 boxes *= K.concatenate([image_shape, image_shape]) return boxes
2. 获取得分
box_scores = box_confidence * box_class_probs # 预测置信度和预测类别概率的乘积
3.非极大值抑制
# (n, 4)
boxes = K.concatenate(boxes, axis=0)
# (n, classes_num)
box_scores = K.concatenate(box_scores, axis=0)
设定阈值,大于此阈值的判断为正类(有目标)
mask = box_scores >= score_threshold
对于每一种类别,采用非极大值抑制
for c in range(num_classes): # 取出所有box_scores >= score_threshold的框,和成绩 class_boxes = tf.boolean_mask(boxes, mask[:, c]) class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c]) # 非极大抑制,去掉box重合程度高的那一些 nms_index = tf.image.non_max_suppression( class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold) # 获取非极大抑制后的结果 # 下列三个分别是 # 框的位置,得分与种类 class_boxes = K.gather(class_boxes, nms_index) class_box_scores = K.gather(class_box_scores, nms_index) classes = K.ones_like(class_box_scores, 'int32') * c boxes_.append(class_boxes) scores_.append(class_box_scores) classes_.append(classes) boxes_ = K.concatenate(boxes_, axis=0) scores_ = K.concatenate(scores_, axis=0) classes_ = K.concatenate(classes_, axis=0) return boxes_, scores_, classes_
非极大值抑制,顾名思义,即如果不是极大值,就将它抑制掉,选取这个类别最大的score,这个就是极大值,是要显示的框,即目标存在与这个位置
将其他位置分别与这个框进行iou计算,如果iou大于阈值,则判定这两个框预测的使同一个目标,则将这个框抑制掉,将此处的score置为0,
一直重复这个过程,知道选择出全部的目标。
4.在图像上画框
out_boxes, out_scores, out_classes = self.yolo_model.predict([image_data, input_image_shape])
for i, c in list(enumerate(out_classes)): predicted_class = self.class_names[c] box = out_boxes[i] score = out_scores[i] top, left, bottom, right = box top = top - 5 left = left - 5 bottom = bottom + 5 right = right + 5 top = max(0, np.floor(top + 0.5).astype('int32')) left = max(0, np.floor(left + 0.5).astype('int32')) bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32')) right = min(image.size[0], np.floor(right + 0.5).astype('int32')) # 画框框 label = '{} {:.2f}'.format(predicted_class, score) draw = ImageDraw.Draw(image) label_size = draw.textsize(label, font) label = label.encode('utf-8') print(label) if top - label_size[1] >= 0: text_origin = np.array([left, top - label_size[1]]) else: text_origin = np.array([left, top + 1]) for i in range(thickness):
# 目标区域画框 draw.rectangle( [left + i, top + i, right - i, bottom - i], outline=self.colors[c])
# text区域画框 draw.rectangle( [tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c])
# text区域写字(lable) draw.text(text_origin, str(label, 'UTF-8'), fill=(0, 0, 0), font=font)