DetectionModelTraining/04_convert_rknn.py

263 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
将 YOLOv8 ONNX 模型转换为 RKNN 格式
适用于 RK3588 / RK3568 / RK3576 等平台
环境要求:
- Ubuntu x86_64 / Docker
- Python 3.8 / 3.9 / 3.10 / 3.11
- rknn-toolkit2 (pip install rknn-toolkit2==2.2.0)
使用方法:
# FP16 模式(推荐,速度快精度高)
python 04_convert_rknn.py best.onnx -o shoe_detector.rknn -t rk3588
# INT8 量化(模型更小,需要校准数据集)
python 04_convert_rknn.py best.onnx -o shoe_detector.rknn -t rk3588 -q -d dataset.txt
支持的 target_platform:
- rk3588 / rk3588s
- rk3568 / rk3566
- rk3576
- rv1106 / rv1103 / rv1103b
- rv1126
"""
import argparse
import os
import sys
from pathlib import Path
def check_environment():
"""检查运行环境"""
try:
from rknn.api import RKNN
print("✓ RKNN Toolkit2 已安装")
return True
except ImportError:
print("✗ 错误: 未安装 RKNN Toolkit2")
print("\n请安装:")
print(" pip install rknn-toolkit2==2.2.0")
print("\n或从源码安装:")
print(" https://github.com/airockchip/rknn-toolkit2")
return False
def create_sample_dataset(onnx_path: str, output_path: str = "dataset.txt", num_samples: int = 20):
"""
创建示例量化校准数据集
用于 INT8 量化时提供校准图片路径
"""
print(f"\n创建示例校准数据集: {output_path}")
print("注意: 请用实际图片替换这些示例路径")
sample_content = f"""# RKNN INT8 量化校准数据集
# 每行一个图片路径,建议使用 20-100 张典型场景图片
# 图片格式: JPG, PNG, BMP 等
# 示例路径(请替换为实际路径):
# /path/to/train/images/img001.jpg
# /path/to/train/images/img002.jpg
# /path/to/valid/images/img001.jpg
# 提示:
# 1. 图片应与实际部署场景相似
# 2. 包含各种光照、角度、背景的样本
# 3. 建议 20-100 张,越多越慢但可能更准
"""
with open(output_path, 'w') as f:
f.write(sample_content)
print(f"✓ 示例数据集已创建: {output_path}")
print(" 请编辑此文件,添加实际的图片路径")
return output_path
def convert_onnx_to_rknn(
onnx_path: str,
output_path: str = None,
target_platform: str = "rk3588",
do_quantization: bool = False,
dataset_path: str = None,
verbose: bool = True
):
"""
转换 ONNX 模型到 RKNN
Args:
onnx_path: ONNX 模型文件路径
output_path: 输出 RKNN 文件路径,默认与 ONNX 同名
target_platform: 目标平台,默认 rk3588
do_quantization: 是否启用 INT8 量化
dataset_path: 量化校准数据集路径txt 文件,每行一张图片路径)
verbose: 是否打印详细信息
"""
if output_path is None:
output_path = onnx_path.replace(".onnx", ".rknn")
# 确保输出目录存在
output_dir = os.path.dirname(output_path)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
print("="*70)
print(f"ONNX 转 RKNN")
print("="*70)
print(f"输入: {onnx_path}")
print(f"输出: {output_path}")
print(f"目标: {target_platform}")
print(f"量化: {'INT8' if do_quantization else 'FP16 (无量化)'}")
if do_quantization:
print(f"校准: {dataset_path}")
print("="*70)
# 检查输入文件
if not os.path.exists(onnx_path):
print(f"\n✗ 错误: 找不到 ONNX 文件: {onnx_path}")
return False
# 检查数据集(如果需要量化)
if do_quantization:
if dataset_path is None:
print("\n✗ 错误: INT8 量化需要提供校准数据集")
print(" 使用 --dataset 指定数据集文件路径")
print(" 或运行 --create-dataset 创建示例")
return False
if not os.path.exists(dataset_path):
print(f"\n✗ 错误: 找不到数据集文件: {dataset_path}")
return False
from rknn.api import RKNN
# 创建 RKNN 对象
rknn = RKNN(verbose=verbose)
try:
# 配置模型
print("\n[1/4] 配置模型...")
rknn.config(
mean_values=[[0, 0, 0]], # YOLOv8 使用 0-255 输入
std_values=[[255, 255, 255]], # 归一化到 0-1
target_platform=target_platform
)
print(" ✓ 完成")
# 加载 ONNX
print("\n[2/4] 加载 ONNX 模型...")
ret = rknn.load_onnx(model=onnx_path)
if ret != 0:
print(" ✗ 加载失败!")
return False
print(" ✓ 完成")
# 构建模型
print("\n[3/4] 构建 RKNN 模型...")
if do_quantization:
print(f" 使用 INT8 量化,校准数据集: {dataset_path}")
ret = rknn.build(do_quantization=True, dataset=dataset_path)
else:
print(" 使用 FP16 模式(无量化)")
ret = rknn.build(do_quantization=False)
if ret != 0:
print(" ✗ 构建失败!")
return False
print(" ✓ 完成")
# 导出 RKNN
print("\n[4/4] 导出 RKNN 模型...")
ret = rknn.export_rknn(output_path)
if ret != 0:
print(" ✗ 导出失败!")
return False
print(" ✓ 完成")
finally:
rknn.release()
# 验证输出
if os.path.exists(output_path):
size_mb = os.path.getsize(output_path) / (1024 * 1024)
print("\n" + "="*70)
print(f"✓ 转换成功!")
print(f" 输出文件: {output_path}")
print(f" 文件大小: {size_mb:.2f} MB")
print("="*70)
print("\n下一步:")
print(f" 1. 复制到 RK3588:")
print(f" scp {output_path} orangepi@<rk3588_ip>:/home/orangepi/apps/OrangePi3588Media/models/")
print(f" 2. 更新配置文件中的模型路径")
return True
else:
print("\n✗ 错误: 输出文件未生成")
return False
def main():
parser = argparse.ArgumentParser(
description="将 YOLOv8 ONNX 模型转换为 RKNN",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# FP16 模式(推荐)
python 04_convert_rknn.py best.onnx -o shoe_detector.rknn
# 指定目标平台
python 04_convert_rknn.py best.onnx -t rk3568
# INT8 量化
python 04_convert_rknn.py best.onnx -q -d dataset.txt
# 创建示例校准数据集
python 04_convert_rknn.py --create-dataset
"""
)
parser.add_argument("onnx", nargs="?", help="ONNX 模型文件路径")
parser.add_argument("-o", "--output", help="输出 RKNN 文件路径")
parser.add_argument("-t", "--target", default="rk3588",
help="目标平台 (默认: rk3588)")
parser.add_argument("-q", "--quantize", action="store_true",
help="启用 INT8 量化")
parser.add_argument("-d", "--dataset", help="量化校准数据集路径 (txt 文件)")
parser.add_argument("--create-dataset", action="store_true",
help="创建示例校准数据集并退出")
parser.add_argument("-v", "--verbose", action="store_true", default=True,
help="显示详细信息")
args = parser.parse_args()
# 创建示例数据集
if args.create_dataset:
create_sample_dataset("best.onnx", "dataset.txt")
return 0
# 检查参数
if args.onnx is None:
parser.print_help()
print("\n错误: 请提供 ONNX 文件路径")
return 1
# 检查环境
if not check_environment():
return 1
# 执行转换
success = convert_onnx_to_rknn(
onnx_path=args.onnx,
output_path=args.output,
target_platform=args.target,
do_quantization=args.quantize,
dataset_path=args.dataset,
verbose=args.verbose
)
return 0 if success else 1
if __name__ == "__main__":
sys.exit(main())