YOLOv8是一种目标检测算法,其推理处理流程如下:
图像输入:首先将待检测的图像输入到模型中。
第一步:加载模型,可根据使用不同的硬件及框架使用不同的模型加载方式,在此就不做详细解释。
第二步:加载图片,对图片进行仿射变换,将图片调整到指定大小,并进行必要的图像变换和归一化操作,以便用于深度学习模型的输入。
def preprocess_warpAffine(self, image):
# 计算缩放比例scale,使得输入图像能够适应指定的输入大小(self.input_size_w, self.input_size_h)。
# 缩放比例取width和height的最小值与相应的缩放因子之商。
scale = min((self.input_size_w / image.shape[1], self.input_size_h / image.shape[0]))
# 计算水平和垂直方向上的偏移量ox和oy,以便将缩放后的图像置于输入大小的中心位置。
ox = (self.input_size_w - scale * image.shape[1]) / 2
oy = (self.input_size_h - scale * image.shape[0]) / 2
# 构造仿射变换矩阵M,其中包含了缩放、平移操作。M是一个2x3的矩阵,包含了缩放因子和偏移量。
M = np.array([
[scale, 0, ox],
[0, scale, oy]
], dtype=np.float32)
# 使用cv2.warpAffine函数对输入图像进行仿射变换,得到缩放后的图像img_pre。
# 这里使用了双线性插值方法(cv2.INTER_LINEAR)来处理变换过程中的像素值。
img_pre = cv2.warpAffine(image, M, (self.input_size_w, self.input_size_h), flags=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_CONSTANT, borderValue=(114, 114, 114))
IM = cv2.invertAffineTransform(M)
# 对缩放后的图像进行颜色通道顺序的反转(RGB转BGR),并将像素值范围从[0, 255]归一化到[0, 1]之间。
img_pre = (img_pre[...,::-1] / 255.0).astype(np.float32)
# 将图像的维度顺序从(HWC)转换为(CHW),并添加额外的批次维度,以满足模型输入的要求。
img_pre = img_pre.transpose(2, 0, 1)[None]
img_pre = np.array(img_pre)
# 返回预处理后的图像img_pre和逆变换矩阵IM(用于后续处理结果的可视化)。
return img_pre, IM
第三步:调用模型进行推理
result = self.model.execute([img_pre, ])
第四步:对模型输出result进行转置操作,使其维度从[1, 8400, 84]变为[1, 84, 8400],结果保存在tmp中。
tmp = result[0].transpose(0, 2, 1)
第五步:调用self.postprocess方法对转置后的输出tmp进行后处理,得到检测框信息,保存在boxes中。
boxes = self.postprocess(tmp, IM)
这段代码完成了对模型输出结果的解析、筛选、坐标变换和NMS处理,最终得到经过后处理的预测框结果。
def postprocess(self, pred, IM=[], conf_thres=0.95, iou_thres=0.45):
# 输入是模型推理的结果,即8400个预测框
# 1,8400,84 [cx,cy,w,h,class*80]
self.boxes = []
# 遍历模型输出的预测结果pred,每个预测框包含了(cx, cy, w, h, class_scores)等信息。
# 对每个预测框进行解析,计算预测框的左上角和右下角坐标,以及置信度和类别。
for item in pred[0]:
cx, cy, w, h = item[:4]
label = item[4:].argmax()
confidence = item[4 + label]
# 根据设定的置信度阈值(conf_thres),筛选出置信度高于阈值的预测框
if confidence < conf_thres:
continue
# 将筛选后的预测框信息保存到self.boxes中,格式为[left, top, right, bottom, confidence, label]
left = cx - w * 0.5
top = cy - h * 0.5
right = cx + w * 0.5
bottom = cy + h * 0.5
self.boxes.append([left, top, right, bottom, confidence, label])
# 对保存的预测框信息进行仿射逆变换操作,将预测框的坐标映射回原始图像空间
self.boxes = np.array(self.boxes)
# print(boxes)
# 根据置信序对预测框进行排序。
if len(self.boxes) == 0:
self.boxes = []
else:
lr = self.boxes[:,[0, 2]]
tb = self.boxes[:,[1, 3]]
self.boxes[:,[0,2]] = IM[0][0] * lr + IM[0][2]
self.boxes[:,[1,3]] = IM[1][1] * tb + IM[1][2]
self.boxes = sorted(self.boxes.tolist(), key=lambda x:x[4], reverse=True)
# 调用NMS(非极大值抑制)函数对预测框进行进一步处理,去除重叠度高的冗余框,最终返回处理后的预测框结果。
return self.NMS(self.boxes, iou_thres)
def NMS(self, boxes, iou_thres):
remove_flags = [False] * len(boxes)
keep_boxes = []
for i, ibox in enumerate(boxes):
if remove_flags[i]:
continue
keep_boxes.append(ibox)
for j in range(i + 1, len(boxes)):
if remove_flags[j]:
continue
jbox = boxes[j]
if(ibox[5] != jbox[5]):
continue
if self.iou(ibox, jbox) > iou_thres:
remove_flags[j] = True
return keep_boxes
def iou(self, box1, box2):
def area_box(box):
return (box[2] - box[0]) * (box[3] - box[1])
left, top = max(box1[:2], box2[:2])
right, bottom = min(box1[2:4], box2[2:4])
union = max((right - left), 0) * max((bottom - top), 0)
cross = area_box(box1) + area_box(box2) - union
if cross == 0 or union == 0:
return 0
return union / cross