Dish_Recognition/tools/export_openvino.py

116 lines
3.5 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.

# coding=utf-8
"""导出 YOLO / ConvNeXt 特征模型为 OpenVINO,供无 NVIDIA 的客户机加速。
用法(在项目根目录)::
pip install openvino onnx
python tools/export_openvino.py
python tools/export_openvino.py --yolo-only
python tools/export_openvino.py --feature-only
python tools/export_openvino.py --imgsz 416
导出后:
- best_openvino_model/ → 启动时自动被 YOLO 加载
- feature_openvino_model/ → 启动时自动被特征提取加载
环境变量(可选):
DISH_YOLO_MODEL / DISH_FEATURE_OPENVINO / DISH_OPENVINO_DEVICE / DISH_YOLO_IMGSZ
"""
from __future__ import annotations
import argparse
import shutil
import sys
from pathlib import Path
_ROOT = Path(__file__).resolve().parents[1]
if str(_ROOT) not in sys.path:
sys.path.insert(0, str(_ROOT))
def export_yolo(imgsz: int = 640) -> Path:
from ultralytics import YOLO
pt = _ROOT / "best.pt"
if not pt.exists():
raise FileNotFoundError(f"找不到权重: {pt}")
model = YOLO(str(pt))
out = model.export(format="openvino", imgsz=imgsz, half=False)
out_path = Path(out)
# ultralytics 通常输出 best_openvino_model/
target = _ROOT / "best_openvino_model"
if out_path.resolve() != target.resolve():
if target.exists():
shutil.rmtree(target)
if out_path.is_dir():
shutil.move(str(out_path), str(target))
else:
target.mkdir(parents=True, exist_ok=True)
shutil.copy2(out_path, target / out_path.name)
print(f"[OK] YOLO OpenVINO → {target}")
return target
def export_feature(imgsz: int = 224) -> Path:
"""导出 ConvNeXt-Tiny 特征骨干为 OpenVINO(输入 NCHW 已归一化)。"""
import numpy as np
import torch
from openvino import convert_model, save_model
from core.common_classifier import FeatureExtractor
extractor = FeatureExtractor().eval()
dummy = torch.randn(1, 3, imgsz, imgsz)
onnx_path = _ROOT / "feature_extractor.onnx"
torch.onnx.export(
extractor,
dummy,
str(onnx_path),
input_names=["input"],
output_names=["features"],
dynamic_axes={"input": {0: "batch"}, "features": {0: "batch"}},
opset_version=17,
)
print(f"[OK] ONNX → {onnx_path}")
ov_model = convert_model(str(onnx_path))
out_dir = _ROOT / "feature_openvino_model"
out_dir.mkdir(parents=True, exist_ok=True)
xml_path = out_dir / "feature_extractor.xml"
save_model(ov_model, str(xml_path))
print(f"[OK] feature OpenVINO → {xml_path}")
# 快速校验
from openvino import Core
compiled = Core().compile_model(str(xml_path), "CPU")
test = np.random.randn(1, 3, imgsz, imgsz).astype(np.float32)
out = compiled([test])[compiled.outputs[0]]
print(f"[OK] smoke test output shape={np.asarray(out).shape}")
return out_dir
def main():
parser = argparse.ArgumentParser(description="导出 OpenVINO 模型")
parser.add_argument("--yolo-only", action="store_true")
parser.add_argument("--feature-only", action="store_true")
parser.add_argument("--imgsz", type=int, default=640, help="YOLO 输入边长")
args = parser.parse_args()
do_yolo = not args.feature_only
do_feat = not args.yolo_only
if args.yolo_only:
do_feat = False
if args.feature_only:
do_yolo = False
if do_yolo:
export_yolo(imgsz=args.imgsz)
if do_feat:
export_feature(imgsz=224)
print("完成。重启识别程序即可自动加载 OpenVINO 模型。")
if __name__ == "__main__":
main()