目标检测模型部署
本文档根据《嘉楠K230开发手册》V1.0(2024-11-30)整理。正文、表格、示例代码与插图均来自原始手册。
YOLOv8 是YOLO 系列实时物体检测器的最新迭代产品,在精度和速度方面都具有尖端性能。在之前YOLO 版本的基础上,YOLOv8 引入了新的功能和优化,使其成为广泛应用中各种目标检测任务的理想选择。
主要功能:
- 先进的骨干和颈部架构: YOLOv8 采用了最先进的骨干和颈部架构,从而提高了特征提取和物体检测性能。
- 无锚分裂Ultralytics 头: YOLOv8 采用无锚分裂Ultralytics 头,与基于锚的方法相比,它有助于提高检测过程的准确性和效率。
- 优化精度与速度之间的权衡: YOLOv8 专注于保持精度与速度之间的最佳平衡,适用于各种应用领域的实时目标检测任务。
- 各种预训练模型: YOLOv8 提供一系列预训练模型,以满足各种任务和性能要求,从而更容易为您的特定用例找到合适的模型。
YOLOv8 系列提供多种模型,每种模型都专门用于计算机视觉中的特定任务。这些模型旨在满足从物体检测到实例分割、姿态/关键点检测、定向物体检测和分类等更复杂任务的各种要求。
YOLOv8 系列的每个变体都针对各自的任务进行了优化,以确保高性能和高精确度。此外,这些模型还兼容各种操作模式,包括推理、验证、训练和输出,便于在部署和开发的不同阶段使用。
PyTorch到ONNX转换
- 流程分析
选择目标检测模型时,一般应选择轻量化的模型。因此我们选择基于YOLO的yolov8n 作为目标检测模型,该模型参数量较小,更适合在嵌入式设备上进行部署。

- 加载pth或ckpt模型到cpu
- 构建 随机模型输入
- 导出onnx模型
**注:**pth、onnx都支持动态输入,而K230的模型暂时不支持动态输入,所以导出onnx时,onnx输入shape固定。
- 执行步骤
- 在Ubuntu端新建终端,直接在终端输入:
conda activate py39_yolov8
- 进入yolov8模型存放目录
cd yolov8_model/

其中yolov8n.pt可通过下面的链接进行下载:
https://github.com/ultralytics/assets/releases/download/v8.2.0/yolov8n.pt
模型对应的代码可访问:ultralytics/ultralytics at v8.2.0
- 执行转换命令,将pytorch模型转换为onnx模型,并支持320*320像素的图像输入:
yolo export model=yolov8n.pt format=onnx imgsz=320

执行完成后可以在当前目录下查看生成的onnx模型文件。
使用ONNXRuntime进行推理
为了验证onnx正确性,我们需要使用ONNXRuntime对onnx进行推理,推理时保证读取图片、预处理、run、后处理、显示结果与pth/ckpt的推理流程一致。
- 读取图像

#ori_img(810,1080,3),opencv读入图片的默认格式为hwc,bgr
image = cv2.imread(image_path)
- 图像预处理
预处理构建(常用的方法:padding_resize,crop_resize,resize,affine、normalization):参考train.py,test.py、predict.py、现成的onnx推理脚本。

def preprocess(image,input_width=320, input_height=320,mean=[0,0,0],std=[1,1,1]):"""预处理输入图像,调整大小、归一化、转换通道顺序、添加批次维度。"""# 获取原始图像尺寸orig_h, orig_w = image.shape[:2]# 计算缩放比例,保持长宽比scale = min(input_width / orig_w, input_height / orig_h)new_w = int(orig_w * scale)new_h = int(orig_h * scale)# 缩放图像resized_image = cv2.resize(image, (new_w, new_h))# 创建一个背景图像canvas = np.ones((input_height, input_width, 3),dtype=np.uint8)*128# 将缩放后的图像粘贴到背景图像中canvas[0:new_h, 0:new_w, :] = resized_image# BGR 转 RGBimg = canvas[:, :, ::-1]# 转换为 float32img = img.astype(np.float32) / 255for i in range(3): img[:, :, i] -= mean[i] img[:, :, i] /= std[i]# HWC 转 CHWimg = np.transpose(img, (2, 0, 1))# 添加批次维度img = np.expand_dims(img, axis=0)onnx_input=img.copy()return onnx_input, scale
参考:(与pth预处理流程一致)人脸检测预处理代码参考predictor.py(k230模型的输入shape目前只支持固定输入,训练时都是批量固定输入的,因此可以借鉴)中调用的预处理,增加onnx推理时必要的pad_to_square、resize_subact_mean处理,保证onnx与pth预处理一致。

- onnx推理
将预处理好的数据,喂给模型,得到onnx推理结果
outputs = ort_session.run(None, {input_name: img_input})
- 后处理
后处理构建(常用的方法:softmax、loc解码、nms等):参考predict.py等测试脚本、现成的onnx推理脚本。
def postprocess(predictions, scale, original_image, conf_threshold=0.25, iou_threshold=0.45, classes=None):"""后处理推理结果,进行非极大抑制(NMS),并将检测框映射回原始图像。"""predictions = predictions[0] # 移除批次维度predictions=np.transpose(predictions,(1,0))# 分离边界框、置信度和类别概率boxes = predictions[:, :4] # x_center, y_center, w, hclass_scores = predictions[:, 4:]scores=np.max(class_scores,axis=1)# 计算置信度class_ids = class_scores.argmax(axis=1)# 过滤低置信度的框mask = scores > conf_thresholdboxes = boxes[mask]scores = scores[mask]class_ids = class_ids[mask]# 转换边界框格式,从 (x_center, y_center, w, h) 转为 (x1, y1, x2, y2)boxes_xy = boxes[:, :2]boxes_wh = boxes[:, 2:4]boxes_xy -= boxes_wh / 2boxes_xy = boxes_xy/scaleboxes_wh = boxes_wh/scaleboxes_xy2 = boxes_xy + boxes_whboxes = np.concatenate([boxes_xy, boxes_xy2], axis=1)# 转换为 float32 类型boxes = boxes.astype(np.float32)scores = scores.astype(np.float32)# 使用 OpenCV 的 NMS 进行非极大抑制indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), conf_threshold, iou_threshold)# 如果没有检测到目标,返回空列表if len(indices) == 0: return []indices = indices.flatten()detections = []for i in indices: box = boxes[i] score = scores[i] class_id = class_ids[i] detections.append({ "box": box, "score": score, "class_id": class_id })return detections
参考:目标检测源码中的predict.py,对模型输入结果:loc(边界框)、conf(得分)、坐标点等进行后处理,进而得到预测框、得分、坐标。

- 显示结果
显示结果:将后处理之后的结果画到原图。
def draw_boxes(image, detections, class_names,colors):"""在图像上绘制检测框和类别标签。"""for det in detections: box = det["box"] score = det["score"] class_id = det["class_id"] x1, y1, x2, y2 = map(int, box) label = f"{class_names[class_id]}: {score:.2f}" # 绘制边界框 cv2.rectangle(image, (x1, y1), (x2, y2), colors[class_id], 2) # 绘制标签 (text_width, text_height), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) cv2.putText(image, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[class_id], 1)return image
- 执行步骤
- 在Ubuntu端新建终端,直接在终端输入:
conda activate py39_yolov8
- 进入ONNX推理源码目录
cd k230_sdk/src/reference/yolov8_Analysis/detect/
- 拷贝ONNX模型至当前目录
cp ~/yolov8_model/yolov8n.onnx .

- 执行onnx推理程序
python test_det_onnx.py

程序会去读取 ../test-images/bus.pg图像进行推理,推理后的图像会保存为当前目录下的onnx_det_result.jpg。推理结果图像如下所示:
