Files
huchukato-ComfyUI-RIFE-Tens…/export_onnx.py
T
huchukatoandDevin 38214ffb2a Use legacy ONNX exporter (dynamo=False) and skip onnxsim
The dynamo exporter in PyTorch 2.13 produces complex shape formulas
(Min, TruncToInt, CEIL_DIV) that TensorRT cannot resolve for dynamic
shapes. The legacy TorchScript exporter produces clean symbolic dims
(batch, height, width). Also skip onnxsim which folds constants based
on dummy input size, breaking TRT large profile builds.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-28 05:13:16 +02:00

200 lines
8.7 KiB
Python

# https://github.com/Fannovel16/ComfyUI-Frame-Interpolation/tree/main/vfi_utils.py
# https://github.com/yester31/TensorRT_Examples/blob/main/timm_to_trt_python1/onnx_export.py
import torch
import pathlib
import traceback
import os
from urllib.parse import urlparse
from torch.hub import download_url_to_file, get_dir
from rife_arch import IFNet
import onnx
from onnxsim import simplify
MODEL_TYPE = pathlib.Path(__file__).parent.name
CKPT_NAME_VER_DICT = {
"rife40.pth": "4.0",
"rife41.pth": "4.0",
"rife42.pth": "4.2",
"rife43.pth": "4.3",
"rife44.pth": "4.3",
"rife45.pth": "4.5",
"rife46.pth": "4.6",
"rife47.pth": "4.7",
"rife48.pth": "4.7",
"rife49.pth": "4.7",
"rife417.pth": "4.17",
"rife425.pth": "4.25",
"rife426.pth": "4.26",
"sudo_rife4_269.662_testV1_scale1.pth": "4.0"
}
BASE_MODEL_DOWNLOAD_URLS = [
"https://github.com/styler00dollar/VSGAN-tensorrt-docker/releases/download/models/",
"https://github.com/Fannovel16/ComfyUI-Frame-Interpolation/releases/download/models/",
"https://github.com/dajes/frame-interpolation-pytorch/releases/download/v1.0.0/"
]
# Per-file fallback URLs for models no longer hosted at the base URLs above.
CKPT_FALLBACK_URLS = {
"rife417.pth": [
"https://huggingface.co/marduk191/rife/resolve/main/rife417.pth",
"https://huggingface.co/MachineDelusions/RIFE/resolve/main/rife417.pth",
],
"rife425.pth": [
"https://huggingface.co/huchukato/favs/resolve/main/RIFE/rife425.pth",
],
"rife426.pth": [
"https://huggingface.co/marduk191/rife/resolve/main/rife426.pth",
"https://huggingface.co/MachineDelusions/RIFE/resolve/main/rife426.pth",
],
"rife47.pth": [
"https://huggingface.co/marduk191/rife/resolve/main/rife47.pth",
"https://huggingface.co/wavespeed/misc/resolve/main/rife/rife47.pth",
"https://huggingface.co/MachineDelusions/RIFE/resolve/main/rife47.pth",
"https://huggingface.co/jasonot/mycomfyui/resolve/main/rife47.pth",
],
"rife49.pth": [
"https://huggingface.co/marduk191/rife/resolve/main/rife49.pth",
"https://huggingface.co/hfmaster/models-moved/resolve/main/rife/rife49.pth",
"https://huggingface.co/MachineDelusions/RIFE/resolve/main/rife49.pth",
"https://huggingface.co/Isi99999/Frame_Interpolation_Models/resolve/main/rife49.pth",
],
"sudo_rife4_269.662_testV1_scale1.pth": [
"https://huggingface.co/marduk191/rife/resolve/main/sudo_rife4_269.662_testV1_scale1.pth",
"https://huggingface.co/uwg/upscaler/resolve/main/ESRGAN/sudo_rife4_269.662_testV1_scale1.pth",
"https://huggingface.co/licyk/sd-upscaler-models/resolve/main/ESRGAN/sudo_rife4_269.662_testV1_scale1.pth",
],
}
TORCH_DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu"
def load_file_from_url(url, model_dir=None, progress=True, file_name=None):
"""Load file form http url, will download models if necessary.
Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py
Args:
url (str): URL to be downloaded.
model_dir (str): The path to save the downloaded model. Should be a full path. If None, use pytorch hub_dir.
Default: None.
progress (bool): Whether to show the download progress. Default: True.
file_name (str): The downloaded file name. If None, use the file name in the url. Default: None.
Returns:
str: The path to the downloaded file.
"""
if model_dir is None: # use the pytorch hub_dir
hub_dir = get_dir()
model_dir = os.path.join(hub_dir, 'checkpoints')
os.makedirs(model_dir, exist_ok=True)
parts = urlparse(url)
file_name = os.path.basename(parts.path)
if file_name is not None:
file_name = file_name
cached_file = os.path.abspath(os.path.join(model_dir, file_name))
if not os.path.exists(cached_file):
print(f'Downloading: "{url}" to {cached_file}\n')
download_url_to_file(url, cached_file, hash_prefix=None, progress=progress)
return cached_file
def load_file_from_github_release(model_type, ckpt_name):
os.makedirs("models", exist_ok=True)
error_strs = []
all_urls = [base + ckpt_name for base in BASE_MODEL_DOWNLOAD_URLS]
all_urls += CKPT_FALLBACK_URLS.get(ckpt_name, [])
for i, url in enumerate(all_urls):
try:
return load_file_from_url(url, "models")
except Exception:
traceback_str = traceback.format_exc()
if i < len(all_urls) - 1:
print("Failed! Trying another endpoint.")
error_strs.append(f"Error when downloading from: {url}\n\n{traceback_str}")
error_str = '\n\n'.join(error_strs)
raise Exception(f"Tried all urls to download {ckpt_name} but no success. Below is the error log:\n\n{error_str}")
def export_onnx(ckpt_name, ensemble, scale_factor):
print(f"PyTorch version: {torch.__version__}")
print(f"ONNX version: {onnx.__version__}")
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
model_path = load_file_from_github_release(MODEL_TYPE, ckpt_name)
arch_ver = CKPT_NAME_VER_DICT[ckpt_name]
interpolation_model = IFNet(arch_ver=arch_ver)
# Some checkpoints (e.g. rife426.pth) contain extra training-only modules
# (teacher.*, caltime.*) that are not part of the inference architecture.
# Filter them out to avoid load_state_dict errors.
raw_state_dict = torch.load(model_path, weights_only=False)
model_keys = set(interpolation_model.state_dict().keys())
filtered_state_dict = {
k: v for k, v in raw_state_dict.items()
if k in model_keys
}
skipped = set(raw_state_dict.keys()) - model_keys
if skipped:
print(f"Filtered out {len(skipped)} training-only keys: {sorted(skipped)[:5]}...")
interpolation_model.load_state_dict(filtered_state_dict, strict=True)
interpolation_model.eval().to(TORCH_DEVICE)
# # dummy data — must be divisible by 16 (RIFE 4.25 scale_list=[16,8,4,2,1])
# and large enough that onnxsim doesn't fix intermediate dims to small values.
# 1024 is divisible by 16/32/64 and is a clean power of 2.
img0 = torch.randn(1, 3, 1024, 1024).to(TORCH_DEVICE)
img1 = torch.randn(1, 3, 1024, 1024).to(TORCH_DEVICE)
timestep = torch.tensor([0.5], dtype=torch.float32).to(TORCH_DEVICE)
# result = (interpolation_model(img0, img1, timestep))
# print(result)
onnx_save_name = f"{ckpt_name.split('.')[0]}_ensemble_{ensemble}_scale_{scale_factor}.onnx"
onnx_save_path = os.path.join("models",onnx_save_name)
dynamic_axes = {
'img0': {0: 'batch', 2: 'height', 3: 'width'},
'img1': {0: 'batch', 2: 'height', 3: 'width'},
'timestep': {0: 'batch'},
'output': {0: 'batch', 2: 'height', 3: 'width'},
}
with torch.no_grad(): # Disable gradients for efficiency
torch.onnx.export(interpolation_model,
(img0, img1, timestep), # ensemble + scale_factor set in forward fn
onnx_save_path,
export_params=True,
opset_version=19,
do_constant_folding=True,
input_names=['img0', 'img1', 'timestep'],
output_names=['output'],
dynamic_axes=dynamic_axes,
dynamo=False, # Use legacy exporter for simpler shape annotations
)
print(f"ONNX model exported to: {onnx_save_path}")
# Verify the exported ONNX model
onnx_model = onnx.load(onnx_save_path)
onnx.checker.check_model(onnx_model) # Perform a validity check
print("ONNX model validation successful!")
# print(onnx.helper.printable_graph(onnx_model.graph))
sim_model_path = os.path.join("models", f"{ckpt_name.split('.')[0]}_ensemble_{ensemble}_scale_{scale_factor}_sim.onnx")
print("=> Skipping ONNX simplify (onnxsim folds constants based on dummy input size, which breaks TRT dynamic shapes)")
# Just save the raw ONNX as the _sim version (skip simplify)
onnx.save(onnx_model, sim_model_path)
print(f"=> Saved raw ONNX (no simplify) to {sim_model_path}")
sim_model = onnx.load(sim_model_path)
onnx.checker.check_model(sim_model)
print("=> ONNX Model exported at ", sim_model_path)
print("=> sim ONNX Model check done!")
export_onnx(ckpt_name="rife425.pth", ensemble=False, scale_factor=1)
# export_onnx(ckpt_name="rife47.pth", ensemble=True, scale_factor=1)
# export_onnx(ckpt_name="rife49.pth", ensemble=True, scale_factor=1)
# export_onnx(ckpt_name="rife417.pth", ensemble=True, scale_factor=1)
# export_onnx(ckpt_name="rife426.pth", ensemble=False, scale_factor=1)
# export_onnx(ckpt_name="sudo_rife4_269.662_testV1_scale1.pth", ensemble=True, scale_factor=1)