1. YOLO26-obb ONNX模型推理概述
YOLO26-obb是基于YOLO系列改进的旋转目标检测模型,专门用于处理带有角度信息的物体检测任务。与常规的YOLO模型不同,obb(oriented bounding box)版本能够输出带旋转角度的检测框,在遥感图像、文档分析等场景中具有显著优势。将训练好的模型转换为ONNX格式后,可以在多种推理引擎上高效运行,实现跨平台部署。
在实际工程中,ONNX模型推理主要涉及三个关键环节:模型转换、推理环境搭建和结果后处理。我们通常会先使用PyTorch或PaddlePaddle训练好的模型,通过官方工具导出为ONNX格式,然后利用ONNX Runtime或其他兼容的推理引擎进行部署。这个过程中需要特别注意输入输出节点的匹配、动态尺寸的设置以及旋转框的编解码方式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模型转换与优化要点
2.1 从训练框架到ONNX的转换
以PyTorch为例,转换YOLO26-obb模型时需要特别注意旋转框的表示方式。典型的转换命令如下:
python复制import torch
model = torch.load('yolo26_obb.pth') # 加载训练好的模型
model.eval()
# 示例输入张量(根据实际模型调整)
dummy_input = torch.randn(1, 3, 640, 640)
# 导出ONNX模型
torch.onnx.export(
model,
dummy_input,
'yolo26_obb.onnx',
input_names=['images'],
output_names=['output'],
dynamic_axes={
'images': {0: 'batch_size'}, # 动态batch
'output': {0: 'batch_size'}
},
opset_version=12
)
关键参数说明:
dynamic_axes允许模型处理可变尺寸输入opset_version需要与后续推理引擎兼容- 对于obb模型,必须确保角度编码方式在转换前后一致
注意:如果模型包含自定义算子(如旋转NMS),需要预先注册对应的ONNX符号
2.2 ONNX模型优化技巧
转换后的模型通常需要进一步优化:
- 图优化:使用onnxruntime的优化工具
bash复制python -m onnxruntime.tools.convert_onnx_models_to_ort yolo26_obb.onnx
- 量化压缩:对FP32模型进行INT8量化
python复制from onnxruntime.quantization import quantize_dynamic
quantize_dynamic('yolo26_obb.onnx',
'yolo26_obb_quant.onnx',
weight_type=QuantType.QInt8)
- 节点裁剪:移除训练专用节点
python复制import onnx
model = onnx.load('yolo26_obb.onnx')
# 删除Dropout等训练节点
3. 推理环境搭建与配置
3.1 ONNX Runtime环境配置
推荐使用onnxruntime-gpu版本以获得最佳性能:
bash复制pip install onnxruntime-gpu==1.15.1 # 对应CUDA 11.8
基础推理代码框架:
python复制import onnxruntime as ort
# 创建推理会话
sess_options = ort.SessionOptions()
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
sess = ort.InferenceSession('yolo26_obb.onnx',
sess_options,
providers=['CUDAExecutionProvider'])
# 准备输入数据
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name
input_data = preprocess(image) # 自定义预处理
# 执行推理
outputs = sess.run([output_name], {input_name: input_data})
3.2 性能优化参数
通过调整Session配置提升性能:
python复制sess_options = ort.SessionOptions()
sess_options.enable_profiling = True # 开启性能分析
sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
sess_options.intra_op_num_threads = 4 # 设置线程数
sess_options.add_session_config_entry('session.disable_prepacking', '1') # 禁用预打包
4. 旋转框解码与后处理
4.1 OBB解码实现
YOLO26-obb的输出通常包含5个参数:(cx, cy, w, h, θ),解码示例:
python复制import numpy as np
def decode_obb(predictions, stride=32):
"""
predictions: [batch, num_anchors, 5]
返回: [x1,y1,x2,y2,x3,y3,x4,y4]格式的旋转框
"""
boxes = []
for pred in predictions[0]: # 处理batch中的第一个样本
cx, cy, w, h, theta = pred
# 转换为四个角点坐标
cos_t = np.cos(theta)
sin_t = np.sin(theta)
half_w = w / 2
half_h = h / 2
# 计算旋转后的角点
x1 = cx + (-half_w)*cos_t - (-half_h)*sin_t
y1 = cy + (-half_w)*sin_t + (-half_h)*cos_t
x2 = cx + (half_w)*cos_t - (-half_h)*sin_t
y2 = cy + (half_w)*sin_t + (-half_h)*cos_t
x3 = cx + (half_w)*cos_t - (half_h)*sin_t
y3 = cy + (half_w)*sin_t + (half_h)*cos_t
x4 = cx + (-half_w)*cos_t - (half_h)*sin_t
y4 = cy + (-half_w)*sin_t + (half_h)*cos_t
boxes.append([x1,y1,x2,y2,x3,y3,x4,y4])
return np.array(boxes)
4.2 旋转NMS实现
常规的NMS不适用于旋转框,需要使用旋转框专用版本:
python复制from rbbox_overlaps import rbbx_overlaps # 需要单独安装
def rotated_nms(boxes, scores, threshold=0.5):
"""
boxes: [N,8]格式的旋转框
scores: [N]对应的置信度
"""
keep = []
order = scores.argsort()[::-1]
while order.size > 0:
i = order[0]
keep.append(i)
if order.size == 1:
break
# 计算旋转IoU
ovr = rbbx_overlaps(
boxes[i].reshape(1,-1),
boxes[order[1:]]
).reshape(-1)
inds = np.where(ovr <= threshold)[0]
order = order[inds + 1]
return keep
5. 跨平台部署方案
5.1 移动端部署(Android/iOS)
使用ONNX Runtime移动端库:
java复制// Android示例
OrtEnvironment env = OrtEnvironment.getEnvironment();
OrtSession.SessionOptions options = new OrtSession.SessionOptions();
options.addCUDA(); // 如果设备支持
byte[] modelData = loadModel("yolo26_obb.onnx");
OrtSession session = env.createSession(modelData, options);
// 准备输入
float[] inputData = prepareInput(image);
OnnxTensor inputTensor = OnnxTensor.createTensor(env, FloatBuffer.wrap(inputData), new long[]{1,3,640,640});
// 执行推理
OrtSession.Result results = session.run(Collections.singletonMap("images", inputTensor));
5.2 边缘设备部署
对于RKNN、TensorRT等平台,需要二次转换:
- TensorRT转换:
bash复制trtexec --onnx=yolo26_obb.onnx \
--saveEngine=yolo26_obb.engine \
--fp16 \
--workspace=4096
- RKNN转换:
python复制from rknn.api import RKNN
rknn = RKNN()
rknn.config(target_platform='rk3588')
rknn.load_onnx(model='yolo26_obb.onnx')
rknn.build(do_quantization=True)
rknn.export_rknn('yolo26_obb.rknn')
6. 性能优化实战技巧
6.1 输入输出层优化
通过分析模型结构,识别可以优化的节点:
python复制import onnx
model = onnx.load('yolo26_obb.onnx')
# 查找可以融合的节点
for node in model.graph.node:
if node.op_type == 'Conv':
# 检查是否可以与后续BN层融合
pass
6.2 内存访问优化
调整输入输出布局减少内存拷贝:
python复制sess_options = ort.SessionOptions()
sess_options.add_session_config_entry('session.use_device_allocator_for_initializers', '1')
sess_options.add_session_config_entry('session.use_ort_memory_allocator', '1')
6.3 多线程处理
合理设置并行参数:
python复制sess_options = ort.SessionOptions()
sess_options.intra_op_num_threads = 4 # 算子内并行
sess_options.inter_op_num_threads = 2 # 算子间并行
sess_options.execution_mode = ort.ExecutionMode.ORT_PARALLEL
7. 常见问题与解决方案
7.1 模型转换问题
问题1:转换时出现"Unsupported ONNX opset version"
- 解决方案:调整opset版本
python复制torch.onnx.export(..., opset_version=11) # 尝试降低版本
问题2:自定义算子不支持
- 解决方案:注册自定义符号
python复制torch.onnx.register_custom_op_symbolic(
'custom::op',
custom_op_symbolic,
opset_version)
7.2 推理精度问题
问题1:FP16精度下结果异常
- 解决方案:
python复制sess_options.add_session_config_entry(
'session.enable_float16_execution',
'0') # 禁用FP16
问题2:量化后精度下降严重
- 解决方案:使用混合精度量化
python复制quantize_dynamic(...,
op_types_to_quantize=['Conv', 'MatMul'])
7.3 性能问题
问题1:首次推理延迟高
- 解决方案:启用预编译
python复制sess_options.add_session_config_entry(
'session.enable_prepacking',
'1')
问题2:内存占用过高
- 解决方案:启用内存优化
python复制sess_options.enable_mem_pattern = False
sess_options.enable_cpu_mem_arena = False
8. 实际应用案例
8.1 遥感图像检测
在遥感图像中检测旋转的车辆、建筑物:
python复制def process_satellite_image(image_path):
# 读取大尺寸遥感图
img = cv2.imread(image_path)
# 切片处理
tiles = slice_image(img, tile_size=640)
results = []
for tile in tiles:
# 预处理
input_data = preprocess(tile)
# 推理
outputs = sess.run(None, {'images': input_data})
# 后处理
boxes = decode_obb(outputs[0])
results.extend(boxes)
# 合并结果
return merge_results(results)
8.2 文档版面分析
检测扫描文档中的旋转文本区域:
python复制def detect_document_rotation(image):
# 预处理
input_data = document_preprocess(image)
# 推理
outputs = sess.run(None, {'images': input_data})
# 获取角度信息
angles = outputs[0][:,4] # 提取角度参数
# 计算主要旋转角度
major_angle = np.median(angles)
return major_angle
9. 模型调优建议
9.1 角度编码优化
将角度表示从弧度制改为正弦余弦编码:
python复制# 修改模型输出层
class OBHead(nn.Module):
def forward(self, x):
# 原始角度输出
angle = self.angle(x) # [B,1,H,W]
# 改为sin/cos编码
sin = torch.sin(angle)
cos = torch.cos(angle)
return torch.cat([xywh, sin, cos], dim=1)
9.2 多尺度训练适配
在导出ONNX时支持多尺度输入:
python复制torch.onnx.export(
...,
dynamic_axes={
'images': {
0: 'batch_size',
2: 'height',
3: 'width'
}
}
)
10. 进阶开发方向
10.1 自定义算子开发
对于不支持的旋转框操作,可开发自定义算子:
cpp复制// 示例:旋转IoU CUDA实现
__global__ void rotated_iou_kernel(
const float* boxes1,
const float* boxes2,
float* iou_matrix,
int n1, int n2) {
// CUDA实现代码
}
10.2 模型蒸馏压缩
使用蒸馏技术压缩YOLO26-obb模型:
python复制# 教师模型推理
with torch.no_grad():
teacher_out = teacher_model(inputs)
# 学生模型训练
student_out = student_model(inputs)
loss = distillation_loss(student_out, teacher_out)
10.3 部署到特殊硬件
针对特定硬件优化:
python复制# 华为Ascend NPU部署
sess_options = ort.SessionOptions()
sess_options.add_session_config_entry(
'session.use_ascend',
'1')
