import os import sys import argparse import warnings import onnx import torch import torch.nn as nn from damo.base_models.core.ops import RepConv, SiLU from damo.config.base import parse_config from damo.detectors.detector import build_local_model from damo.utils.model_utils import replace_module class DeepStreamOutput(nn.Module): def __init__(self): super().__init__() def forward(self, x): boxes = x[1] scores, classes = torch.max(x[0], 2, keepdim=True) return torch.cat((boxes, scores, classes), dim=2) def suppress_warnings(): warnings.filterwarnings('ignore', category=torch.jit.TracerWarning) warnings.filterwarnings('ignore', category=UserWarning) warnings.filterwarnings('ignore', category=DeprecationWarning) def damoyolo_export(weights, config_file, device): config = parse_config(config_file) config.model.head.export_with_post = True model = build_local_model(config, device) ckpt = torch.load(weights, map_location=device) model.eval() if 'model' in ckpt: ckpt = ckpt['model'] model.load_state_dict(ckpt, strict=True) model = replace_module(model, nn.SiLU, SiLU) for layer in model.modules(): if isinstance(layer, RepConv): layer.switch_to_deploy() model.head.nms = False return config, model def main(args): suppress_warnings() device = torch.device('cpu') cfg, model = damoyolo_export(args.weights, args.config, device) model = nn.Sequential(model, DeepStreamOutput()) img_size = args.size * 2 if len(args.size) == 1 else args.size onnx_input_im = torch.zeros(1, 3, *img_size).to(device) onnx_output_file = cfg.miscs['exp_name'] + '.onnx' torch.onnx.export(model, onnx_input_im, onnx_output_file, verbose=False, opset_version=args.opset, do_constant_folding=True, input_names=['input'], output_names=['output'], dynamic_axes=None) if args.simplify: import onnxsim model_onnx = onnx.load(onnx_output_file) model_onnx, _ = onnxsim.simplify(model_onnx) onnx.save(model_onnx, onnx_output_file) def parse_args(): parser = argparse.ArgumentParser(description='DeepStream DAMO-YOLO conversion') parser.add_argument('-w', '--weights', required=True, help='Input weights (.pth) file path (required)') parser.add_argument('-c', '--config', required=True, help='Input config (.py) file path (required)') parser.add_argument('-s', '--size', nargs='+', type=int, default=[640], help='Inference size [H,W] (default [640])') parser.add_argument('--opset', type=int, default=11, help='ONNX opset version') parser.add_argument('--simplify', action='store_true', help='ONNX simplify model') args = parser.parse_args() if not os.path.isfile(args.weights): raise SystemExit('Invalid weights file') if not os.path.isfile(args.config): raise SystemExit('Invalid config file') return args if __name__ == '__main__': args = parse_args() sys.exit(main(args))