| |
| """ |
| FastDVDnet ONNX 视频推理脚本,输出 原帧 | ONNX 去噪结果 拼接视频。 |
| |
| 输入视频逐帧 resize 到 ONNX 固定输入尺寸 (HxW),组成 5 帧窗口 |
| [t-2,t-1,t,t+1,t+2] 推理,输出 resize 回原始尺寸生成拼接视频。 |
| |
| 用法: |
| python onnx_video_infer.py \ |
| --onnx ./fastdvdnet_640x480.onnx \ |
| --video mp4/drone.mp4 \ |
| --noise_sigma 25 \ |
| --out_dir ./video_infer_results/onnx_test |
| """ |
| import argparse |
| import os |
| import time |
|
|
| import cv2 |
| import numpy as np |
| import onnxruntime as ort |
|
|
|
|
| def get_onnx_shapes(onnx_path): |
| import onnx |
| m = onnx.load(onnx_path) |
| x_dims = [d.dim_value for d in m.graph.input[0].type.tensor_type.shape.dim] |
| return x_dims |
|
|
|
|
| def read_video(video_path, max_frames=0): |
| cap = cv2.VideoCapture(video_path) |
| if not cap.isOpened(): |
| raise RuntimeError("failed to open video: {}".format(video_path)) |
| fps = cap.get(cv2.CAP_PROP_FPS) or 25.0 |
| orig_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| orig_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| frames_bgr = [] |
| while True: |
| ok, frame = cap.read() |
| if not ok: |
| break |
| frames_bgr.append(frame) |
| if max_frames and len(frames_bgr) >= max_frames: |
| break |
| cap.release() |
| if not frames_bgr: |
| raise RuntimeError("no frames read from {}".format(video_path)) |
| return frames_bgr, fps, orig_w, orig_h |
|
|
|
|
| def reflect_index(idx, length): |
| if length <= 1: |
| return 0 |
| while idx < 0 or idx >= length: |
| if idx < 0: |
| idx = -idx |
| if idx >= length: |
| idx = 2 * (length - 1) - idx |
| return idx |
|
|
|
|
| def bgr_to_onnx_input(frame_bgr, onnx_h, onnx_w): |
| """resize BGR uint8 to ONNX RGB float32 [1,3,H,W] in [0,1]""" |
| resized = cv2.resize(frame_bgr, (onnx_w, onnx_h), interpolation=cv2.INTER_AREA) |
| rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB) |
| chw = rgb.astype(np.float32).transpose(2, 0, 1) / 255.0 |
| return chw |
|
|
|
|
| def chw_to_bgr_uint8(chw, target_w, target_h): |
| """[3,H,W] float32 in [0,1] -> BGR uint8 resized to target_w x target_h""" |
| hwc = (chw * 255.0).clip(0, 255).astype(np.uint8).transpose(1, 2, 0) |
| rgb = hwc |
| bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) |
| if bgr.shape[1] != target_w or bgr.shape[0] != target_h: |
| bgr = cv2.resize(bgr, (target_w, target_h), interpolation=cv2.INTER_LINEAR) |
| return bgr |
|
|
|
|
| def denoise_onnx(frames_bgr, noise_sigma_01, sess, x_shape, input_names, output_names): |
| numframes = len(frames_bgr) |
| _, _, onnx_h, onnx_w = x_shape |
| temp_psz, ctrl = 5, 2 |
|
|
| |
| chw_cache = {} |
| def get_chw(i): |
| i = i % numframes |
| if i not in chw_cache: |
| chw_cache[i] = bgr_to_onnx_input(frames_bgr[reflect_index(i, numframes)], onnx_h, onnx_w) |
| return chw_cache[i] |
|
|
| den_frames_bgr = [] |
| inframes = [] |
|
|
| for fridx in range(numframes): |
| if not inframes: |
| for off in range(temp_psz): |
| inframes.append(get_chw(fridx + off - ctrl)) |
| else: |
| del inframes[0] |
| inframes.append(get_chw(fridx + ctrl)) |
|
|
| |
| noisy = np.concatenate(inframes, axis=0)[None, :, :, :].astype(np.float32) |
| noise_map = np.full((1, 1, onnx_h, onnx_w), noise_sigma_01, dtype=np.float32) |
| feeds = {input_names[0]: noisy, input_names[1]: noise_map} |
| out = sess.run(output_names, feeds)[0] |
| out = np.clip(out, 0.0, 1.0) |
| den_bgr = chw_to_bgr_uint8(out[0], frames_bgr[0].shape[1], frames_bgr[0].shape[0]) |
| den_frames_bgr.append(den_bgr) |
|
|
| return den_frames_bgr |
|
|
|
|
| def write_side_by_side(frames_orig, frames_den, out_path, fps, label=True): |
| h, w = frames_orig[0].shape[:2] |
| os.makedirs(os.path.dirname(os.path.abspath(out_path)), exist_ok=True) |
| fourcc = cv2.VideoWriter_fourcc(*"mp4v") |
| writer = cv2.VideoWriter(out_path, fourcc, fps, (w * 2, h)) |
| if not writer.isOpened(): |
| raise RuntimeError("failed to create video writer: {}".format(out_path)) |
| for orig, den in zip(frames_orig, frames_den): |
| canvas = np.concatenate([orig, den], axis=1) |
| if label: |
| cv2.putText(canvas, "Original", (16, 34), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2, cv2.LINE_AA) |
| cv2.putText(canvas, "ONNX Denoised", (w + 16, 34), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2, cv2.LINE_AA) |
| writer.write(canvas) |
| writer.release() |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="FastDVDnet ONNX video inference side-by-side") |
| parser.add_argument("--onnx", type=str, default="./fastdvdnet_640x480.onnx") |
| parser.add_argument("--video", type=str, required=True, help="input mp4 video") |
| parser.add_argument("--noise_sigma", type=float, default=25.0, help="noise sigma 0-255") |
| parser.add_argument("--out_dir", type=str, default="./video_infer_results/onnx_test") |
| parser.add_argument("--max_frames", type=int, default=0, help="0 means all frames") |
| parser.add_argument("--no_label", action="store_true") |
| args = parser.parse_args() |
|
|
| x_shape = get_onnx_shapes(args.onnx) |
| _, _, onnx_h, onnx_w = x_shape |
| print("ONNX input shape: {}".format(x_shape)) |
|
|
| sess = ort.InferenceSession(args.onnx, providers=["CPUExecutionProvider"]) |
| input_names = [i.name for i in sess.get_inputs()] |
| output_names = [o.name for o in sess.get_outputs()] |
| print("providers:", sess.get_providers()) |
| print("inputs:", [(n, list(i.shape)) for n, i in zip(input_names, sess.get_inputs())]) |
| print("outputs:", [(n, list(o.shape)) for n, o in zip(output_names, sess.get_outputs())]) |
|
|
| base = os.path.splitext(os.path.basename(args.video))[0] |
| out_path = os.path.join(args.out_dir, "{}_onnx_side_by_side.mp4".format(base)) |
|
|
| t0 = time.time() |
| frames_bgr, fps, orig_w, orig_h = read_video(args.video, args.max_frames) |
| print("video: {}x{} @ {:.1f}fps, {} frames".format(orig_w, orig_h, fps, len(frames_bgr))) |
| print("onnx input: {}x{}".format(onnx_w, onnx_h)) |
|
|
| sigma01 = args.noise_sigma / 255.0 |
| den = denoise_onnx(frames_bgr, sigma01, sess, x_shape, input_names, output_names) |
| write_side_by_side(frames_bgr, den, out_path, fps, label=not args.no_label) |
| dt = time.time() - t0 |
|
|
| print("[OK] {} -> {}".format(args.video, out_path)) |
| print(" frames={} time={:.2f}s fps={:.2f}".format(len(frames_bgr), dt, len(frames_bgr) / max(dt, 0.001))) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|