wzf19947 commited on
Commit
1266aec
·
1 Parent(s): 76f3fe4

first commit

Browse files
.gitattributes CHANGED
@@ -33,8 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- *.gif filter=lfs diff=lfs merge=lfs -text
37
- *.axmodel filter=lfs diff=lfs merge=lfs -text
38
- *.mp4 filter=lfs diff=lfs merge=lfs -text
39
  *.png filter=lfs diff=lfs merge=lfs -text
 
 
40
  *.jpg filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
36
  *.png filter=lfs diff=lfs merge=lfs -text
37
+ *.PNG filter=lfs diff=lfs merge=lfs -text
38
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
39
  *.jpg filter=lfs diff=lfs merge=lfs -text
40
+ *.axmodel filter=lfs diff=lfs merge=lfs -text
41
+ *.gif filter=lfs diff=lfs merge=lfs -text
DnCNN/model_convert/DnCNN.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "input": "./dncnn_color_blind_416x416_sim.onnx",
3
+ "output_dir": "./DnCNN",
4
+ "output_name": "dncnn_color_blind_416x416_sim.axmodel",
5
+ "model_type": "ONNX",
6
+ "target_hardware": "AX650",
7
+ "npu_mode": "NPU3",
8
+ "quant": {
9
+ "input_configs": [
10
+ {
11
+ "tensor_name": "input",
12
+ "calibration_dataset": "./BSD.tar.gz",
13
+ "calibration_size": 32,
14
+ "calibration_mean": [0.0, 0.0, 0.0],
15
+ "calibration_std": [255.0, 255.0, 255.0]
16
+ }
17
+ ],
18
+ "calibration_method": "MinMax",
19
+ "precision_analysis": true,
20
+ "transformer_opt_level":1,
21
+ // "enable_smooth_quant": true,
22
+ "precision_analysis_method": "EndToEnd",
23
+ "precision_analysis_mode": "NPUBackend",
24
+ "device": "cuda:1",
25
+ // "layer_configs": [
26
+ // {
27
+ // "start_tensor_names": ["DEFAULT"],
28
+ // "end_tensor_names": ["DEFAULT"],
29
+ // "data_type": "U16"
30
+ // }
31
+ // ]
32
+ },
33
+ "input_processors": [
34
+ {
35
+ "tensor_name": "input",
36
+ "tensor_format": "RGB",
37
+ "tensor_layout": "NCHW",
38
+ "src_format": "BGR",
39
+ "src_dtype": "U8",
40
+ "src_layout": "NCHW",
41
+ "csc_mode": "NoCSC"
42
+ }
43
+ // {
44
+ // "tensor_name": "input",
45
+ // "tensor_format": "RGB",
46
+ // "tensor_layout": "NCHW",
47
+ // "src_format": "YUV420SP",
48
+ // "src_layout": "NHWC",
49
+ // "src_dtype": "U8",
50
+ // "csc_mode": "FullRange",
51
+ // "csc_mat": [1.164, 0, 1.596, -222.912, 1.164, -0.392, -0.813, 135.616, 1.164, 2.017, 0, -276.8]
52
+ // }
53
+ ],
54
+ "compiler": {
55
+ "check": 2
56
+ }
57
+ }
58
+
DnCNN/model_convert/axmodel/dncnn_color_blind_416x416_sim.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:028bfaddf44376cac8fdcc458bb6cfa85b0345d0a26eb5cfd124ca613949fe7f
3
+ size 818237
DnCNN/pic/3096.png ADDED

Git LFS Details

  • SHA256: 8fd9f62bad0f64a35563689bad9cc9ded51c4d9b6332952fe9f0d57303c6bdd5
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
DnCNN/python/axmodel_infer.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import cv2
4
+ import numpy as np
5
+ import axengine as axe
6
+
7
+ # ============================================================
8
+ # 默认参数(可按需修改)
9
+ # ============================================================
10
+ AXMODEL_PATH = "dncnn_color_blind_416x416_sim.axmodel"
11
+ OUTPUT_DIR = "./"
12
+ # AX 量化模型输入格式: "float32" 或 "uint8",取决于编译时的量化配置
13
+ INPUT_DTYPE = "uint8" # 量化模型通常为 uint8;float 模型设为 "float32"
14
+ NOISE_SIGMA = 25 # 对原图施加的 AWGN 噪声强度(uint8 尺度,设为 0 则不加噪)
15
+
16
+ # ============================================================
17
+
18
+
19
+ def load_image(img_path):
20
+ """读取图像,返回 BGR uint8 原始图"""
21
+ img_bgr = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
22
+ if img_bgr is None:
23
+ raise FileNotFoundError(f"无法读取图像: {img_path}")
24
+ if img_bgr.ndim == 2:
25
+ img_bgr = cv2.cvtColor(img_bgr, cv2.COLOR_GRAY2BGR)
26
+ return img_bgr
27
+
28
+
29
+ def add_awgn(img_bgr, sigma):
30
+ """对 BGR uint8 图像施加 AWGN,返回加噪后的 BGR uint8 图像"""
31
+ noise = np.random.randn(*img_bgr.shape).astype(np.float32) * sigma
32
+ noisy = img_bgr.astype(np.float32) + noise
33
+ noisy = np.clip(noisy, 0, 255).astype(np.uint8)
34
+ return noisy
35
+
36
+
37
+ def preprocess(img_bgr, model_h, model_w):
38
+ """
39
+ 预处理: BGR->RGB, resize to model size, 按 INPUT_DTYPE 做归一化或保持 uint8
40
+ 返回: 模型输入张量, 原始尺寸 (h, w)
41
+ """
42
+ orig_h, orig_w = img_bgr.shape[:2]
43
+ img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
44
+ img_resized = cv2.resize(img_rgb, (model_w, model_h), interpolation=cv2.INTER_LINEAR)
45
+
46
+ if INPUT_DTYPE == "float32":
47
+ img_float = img_resized.astype(np.float32) / 255.0
48
+ tensor = np.transpose(img_float, (2, 0, 1))[np.newaxis, ...].astype(np.float32)
49
+ else:
50
+ tensor = np.transpose(img_resized, (2, 0, 1))[np.newaxis, ...].astype(np.uint8)
51
+ return tensor, (orig_h, orig_w)
52
+
53
+
54
+ def postprocess(tensor, orig_h, orig_w):
55
+ """
56
+ 后处理: squeeze batch, CHW->HWC, clip, uint8, resize 回原始尺寸
57
+ 返回: BGR uint8 图像
58
+ """
59
+ if tensor.dtype == np.uint8:
60
+ arr = np.squeeze(tensor, axis=0)
61
+ arr = np.transpose(arr, (1, 2, 0))
62
+ arr_uint = arr
63
+ else:
64
+ arr = np.clip(np.squeeze(tensor, axis=0), 0.0, 1.0)
65
+ arr = np.transpose(arr, (1, 2, 0))
66
+ arr_uint = (arr * 255.0).round().astype(np.uint8)
67
+ arr_uint = cv2.resize(arr_uint, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
68
+ result_bgr = cv2.cvtColor(arr_uint, cv2.COLOR_RGB2BGR)
69
+ return result_bgr
70
+
71
+
72
+ def make_concat(original_bgr, noisy_bgr, result_bgr,
73
+ label_left="Original", label_mid="Noisy", label_right="DnCNN AXModel"):
74
+ """左中右拼接原图、加噪图与结果图,并添加顶部标注"""
75
+ imgs = [original_bgr, noisy_bgr, result_bgr]
76
+ labels = [label_left, label_mid, label_right]
77
+ hs = [im.shape[0] for im in imgs]
78
+ h = max(hs)
79
+
80
+ resized = []
81
+ for im in imgs:
82
+ hh, ww = im.shape[:2]
83
+ if hh != h:
84
+ im = cv2.resize(im, (int(ww * h / hh), h), interpolation=cv2.INTER_LINEAR)
85
+ resized.append(im)
86
+
87
+ ws = [im.shape[1] for im in resized]
88
+ total_w = sum(ws)
89
+ label_h = max(30, h // 25)
90
+ canvas = np.full((h + label_h, total_w, 3), 255, dtype=np.uint8)
91
+
92
+ x = 0
93
+ for idx, im in enumerate(resized):
94
+ canvas[label_h:, x:x + ws[idx]] = im
95
+ x += ws[idx]
96
+
97
+ font = cv2.FONT_HERSHEY_SIMPLEX
98
+ font_scale = label_h / 30.0
99
+ thickness = max(1, int(font_scale))
100
+ color = (0, 0, 0)
101
+
102
+ x = 0
103
+ for idx, (label, w) in enumerate(zip(labels, ws)):
104
+ (tw, th), _ = cv2.getTextSize(label, font, font_scale, thickness)
105
+ cv2.putText(canvas, label, (x + w // 2 - tw // 2, label_h - (label_h - th) // 2),
106
+ font, font_scale, color, thickness, cv2.LINE_AA)
107
+ x += w
108
+
109
+ return canvas
110
+
111
+
112
+ def main():
113
+ img_path = './3096.png'
114
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
115
+
116
+ # ---- 加载 AX 模型 ----
117
+ session = axe.InferenceSession(AXMODEL_PATH, providers=["AxEngineExecutionProvider"])
118
+ input_name = session.get_inputs()[0].name
119
+ _, _, model_h, model_w = session.get_inputs()[0].shape
120
+
121
+ # ---- 读取原图 ----
122
+ img_bgr = load_image(img_path)
123
+
124
+ # ---- 加噪 ----
125
+ if NOISE_SIGMA > 0:
126
+ noisy_bgr = add_awgn(img_bgr, NOISE_SIGMA)
127
+ else:
128
+ noisy_bgr = img_bgr
129
+
130
+ # ---- 预处理(对加噪图) ----
131
+ tensor, (orig_h, orig_w) = preprocess(noisy_bgr, model_h, model_w)
132
+
133
+ # ---- AX 推理 ----
134
+ result = session.run(None, {input_name: tensor})[0]
135
+
136
+ # ---- 后处理 & 还原原始尺寸 ----
137
+ result_bgr = postprocess(result, orig_h, orig_w)
138
+
139
+ # ---- 三图拼接输出 ----
140
+ label_mid = f"Noisy (sigma={NOISE_SIGMA})" if NOISE_SIGMA > 0 else "Input"
141
+ concat = make_concat(img_bgr, noisy_bgr, result_bgr, label_mid=label_mid)
142
+
143
+ name = os.path.splitext(os.path.basename(img_path))[0]
144
+ out_path = os.path.join(OUTPUT_DIR, "axmodel_res.png")
145
+ cv2.imwrite(out_path, concat)
146
+ print(f"输出已保存: {out_path}")
147
+
148
+
149
+ if __name__ == "__main__":
150
+ main()
DnCNN/python/onnx_infer.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ DnCNN ONNX 单图推理脚本
4
+ - 读取原图 → 加 AWGN 噪声 → 预处理 → ONNX 推理 → 后处理 → 还原原图大小
5
+ - 输出: 原图 + 加噪图 + 结果图 的三图左右拼接(带标注)
6
+ - 无 torch 依赖,仅需 cv2 / numpy / onnxruntime
7
+ - 所有参数已写为 default 常量,可直接运行
8
+ """
9
+
10
+ import os
11
+ import sys
12
+
13
+ import cv2
14
+ import numpy as np
15
+ import onnxruntime as ort
16
+
17
+ # ============================================================
18
+ # 默认参数(可按需修改)
19
+ # ============================================================
20
+ ONNX_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "model_zoo", "dncnn_color_blind_640x640_sim.onnx")
21
+ OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "results", "dncnn_onnx_infer")
22
+ NOISE_SIGMA = 25 # 对原图施加的 AWGN 噪声强度(uint8 尺度,设为 0 则不加噪)
23
+ PROVIDERS = ["CPUExecutionProvider"] # ONNX Runtime 执行后端
24
+
25
+ # ============================================================
26
+
27
+
28
+ def load_image(img_path):
29
+ """读取图像,返回 BGR uint8 原始图"""
30
+ img_bgr = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
31
+ if img_bgr is None:
32
+ raise FileNotFoundError(f"无法读取图像: {img_path}")
33
+ if img_bgr.ndim == 2:
34
+ img_bgr = cv2.cvtColor(img_bgr, cv2.COLOR_GRAY2BGR)
35
+ return img_bgr
36
+
37
+
38
+ def add_awgn(img_bgr, sigma):
39
+ """对 BGR uint8 图像施加 AWGN,返回加噪后的 BGR uint8 图像"""
40
+ noise = np.random.randn(*img_bgr.shape).astype(np.float32) * sigma
41
+ noisy = img_bgr.astype(np.float32) + noise
42
+ noisy = np.clip(noisy, 0, 255).astype(np.uint8)
43
+ return noisy
44
+
45
+
46
+ def preprocess(img_bgr, model_h, model_w):
47
+ """
48
+ 预处理: BGR->RGB, resize to model size, normalize [0,1], HWC->CHW, add batch
49
+ 返回: (1, 3, H, W) float32 张量, 原始尺寸 (h, w)
50
+ """
51
+ orig_h, orig_w = img_bgr.shape[:2]
52
+ img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
53
+ img_resized = cv2.resize(img_rgb, (model_w, model_h), interpolation=cv2.INTER_LINEAR)
54
+ img_float = img_resized.astype(np.float32) / 255.0
55
+ tensor = np.transpose(img_float, (2, 0, 1))[np.newaxis, ...].astype(np.float32)
56
+ return tensor, (orig_h, orig_w)
57
+
58
+
59
+ def postprocess(tensor, orig_h, orig_w):
60
+ """
61
+ 后处理: squeeze batch, CHW->HWC, clip [0,1], uint8, resize 回原始尺寸
62
+ 返回: BGR uint8 图像
63
+ """
64
+ arr = np.clip(np.squeeze(tensor, axis=0), 0.0, 1.0) # (3, H, W)
65
+ arr = np.transpose(arr, (1, 2, 0)) # (H, W, 3)
66
+ arr_uint = (arr * 255.0).round().astype(np.uint8)
67
+ arr_uint = cv2.resize(arr_uint, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
68
+ result_bgr = cv2.cvtColor(arr_uint, cv2.COLOR_RGB2BGR)
69
+ return result_bgr
70
+
71
+
72
+ def make_concat(original_bgr, noisy_bgr, result_bgr,
73
+ label_left="Original", label_mid="Noisy", label_right="DnCNN ONNX"):
74
+ """左中右拼接原图、加噪图与结果图,并添加顶部标注"""
75
+ imgs = [original_bgr, noisy_bgr, result_bgr]
76
+ labels = [label_left, label_mid, label_right]
77
+ hs = [im.shape[0] for im in imgs]
78
+ h = max(hs)
79
+
80
+ resized = []
81
+ for im in imgs:
82
+ hh, ww = im.shape[:2]
83
+ if hh != h:
84
+ im = cv2.resize(im, (int(ww * h / hh), h), interpolation=cv2.INTER_LINEAR)
85
+ resized.append(im)
86
+
87
+ ws = [im.shape[1] for im in resized]
88
+ total_w = sum(ws)
89
+ label_h = max(30, h // 25)
90
+ canvas = np.full((h + label_h, total_w, 3), 255, dtype=np.uint8)
91
+
92
+ x = 0
93
+ for idx, im in enumerate(resized):
94
+ canvas[label_h:, x:x + ws[idx]] = im
95
+ x += ws[idx]
96
+
97
+ font = cv2.FONT_HERSHEY_SIMPLEX
98
+ font_scale = label_h / 30.0
99
+ thickness = max(1, int(font_scale))
100
+ color = (0, 0, 0)
101
+
102
+ x = 0
103
+ for idx, (label, w) in enumerate(zip(labels, ws)):
104
+ (tw, th), _ = cv2.getTextSize(label, font, font_scale, thickness)
105
+ cv2.putText(canvas, label, (x + w // 2 - tw // 2, label_h - (label_h - th) // 2),
106
+ font, font_scale, color, thickness, cv2.LINE_AA)
107
+ x += w
108
+
109
+ return canvas
110
+
111
+
112
+ def main():
113
+ if len(sys.argv) < 2:
114
+ print(f"用法: python {os.path.basename(__file__)} <图像路径>")
115
+ print(f"默认 ONNX 模型: {ONNX_PATH}")
116
+ print(f"噪声 sigma: {NOISE_SIGMA}")
117
+ print(f"输出目录: {OUTPUT_DIR}")
118
+ sys.exit(1)
119
+
120
+ img_path = sys.argv[1]
121
+ if not os.path.isfile(img_path):
122
+ print(f"错误: 图像不存在: {img_path}")
123
+ sys.exit(1)
124
+ if not os.path.isfile(ONNX_PATH):
125
+ print(f"错误: ONNX 模型不存在: {ONNX_PATH}")
126
+ print(f"请修改脚本顶部 ONNX_PATH 常量,或先导出 ONNX 模型。")
127
+ sys.exit(1)
128
+
129
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
130
+
131
+ # ---- 加载 ONNX 模型 ----
132
+ session = ort.InferenceSession(ONNX_PATH, providers=PROVIDERS)
133
+ input_name = session.get_inputs()[0].name
134
+ _, _, model_h, model_w = session.get_inputs()[0].shape
135
+
136
+ # ---- 读取原图 ----
137
+ img_bgr = load_image(img_path)
138
+
139
+ # ---- 加噪 ----
140
+ if NOISE_SIGMA > 0:
141
+ noisy_bgr = add_awgn(img_bgr, NOISE_SIGMA)
142
+ else:
143
+ noisy_bgr = img_bgr
144
+
145
+ # ---- 预处理(对加噪图) ----
146
+ tensor, (orig_h, orig_w) = preprocess(noisy_bgr, model_h, model_w)
147
+
148
+ # ---- ONNX 推理 ----
149
+ result = session.run(None, {input_name: tensor})[0]
150
+
151
+ # ---- 后处理 & 还原原始尺寸 ----
152
+ result_bgr = postprocess(result, orig_h, orig_w)
153
+
154
+ # ---- 三图拼接输出 ----
155
+ label_mid = f"Noisy (sigma={NOISE_SIGMA})" if NOISE_SIGMA > 0 else "Input"
156
+ concat = make_concat(img_bgr, noisy_bgr, result_bgr, label_mid=label_mid)
157
+
158
+ name = os.path.splitext(os.path.basename(img_path))[0]
159
+ out_path = os.path.join(OUTPUT_DIR, f"{name}_concat.png")
160
+ cv2.imwrite(out_path, concat)
161
+ print(f"输出已保存: {out_path}")
162
+
163
+
164
+ if __name__ == "__main__":
165
+ main()
DnCNN/res/axmodel_res.png ADDED

Git LFS Details

  • SHA256: 38fe79a738cd0262d6478a24a806d2bd25f9ba247b3193f9f7c2013cda7f6da0
  • Pointer size: 131 Bytes
  • Size of remote file: 946 kB
FFDNet/model_convert/FFDNet.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "input": "./ffdnet_color_fixed_sigma10_416x416_sim.onnx",
3
+ "output_dir": "./FFDNet",
4
+ "output_name": "ffdnet_color_fixed_sigma10_416x416_sim.axmodel",
5
+ "model_type": "ONNX",
6
+ "target_hardware": "AX650",
7
+ "npu_mode": "NPU3",
8
+ "quant": {
9
+ "input_configs": [
10
+ {
11
+ "tensor_name": "input",
12
+ "calibration_dataset": "./BSD.tar.gz",
13
+ "calibration_size": 32,
14
+ "calibration_mean": [0.0, 0.0, 0.0],
15
+ "calibration_std": [255.0, 255.0, 255.0]
16
+ }
17
+ ],
18
+ "calibration_method": "MinMax",
19
+ "precision_analysis": true,
20
+ "transformer_opt_level":1,
21
+ // "enable_smooth_quant": true,
22
+ "precision_analysis_method": "EndToEnd",
23
+ "precision_analysis_mode": "NPUBackend",
24
+ "device": "cuda:1",
25
+ // "layer_configs": [
26
+ // {
27
+ // "start_tensor_names": ["DEFAULT"],
28
+ // "end_tensor_names": ["DEFAULT"],
29
+ // "data_type": "U16"
30
+ // }
31
+ // ]
32
+ },
33
+ "input_processors": [
34
+ {
35
+ "tensor_name": "input",
36
+ "tensor_format": "RGB",
37
+ "tensor_layout": "NCHW",
38
+ "src_format": "BGR",
39
+ "src_dtype": "U8",
40
+ "src_layout": "NCHW",
41
+ "csc_mode": "NoCSC"
42
+ }
43
+ // {
44
+ // "tensor_name": "input",
45
+ // "tensor_format": "RGB",
46
+ // "tensor_layout": "NCHW",
47
+ // "src_format": "YUV420SP",
48
+ // "src_layout": "NHWC",
49
+ // "src_dtype": "U8",
50
+ // "csc_mode": "FullRange",
51
+ // "csc_mat": [1.164, 0, 1.596, -222.912, 1.164, -0.392, -0.813, 135.616, 1.164, 2.017, 0, -276.8]
52
+ // }
53
+ ],
54
+ "compiler": {
55
+ "check": 2
56
+ }
57
+ }
58
+
FFDNet/model_convert/axmodel/ffdnet_color_fixed_sigma10_640x640_sim.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe16b107c8a141e2d59e6090c4f4728b0238c7855156df2876181356a9c38778
3
+ size 1101373
FFDNet/pic/3096.png ADDED

Git LFS Details

  • SHA256: 8fd9f62bad0f64a35563689bad9cc9ded51c4d9b6332952fe9f0d57303c6bdd5
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
FFDNet/python/axmodel_infer.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import sys
4
+ import cv2
5
+ import numpy as np
6
+ import axengine as axe
7
+
8
+ # ============================================================
9
+ # 默认参数(可按需修改)
10
+ # ============================================================
11
+ AXMODEL_PATH = './ffdnet_color_fixed_sigma10_640x640_sim.axmodel'
12
+ OUTPUT_DIR = './'
13
+ # AX 量化模型输入格式: "float32" 或 "uint8",取决于编译时的量化配置
14
+ INPUT_DTYPE = "uint8" # 量化模型通常为 uint8;float 模型设为 "float32"
15
+ USE_FIXED_SIGMA = True # True: 单输入模型;False: 双输入模型(input + sigma)
16
+ MODEL_SIGMA = 10 # 模型 sigma(uint8 尺度,仅 USE_FIXED_SIGMA=False 时生效)
17
+ NOISE_SIGMA = 10 # 对原图施加的 AWGN 噪声强度(uint8 尺度,设为 0 则不加噪)
18
+
19
+ # ============================================================
20
+
21
+
22
+ def load_image(img_path):
23
+ """读取图像,返回 BGR uint8 原始图"""
24
+ img_bgr = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
25
+ if img_bgr is None:
26
+ raise FileNotFoundError(f"无法读取图像: {img_path}")
27
+ if img_bgr.ndim == 2:
28
+ img_bgr = cv2.cvtColor(img_bgr, cv2.COLOR_GRAY2BGR)
29
+ return img_bgr
30
+
31
+
32
+ def add_awgn(img_bgr, sigma):
33
+ """对 BGR uint8 图像施加 AWGN,返回加噪后的 BGR uint8 图像"""
34
+ noise = np.random.randn(*img_bgr.shape).astype(np.float32) * sigma
35
+ noisy = img_bgr.astype(np.float32) + noise
36
+ noisy = np.clip(noisy, 0, 255).astype(np.uint8)
37
+ return noisy
38
+
39
+
40
+ def preprocess(img_bgr, model_h, model_w):
41
+ """
42
+ 预处理: BGR->RGB, resize to model size, 按 INPUT_DTYPE 做归一化或保持 uint8
43
+ 返回: 模型输入张量, 原始尺寸 (h, w)
44
+ """
45
+ orig_h, orig_w = img_bgr.shape[:2]
46
+ img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
47
+ img_resized = cv2.resize(img_rgb, (model_w, model_h), interpolation=cv2.INTER_LINEAR)
48
+
49
+ if INPUT_DTYPE == "float32":
50
+ img_float = img_resized.astype(np.float32) / 255.0
51
+ tensor = np.transpose(img_float, (2, 0, 1))[np.newaxis, ...].astype(np.float32)
52
+ else:
53
+ tensor = np.transpose(img_resized, (2, 0, 1))[np.newaxis, ...].astype(np.uint8)
54
+ return tensor, (orig_h, orig_w)
55
+
56
+
57
+ def postprocess(tensor, orig_h, orig_w):
58
+ """
59
+ 后处理: squeeze batch, CHW->HWC, clip, uint8, resize 回原始尺寸
60
+ 返回: BGR uint8 图像
61
+ """
62
+ if tensor.dtype == np.uint8:
63
+ arr = np.squeeze(tensor, axis=0)
64
+ arr = np.transpose(arr, (1, 2, 0))
65
+ arr_uint = arr
66
+ else:
67
+ arr = np.clip(np.squeeze(tensor, axis=0), 0.0, 1.0)
68
+ arr = np.transpose(arr, (1, 2, 0))
69
+ arr_uint = (arr * 255.0).round().astype(np.uint8)
70
+ arr_uint = cv2.resize(arr_uint, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
71
+ result_bgr = cv2.cvtColor(arr_uint, cv2.COLOR_RGB2BGR)
72
+ return result_bgr
73
+
74
+
75
+ def make_concat(original_bgr, noisy_bgr, result_bgr,
76
+ label_left="Original", label_mid="Noisy", label_right="FFDNet AXModel"):
77
+ """左中右拼接原图、加噪图与结果图,并添加顶部标注"""
78
+ imgs = [original_bgr, noisy_bgr, result_bgr]
79
+ labels = [label_left, label_mid, label_right]
80
+ hs = [im.shape[0] for im in imgs]
81
+ h = max(hs)
82
+
83
+ resized = []
84
+ for im in imgs:
85
+ hh, ww = im.shape[:2]
86
+ if hh != h:
87
+ im = cv2.resize(im, (int(ww * h / hh), h), interpolation=cv2.INTER_LINEAR)
88
+ resized.append(im)
89
+
90
+ ws = [im.shape[1] for im in resized]
91
+ total_w = sum(ws)
92
+ label_h = max(30, h // 25)
93
+ canvas = np.full((h + label_h, total_w, 3), 255, dtype=np.uint8)
94
+
95
+ x = 0
96
+ for idx, im in enumerate(resized):
97
+ canvas[label_h:, x:x + ws[idx]] = im
98
+ x += ws[idx]
99
+
100
+ font = cv2.FONT_HERSHEY_SIMPLEX
101
+ font_scale = label_h / 30.0
102
+ thickness = max(1, int(font_scale))
103
+ color = (0, 0, 0)
104
+
105
+ x = 0
106
+ for idx, (label, w) in enumerate(zip(labels, ws)):
107
+ (tw, th), _ = cv2.getTextSize(label, font, font_scale, thickness)
108
+ cv2.putText(canvas, label, (x + w // 2 - tw // 2, label_h - (label_h - th) // 2),
109
+ font, font_scale, color, thickness, cv2.LINE_AA)
110
+ x += w
111
+
112
+ return canvas
113
+
114
+
115
+ def main():
116
+ img_path = './3096.png' # 输入图像路径,可按需修改
117
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
118
+
119
+ # ---- 加载 AX 模型 ----
120
+ session = axe.InferenceSession(AXMODEL_PATH, providers=["AxEngineExecutionProvider"])
121
+ input_name = session.get_inputs()[0].name
122
+ _, _, model_h, model_w = session.get_inputs()[0].shape
123
+
124
+ # ---- 读取原图 ----
125
+ img_bgr = load_image(img_path)
126
+
127
+ # ---- 加噪 ----
128
+ if NOISE_SIGMA > 0:
129
+ noisy_bgr = add_awgn(img_bgr, NOISE_SIGMA)
130
+ else:
131
+ noisy_bgr = img_bgr
132
+
133
+ # ---- 预处理(对加噪图) ----
134
+ tensor, (orig_h, orig_w) = preprocess(noisy_bgr, model_h, model_w)
135
+
136
+ # ---- AX 推理 ----
137
+ if USE_FIXED_SIGMA:
138
+ result = session.run(None, {input_name: tensor})[0]
139
+ else:
140
+ sigma_dtype = np.float32 if INPUT_DTYPE == "float32" else np.uint8
141
+ sigma_tensor = np.full((1, 1, 1, 1), MODEL_SIGMA / 255.0 if INPUT_DTYPE == "float32" else MODEL_SIGMA, dtype=sigma_dtype)
142
+ sigma_name = session.get_inputs()[1].name
143
+ result = session.run(None, {input_name: tensor, sigma_name: sigma_tensor})[0]
144
+
145
+ # ---- 后处理 & 还原原始尺寸 ----
146
+ result_bgr = postprocess(result, orig_h, orig_w)
147
+
148
+ # ---- 三图拼接输出 ----
149
+ label_right = f"FFDNet AXModel (sigma={MODEL_SIGMA})" if not USE_FIXED_SIGMA else "FFDNet AXModel"
150
+ label_mid = f"Noisy (sigma={NOISE_SIGMA})" if NOISE_SIGMA > 0 else "Input"
151
+ concat = make_concat(img_bgr, noisy_bgr, result_bgr,
152
+ label_mid=label_mid, label_right=label_right)
153
+
154
+ name = os.path.splitext(os.path.basename(img_path))[0]
155
+ out_path = os.path.join(OUTPUT_DIR, "axmodel_res.png")
156
+ cv2.imwrite(out_path, concat)
157
+ print(f"输出已保存: {out_path}")
158
+
159
+
160
+ if __name__ == "__main__":
161
+ main()
FFDNet/python/onnx_infer.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ FFDNet ONNX 单图推理脚本
4
+ - 读取原图 → 加 AWGN 噪声 → 预处理 → ONNX 推理 → 后处理 → 还原原图大小
5
+ - 输出: 原图 + 加噪图 + 结果图 的三图左右拼接(带标注)
6
+ - 无 torch 依赖,仅需 cv2 / numpy / onnxruntime
7
+ - 所有参数已写为 default 常量,可直接运行
8
+ """
9
+
10
+ import os
11
+ import sys
12
+
13
+ import cv2
14
+ import numpy as np
15
+ import onnxruntime as ort
16
+
17
+ # ============================================================
18
+ # 默认参数(可按需修改)
19
+ # ============================================================
20
+ ONNX_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "model_zoo", "ffdnet_color_fixed_sigma10_640x640_sim.onnx")
21
+ OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "results", "ffdnet_onnx_infer")
22
+ # 若使用非固定 sigma 的模型,设置 USE_FIXED_SIGMA = False 并填写 MODEL_SIGMA
23
+ USE_FIXED_SIGMA = True # True: 单输入 fixed-sigma 模型
24
+ MODEL_SIGMA = 10 # 模型 sigma(uint8 尺度,仅 USE_FIXED_SIGMA=False 时生效)
25
+ NOISE_SIGMA = 10 # 对原图施加的 AWGN 噪声强度(uint8 尺度,设为 0 则不加噪)
26
+ PROVIDERS = ["CPUExecutionProvider"] # ONNX Runtime 执行后端
27
+
28
+ # ============================================================
29
+
30
+
31
+ def load_image(img_path):
32
+ """读取图像,返回 BGR uint8 原始图"""
33
+ img_bgr = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
34
+ if img_bgr is None:
35
+ raise FileNotFoundError(f"无法读取图像: {img_path}")
36
+ if img_bgr.ndim == 2:
37
+ img_bgr = cv2.cvtColor(img_bgr, cv2.COLOR_GRAY2BGR)
38
+ return img_bgr
39
+
40
+
41
+ def add_awgn(img_bgr, sigma):
42
+ """对 BGR uint8 图像施加 AWGN,返回加噪后的 BGR uint8 图像"""
43
+ noise = np.random.randn(*img_bgr.shape).astype(np.float32) * sigma
44
+ noisy = img_bgr.astype(np.float32) + noise
45
+ noisy = np.clip(noisy, 0, 255).astype(np.uint8)
46
+ return noisy
47
+
48
+
49
+ def preprocess(img_bgr, model_h, model_w):
50
+ """
51
+ 预处理: BGR->RGB, resize to model size, normalize [0,1], HWC->CHW, add batch
52
+ 返回: (1, 3, H, W) float32 张量, 原始尺寸 (h, w)
53
+ """
54
+ orig_h, orig_w = img_bgr.shape[:2]
55
+ img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
56
+ img_resized = cv2.resize(img_rgb, (model_w, model_h), interpolation=cv2.INTER_LINEAR)
57
+ img_float = img_resized.astype(np.float32) / 255.0
58
+ tensor = np.transpose(img_float, (2, 0, 1))[np.newaxis, ...].astype(np.float32)
59
+ return tensor, (orig_h, orig_w)
60
+
61
+
62
+ def postprocess(tensor, orig_h, orig_w):
63
+ """
64
+ 后处理: squeeze batch, CHW->HWC, clip [0,1], uint8, resize 回原始尺寸
65
+ 返回: BGR uint8 图像
66
+ """
67
+ arr = np.clip(np.squeeze(tensor, axis=0), 0.0, 1.0) # (3, H, W)
68
+ arr = np.transpose(arr, (1, 2, 0)) # (H, W, 3)
69
+ arr_uint = (arr * 255.0).round().astype(np.uint8)
70
+ arr_uint = cv2.resize(arr_uint, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR)
71
+ result_bgr = cv2.cvtColor(arr_uint, cv2.COLOR_RGB2BGR)
72
+ return result_bgr
73
+
74
+
75
+ def make_concat(original_bgr, noisy_bgr, result_bgr,
76
+ label_left="Original", label_mid="Noisy", label_right="FFDNet ONNX"):
77
+ """左中右拼接原图、加噪图与结果图,并添加顶部标注"""
78
+ imgs = [original_bgr, noisy_bgr, result_bgr]
79
+ labels = [label_left, label_mid, label_right]
80
+ hs = [im.shape[0] for im in imgs]
81
+ h = max(hs)
82
+
83
+ # 统一高度
84
+ resized = []
85
+ for im in imgs:
86
+ hh, ww = im.shape[:2]
87
+ if hh != h:
88
+ im = cv2.resize(im, (int(ww * h / hh), h), interpolation=cv2.INTER_LINEAR)
89
+ resized.append(im)
90
+
91
+ ws = [im.shape[1] for im in resized]
92
+ total_w = sum(ws)
93
+ label_h = max(30, h // 25)
94
+ canvas = np.full((h + label_h, total_w, 3), 255, dtype=np.uint8)
95
+
96
+ x = 0
97
+ for idx, im in enumerate(resized):
98
+ canvas[label_h:, x:x + ws[idx]] = im
99
+ x += ws[idx]
100
+
101
+ # 标注文字
102
+ font = cv2.FONT_HERSHEY_SIMPLEX
103
+ font_scale = label_h / 30.0
104
+ thickness = max(1, int(font_scale))
105
+ color = (0, 0, 0)
106
+
107
+ x = 0
108
+ for idx, (label, w) in enumerate(zip(labels, ws)):
109
+ (tw, th), _ = cv2.getTextSize(label, font, font_scale, thickness)
110
+ cv2.putText(canvas, label, (x + w // 2 - tw // 2, label_h - (label_h - th) // 2),
111
+ font, font_scale, color, thickness, cv2.LINE_AA)
112
+ x += w
113
+
114
+ return canvas
115
+
116
+
117
+ def main():
118
+ if len(sys.argv) < 2:
119
+ print(f"用法: python {os.path.basename(__file__)} <图像路径>")
120
+ print(f"默认 ONNX 模型: {ONNX_PATH}")
121
+ print(f"噪声 sigma: {NOISE_SIGMA}")
122
+ print(f"输出目录: {OUTPUT_DIR}")
123
+ sys.exit(1)
124
+
125
+ img_path = sys.argv[1]
126
+ if not os.path.isfile(img_path):
127
+ print(f"错误: 图像不存在: {img_path}")
128
+ sys.exit(1)
129
+ if not os.path.isfile(ONNX_PATH):
130
+ print(f"错误: ONNX 模型不存在: {ONNX_PATH}")
131
+ print(f"请修改脚本顶��� ONNX_PATH 常量,或先导出 ONNX 模型。")
132
+ sys.exit(1)
133
+
134
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
135
+
136
+ # ---- 加载 ONNX 模型 ----
137
+ session = ort.InferenceSession(ONNX_PATH, providers=PROVIDERS)
138
+ input_name = session.get_inputs()[0].name
139
+ _, _, model_h, model_w = session.get_inputs()[0].shape
140
+
141
+ # ---- 读取原图 ----
142
+ img_bgr = load_image(img_path)
143
+
144
+ # ---- 加噪 ----
145
+ if NOISE_SIGMA > 0:
146
+ noisy_bgr = add_awgn(img_bgr, NOISE_SIGMA)
147
+ else:
148
+ noisy_bgr = img_bgr
149
+
150
+ # ---- 预处理(对加噪图) ----
151
+ tensor, (orig_h, orig_w) = preprocess(noisy_bgr, model_h, model_w)
152
+
153
+ # ---- ONNX 推理 ----
154
+ if USE_FIXED_SIGMA:
155
+ result = session.run(None, {input_name: tensor})[0]
156
+ else:
157
+ sigma_tensor = np.full((1, 1, 1, 1), MODEL_SIGMA / 255.0, dtype=np.float32)
158
+ sigma_name = session.get_inputs()[1].name
159
+ result = session.run(None, {input_name: tensor, sigma_name: sigma_tensor})[0]
160
+
161
+ # ---- 后处理 & 还原原始尺寸 ----
162
+ result_bgr = postprocess(result, orig_h, orig_w)
163
+
164
+ # ---- 三图拼接输出 ----
165
+ label_right = f"FFDNet ONNX (sigma={MODEL_SIGMA})" if not USE_FIXED_SIGMA else "FFDNet ONNX"
166
+ label_mid = f"Noisy (sigma={NOISE_SIGMA})" if NOISE_SIGMA > 0 else "Input"
167
+ concat = make_concat(img_bgr, noisy_bgr, result_bgr,
168
+ label_mid=label_mid, label_right=label_right)
169
+
170
+ name = os.path.splitext(os.path.basename(img_path))[0]
171
+ out_path = os.path.join(OUTPUT_DIR, f"{name}_concat.png")
172
+ cv2.imwrite(out_path, concat)
173
+ print(f"输出已保存: {out_path}")
174
+
175
+
176
+ if __name__ == "__main__":
177
+ main()
FFDNet/res/axmodel_res.png ADDED

Git LFS Details

  • SHA256: ecd4fdbc3bff00f50c4fed04ae371e7c6002cf3dbee56be9551f58c08217d33d
  • Pointer size: 131 Bytes
  • Size of remote file: 749 kB
NAFNet/demo/noisy.png ADDED

Git LFS Details

  • SHA256: 403034182fa320130dae0d75b92e85e0850771378e674d65455c403a4958e29c
  • Pointer size: 131 Bytes
  • Size of remote file: 171 kB
NAFNet/model_convert/NAFNet.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "input": "./NAFNet-SIDD-width64-256x256.onnx",
3
+ "output_dir": "./NAFNet",
4
+ "output_name": "NAFNet_1_3_256_256.axmodel",
5
+ "model_type": "ONNX",
6
+ "target_hardware": "AX650",
7
+ "npu_mode": "NPU3",
8
+ "quant": {
9
+ "input_configs": [
10
+ {
11
+ "tensor_name": "input",
12
+ "calibration_dataset": "./SIDD.tar.gz",
13
+ "calibration_size": 32,
14
+ "calibration_mean": [0.0, 0.0, 0.0],
15
+ "calibration_std": [255.0, 255.0, 255.0]
16
+ }
17
+ ],
18
+ "calibration_method": "MinMax",
19
+ "precision_analysis": true,
20
+ "transformer_opt_level":1,
21
+ // "enable_smooth_quant": true,
22
+ "precision_analysis_method": "EndToEnd",
23
+ "precision_analysis_mode": "NPUBackend",
24
+ "device": "cuda:1",
25
+ // "layer_configs": [
26
+ // {
27
+ // "start_tensor_names": ["DEFAULT"],
28
+ // "end_tensor_names": ["DEFAULT"],
29
+ // "data_type": "U16"
30
+ // }
31
+ // ]
32
+ },
33
+ "input_processors": [
34
+ {
35
+ "tensor_name": "input",
36
+ "tensor_format": "RGB",
37
+ "tensor_layout": "NCHW",
38
+ "src_format": "BGR",
39
+ "src_dtype": "U8",
40
+ "src_layout": "NCHW",
41
+ "csc_mode": "NoCSC"
42
+ }
43
+ // {
44
+ // "tensor_name": "input",
45
+ // "tensor_format": "RGB",
46
+ // "tensor_layout": "NCHW",
47
+ // "src_format": "YUV420SP",
48
+ // "src_layout": "NHWC",
49
+ // "src_dtype": "U8",
50
+ // "csc_mode": "FullRange",
51
+ // "csc_mat": [1.164, 0, 1.596, -222.912, 1.164, -0.392, -0.813, 135.616, 1.164, 2.017, 0, -276.8]
52
+ // }
53
+ ],
54
+ "compiler": {
55
+ "check": 2
56
+ }
57
+ }
58
+
NAFNet/model_convert/axmodel/NAFNet_1_3_256_256.axmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:486ca86f7cf099d827ae9c468821287843cd804a1a649a804d5c7d3e71107ac8
3
+ size 134584082
NAFNet/python/axmodel_infer.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Single-image NAFNet axmodel inference with side-by-side comparison output."""
3
+
4
+ import os
5
+
6
+ import cv2
7
+ import numpy as np
8
+ import axengine as axe
9
+
10
+ # ============================================================
11
+ # 默认参数(按需修改)
12
+ # ============================================================
13
+ AXMODEL_PATH = 'NAFNet_1_3_256_256.axmodel'
14
+ INPUT_PATH = 'demo/noisy.png'
15
+ OUTPUT_PATH = 'axmodel_compare.png'
16
+
17
+
18
+ def read_image(path):
19
+ bgr = cv2.imread(path, cv2.IMREAD_COLOR)
20
+ if bgr is None:
21
+ raise FileNotFoundError(f'Cannot read image: {path}')
22
+ rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB).astype(np.uint8)
23
+ tensor = np.transpose(rgb, (2, 0, 1))[None, ...]
24
+ return tensor, bgr
25
+
26
+
27
+ def save_comparison(orig_bgr, output_tensor, path):
28
+ """拼接原图与结果图并标注文字,输出图还原到原图大小后再拼接"""
29
+ output = np.squeeze(output_tensor, axis=0)
30
+ output = np.clip(output, 0.0, 1.0)
31
+ output_rgb = np.transpose(output, (1, 2, 0))
32
+ output_bgr = cv2.cvtColor((output_rgb * 255.0).round().astype(np.uint8), cv2.COLOR_RGB2BGR)
33
+
34
+ h, w = orig_bgr.shape[:2]
35
+ if output_bgr.shape[:2] != (h, w):
36
+ output_bgr = cv2.resize(output_bgr, (w, h), interpolation=cv2.INTER_LINEAR)
37
+
38
+ font = cv2.FONT_HERSHEY_SIMPLEX
39
+ font_scale = max(h, w) / 512.0
40
+ thickness = max(1, int(font_scale * 2))
41
+ color = (255, 255, 255)
42
+
43
+ cv2.putText(orig_bgr, 'noisy', (int(10 * font_scale), int(30 * font_scale)),
44
+ font, font_scale, color, thickness, cv2.LINE_AA)
45
+ cv2.putText(output_bgr, 'denoised', (int(10 * font_scale), int(30 * font_scale)),
46
+ font, font_scale, color, thickness, cv2.LINE_AA)
47
+
48
+ compare = np.concatenate([orig_bgr, output_bgr], axis=1)
49
+ os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
50
+ cv2.imwrite(path, compare)
51
+
52
+
53
+ def main():
54
+ inp, orig_bgr = read_image(INPUT_PATH)
55
+
56
+ session = axe.InferenceSession(AXMODEL_PATH, providers=['AxEngineExecutionProvider'])
57
+ input_name = session.get_inputs()[0].name
58
+ output_name = session.get_outputs()[0].name
59
+ expected_shape = session.get_inputs()[0].shape
60
+ if list(inp.shape) != expected_shape:
61
+ raise ValueError(f'Input shape {list(inp.shape)} does not match fixed axmodel shape {expected_shape}.')
62
+
63
+ out = session.run([output_name], {input_name: inp})[0]
64
+ save_comparison(orig_bgr, out, OUTPUT_PATH)
65
+ print(f'axmodel inference finished: {OUTPUT_PATH}')
66
+
67
+
68
+ if __name__ == '__main__':
69
+ main()
NAFNet/python/onnx_infer.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Single-image NAFNet ONNX inference with side-by-side comparison output."""
3
+
4
+ import os
5
+
6
+ import cv2
7
+ import numpy as np
8
+ import onnxruntime as ort
9
+
10
+ # ============================================================
11
+ # 默认参数(按需修改)
12
+ # ============================================================
13
+ ONNX_PATH = 'experiments/onnx/NAFNet-SIDD-width64-256x256.onnx'
14
+ INPUT_PATH = 'demo/noisy.png'
15
+ OUTPUT_PATH = 'demo/denoise_onnx_compare.png'
16
+
17
+
18
+ def read_image(path):
19
+ bgr = cv2.imread(path, cv2.IMREAD_COLOR)
20
+ if bgr is None:
21
+ raise FileNotFoundError(f'Cannot read image: {path}')
22
+ rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
23
+ tensor = np.transpose(rgb, (2, 0, 1))[None, ...]
24
+ return tensor, bgr
25
+
26
+
27
+ def save_comparison(orig_bgr, output_tensor, path):
28
+ """拼接原图与结果图并标注文字,输出图还原到原图大小后再拼接"""
29
+ output = np.squeeze(output_tensor, axis=0)
30
+ output = np.clip(output, 0.0, 1.0)
31
+ output_rgb = np.transpose(output, (1, 2, 0))
32
+ output_bgr = cv2.cvtColor((output_rgb * 255.0).round().astype(np.uint8), cv2.COLOR_RGB2BGR)
33
+
34
+ h, w = orig_bgr.shape[:2]
35
+ if output_bgr.shape[:2] != (h, w):
36
+ output_bgr = cv2.resize(output_bgr, (w, h), interpolation=cv2.INTER_LINEAR)
37
+
38
+ font = cv2.FONT_HERSHEY_SIMPLEX
39
+ font_scale = max(h, w) / 512.0
40
+ thickness = max(1, int(font_scale * 2))
41
+ color = (255, 255, 255)
42
+
43
+ cv2.putText(orig_bgr, 'input', (int(10 * font_scale), int(30 * font_scale)),
44
+ font, font_scale, color, thickness, cv2.LINE_AA)
45
+ cv2.putText(output_bgr, 'onnx', (int(10 * font_scale), int(30 * font_scale)),
46
+ font, font_scale, color, thickness, cv2.LINE_AA)
47
+
48
+ compare = np.concatenate([orig_bgr, output_bgr], axis=1)
49
+ os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
50
+ cv2.imwrite(path, compare)
51
+
52
+
53
+ def main():
54
+ inp, orig_bgr = read_image(INPUT_PATH)
55
+
56
+ providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if ort.get_device() == 'GPU' else ['CPUExecutionProvider']
57
+ session = ort.InferenceSession(ONNX_PATH, providers=providers)
58
+ input_name = session.get_inputs()[0].name
59
+ output_name = session.get_outputs()[0].name
60
+ expected_shape = session.get_inputs()[0].shape
61
+ if list(inp.shape) != expected_shape:
62
+ raise ValueError(f'Input shape {list(inp.shape)} does not match fixed ONNX shape {expected_shape}.')
63
+
64
+ out = session.run([output_name], {input_name: inp.astype(np.float32)})[0]
65
+ save_comparison(orig_bgr, out, OUTPUT_PATH)
66
+ print(f'ONNX inference finished: {OUTPUT_PATH}')
67
+
68
+
69
+ if __name__ == '__main__':
70
+ main()
NAFNet/res/axmodel_compare.png ADDED

Git LFS Details

  • SHA256: 62d981cd01499cf6314f7ffe4d37c761b722152b8a2e83cec4ee7f603192bb2a
  • Pointer size: 131 Bytes
  • Size of remote file: 261 kB
README.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ pipeline_tag: image-to-image
6
+ tags:
7
+ - denosing
8
+ - ImageEnhancement
9
+ ---
10
+
11
+ # ImageDenosing
12
+
13
+ This is a collection of image denosing algorithms, models have been converted to run on the Axera NPU using **w8a8** quantization.
14
+
15
+ This model has been optimized with the following LoRA:
16
+
17
+ Compatible with Pulsar2 version: 6.0 115775d3
18
+
19
+ ## Convert tools links:
20
+
21
+ For those who are interested in model conversion, you can try to export axmodel through
22
+
23
+ - [Pulsar2 Link, How to Convert ONNX to axmodel](https://pulsar2-docs.readthedocs.io/en/latest/pulsar2/introduction.html)
24
+
25
+
26
+ ## Support Platform
27
+
28
+ - AX650
29
+ - [M4N-Dock(爱芯派Pro)](https://wiki.sipeed.com/hardware/zh/maixIV/m4ndock/m4ndock.html)
30
+ - [M.2 Accelerator card](https://docs.m5stack.com/en/ai_hardware/LLM-8850_Card)
31
+
32
+
33
+ ## 性能基准测试 (Performance Benchmark)
34
+
35
+ | 模型 | 输入分辨率 | AX650板端耗时 |
36
+ |------|-----------|--------------|
37
+ | NAFNet | 256×256 | 87ms |
38
+ | DnCNN | 416×416 | 22ms |
39
+ | FFDNet | 640×640 | 19.3ms |
40
+ | FastDVDnet | 640×480 | 61.2ms |
41
+
42
+ ## How to use
43
+
44
+ Download all files from this repository to the device
45
+
46
+ ```
47
+ 模型文件组织方式如下:
48
+ .
49
+ |-- demo
50
+ | `-- noisy.png
51
+ |-- model_convert
52
+ | |-- NAFNet.json
53
+ | `-- axmodel
54
+ | `-- NAFNet_1_3_256_256.axmodel
55
+ |-- python
56
+ | |-- axmodel_infer.py
57
+ | `-- onnx_infer.py
58
+ `-- res
59
+ `-- axmodel_compare.png
60
+
61
+
62
+ ```
63
+
64
+ ### Inference
65
+ #### Image Denoising
66
+
67
+ 图片推理,执行命令`python3 axmodel_infer.py`:
68
+ ```
69
+ (base) root@ax650:~/NAFNet# python3 axmodel_infer.py
70
+ [INFO] Available providers: ['AxEngineExecutionProvider', 'AXCLRTExecutionProvider']
71
+ [INFO] Using provider: AxEngineExecutionProvider
72
+ [INFO] Chip type: ChipType.MC50
73
+ [INFO] VNPU type: VNPUType.DISABLED
74
+ [INFO] Engine version: 2.12.0s
75
+ [INFO] Model type: 2 (triple core)
76
+ [INFO] Compiler version: 6.0 115775d3
77
+ axmodel inference finished: axmodel_compare.png
78
+
79
+ ```
80
+
81
+ 推理结果样例:
82
+ ![alt text](NAFNet/res/axmodel_compare.png)
83
+
84
+ #### Video Denoising
85
+
86
+ 视频推理,执行命令`python3 axmodel_video_infer.py`:
87
+ ```
88
+ (base) root@ax650:~/fastdvdnet# python3 axmodel_video_infer.py
89
+ [INFO] Available providers: ['AxEngineExecutionProvider', 'AXCLRTExecutionProvider']
90
+ [INFO] Using provider: AxEngineExecutionProvider
91
+ [INFO] Chip type: ChipType.MC50
92
+ [INFO] VNPU type: VNPUType.DISABLED
93
+ [INFO] Engine version: 2.12.0s
94
+ [INFO] Model type: 2 (triple core)
95
+ [INFO] Compiler version: 6.0 115775d3
96
+ providers: AxEngineExecutionProvider
97
+ inputs: [('noisy', [1, 15, 480, 640], 'float32'), ('noise_map', [1, 1, 480, 640], 'float32')]
98
+ outputs: [('denoised', [1, 3, 480, 640])]
99
+ video: 854x480 @ 25.0fps, 67 frames
100
+ axmodel input: 640x480
101
+ [OK] ./people-sunset.mp4 -> ./people-sunset_axmodel_side_by_side.mp4
102
+ frames=67 time=17.57s fps=3.81
103
+
104
+ ```
105
+
106
+ 视频降噪样例:
107
+ <video controls src="fastDVDnet/res/people-sunset_axmodel_side_by_side.mp4" title="Title"></video>
config.json ADDED
File without changes
fastDVDnet/model_quant/fastDVDnet.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // 临时跑通版,真实需要拼接图做15通道输入
2
+ {
3
+ "input": "./fastdvdnet_640x480.onnx",
4
+ "output_dir": "./fastdvdnet",
5
+ "output_name": "fastdvdnet_640x480.axmodel",
6
+ "model_type": "ONNX",
7
+ "target_hardware": "AX650",
8
+ "npu_mode": "NPU3",
9
+ "quant": {
10
+ "input_configs": [
11
+ {
12
+ "tensor_name": "noisy",
13
+ "calibration_dataset": "fastdvdnet.tar.gz",
14
+ "calibration_format": "Numpy",
15
+ "calibration_size": 5,
16
+ "calibration_mean": [0.0],
17
+ "calibration_std": [1.0]
18
+ },
19
+ {
20
+ "tensor_name": "noise_map",
21
+ "calibration_dataset": "./fastDVD_noise_map.tar.gz",
22
+ "calibration_format": "Numpy",
23
+ "calibration_size": 5,
24
+ "calibration_mean": [0.0],
25
+ "calibration_std": [1.0]
26
+ }
27
+ ],
28
+ "calibration_method": "MinMax",
29
+ "precision_analysis": true,
30
+ "transformer_opt_level": 1,
31
+ "precision_analysis_method": "EndToEnd",
32
+ "precision_analysis_mode": "NPUBackend",
33
+ "device": "cuda:1"
34
+ },
35
+ "input_processors": [
36
+ {
37
+ "tensor_name": "noisy",
38
+ "tensor_layout": "NCHW",
39
+ "src_dtype": "FP32",
40
+ "src_layout": "NCHW",
41
+ "csc_mode": "NoCSC",
42
+ "mean": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
43
+ "std": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
44
+ },
45
+ {
46
+ "tensor_name": "noise_map",
47
+ "tensor_layout": "NCHW",
48
+ "src_dtype": "FP32",
49
+ "src_layout": "NCHW",
50
+ "csc_mode": "NoCSC",
51
+ "mean": [0.0],
52
+ "std": [1.0]
53
+ }
54
+ ],
55
+ "compiler": {
56
+ "check": 2
57
+ }
58
+ }
fastDVDnet/python/axmodel_video_infer.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """FastDVDnet axmodel video inference, side-by-side output.
3
+ Input: mp4 video -> resize to axmodel fixed input -> 5-frame window -> axmodel -> output video.
4
+ Input dtype is inferred from the axmodel session so both float and uint8 models work."""
5
+ import argparse, os, time
6
+ import cv2, numpy as np
7
+ import axengine as axe
8
+
9
+
10
+ def _resolve_numpy_dtype(dtype, fallback):
11
+ try:
12
+ return np.dtype(dtype)
13
+ except TypeError:
14
+ name = str(dtype).lower()
15
+ if "float16" in name or "fp16" in name:
16
+ return np.dtype(np.float16)
17
+ if "float" in name:
18
+ return np.dtype(np.float32)
19
+ if "uint8" in name:
20
+ return np.dtype(np.uint8)
21
+ if "int8" in name:
22
+ return np.dtype(np.int8)
23
+ return np.dtype(fallback)
24
+
25
+ def read_video(video_path, max_frames=0):
26
+ cap=cv2.VideoCapture(video_path)
27
+ if not cap.isOpened(): raise RuntimeError('open fail: '+video_path)
28
+ fps=cap.get(cv2.CAP_PROP_FPS) or 25.0
29
+ ow=int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)); oh=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
30
+ frames=[]
31
+ while True:
32
+ ok,f=cap.read()
33
+ if not ok: break
34
+ frames.append(f)
35
+ if max_frames and len(frames)>=max_frames: break
36
+ cap.release()
37
+ if not frames: raise RuntimeError('no frames')
38
+ return frames,fps,ow,oh
39
+
40
+ def reflect_index(idx,length):
41
+ if length<=1: return 0
42
+ while idx<0 or idx>=length:
43
+ if idx<0: idx=-idx
44
+ if idx>=length: idx=2*(length-1)-idx
45
+ return idx
46
+
47
+ def bgr_to_ax_input(fb, ah, aw, noisy_dtype):
48
+ r = cv2.resize(fb, (aw, ah), interpolation=cv2.INTER_AREA)
49
+ r = cv2.cvtColor(r, cv2.COLOR_BGR2RGB)
50
+ chw = r.transpose(2, 0, 1)
51
+ noisy_dtype = _resolve_numpy_dtype(noisy_dtype, np.float32)
52
+ if np.issubdtype(noisy_dtype, np.floating):
53
+ return (chw.astype(np.float32) / 255.0).astype(noisy_dtype, copy=False)
54
+ return chw.astype(noisy_dtype, copy=False)
55
+
56
+ def chw_float_to_bgr_u8(chw,tw,th):
57
+ hwc=(chw*255.).clip(0,255).astype(np.uint8).transpose(1,2,0)
58
+ bgr=cv2.cvtColor(hwc,cv2.COLOR_RGB2BGR)
59
+ if bgr.shape[1]!=tw or bgr.shape[0]!=th:
60
+ bgr=cv2.resize(bgr,(tw,th),interpolation=cv2.INTER_LINEAR)
61
+ return bgr
62
+
63
+ def denoise_axmodel(frames_bgr, noise_sigma_01, sess, ax_h, ax_w,
64
+ input_names, output_names, noisy_dtype, noise_map_dtype):
65
+ numframes = len(frames_bgr)
66
+ temp_psz, ctrl = 5, 2
67
+
68
+ chw_cache = {}
69
+ def get_chw(i):
70
+ i = i % numframes
71
+ if i not in chw_cache:
72
+ chw_cache[i] = bgr_to_ax_input(
73
+ frames_bgr[reflect_index(i, numframes)], ax_h, ax_w, noisy_dtype)
74
+ return chw_cache[i]
75
+
76
+ den_frames_bgr = []
77
+ inframes = []
78
+
79
+ for fridx in range(numframes):
80
+ if not inframes:
81
+ for off in range(temp_psz):
82
+ inframes.append(get_chw(fridx + off - ctrl))
83
+ else:
84
+ del inframes[0]
85
+ inframes.append(get_chw(fridx + ctrl))
86
+
87
+ noisy = np.concatenate(inframes, axis=0)[None, :, :, :]
88
+ noise_map = np.full(
89
+ (1, 1, ax_h, ax_w),
90
+ noise_sigma_01,
91
+ dtype=_resolve_numpy_dtype(noise_map_dtype, np.float32),
92
+ )
93
+ feeds = {input_names[0]: noisy, input_names[1]: noise_map}
94
+ out = sess.run(output_names, feeds)[0]
95
+ out = np.clip(out, 0.0, 1.0)
96
+ den_bgr = chw_float_to_bgr_u8(
97
+ out[0], frames_bgr[0].shape[1], frames_bgr[0].shape[0])
98
+ den_frames_bgr.append(den_bgr)
99
+
100
+ return den_frames_bgr
101
+
102
+
103
+ def write_side_by_side(frames_orig, frames_den, out_path, fps, label=True):
104
+ h, w = frames_orig[0].shape[:2]
105
+ os.makedirs(os.path.dirname(os.path.abspath(out_path)), exist_ok=True)
106
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
107
+ writer = cv2.VideoWriter(out_path, fourcc, fps, (w * 2, h))
108
+ if not writer.isOpened():
109
+ raise RuntimeError("video writer fail: {}".format(out_path))
110
+ for orig, den in zip(frames_orig, frames_den):
111
+ canvas = np.concatenate([orig, den], axis=1)
112
+ if label:
113
+ cv2.putText(canvas, "Original", (16, 34), cv2.FONT_HERSHEY_SIMPLEX,
114
+ 1.0, (0, 255, 255), 2, cv2.LINE_AA)
115
+ cv2.putText(canvas, "AXModel Denoised", (w + 16, 34), cv2.FONT_HERSHEY_SIMPLEX,
116
+ 1.0, (0, 255, 255), 2, cv2.LINE_AA)
117
+ writer.write(canvas)
118
+ writer.release()
119
+
120
+
121
+ def main():
122
+ parser = argparse.ArgumentParser(description="FastDVDnet axmodel video inference")
123
+ parser.add_argument("--axmodel", type=str, default="./fastdvdnet_640x480.axmodel")
124
+ parser.add_argument("--video", type=str, default='./people-sunset.mp4', help="input mp4 video")
125
+ parser.add_argument("--noise_sigma", type=float, default=25.0, help="0-255")
126
+ parser.add_argument("--out_dir", type=str, default="./")
127
+ parser.add_argument("--max_frames", type=int, default=0, help="0=all")
128
+ parser.add_argument("--no_label", action="store_true")
129
+ args = parser.parse_args()
130
+
131
+ sess = axe.InferenceSession(args.axmodel, providers=["AxEngineExecutionProvider"])
132
+ inputs = sess.get_inputs()
133
+ input_names = [i.name for i in inputs]
134
+ output_names = [o.name for o in sess.get_outputs()]
135
+ noisy_dtype = getattr(inputs[0], "dtype", np.float32)
136
+ noise_map_dtype = getattr(inputs[1], "dtype", np.float32)
137
+ ax_h = inputs[0].shape[2]
138
+ ax_w = inputs[0].shape[3]
139
+
140
+ print("providers:", sess.get_providers())
141
+ print("inputs:", [(n, list(i.shape), str(getattr(i, "dtype", "unknown"))) for n, i in zip(input_names, inputs)])
142
+ print("outputs:", [(n, list(o.shape)) for n, o in zip(output_names, sess.get_outputs())])
143
+
144
+ base = os.path.splitext(os.path.basename(args.video))[0]
145
+ out_path = os.path.join(args.out_dir, base + "_axmodel_side_by_side.mp4")
146
+
147
+ t0 = time.time()
148
+ frames_bgr, fps, orig_w, orig_h = read_video(args.video, args.max_frames)
149
+ print("video: {}x{} @ {:.1f}fps, {} frames".format(orig_w, orig_h, fps, len(frames_bgr)))
150
+ print("axmodel input: {}x{}".format(ax_w, ax_h))
151
+
152
+ sigma01 = args.noise_sigma / 255.0
153
+ den = denoise_axmodel(
154
+ frames_bgr, sigma01, sess, ax_h, ax_w,
155
+ input_names, output_names, noisy_dtype, noise_map_dtype,
156
+ )
157
+ write_side_by_side(frames_bgr, den, out_path, fps, label=not args.no_label)
158
+ dt = time.time() - t0
159
+
160
+ print("[OK] {} -> {}".format(args.video, out_path))
161
+ print(" frames={} time={:.2f}s fps={:.2f}".format(
162
+ len(frames_bgr), dt, len(frames_bgr) / max(dt, 0.001)))
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()
fastDVDnet/python/onnx_video_infer.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """
3
+ FastDVDnet ONNX 视频推理脚本,输出 原帧 | ONNX 去噪结果 拼接视频。
4
+
5
+ 输入视频逐帧 resize 到 ONNX 固定输入尺寸 (HxW),组成 5 帧窗口
6
+ [t-2,t-1,t,t+1,t+2] 推理,输出 resize 回原始尺寸生成拼接视频。
7
+
8
+ 用法:
9
+ python onnx_video_infer.py \
10
+ --onnx ./fastdvdnet_640x480.onnx \
11
+ --video mp4/drone.mp4 \
12
+ --noise_sigma 25 \
13
+ --out_dir ./video_infer_results/onnx_test
14
+ """
15
+ import argparse
16
+ import os
17
+ import time
18
+
19
+ import cv2
20
+ import numpy as np
21
+ import onnxruntime as ort
22
+
23
+
24
+ def get_onnx_shapes(onnx_path):
25
+ import onnx
26
+ m = onnx.load(onnx_path)
27
+ x_dims = [d.dim_value for d in m.graph.input[0].type.tensor_type.shape.dim]
28
+ return x_dims # [N, 15, H, W]
29
+
30
+
31
+ def read_video(video_path, max_frames=0):
32
+ cap = cv2.VideoCapture(video_path)
33
+ if not cap.isOpened():
34
+ raise RuntimeError("failed to open video: {}".format(video_path))
35
+ fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
36
+ orig_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
37
+ orig_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
38
+ frames_bgr = []
39
+ while True:
40
+ ok, frame = cap.read()
41
+ if not ok:
42
+ break
43
+ frames_bgr.append(frame)
44
+ if max_frames and len(frames_bgr) >= max_frames:
45
+ break
46
+ cap.release()
47
+ if not frames_bgr:
48
+ raise RuntimeError("no frames read from {}".format(video_path))
49
+ return frames_bgr, fps, orig_w, orig_h
50
+
51
+
52
+ def reflect_index(idx, length):
53
+ if length <= 1:
54
+ return 0
55
+ while idx < 0 or idx >= length:
56
+ if idx < 0:
57
+ idx = -idx
58
+ if idx >= length:
59
+ idx = 2 * (length - 1) - idx
60
+ return idx
61
+
62
+
63
+ def bgr_to_onnx_input(frame_bgr, onnx_h, onnx_w):
64
+ """resize BGR uint8 to ONNX RGB float32 [1,3,H,W] in [0,1]"""
65
+ resized = cv2.resize(frame_bgr, (onnx_w, onnx_h), interpolation=cv2.INTER_AREA)
66
+ rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)
67
+ chw = rgb.astype(np.float32).transpose(2, 0, 1) / 255.0
68
+ return chw # [3, H, W]
69
+
70
+
71
+ def chw_to_bgr_uint8(chw, target_w, target_h):
72
+ """[3,H,W] float32 in [0,1] -> BGR uint8 resized to target_w x target_h"""
73
+ hwc = (chw * 255.0).clip(0, 255).astype(np.uint8).transpose(1, 2, 0)
74
+ rgb = hwc # already RGB from ONNX output
75
+ bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
76
+ if bgr.shape[1] != target_w or bgr.shape[0] != target_h:
77
+ bgr = cv2.resize(bgr, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
78
+ return bgr
79
+
80
+
81
+ def denoise_onnx(frames_bgr, noise_sigma_01, sess, x_shape, input_names, output_names):
82
+ numframes = len(frames_bgr)
83
+ _, _, onnx_h, onnx_w = x_shape
84
+ temp_psz, ctrl = 5, 2
85
+
86
+ # 缓存 resize 后的 CHW 帧
87
+ chw_cache = {}
88
+ def get_chw(i):
89
+ i = i % numframes
90
+ if i not in chw_cache:
91
+ chw_cache[i] = bgr_to_onnx_input(frames_bgr[reflect_index(i, numframes)], onnx_h, onnx_w)
92
+ return chw_cache[i]
93
+
94
+ den_frames_bgr = []
95
+ inframes = [] # numpy [3,H,W] each
96
+
97
+ for fridx in range(numframes):
98
+ if not inframes:
99
+ for off in range(temp_psz):
100
+ inframes.append(get_chw(fridx + off - ctrl))
101
+ else:
102
+ del inframes[0]
103
+ inframes.append(get_chw(fridx + ctrl))
104
+
105
+ # concat 5 frames -> [1, 15, H, W]
106
+ noisy = np.concatenate(inframes, axis=0)[None, :, :, :].astype(np.float32)
107
+ noise_map = np.full((1, 1, onnx_h, onnx_w), noise_sigma_01, dtype=np.float32)
108
+ feeds = {input_names[0]: noisy, input_names[1]: noise_map}
109
+ out = sess.run(output_names, feeds)[0] # [1, 3, H, W]
110
+ out = np.clip(out, 0.0, 1.0)
111
+ den_bgr = chw_to_bgr_uint8(out[0], frames_bgr[0].shape[1], frames_bgr[0].shape[0])
112
+ den_frames_bgr.append(den_bgr)
113
+
114
+ return den_frames_bgr
115
+
116
+
117
+ def write_side_by_side(frames_orig, frames_den, out_path, fps, label=True):
118
+ h, w = frames_orig[0].shape[:2]
119
+ os.makedirs(os.path.dirname(os.path.abspath(out_path)), exist_ok=True)
120
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
121
+ writer = cv2.VideoWriter(out_path, fourcc, fps, (w * 2, h))
122
+ if not writer.isOpened():
123
+ raise RuntimeError("failed to create video writer: {}".format(out_path))
124
+ for orig, den in zip(frames_orig, frames_den):
125
+ canvas = np.concatenate([orig, den], axis=1)
126
+ if label:
127
+ cv2.putText(canvas, "Original", (16, 34), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2, cv2.LINE_AA)
128
+ cv2.putText(canvas, "ONNX Denoised", (w + 16, 34), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2, cv2.LINE_AA)
129
+ writer.write(canvas)
130
+ writer.release()
131
+
132
+
133
+ def main():
134
+ parser = argparse.ArgumentParser(description="FastDVDnet ONNX video inference side-by-side")
135
+ parser.add_argument("--onnx", type=str, default="./fastdvdnet_640x480.onnx")
136
+ parser.add_argument("--video", type=str, required=True, help="input mp4 video")
137
+ parser.add_argument("--noise_sigma", type=float, default=25.0, help="noise sigma 0-255")
138
+ parser.add_argument("--out_dir", type=str, default="./video_infer_results/onnx_test")
139
+ parser.add_argument("--max_frames", type=int, default=0, help="0 means all frames")
140
+ parser.add_argument("--no_label", action="store_true")
141
+ args = parser.parse_args()
142
+
143
+ x_shape = get_onnx_shapes(args.onnx)
144
+ _, _, onnx_h, onnx_w = x_shape
145
+ print("ONNX input shape: {}".format(x_shape))
146
+
147
+ sess = ort.InferenceSession(args.onnx, providers=["CPUExecutionProvider"])
148
+ input_names = [i.name for i in sess.get_inputs()]
149
+ output_names = [o.name for o in sess.get_outputs()]
150
+ print("providers:", sess.get_providers())
151
+ print("inputs:", [(n, list(i.shape)) for n, i in zip(input_names, sess.get_inputs())])
152
+ print("outputs:", [(n, list(o.shape)) for n, o in zip(output_names, sess.get_outputs())])
153
+
154
+ base = os.path.splitext(os.path.basename(args.video))[0]
155
+ out_path = os.path.join(args.out_dir, "{}_onnx_side_by_side.mp4".format(base))
156
+
157
+ t0 = time.time()
158
+ frames_bgr, fps, orig_w, orig_h = read_video(args.video, args.max_frames)
159
+ print("video: {}x{} @ {:.1f}fps, {} frames".format(orig_w, orig_h, fps, len(frames_bgr)))
160
+ print("onnx input: {}x{}".format(onnx_w, onnx_h))
161
+
162
+ sigma01 = args.noise_sigma / 255.0
163
+ den = denoise_onnx(frames_bgr, sigma01, sess, x_shape, input_names, output_names)
164
+ write_side_by_side(frames_bgr, den, out_path, fps, label=not args.no_label)
165
+ dt = time.time() - t0
166
+
167
+ print("[OK] {} -> {}".format(args.video, out_path))
168
+ print(" frames={} time={:.2f}s fps={:.2f}".format(len(frames_bgr), dt, len(frames_bgr) / max(dt, 0.001)))
169
+
170
+
171
+ if __name__ == "__main__":
172
+ main()
fastDVDnet/res/people-sunset_axmodel_side_by_side.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1d30d4383609735400fd7a496e366bc836ead97f5a34fae4a7eb806db43e3e40
3
+ size 2146980
fastDVDnet/video/people-sunset.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f6fd9f0057d761a0eafb8dc47e0bf046387e3cb0950004916b6569dc6effaf72
3
+ size 2008424