diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..07f4f2e2f2535e217458635dd0b972f424ee27b8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.axmodel filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.JPG filter=lfs diff=lfs merge=lfs -text diff --git a/AOD-Net/model_convert/AOD-Net.json b/AOD-Net/model_convert/AOD-Net.json new file mode 100644 index 0000000000000000000000000000000000000000..770cb5113a5138e288801a9b237da9dfc0e81c60 --- /dev/null +++ b/AOD-Net/model_convert/AOD-Net.json @@ -0,0 +1,39 @@ +{ + "input": "./aodnet_1x3x480x640.onnx", + "output_dir": "./aodnet", + "output_name": "aodnet.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./hazy.tar.gz", + "calibration_size": -1, + "calibration_mean": [127.5, 127.5, 127.5], + "calibration_std": [127.5, 127.5, 127.5] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + ], + "compiler": { + "check": 2 + } +} + diff --git a/AOD-Net/model_convert/axmodel/aodnet_1x3x480x640_sim.axmodel b/AOD-Net/model_convert/axmodel/aodnet_1x3x480x640_sim.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..ef0fce1fc6b0a39b5fdaf569519aaf3796ac2cf1 --- /dev/null +++ b/AOD-Net/model_convert/axmodel/aodnet_1x3x480x640_sim.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9804d02f8654a848deb05aaa9679b4913884b7fa1fbd06174d35e3257156bf4 +size 51047 diff --git a/AOD-Net/pic/canyon1.jpg b/AOD-Net/pic/canyon1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4de53e7cc2bf3ac5a011a4324230c6e670b18dc6 --- /dev/null +++ b/AOD-Net/pic/canyon1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e973833f96e7f5c0eac3db0352cab0574595c635e8ba4b437f9c104d7f45295e +size 863895 diff --git a/AOD-Net/pic/canyon2.jpg b/AOD-Net/pic/canyon2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bbe1d87ffcacf78c248fc1b50a802d46820d1a42 --- /dev/null +++ b/AOD-Net/pic/canyon2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3b05599d760033586a4f36ee1d88b7718e964a917269742ccc4ea977067d3e1 +size 372850 diff --git a/AOD-Net/pic/forest1.jpg b/AOD-Net/pic/forest1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..edb01790609c8b3a61f13b717082c4e8923c0000 --- /dev/null +++ b/AOD-Net/pic/forest1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e5bc940ea9778b5184bac10613a1262a7b125abcb7e4248b04ed4ea540bc6c6 +size 567344 diff --git a/AOD-Net/pic/school1.jpg b/AOD-Net/pic/school1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e366478d454ea1cbdd901997108f953355dca94b --- /dev/null +++ b/AOD-Net/pic/school1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3752e73363dcba72d5961e843e661aa8ce2fcb59b0f2c056ebb557e988e7dcf5 +size 137470 diff --git a/AOD-Net/pic/school2.jpg b/AOD-Net/pic/school2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..111af209bd96204f218376bf84dfdd8816144cf3 --- /dev/null +++ b/AOD-Net/pic/school2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba7033c13188a605c1c90b4947810e525531bb096f1f35f59ecfcecb700de871 +size 200001 diff --git a/AOD-Net/pic/school3.jpg b/AOD-Net/pic/school3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43e68deaf5c2541eb8e46ed40acdb1992d351621 --- /dev/null +++ b/AOD-Net/pic/school3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b96f0cdbdea7399743cf86d4487dc406e2169c74863c3be0c4caf27367c451d +size 237129 diff --git a/AOD-Net/pic/school4.jpg b/AOD-Net/pic/school4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..28ae8e612013fc4d2526f02a2bcaa113b99520e6 --- /dev/null +++ b/AOD-Net/pic/school4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d0d7e87ef378c3e592f328aaf21dcb9f285654a00b278c4ac807f6d125c6f3e +size 174059 diff --git a/AOD-Net/pic/tiananmen1.png b/AOD-Net/pic/tiananmen1.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc1267cd1000d5fc3bfcc67eb0e8876dc1f57b3 --- /dev/null +++ b/AOD-Net/pic/tiananmen1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a6be814342c375a064ff7939ea316cd0dee0ec6058fc10fb6616eef0afbc509 +size 354406 diff --git a/AOD-Net/pic/tiananmen2.png b/AOD-Net/pic/tiananmen2.png new file mode 100644 index 0000000000000000000000000000000000000000..5f8e7d241edf358104faaf70fa5a60e4ff502341 --- /dev/null +++ b/AOD-Net/pic/tiananmen2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57a4b8a63efa2bb4499ec155cca3c184e0b7b1d27049712677174177441065e7 +size 387604 diff --git a/AOD-Net/pic/tiananmen3.jpg b/AOD-Net/pic/tiananmen3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f63b2a6e16c33187025d01356b548f68777ad906 --- /dev/null +++ b/AOD-Net/pic/tiananmen3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f87d1c2d2f7aebee4afdc92dda6584f978140a240d2830a4bd8f953c204bd340 +size 720056 diff --git a/AOD-Net/pic/underwater1.JPG b/AOD-Net/pic/underwater1.JPG new file mode 100644 index 0000000000000000000000000000000000000000..e34f5c8acbcf5ac878802806ab1cf4c8b959f18c --- /dev/null +++ b/AOD-Net/pic/underwater1.JPG @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06c1f3b92b938c7a322df31d9edafd574ff4824c51cf4dc2869b74448dd9dc1a +size 313431 diff --git a/AOD-Net/pic/underwater2.JPG b/AOD-Net/pic/underwater2.JPG new file mode 100644 index 0000000000000000000000000000000000000000..3d1b1270ad55c713894f6c16603741bf3651d8b3 --- /dev/null +++ b/AOD-Net/pic/underwater2.JPG @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32fe577274dc08253a6aba8cad3432cb36991922720c4592fa9ff3fcfe17a83b +size 437380 diff --git a/AOD-Net/pic/underwater3.JPG b/AOD-Net/pic/underwater3.JPG new file mode 100644 index 0000000000000000000000000000000000000000..c2e6d4847c9c2499752ce0c2d33297f1ddf6b30e --- /dev/null +++ b/AOD-Net/pic/underwater3.JPG @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d390c298c3cc657c66e251ad8b1d4e871113c543fb7d42661ca06a4a02118a2 +size 260981 diff --git a/AOD-Net/pic/underwater4.jpg b/AOD-Net/pic/underwater4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..87b2b221798999d705f90b9d968ce2b3bbe300b9 --- /dev/null +++ b/AOD-Net/pic/underwater4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0a3edcb06e21afb3c194374cb1c6cfb1f0f89c545178d47ef30242022e45be0 +size 186193 diff --git a/AOD-Net/python/axmodel_infer.py b/AOD-Net/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfca12948688d1fc0baf7e04a267bbee420a73e --- /dev/null +++ b/AOD-Net/python/axmodel_infer.py @@ -0,0 +1,67 @@ +import argparse + +import numpy as np +import axengine as axe +from PIL import Image, ImageDraw, ImageFont + + +def draw_label(img, text): + draw = ImageDraw.Draw(img) + try: + font = ImageFont.truetype("DejaVuSans-Bold.ttf", max(18, img.height // 36)) + except Exception: + font = ImageFont.load_default() + padding = 6 + bbox = draw.textbbox((0, 0), text, font=font) + box_w = bbox[2] - bbox[0] + padding * 2 + box_h = bbox[3] - bbox[1] + padding * 2 + draw.rectangle([0, 0, box_w, box_h], fill=(0, 0, 0)) + draw.text((padding, padding), text, fill=(255, 255, 255), font=font) + + +def main(): + parser = argparse.ArgumentParser(description="axmodel single-image dehazing inference.") + parser.add_argument("--axmodel", default="aodnet_1x3x480x640_sim.axmodel", help="axmodel model path") + parser.add_argument("--input_image", default='./pic/canyon2.jpg', help="hazy image path") + parser.add_argument("--output", default="axmodel_compare.png", help="output comparison path (hazy | dehazed)") + parser.add_argument("--height", type=int, default=480, help="resize height (match axmodel input)") + parser.add_argument("--width", type=int, default=640, help="resize width (match axmodel input)") + parser.add_argument("--normalize", action="store_true", help="use (x-0.5)/0.5 normalization") + parser.add_argument("--no_label", action="store_true", help="do not draw hazy/dehazed labels") + args = parser.parse_args() + + img = Image.open(args.input_image).convert("RGB") + orig_size = img.size + img_resized = img.resize((args.width, args.height), Image.BILINEAR) + + arr = np.asarray(img_resized).astype(np.float32) + arr = arr.transpose(2, 0, 1)[None, :, :, :].astype(np.uint8) + + sess = axe.InferenceSession(args.axmodel, providers=["AxEngineExecutionProvider"]) + input_name = sess.get_inputs()[0].name + out = sess.run(None, {input_name: arr})[0] + + out = out[0].transpose(1, 2, 0) + out = np.clip(out, 0.0, 1.0) + out_img = Image.fromarray((out * 255.0).round().astype(np.uint8)) + # 还原到原图大小 + if out_img.size != orig_size: + out_img = out_img.resize(orig_size, Image.BICUBIC) + + hazy_labeled = img.copy() + dehazed_labeled = out_img.copy() + if not args.no_label: + draw_label(hazy_labeled, "hazy") + draw_label(dehazed_labeled, "dehazed") + + compare = Image.new("RGB", (img.width + out_img.width, max(img.height, out_img.height))) + compare.paste(hazy_labeled, (0, 0)) + compare.paste(dehazed_labeled, (img.width, 0)) + compare.save(args.output) + + print("Saved:", args.output) + print("Input: {:.0f}x{:.0f} -> axmodel: {}x{}".format(*orig_size, args.height, args.width)) + + +if __name__ == "__main__": + main() diff --git a/AOD-Net/python/onnx_infer.py b/AOD-Net/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..a8ac5dfec2697865d62a67ff2608351e213cce3e --- /dev/null +++ b/AOD-Net/python/onnx_infer.py @@ -0,0 +1,45 @@ +import argparse + +import numpy as np +import onnxruntime as ort +from PIL import Image + + +def main(): + parser = argparse.ArgumentParser(description="ONNX single-image dehazing inference.") + parser.add_argument("--onnx", default="aodnet_1x3x480x640_sim.onnx", help="ONNX model path") + parser.add_argument("--input_image", required=True, help="hazy image path") + parser.add_argument("--output", default="onnx_result.png", help="output path") + parser.add_argument("--height", type=int, default=480, help="resize height (match ONNX input)") + parser.add_argument("--width", type=int, default=640, help="resize width (match ONNX input)") + parser.add_argument("--normalize", action="store_true", help="use (x-0.5)/0.5 normalization") + args = parser.parse_args() + + img = Image.open(args.input_image).convert("RGB") + orig_size = img.size + img = img.resize((args.width, args.height), Image.BILINEAR) + + arr = np.asarray(img).astype(np.float32) / 255.0 + if args.normalize: + arr = (arr - 0.5) / 0.5 + arr = arr.transpose(2, 0, 1)[None, :, :, :].astype(np.float32) + + sess = ort.InferenceSession(args.onnx, providers=["CPUExecutionProvider"]) + input_name = sess.get_inputs()[0].name + out = sess.run(None, {input_name: arr})[0] + + out = out[0].transpose(1, 2, 0) + out = np.clip(out, 0.0, 1.0) + out_img = Image.fromarray((out * 255.0).round().astype(np.uint8)) + + compare = Image.new("RGB", (img.width + out_img.width, max(img.height, out_img.height))) + compare.paste(img, (0, 0)) + compare.paste(out_img, (img.width, 0)) + compare.save(args.output) + + print("Saved:", args.output) + print("Input: {:.0f}x{:.0f} -> ONNX: {}x{}".format(*orig_size, args.height, args.width)) + + +if __name__ == "__main__": + main() diff --git a/AOD-Net/res/axmodel_result.png b/AOD-Net/res/axmodel_result.png new file mode 100644 index 0000000000000000000000000000000000000000..290ef85dea21f6d3ae45324de351d9ced272eb44 --- /dev/null +++ b/AOD-Net/res/axmodel_result.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46786d662c6305e119ed923095c4bdda90e280bcc5f94c7b118bb537bd8edfa9 +size 875279 diff --git a/DehazeFormer/model_convert/axmodel/dehazeformer-t-512-constant.axmodel b/DehazeFormer/model_convert/axmodel/dehazeformer-t-512-constant.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..58b2d64874646922820653aa099d3aced7cfec15 --- /dev/null +++ b/DehazeFormer/model_convert/axmodel/dehazeformer-t-512-constant.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f204bfa4061ac07ecaf6efb1e387edb81dd3a352c01d31dd690850f8778cc92 +size 7272273 diff --git a/DehazeFormer/model_convert/dehazeformer.json b/DehazeFormer/model_convert/dehazeformer.json new file mode 100644 index 0000000000000000000000000000000000000000..0eecd95d3ad0e39757d5c1f0890cbb6ef46e0365 --- /dev/null +++ b/DehazeFormer/model_convert/dehazeformer.json @@ -0,0 +1,39 @@ +{ + "input": "./dehazeformer-t-512-constant.onnx", + "output_dir": "./dehazeformer-t-512-constant", + "output_name": "dehazeformer-t-512-constant.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./hazy.tar.gz", + "calibration_size": -1, + "calibration_mean": [127.5, 127.5, 127.5], + "calibration_std": [127.5, 127.5, 127.5] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + ], + "compiler": { + "check": 2 + } +} + diff --git a/DehazeFormer/pic/00000_0_0.1800.png b/DehazeFormer/pic/00000_0_0.1800.png new file mode 100644 index 0000000000000000000000000000000000000000..894f64dff9619d8dd72ff0e8e7c1f12d8345ab1b --- /dev/null +++ b/DehazeFormer/pic/00000_0_0.1800.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1761dc0533f77290c0d2e67d71fa2dae0628b2e18286402b5f13227f2ab13137 +size 312198 diff --git a/DehazeFormer/python/axmodel_infer.py b/DehazeFormer/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..6d8f52ceee401a427ee30b29505278d887b26c11 --- /dev/null +++ b/DehazeFormer/python/axmodel_infer.py @@ -0,0 +1,118 @@ +import argparse +from pathlib import Path + +import cv2 +import numpy as np +import axengine as axe + +def hwc_to_chw(img): + return np.transpose(img, axes=[2, 0, 1]).copy() + + +def chw_to_hwc(img): + return np.transpose(img, axes=[1, 2, 0]).copy() + + +def read_rgb_float(image_path, size=None): + img = cv2.imread(str(image_path)) + img = img[:, :, ::-1] # BGR -> RGB + img = cv2.resize(img, size, interpolation=cv2.INTER_AREA) + return img + +def preprocess(image_path, size=None): + img = read_rgb_float(image_path, size=size) + return hwc_to_chw(img).astype(np.uint8)[None, ...] + + +def get_axmodel_input_size(session): + shape = session.get_inputs()[0].shape # [N, C, H, W] + h, w = shape[2], shape[3] + if isinstance(h, int) and isinstance(w, int): + return w, h + return None + + +def infer_axmodel(session, image_path, size=None): + tensor = preprocess(image_path, size=size) + input_name = session.get_inputs()[0].name + output_name = session.get_outputs()[0].name + out = session.run([output_name], {input_name: tensor})[0] + out = np.clip(out, -1, 1) + out = out * 0.5 + 0.5 + return chw_to_hwc(out.squeeze(0)) + + +def load_session(axmodel_path): + providers = ['AxEngineExecutionProvider'] + return axe.InferenceSession(str(axmodel_path), providers=providers) + + +def save_comparison(hazy_path, dehazed, output_path, size=None): + """拼接原图(hazy)和去雾图(dehazed),并标注标签""" + hazy = cv2.imread(str(hazy_path)) + if hazy is None: + raise FileNotFoundError(f'Failed to read image: {hazy_path}') + + h, w = dehazed.shape[:2] + if hazy.shape[:2] != (h, w): + hazy = cv2.resize(hazy, (w, h), interpolation=cv2.INTER_AREA) + + # hazy 是 BGR,dehazed 是 RGB -> 转 BGR 再拼接 + dehazed_bgr = cv2.cvtColor( + np.round(dehazed * 255).clip(0, 255).astype(np.uint8), + cv2.COLOR_RGB2BGR, + ) + + pad = 40 + canvas = np.full((h + pad, w * 2, 3), 255, dtype=np.uint8) + canvas[pad:, :w] = hazy + canvas[pad:, w:] = dehazed_bgr + + font = cv2.FONT_HERSHEY_SIMPLEX + cv2.putText(canvas, 'Hazy', (w // 2 - 40, pad - 8), font, 0.9, (0, 0, 0), 2, cv2.LINE_AA) + cv2.putText(canvas, 'Dehazed', (w + w // 2 - 60, pad - 8), font, 0.9, (0, 0, 0), 2, cv2.LINE_AA) + + output_path.parent.mkdir(parents=True, exist_ok=True) + cv2.imwrite(str(output_path), canvas) + + +def main(args): + axmodel_path = Path(args.axmodel) + if not axmodel_path.is_file(): + raise FileNotFoundError(f'axmodel model not found: {axmodel_path}') + + input_path = Path(args.input) + if not input_path.is_file(): + raise FileNotFoundError(f'Input image not found: {input_path}') + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + session = load_session(axmodel_path) + + axmodel_size = get_axmodel_input_size(session) + if args.width > 0 and args.height > 0: + infer_size = (args.width, args.height) + elif axmodel_size is not None: + infer_size = axmodel_size + else: + infer_size = None + + print(f'axmodel: {axmodel_path}') + print(f'Input: {input_path}') + print(f'Size: {infer_size}') + print(f'Output: {output_path}') + + out_img = infer_axmodel(session, input_path, size=infer_size) + save_comparison(input_path, out_img, output_path) + print('Done.') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Single-image axmodel inference for DehazeFormer.') + parser.add_argument('--input', default='./00000_0_0.1800.png', type=str, help='path to input image') + parser.add_argument('--axmodel', default='./dehazeformer-t-512-constant.axmodel', type=str, help='path to axmodel model') + parser.add_argument('--output', default='output.png', type=str, help='path to output image') + parser.add_argument('--width', default=-1, type=int, help='resize input width (-1 uses axmodel fixed size)') + parser.add_argument('--height', default=-1, type=int, help='resize input height (-1 uses axmodel fixed size)') + main(parser.parse_args()) diff --git a/DehazeFormer/python/onnx_infer.py b/DehazeFormer/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..acfd237386a15ac9c0c717df1bd4632652a45db0 --- /dev/null +++ b/DehazeFormer/python/onnx_infer.py @@ -0,0 +1,126 @@ +import argparse +from pathlib import Path + +import cv2 +import numpy as np +import onnxruntime as ort + +def hwc_to_chw(img): + return np.transpose(img, axes=[2, 0, 1]).copy() + + +def chw_to_hwc(img): + return np.transpose(img, axes=[1, 2, 0]).copy() + + +def read_rgb_float(image_path, size=None): + img = cv2.imread(str(image_path)) + if img is None: + raise FileNotFoundError(f'Failed to read image: {image_path}') + img = img[:, :, ::-1] # BGR -> RGB + if size is not None: + img = cv2.resize(img, size, interpolation=cv2.INTER_AREA) + return img.astype(np.float32) / 255.0 + + +def preprocess(image_path, size=None): + img = read_rgb_float(image_path, size=size) * 2 - 1 + return hwc_to_chw(img).astype(np.float32)[None, ...] + + +def get_onnx_input_size(session): + shape = session.get_inputs()[0].shape # [N, C, H, W] + h, w = shape[2], shape[3] + if isinstance(h, int) and isinstance(w, int): + return w, h + return None + + +def infer_onnx(session, image_path, size=None): + tensor = preprocess(image_path, size=size) + input_name = session.get_inputs()[0].name + output_name = session.get_outputs()[0].name + out = session.run([output_name], {input_name: tensor})[0] + out = np.clip(out, -1, 1) + out = out * 0.5 + 0.5 + return chw_to_hwc(out.squeeze(0)) + + +def load_session(onnx_path, use_cpu=False): + providers = [] + if not use_cpu and 'CUDAExecutionProvider' in ort.get_available_providers(): + providers.append('CUDAExecutionProvider') + providers.append('CPUExecutionProvider') + return ort.InferenceSession(str(onnx_path), providers=providers) + + +def save_comparison(hazy_path, dehazed, output_path, size=None): + """拼接原图(hazy)和去雾图(dehazed),并标注标签""" + hazy = cv2.imread(str(hazy_path)) + if hazy is None: + raise FileNotFoundError(f'Failed to read image: {hazy_path}') + + h, w = dehazed.shape[:2] + if hazy.shape[:2] != (h, w): + hazy = cv2.resize(hazy, (w, h), interpolation=cv2.INTER_AREA) + + # hazy 是 BGR,dehazed 是 RGB -> 转 BGR 再拼接 + dehazed_bgr = cv2.cvtColor( + np.round(dehazed * 255).clip(0, 255).astype(np.uint8), + cv2.COLOR_RGB2BGR, + ) + + pad = 40 + canvas = np.full((h + pad, w * 2, 3), 255, dtype=np.uint8) + canvas[pad:, :w] = hazy + canvas[pad:, w:] = dehazed_bgr + + font = cv2.FONT_HERSHEY_SIMPLEX + cv2.putText(canvas, 'Hazy', (w // 2 - 40, pad - 8), font, 0.9, (0, 0, 0), 2, cv2.LINE_AA) + cv2.putText(canvas, 'Dehazed', (w + w // 2 - 60, pad - 8), font, 0.9, (0, 0, 0), 2, cv2.LINE_AA) + + output_path.parent.mkdir(parents=True, exist_ok=True) + cv2.imwrite(str(output_path), canvas) + + +def main(args): + onnx_path = Path(args.onnx) + if not onnx_path.is_file(): + raise FileNotFoundError(f'ONNX model not found: {onnx_path}') + + input_path = Path(args.input) + if not input_path.is_file(): + raise FileNotFoundError(f'Input image not found: {input_path}') + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + session = load_session(onnx_path, args.cpu) + + onnx_size = get_onnx_input_size(session) + if args.width > 0 and args.height > 0: + infer_size = (args.width, args.height) + elif onnx_size is not None: + infer_size = onnx_size + else: + infer_size = None + + print(f'ONNX: {onnx_path}') + print(f'Input: {input_path}') + print(f'Size: {infer_size}') + print(f'Output: {output_path}') + + out_img = infer_onnx(session, input_path, size=infer_size) + save_comparison(input_path, out_img, output_path) + print('Done.') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Single-image ONNX inference for DehazeFormer.') + parser.add_argument('--input', required=True, type=str, help='path to input image') + parser.add_argument('--onnx', required=True, type=str, help='path to ONNX model') + parser.add_argument('--output', default='output.png', type=str, help='path to output image') + parser.add_argument('--width', default=-1, type=int, help='resize input width (-1 uses ONNX fixed size)') + parser.add_argument('--height', default=-1, type=int, help='resize input height (-1 uses ONNX fixed size)') + parser.add_argument('--cpu', action='store_true', help='force CPU inference') + main(parser.parse_args()) diff --git a/DehazeFormer/res/output.png b/DehazeFormer/res/output.png new file mode 100644 index 0000000000000000000000000000000000000000..cd5b3e557a0e1d9ae3eb03d5b52c606f60f4a4a5 --- /dev/null +++ b/DehazeFormer/res/output.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7328f26954f57f6841e3219bf69a138bde87684a86bb1a62e218fcfece74249 +size 685321 diff --git a/FFA-Net/model_convert/FFA-net.json b/FFA-Net/model_convert/FFA-net.json new file mode 100644 index 0000000000000000000000000000000000000000..0ca887c98192166cf7311d516b29e6dcb414ba5a --- /dev/null +++ b/FFA-Net/model_convert/FFA-net.json @@ -0,0 +1,58 @@ +{ + "input": "./ffa_ots_512x512.onnx", + "output_dir": "./FFA-Net", + "output_name": "FFANet.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./RESIDE.tar.gz", + "calibration_size": 1, + "calibration_mean": [163.2, 153, 147.9], + "calibration_std": [35.7, 38.25, 38.76] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + // "enable_smooth_quant": true, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + "device": "cuda:1", + // "layer_configs": [ + // { + // "start_tensor_names": ["DEFAULT"], + // "end_tensor_names": ["DEFAULT"], + // "data_type": "U16" + // } + // ] + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + // { + // "tensor_name": "input", + // "tensor_format": "RGB", + // "tensor_layout": "NCHW", + // "src_format": "YUV420SP", + // "src_layout": "NHWC", + // "src_dtype": "U8", + // "csc_mode": "FullRange", + // "csc_mat": [1.164, 0, 1.596, -222.912, 1.164, -0.392, -0.813, 135.616, 1.164, 2.017, 0, -276.8] + // } + ], + "compiler": { + "check": 2 + } +} + diff --git a/FFA-Net/model_convert/axmodel/FFANet.axmodel b/FFA-Net/model_convert/axmodel/FFANet.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..68584d7689084e7c71cbd0197aad66184726701a --- /dev/null +++ b/FFA-Net/model_convert/axmodel/FFANet.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dea9d52119bb4d0ae79b724a24218a79028f572ec654a8fc39dd20dce82b558e +size 22790300 diff --git a/FFA-Net/pic/nh(1).jpg b/FFA-Net/pic/nh(1).jpg new file mode 100644 index 0000000000000000000000000000000000000000..edb01790609c8b3a61f13b717082c4e8923c0000 --- /dev/null +++ b/FFA-Net/pic/nh(1).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e5bc940ea9778b5184bac10613a1262a7b125abcb7e4248b04ed4ea540bc6c6 +size 567344 diff --git a/FFA-Net/pic/nh(2).jpg b/FFA-Net/pic/nh(2).jpg new file mode 100644 index 0000000000000000000000000000000000000000..f111250bfed72c0af92ae77a69aa0880749b9413 --- /dev/null +++ b/FFA-Net/pic/nh(2).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30ee21c78343095e984370e392e396a090fad53222e6cb1422607a91ffc01d9c +size 199545 diff --git a/FFA-Net/pic/nh(3).jpg b/FFA-Net/pic/nh(3).jpg new file mode 100644 index 0000000000000000000000000000000000000000..70570165acec9147dc31f80fbb782c6840712da2 --- /dev/null +++ b/FFA-Net/pic/nh(3).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:524f451be9f6de579334bcc9bb5c7e7c0f6cfd0aa2bc9f71bc9392de65e81db3 +size 36409 diff --git a/FFA-Net/pic/nh(4).jpg b/FFA-Net/pic/nh(4).jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea0d2ee4ede2364a7363849dded910cefe5ab5a5 --- /dev/null +++ b/FFA-Net/pic/nh(4).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1aac5f17ecf644ffc0e3939d0c7060776f6761998d23fb8a76648dc99903d9bc +size 26263 diff --git a/FFA-Net/pic/nh(5).png b/FFA-Net/pic/nh(5).png new file mode 100644 index 0000000000000000000000000000000000000000..8bc1267cd1000d5fc3bfcc67eb0e8876dc1f57b3 --- /dev/null +++ b/FFA-Net/pic/nh(5).png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a6be814342c375a064ff7939ea316cd0dee0ec6058fc10fb6616eef0afbc509 +size 354406 diff --git a/FFA-Net/python/axmodel_infer.py b/FFA-Net/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..9c411b89345deddc1529d450ccb541dc1ca18c61 --- /dev/null +++ b/FFA-Net/python/axmodel_infer.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Single-image FFA-Net axmodel inference.""" + +import argparse +import sys +from pathlib import Path + +import numpy as np +import axengine as axe +from PIL import Image + + +FILE = Path(__file__).resolve() +NET_DIR = FILE.parent +ROOT_DIR = NET_DIR.parent +sys.path.insert(0, str(NET_DIR)) + + +def parse_args(): + parser = argparse.ArgumentParser(description="FFA-Net axmodel single-image inference.") + parser.add_argument("--axmodel", default='./FFANet.axmodel', help="axmodel model path.") + parser.add_argument("--input", default='outdoor_natural/nh(4).jpg', help="Path to input hazy image.") + parser.add_argument("--output", default="axmodel_result.png", help="Output image path.") + parser.add_argument("--height", type=int, default=512, help="axmodel input height.") + parser.add_argument("--width", type=int, default=512, help="axmodel input width.") + return parser.parse_args() + +# MEAN = np.array([0.64, 0.6, 0.58], dtype=np.float32).reshape(3, 1, 1) +# STD = np.array([0.14, 0.15, 0.152], dtype=np.float32).reshape(3, 1, 1) + +def preprocess(image_path, height, width): + image = Image.open(image_path).convert("RGB") + image = image.resize((width, height), Image.BICUBIC) + arr = np.asarray(image).astype(np.float32) + arr = arr.transpose(2, 0, 1) + # arr = (arr - MEAN) / STD # 训练同款归一化 + return arr[None, ...].astype(np.uint8) + + +def postprocess(output): + arr = np.squeeze(output, axis=0).transpose(1, 2, 0) + arr = np.clip(arr, 0.0, 1.0) + return Image.fromarray((arr * 255.0 + 0.5).astype(np.uint8)) + + +def main(): + args = parse_args() + + inp = preprocess(args.input, args.height, args.width) + session = axe.InferenceSession(args.axmodel, providers=["AxEngineExecutionProvider"]) + input_name = session.get_inputs()[0].name + out = session.run(None, {input_name: inp})[0] + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + result = postprocess(out) + result.save(str(output_path)) + print(f"Saved: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/FFA-Net/python/onnx_infer.py b/FFA-Net/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..de8424699d2efb1ad0290a738d6013a943d5d4ba --- /dev/null +++ b/FFA-Net/python/onnx_infer.py @@ -0,0 +1,94 @@ +"""Single-image FFA-Net ONNX inference with side-by-side comparison output.""" + +import argparse +import sys +from pathlib import Path + +import numpy as np +import onnxruntime as ort +from PIL import Image, ImageDraw, ImageFont + + +FILE = Path(__file__).resolve() +NET_DIR = FILE.parent +ROOT_DIR = NET_DIR.parent +sys.path.insert(0, str(NET_DIR)) + + +def parse_args(): + parser = argparse.ArgumentParser(description="FFA-Net ONNX single-image inference.") + parser.add_argument("--onnx", default='onnx/ffa_ots_512x512.onnx', help="ONNX model path.") + parser.add_argument("--input", default='outdoor_natural/nh(2).jpg', help="Path to input hazy image.") + parser.add_argument("--output", default="onnx_compare.png", help="Output comparison image path (hazy | dehazed).") + parser.add_argument("--height", type=int, default=512, help="ONNX input height.") + parser.add_argument("--width", type=int, default=512, help="ONNX input width.") + parser.add_argument("--no_label", action="store_true", help="Do not draw hazy/dehazed labels.") + return parser.parse_args() + + +MEAN = np.array([0.64, 0.6, 0.58], dtype=np.float32).reshape(3, 1, 1) +STD = np.array([0.14, 0.15, 0.152], dtype=np.float32).reshape(3, 1, 1) + + +def preprocess(image_path, height, width): + image = Image.open(image_path).convert("RGB") + image = image.resize((width, height), Image.BICUBIC) + arr = np.asarray(image).astype(np.float32) / 255.0 + arr = arr.transpose(2, 0, 1) + arr = (arr - MEAN) / STD # 训练同款归一化 + return arr[None, ...].astype(np.float32) + + +def postprocess(output): + arr = np.squeeze(output, axis=0).transpose(1, 2, 0) + arr = np.clip(arr, 0.0, 1.0) + return Image.fromarray((arr * 255.0 + 0.5).astype(np.uint8)) + + +def draw_label(img, text): + draw = ImageDraw.Draw(img) + try: + font = ImageFont.truetype("DejaVuSans-Bold.ttf", max(16, img.height // 40)) + except Exception: + font = ImageFont.load_default() + padding = max(5, img.height // 140) + bbox = draw.textbbox((0, 0), text, font=font) + box_w = bbox[2] - bbox[0] + padding * 2 + box_h = bbox[3] - bbox[1] + padding * 2 + draw.rectangle([0, 0, box_w, box_h], fill=(0, 0, 0)) + draw.text((padding, padding), text, fill=(255, 255, 255), font=font) + + +def make_compare(hazy, dehazed, with_label=True): + hazy = hazy.convert("RGB") + dehazed = dehazed.convert("RGB") + if with_label: + hazy = hazy.copy() + dehazed = dehazed.copy() + draw_label(hazy, "hazy") + draw_label(dehazed, "dehazed") + canvas = Image.new("RGB", (hazy.width + dehazed.width, hazy.height), color=(255, 255, 255)) + canvas.paste(hazy, (0, 0)) + canvas.paste(dehazed, (hazy.width, 0)) + return canvas + + +def main(): + args = parse_args() + + hazy_img = Image.open(args.input).convert("RGB").resize((args.width, args.height), Image.BICUBIC) + inp = preprocess(args.input, args.height, args.width) + session = ort.InferenceSession(args.onnx, providers=["CPUExecutionProvider"]) + input_name = session.get_inputs()[0].name + out = session.run(None, {input_name: inp})[0] + + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + dehazed = postprocess(out) + compare = make_compare(hazy_img, dehazed, with_label=not args.no_label) + compare.save(str(output_path)) + print(f"Saved: {output_path}") + + +if __name__ == "__main__": + main() diff --git a/FFA-Net/res/axmodel_compare.png b/FFA-Net/res/axmodel_compare.png new file mode 100644 index 0000000000000000000000000000000000000000..4928b77b73647b010bff1082c936e41345b988af --- /dev/null +++ b/FFA-Net/res/axmodel_compare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64c3c8d18b85cdd9b33b5ae6fdecbae64aec05ab5ae9b1df2a6f112e3d67bc38 +size 1093877 diff --git a/GCANet/model_convert/GCANet.json b/GCANet/model_convert/GCANet.json new file mode 100644 index 0000000000000000000000000000000000000000..d8b500f1a042f527e3688c1255219d9118ea4504 --- /dev/null +++ b/GCANet/model_convert/GCANet.json @@ -0,0 +1,50 @@ +{ + "input": "./gcanet_dehaze_512x512_sim.onnx", + "output_dir": "./GCANet_fix", + "output_name": "GCANet_U16.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./reside_quant_npy.tar.gz", + "calibration_format": "Numpy", + "calibration_size": 1, + "calibration_mean": [0,0,0,0], + "calibration_std": [1,1,1,1] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + "device": "cpu", + "layer_configs": [ + { + "start_tensor_names": ["DEFAULT"], + "end_tensor_names": ["DEFAULT"], + "data_type": "U16" + } + ] + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "AutoColorSpace", + "tensor_layout": "NCHW", + "src_format": "AutoColorSpace", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC", + "mean": [128,128,128,128], + "std": [1,1,1,1] + } + ], + "compiler": { + "check": 2 + } +} + diff --git a/GCANet/model_convert/axmodel/GCANet_U16.axmodel b/GCANet/model_convert/axmodel/GCANet_U16.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..a01b2b420d3916bcef4ff9d79e6433321c8757ed --- /dev/null +++ b/GCANet/model_convert/axmodel/GCANet_U16.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e56c416223bd2737dcf7af420e628d5a100ee07b53533fdee3ee09baa66f895 +size 3573592 diff --git a/GCANet/pic/0051_0.8_0.2_input.png b/GCANet/pic/0051_0.8_0.2_input.png new file mode 100644 index 0000000000000000000000000000000000000000..7c700ca89979878a6df5e1634320b14e8da4c426 --- /dev/null +++ b/GCANet/pic/0051_0.8_0.2_input.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae2885d3ccc0f5723805dabb6b865226b7da7c2236b5b41f828a0e017b145c4e +size 240849 diff --git a/GCANet/pic/0099_0.9_0.16_input.png b/GCANet/pic/0099_0.9_0.16_input.png new file mode 100644 index 0000000000000000000000000000000000000000..3571c36542c17d91d4b86aa0fd5b6b3774bf828e --- /dev/null +++ b/GCANet/pic/0099_0.9_0.16_input.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4cbdd98b9c6b465c4557134ca6c82f74cfbd15e0c6f97e14e63f5141c6804cd +size 597179 diff --git a/GCANet/python/axmodel_infer.py b/GCANet/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..08b33a6c28690a98182a2402ad629123cb6fd22e --- /dev/null +++ b/GCANet/python/axmodel_infer.py @@ -0,0 +1,123 @@ +import argparse +import os + +import numpy as np +import axengine as axe +from PIL import Image, ImageDraw, ImageFont + + +HEIGHT = 512 +WIDTH = 512 +IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP') + + +def parse_args(): + parser = argparse.ArgumentParser(description='Run GCANet axmodel inference with fixed 512x512 input.') + parser.add_argument('--task', default='dehaze', choices=['dehaze', 'derain']) + parser.add_argument('--axmodel', default='GCANet_u16.axmodel', help='Path to axmodel model.') + parser.add_argument('--indir', default='examples') + parser.add_argument('--outdir', default='axmodel_output') + parser.add_argument( + '--input-mode', + default='raw_u8', + choices=['compat_centered_u8', 'raw_u8'], + help='compat_centered_u8 keeps the historical centered input encoding for the current axmodel; raw_u8 follows the declared U8 input processor literally.', + ) + return parser.parse_args() + + +def make_dataset(image_dir): + images = [] + assert os.path.isdir(image_dir), '%s is not a valid directory' % image_dir + for root, _, fnames in sorted(os.walk(image_dir)): + for fname in fnames: + if fname.endswith(IMG_EXTENSIONS): + images.append(os.path.join(root, fname)) + return images + + +def edge_compute_np(img_chw): + x_diffx = np.abs(img_chw[:, :, 1:] - img_chw[:, :, :-1]) + x_diffy = np.abs(img_chw[:, 1:, :] - img_chw[:, :-1, :]) + + edge = np.zeros_like(img_chw, dtype=np.float32) + edge[:, :, 1:] += x_diffx + edge[:, :, :-1] += x_diffx + edge[:, 1:, :] += x_diffy + edge[:, :-1, :] += x_diffy + edge = np.sum(edge, axis=0, keepdims=True) / 3.0 + edge = edge / 4.0 + return edge.astype(np.float32) + + +def preprocess(img_path, input_mode): + img = Image.open(img_path).convert('RGB') + img = img.resize((WIDTH, HEIGHT), Image.BICUBIC) + img_rgb = np.array(img).astype(np.float32) + img_rgb_chw = np.transpose(img_rgb, (2, 0, 1)) + edge = edge_compute_np(img_rgb_chw) + + # This 4-channel model is calibrated from RGB+edge NCHW tensors. + # The build config marks src_format=BGR/tensor_format=RGB, but with + # csc_mode=NoCSC there is no runtime color conversion, so feeding BGR here + # would silently swap channels and introduce artifacts. + model_input = np.concatenate((img_rgb_chw, edge), axis=0)[None, :, :, :] + if input_mode == 'compat_centered_u8': + # The original float model consumes (rgb+edge-128). The current axmodel + # was compiled as U8 input without explicit dequant params, so we keep + # the historical wraparound encoding here for compatibility. + model_input = np.mod(np.round(model_input - 128.0), 256.0).astype(np.uint8) + else: + model_input = np.clip(np.round(model_input), 0, 255).astype(np.uint8) + return img_rgb_chw, model_input + + +def postprocess(pred, img_chw, only_residual): + out = pred[0].astype(np.float32) + if only_residual: + out = out + img_chw + out = np.round(out).clip(0, 255).astype(np.uint8) + out = np.transpose(out, (1, 2, 0)) + return out + + +def make_comparison_image(input_chw, output_hwc, task): + input_hwc = np.transpose(input_chw, (1, 2, 0)).clip(0, 255).astype(np.uint8) + input_img = Image.fromarray(input_hwc, mode='RGB') + output_img = Image.fromarray(output_hwc, mode='RGB') + + title_height = 32 + comparison = Image.new('RGB', (WIDTH * 2, HEIGHT + title_height), color='white') + comparison.paste(input_img, (0, title_height)) + comparison.paste(output_img, (WIDTH, title_height)) + + draw = ImageDraw.Draw(comparison) + font = ImageFont.load_default() + right_label = 'dehaze' if task == 'dehaze' else task + draw.text((8, 8), 'hazy', fill='black', font=font) + draw.text((WIDTH + 8, 8), right_label, fill='black', font=font) + draw.line((WIDTH, 0, WIDTH, HEIGHT + title_height), fill='black', width=1) + return comparison + + +def main(): + args = parse_args() + axmodel_path = args.axmodel or os.path.join('onnx', 'gcanet_%s_512x512_sim.axmodel' % args.task) + only_residual = args.task == 'dehaze' + os.makedirs(args.outdir, exist_ok=True) + + session = axe.InferenceSession(axmodel_path, providers=['AxEngineExecutionProvider']) + input_name = session.get_inputs()[0].name + + for img_path in make_dataset(args.indir): + img_chw, model_input = preprocess(img_path, args.input_mode) + pred = session.run(None, {input_name: model_input})[0] + out_img = postprocess(pred, img_chw, only_residual) + comparison = make_comparison_image(img_chw, out_img, args.task) + save_name = os.path.splitext(os.path.basename(img_path))[0] + '_%s_compare.png' % args.task + comparison.save(os.path.join(args.outdir, save_name)) + print('Saved:', os.path.join(args.outdir, save_name)) + + +if __name__ == '__main__': + main() diff --git a/GCANet/python/onnx_infer.py b/GCANet/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..7d53e6278591440d08f010a91ea363850f4bc10d --- /dev/null +++ b/GCANet/python/onnx_infer.py @@ -0,0 +1,85 @@ +import argparse +import os + +import numpy as np +import onnxruntime as ort +from PIL import Image + + +HEIGHT = 512 +WIDTH = 512 +IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP') + + +def parse_args(): + parser = argparse.ArgumentParser(description='Run GCANet ONNX inference with fixed 512x512 input.') + parser.add_argument('--task', default='dehaze', choices=['dehaze', 'derain']) + parser.add_argument('--onnx', default=None, help='Path to ONNX model. Default: onnx/gcanet_{task}_512x512_sim.onnx') + parser.add_argument('--indir', default='examples') + parser.add_argument('--outdir', default='onnx_output') + return parser.parse_args() + + +def make_dataset(image_dir): + images = [] + assert os.path.isdir(image_dir), '%s is not a valid directory' % image_dir + for root, _, fnames in sorted(os.walk(image_dir)): + for fname in fnames: + if fname.endswith(IMG_EXTENSIONS): + images.append(os.path.join(root, fname)) + return images + + +def edge_compute_np(img_chw): + x_diffx = np.abs(img_chw[:, :, 1:] - img_chw[:, :, :-1]) + x_diffy = np.abs(img_chw[:, 1:, :] - img_chw[:, :-1, :]) + + edge = np.zeros_like(img_chw, dtype=np.float32) + edge[:, :, 1:] += x_diffx + edge[:, :, :-1] += x_diffx + edge[:, 1:, :] += x_diffy + edge[:, :-1, :] += x_diffy + edge = np.sum(edge, axis=0, keepdims=True) / 3.0 + edge = edge / 4.0 + return edge.astype(np.float32) + + +def preprocess(img_path): + img = Image.open(img_path).convert('RGB') + img = img.resize((WIDTH, HEIGHT), Image.BICUBIC) + img_np = np.array(img).astype(np.float32) + img_chw = np.transpose(img_np, (2, 0, 1)) + edge = edge_compute_np(img_chw) + model_input = np.concatenate((img_chw, edge), axis=0)[None, :, :, :] - 128.0 + return img_chw, model_input.astype(np.float32) + + +def postprocess(pred, img_chw, only_residual): + out = pred[0] + if only_residual: + out = out + img_chw + out = np.round(out).clip(0, 255).astype(np.uint8) + out = np.transpose(out, (1, 2, 0)) + return out + + +def main(): + args = parse_args() + onnx_path = args.onnx or os.path.join('onnx', 'gcanet_%s_512x512_sim.onnx' % args.task) + only_residual = args.task == 'dehaze' + os.makedirs(args.outdir, exist_ok=True) + + session = ort.InferenceSession(onnx_path, providers=['CPUExecutionProvider']) + input_name = session.get_inputs()[0].name + + for img_path in make_dataset(args.indir): + img_chw, model_input = preprocess(img_path) + pred = session.run(None, {input_name: model_input})[0] + out_img = postprocess(pred, img_chw, only_residual) + save_name = os.path.splitext(os.path.basename(img_path))[0] + '_%s_onnx.png' % args.task + Image.fromarray(out_img).save(os.path.join(args.outdir, save_name)) + print('Saved:', os.path.join(args.outdir, save_name)) + + +if __name__ == '__main__': + main() diff --git a/GCANet/res/0051_0.8_0.2_input_dehaze_compare.png b/GCANet/res/0051_0.8_0.2_input_dehaze_compare.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8007ab645989c0a027b68715d995b521ba7aa5 --- /dev/null +++ b/GCANet/res/0051_0.8_0.2_input_dehaze_compare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c1b4c26621dd57b34efd54ab25e4b76eee88f211194038673d2947fd3c9a770 +size 630147 diff --git a/GCANet/res/0099_0.9_0.16_input_dehaze_compare.png b/GCANet/res/0099_0.9_0.16_input_dehaze_compare.png new file mode 100644 index 0000000000000000000000000000000000000000..0ab1ccf2ee41cad0584a8e49409ae9be16e9751e --- /dev/null +++ b/GCANet/res/0099_0.9_0.16_input_dehaze_compare.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f005b85b423047c5b67fdeab0777fa6032aaf33611f190a3ec4672689d16513f +size 903554 diff --git a/GridDehazeNet/model_convert/GridDehazeNet.json b/GridDehazeNet/model_convert/GridDehazeNet.json new file mode 100644 index 0000000000000000000000000000000000000000..1d44ab7dc99ade05a4ee426b375db8409a463cee --- /dev/null +++ b/GridDehazeNet/model_convert/GridDehazeNet.json @@ -0,0 +1,41 @@ +{ + "input": "./griddehazenet_outdoor_1x3x480x640_npu_safe.onnx", + "output_dir": "./GridDehazeNet", + "output_name": "GridDehazeNet.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./RESIDE.tar.gz", + "calibration_format": "Image", + "calibration_size": 32, + "calibration_mean": [127.5, 127.5, 127.5], + "calibration_std": [127.5, 127.5, 127.5] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + "device": "cuda:1", + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + ], + "compiler": { + "check": 2 + } +} + diff --git a/GridDehazeNet/model_convert/axmodel/GridDehazeNet.axmodel b/GridDehazeNet/model_convert/axmodel/GridDehazeNet.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..b7e45aaec0902063ef238be4f218765b67d69387 --- /dev/null +++ b/GridDehazeNet/model_convert/axmodel/GridDehazeNet.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:996e7c24541934f003860a9c60af7afddcdb3a4042ae1be31cd8d9f057815b47 +size 4680186 diff --git a/GridDehazeNet/pic/0001_0.8_0.2.jpg b/GridDehazeNet/pic/0001_0.8_0.2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f74de07e24b2e42b974dffdd23f666b9164a163f --- /dev/null +++ b/GridDehazeNet/pic/0001_0.8_0.2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe92fccd590a6a354d8e526b0bfbb241fd5f26118ba1df96a213c3c9dc28929a +size 163827 diff --git a/GridDehazeNet/python/axmodel_infer.py b/GridDehazeNet/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..a1be071382c018a42a7cfc63a3bfd81464d13264 --- /dev/null +++ b/GridDehazeNet/python/axmodel_infer.py @@ -0,0 +1,81 @@ +""" +Run GridDehazeNet axmodel inference on a single image. +Output: side-by-side image (Hazy | Dehazed), result resized to original size. +""" +import argparse +import os + +import numpy as np +import axengine as axe +from PIL import Image, ImageDraw, ImageFont + + +def _get_font(size): + for path in ( + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", + "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", + "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", + ): + if os.path.exists(path): + return ImageFont.truetype(path, size) + return ImageFont.load_default() + + +def _label_image(image, text, font): + draw = ImageDraw.Draw(image) + bbox = draw.textbbox((0, 0), text, font=font) + w, h = bbox[2] - bbox[0], bbox[3] - bbox[1] + pad = 6 + draw.rectangle([(0, 0), (w + pad * 2, h + pad * 2)], fill=(0, 0, 0, 180)) + draw.text((pad, pad), text, fill=(255, 255, 255), font=font) + + +def _tensor_to_image(tensor): + arr = np.squeeze(tensor, axis=0).transpose(1, 2, 0) + arr = np.clip(arr, 0.0, 1.0) + return Image.fromarray((arr * 255.0 + 0.5).astype(np.uint8)) + + +def main(): + parser = argparse.ArgumentParser(description="GridDehazeNet axmodel single-image inference") + parser.add_argument("-axmodel", default="GridDehazeNet.axmodel") + parser.add_argument("-input", default="./0001_0.8_0.2.jpg", help="image file") + parser.add_argument("-output", default="axmodel_result.png") + parser.add_argument("-height", type=int, default=480) + parser.add_argument("-width", type=int, default=640) + args = parser.parse_args() + + font = _get_font(24) + session = axe.InferenceSession(args.axmodel, providers=["AxEngineExecutionProvider"]) + input_name = session.get_inputs()[0].name + output_name = session.get_outputs()[0].name + + original = Image.open(args.input).convert("RGB") + orig_w, orig_h = original.size + + image = Image.open(args.input).convert("RGB") + if image.size != (args.width, args.height): + image = image.resize((args.width, args.height), Image.BICUBIC) + arr = np.asarray(image).astype(np.float32) + arr = arr.transpose(2, 0, 1)[None, ...] + arr = arr.astype(np.uint8) + + out = session.run([output_name], {input_name: arr})[0] + dehazed = _tensor_to_image(out) + if dehazed.size != (orig_w, orig_h): + dehazed = dehazed.resize((orig_w, orig_h), Image.BICUBIC) + + hazy_labeled = original.copy() + dehazed_labeled = dehazed.copy() + _label_image(hazy_labeled, "Hazy", font) + _label_image(dehazed_labeled, "Dehazed", font) + + concat = Image.new("RGB", (orig_w * 2, orig_h)) + concat.paste(hazy_labeled, (0, 0)) + concat.paste(dehazed_labeled, (orig_w, 0)) + concat.save(args.output) + print(args.output) + + +if __name__ == "__main__": + main() diff --git a/GridDehazeNet/python/onnx_infer.py b/GridDehazeNet/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..f99c43484ff3c3bc387585279923c3216bf8f031 --- /dev/null +++ b/GridDehazeNet/python/onnx_infer.py @@ -0,0 +1,81 @@ +""" +Run GridDehazeNet ONNX inference on a single image. +Output: side-by-side image (Hazy | Dehazed), result resized to original size. +""" +import argparse +import os + +import numpy as np +import onnxruntime as ort +from PIL import Image, ImageDraw, ImageFont + + +def _get_font(size): + for path in ( + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", + "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", + "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", + ): + if os.path.exists(path): + return ImageFont.truetype(path, size) + return ImageFont.load_default() + + +def _label_image(image, text, font): + draw = ImageDraw.Draw(image) + bbox = draw.textbbox((0, 0), text, font=font) + w, h = bbox[2] - bbox[0], bbox[3] - bbox[1] + pad = 6 + draw.rectangle([(0, 0), (w + pad * 2, h + pad * 2)], fill=(0, 0, 0, 180)) + draw.text((pad, pad), text, fill=(255, 255, 255), font=font) + + +def _tensor_to_image(tensor): + arr = np.squeeze(tensor, axis=0).transpose(1, 2, 0) + arr = np.clip(arr, 0.0, 1.0) + return Image.fromarray((arr * 255.0 + 0.5).astype(np.uint8)) + + +def main(): + parser = argparse.ArgumentParser(description="GridDehazeNet ONNX single-image inference") + parser.add_argument("-onnx", default="griddehazenet_outdoor_1x3x480x640_npu_safe.onnx") + parser.add_argument("-input", default="data/test/SOTS/outdoor/hazy/0001_0.8_0.2.jpg", help="image file") + parser.add_argument("-output", default="onnx_result_concat.png") + parser.add_argument("-height", type=int, default=480) + parser.add_argument("-width", type=int, default=640) + args = parser.parse_args() + + font = _get_font(24) + session = ort.InferenceSession(args.onnx, providers=["CUDAExecutionProvider", "CPUExecutionProvider"]) + input_name = session.get_inputs()[0].name + output_name = session.get_outputs()[0].name + + original = Image.open(args.input).convert("RGB") + orig_w, orig_h = original.size + + image = Image.open(args.input).convert("RGB") + if image.size != (args.width, args.height): + image = image.resize((args.width, args.height), Image.BICUBIC) + arr = np.asarray(image).astype(np.float32) / 255.0 + arr = (arr - 0.5) / 0.5 + arr = arr.transpose(2, 0, 1)[None, ...] + + out = session.run([output_name], {input_name: arr})[0] + dehazed = _tensor_to_image(out) + if dehazed.size != (orig_w, orig_h): + dehazed = dehazed.resize((orig_w, orig_h), Image.BICUBIC) + + hazy_labeled = original.copy() + dehazed_labeled = dehazed.copy() + _label_image(hazy_labeled, "Hazy", font) + _label_image(dehazed_labeled, "Dehazed", font) + + concat = Image.new("RGB", (orig_w * 2, orig_h)) + concat.paste(hazy_labeled, (0, 0)) + concat.paste(dehazed_labeled, (orig_w, 0)) + concat.save(args.output) + print(args.output) + + +if __name__ == "__main__": + main() diff --git a/GridDehazeNet/res/axmodel_result.png b/GridDehazeNet/res/axmodel_result.png new file mode 100644 index 0000000000000000000000000000000000000000..5487c706fb422353ebec6f631eae50e9ad465a5d --- /dev/null +++ b/GridDehazeNet/res/axmodel_result.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:118c4b3f2a5b450197a148b189358fffbe9bd3f7f0d678c7a3f450410c3211e9 +size 712177 diff --git a/LightDehazeNet/model_convert/LightDehazeNet.json b/LightDehazeNet/model_convert/LightDehazeNet.json new file mode 100644 index 0000000000000000000000000000000000000000..6852192dcf7b995d7c659031d9ef73f9a9236732 --- /dev/null +++ b/LightDehazeNet/model_convert/LightDehazeNet.json @@ -0,0 +1,39 @@ +{ + "input": "./trained_LDNet.onnx", + "output_dir": "./LightDehazeNet", + "output_name": "LightDehazeNet.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./hazy.tar.gz", + "calibration_size": -1, + "calibration_mean": [0,0,0], + "calibration_std": [255,255,255] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + ], + "compiler": { + "check": 2 + } +} + diff --git a/LightDehazeNet/model_convert/axmodel/LightDehazeNet.axmodel b/LightDehazeNet/model_convert/axmodel/LightDehazeNet.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..ee7f3598f92c83be9a17b7d7da199e1103df0e98 --- /dev/null +++ b/LightDehazeNet/model_convert/axmodel/LightDehazeNet.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eecc48deeca9e464ff9dc84558946064dc86b8da3fc7306fb9628888bb5d4cf1 +size 115003 diff --git a/LightDehazeNet/pic/indoor_synthetic/sih(1).jpg b/LightDehazeNet/pic/indoor_synthetic/sih(1).jpg new file mode 100644 index 0000000000000000000000000000000000000000..79e25f5dfa6ee0dc0ce1aa2a5c0fd4186e14ff38 --- /dev/null +++ b/LightDehazeNet/pic/indoor_synthetic/sih(1).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f31ba97796e3219b7987e5ba55ac6c1e2649a9dd9988f79f8e0800423df1c6a6 +size 37034 diff --git a/LightDehazeNet/pic/indoor_synthetic/sih(2).jpg b/LightDehazeNet/pic/indoor_synthetic/sih(2).jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd42e2284a693783199529fdaa9eb2922a354641 --- /dev/null +++ b/LightDehazeNet/pic/indoor_synthetic/sih(2).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1a500995d4670bac635de74701750d67f37f3528c51b06104867260991afb30 +size 28820 diff --git a/LightDehazeNet/pic/indoor_synthetic/sih(3).jpg b/LightDehazeNet/pic/indoor_synthetic/sih(3).jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e8b3468a1cdcf9ac009b882add8e27e17fa417d --- /dev/null +++ b/LightDehazeNet/pic/indoor_synthetic/sih(3).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d31e7e28799a7142eaf52a3adda3c91d38ea10ed7e8f1b7196d4b179439f316 +size 21020 diff --git a/LightDehazeNet/pic/indoor_synthetic/sih(4).jpg b/LightDehazeNet/pic/indoor_synthetic/sih(4).jpg new file mode 100644 index 0000000000000000000000000000000000000000..90a438a8eaa1b27859882e549a9c11a20d894a64 --- /dev/null +++ b/LightDehazeNet/pic/indoor_synthetic/sih(4).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e4179e0d71069d2046d76bb299fdd8108180e1165cadfdab891ca3e129fc7e7 +size 25965 diff --git a/LightDehazeNet/pic/indoor_synthetic/sih(5).jpg b/LightDehazeNet/pic/indoor_synthetic/sih(5).jpg new file mode 100644 index 0000000000000000000000000000000000000000..56d1981f1dd8759d8ca5ca78616116a3bd7f148e --- /dev/null +++ b/LightDehazeNet/pic/indoor_synthetic/sih(5).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7dd60202f6930d4d71df49ca46bba09317fc08b7a8703a0e2f89d6fead22b90 +size 38037 diff --git a/LightDehazeNet/pic/outdoor_natural/nh(1).jpg b/LightDehazeNet/pic/outdoor_natural/nh(1).jpg new file mode 100644 index 0000000000000000000000000000000000000000..edb01790609c8b3a61f13b717082c4e8923c0000 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_natural/nh(1).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e5bc940ea9778b5184bac10613a1262a7b125abcb7e4248b04ed4ea540bc6c6 +size 567344 diff --git a/LightDehazeNet/pic/outdoor_natural/nh(2).jpg b/LightDehazeNet/pic/outdoor_natural/nh(2).jpg new file mode 100644 index 0000000000000000000000000000000000000000..f111250bfed72c0af92ae77a69aa0880749b9413 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_natural/nh(2).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30ee21c78343095e984370e392e396a090fad53222e6cb1422607a91ffc01d9c +size 199545 diff --git a/LightDehazeNet/pic/outdoor_natural/nh(3).jpg b/LightDehazeNet/pic/outdoor_natural/nh(3).jpg new file mode 100644 index 0000000000000000000000000000000000000000..70570165acec9147dc31f80fbb782c6840712da2 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_natural/nh(3).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:524f451be9f6de579334bcc9bb5c7e7c0f6cfd0aa2bc9f71bc9392de65e81db3 +size 36409 diff --git a/LightDehazeNet/pic/outdoor_natural/nh(4).jpg b/LightDehazeNet/pic/outdoor_natural/nh(4).jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea0d2ee4ede2364a7363849dded910cefe5ab5a5 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_natural/nh(4).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1aac5f17ecf644ffc0e3939d0c7060776f6761998d23fb8a76648dc99903d9bc +size 26263 diff --git a/LightDehazeNet/pic/outdoor_natural/nh(5).png b/LightDehazeNet/pic/outdoor_natural/nh(5).png new file mode 100644 index 0000000000000000000000000000000000000000..8bc1267cd1000d5fc3bfcc67eb0e8876dc1f57b3 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_natural/nh(5).png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a6be814342c375a064ff7939ea316cd0dee0ec6058fc10fb6616eef0afbc509 +size 354406 diff --git a/LightDehazeNet/pic/outdoor_synthetic/soh(1).jpg b/LightDehazeNet/pic/outdoor_synthetic/soh(1).jpg new file mode 100644 index 0000000000000000000000000000000000000000..97dc21a1c0af248de368a1651a839d20b0258736 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_synthetic/soh(1).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:103aace231bd6c5b5f14ad5f009cb8fea60e1397b581f83fdeea4610b2aba013 +size 140885 diff --git a/LightDehazeNet/pic/outdoor_synthetic/soh(2).jpg b/LightDehazeNet/pic/outdoor_synthetic/soh(2).jpg new file mode 100644 index 0000000000000000000000000000000000000000..fdad1399ac12e5eee564f1865f7ae0e2c7330394 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_synthetic/soh(2).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab7e36204545c8cda8444205ab523f7c32b39d7cba259b97237c416b9c41ce2d +size 141592 diff --git a/LightDehazeNet/pic/outdoor_synthetic/soh(3).jpg b/LightDehazeNet/pic/outdoor_synthetic/soh(3).jpg new file mode 100644 index 0000000000000000000000000000000000000000..f903950f581465a1c37958be088048543fc4aebd --- /dev/null +++ b/LightDehazeNet/pic/outdoor_synthetic/soh(3).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f7554c7ec187dfb379f51f268d9212ea0f661bd6b86b491d6459ef5c37de68e +size 118652 diff --git a/LightDehazeNet/pic/outdoor_synthetic/soh(4).jpg b/LightDehazeNet/pic/outdoor_synthetic/soh(4).jpg new file mode 100644 index 0000000000000000000000000000000000000000..29132b539480c83dba0d2e15f0c002b5a43ce176 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_synthetic/soh(4).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5effd1a6322878b9f2dc832b042d401fcf2b3daadd9131b966aa01ec035c1f9 +size 130647 diff --git a/LightDehazeNet/pic/outdoor_synthetic/soh(5).jpg b/LightDehazeNet/pic/outdoor_synthetic/soh(5).jpg new file mode 100644 index 0000000000000000000000000000000000000000..4a242887a55bf70024f2a794eb37ad316c0cb520 --- /dev/null +++ b/LightDehazeNet/pic/outdoor_synthetic/soh(5).jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71a724335bff81b44b9bba62f07501a61532acf1a1e5401a10a1b450c24f3745 +size 114954 diff --git a/LightDehazeNet/python/axmodel_infer.py b/LightDehazeNet/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..d217b85da8b175cb3c69dc02d47478ddacce04f9 --- /dev/null +++ b/LightDehazeNet/python/axmodel_infer.py @@ -0,0 +1,61 @@ +import argparse +import os + +import numpy as np +import axengine as axe +from PIL import Image + + +def preprocess(image_path, height=None, width=None): + image = Image.open(image_path).convert("RGB") + if height is not None and width is not None: + image = image.resize((width, height), Image.BICUBIC) + # 不做均值归一化,仅转 NCHW/RGB,保存为 uint8 + image_np = np.asarray(image).astype(np.uint8) + image_np = np.transpose(image_np, (2, 0, 1))[np.newaxis, ...] + return image_np, image + + +def postprocess(output): + output = np.squeeze(output, axis=0) + output = np.transpose(output, (1, 2, 0)) + output = np.clip(output, 0.0, 1.0) + return Image.fromarray((output * 255.0).round().astype(np.uint8)) + + +def infer_image(args): + session = axe.InferenceSession(args.axmodel, providers=["AxEngineExecutionProvider"]) + input_name = session.get_inputs()[0].name + output_name = session.get_outputs()[0].name + + input_tensor, hazy_image = preprocess(args.image, args.height, args.width) + output = session.run([output_name], {input_name: input_tensor})[0] + dehaze_image = postprocess(output) + + output_dir = os.path.dirname(args.output) + if output_dir: + os.makedirs(output_dir, exist_ok=True) + + compare_image = Image.new("RGB", (hazy_image.width + dehaze_image.width, hazy_image.height)) + compare_image.paste(hazy_image, (0, 0)) + compare_image.paste(dehaze_image, (hazy_image.width, 0)) + compare_image.save(args.output) + print("Comparison image saved to:", args.output) + + +def parse_args(): + parser = argparse.ArgumentParser(description="Run Light-DehazeNet axmodel inference on one image.") + parser.add_argument("-m", "--axmodel", default="./LightDehazeNet.axmodel", help="path to axmodel model") + parser.add_argument("-i", "--image", default='query_hazy_images/outdoor_natural/nh(5).png', help="path to input hazy image") + parser.add_argument("-o", "--output", default="axmodel_dehaze.jpg", help="path to save side-by-side comparison image") + parser.add_argument("--height", type=int, default=640, help="resize input to this height before inference") + parser.add_argument("--width", type=int, default=480, help="resize input to this width before inference") + args = parser.parse_args() + + if (args.height is None) != (args.width is None): + parser.error("--height and --width must be specified together") + return args + + +if __name__ == "__main__": + infer_image(parse_args()) diff --git a/LightDehazeNet/res/axmodel_dehaze.jpg b/LightDehazeNet/res/axmodel_dehaze.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ce075141f9115ec3bd5e807de8bd9037a124356 --- /dev/null +++ b/LightDehazeNet/res/axmodel_dehaze.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e9320790e7959654783415dfef44af17aa9c9b48bf9bba7864ed6beb016a2d6 +size 60470 diff --git a/MixDehazeNet/README.md b/MixDehazeNet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..37b6c214ba0517cce1963f0847d79f07af657f72 --- /dev/null +++ b/MixDehazeNet/README.md @@ -0,0 +1,89 @@ +--- +license: mit +language: +- en +pipeline_tag: image-to-image +tags: +- dehazing +- ImageEnhancement + +# ImageDehazing + +This is a collection of image dehazing algorithms, models have been converted to run on the Axera NPU using **w8a8** quantization. + +This model has been optimized with the following LoRA: + +Compatible with Pulsar2 version: 6.0 115775d3 + +## Convert tools links: + +For those who are interested in model conversion, you can try to export axmodel through + +- [Pulsar2 Link, How to Convert ONNX to axmodel](https://pulsar2-docs.readthedocs.io/en/latest/pulsar2/introduction.html) + + +## Support Platform + +- AX650 + - [M4N-Dock(爱芯派Pro)](https://wiki.sipeed.com/hardware/zh/maixIV/m4ndock/m4ndock.html) + - [M.2 Accelerator card](https://docs.m5stack.com/en/ai_hardware/LLM-8850_Card) + + +| 模型 | 输入分辨率 | AX650板端耗时 | +|------|-----------|--------------| +| AOD-Net | 640×480 | 2.4 ms | +| Light-Dehazenet | 480×640 | 9.2 ms | +| DehazeFormer_t | 512×512 | 161ms | +| MixDehazeNet | 256×256 | 38 ms | +| GCANet | 512×512 | 80 ms | +| GridDehazeNet | 640×480 | 113 ms | +| DEA-Net | 512×512 | 127 ms | +| FFA-Net | 512×512 | 873 ms | + + +## How to use + +Download all files from this repository to the device + +``` +所有模型文件组织方式均如下: +. +|-- model_convert +| |-- axmodel +| | `-- dehazeformer-t-512-constant.axmodel +| `-- dehazeformer.json +|-- pic +| `-- 00000_0_0.1800.png +|-- python +| |-- axmodel_infer.py +| `-- onnx_infer.py +`-- res + `-- output.png + +``` + +### Inference +#### Inference with AX650 Host, such as M4N-Dock(爱芯派Pro) +模型推理,执行命令: +``` +(base) root@ax650:~/GCANet# python axmodel_infer.py +[INFO] Available providers: ['AxEngineExecutionProvider', 'AXCLRTExecutionProvider'] +[INFO] Using provider: AxEngineExecutionProvider +[INFO] Chip type: ChipType.MC50 +[INFO] VNPU type: VNPUType.DISABLED +[INFO] Engine version: 2.12.0s +[INFO] Model type: 2 (triple core) +[INFO] Compiler version: 7.0 22923e4e +Saved: axmodel_output/0051_0.8_0.2_input_dehaze_compare.png +Saved: axmodel_output/0099_0.9_0.16_input_dehaze_compare.png + + +``` + +``` +推理结果样例: + +![](GCANet/res/0099_0.9_0.16_input_dehaze_compare.png) +![](GCANet/res/0051_0.8_0.2_input_dehaze_compare.png) + +``` \ No newline at end of file diff --git a/MixDehazeNet/model_convert/MixDehazeNet.json b/MixDehazeNet/model_convert/MixDehazeNet.json new file mode 100644 index 0000000000000000000000000000000000000000..4fee82b6865aa910b699c89284cc1710e29ba832 --- /dev/null +++ b/MixDehazeNet/model_convert/MixDehazeNet.json @@ -0,0 +1,59 @@ +{ + "input": "./MixDehazeNet_s_256.onnx", + "output_dir": "./MixDehazeNet", + "output_name": "MixDehazeNet.axmodel", + "model_type": "ONNX", + "target_hardware": "AX650", + "npu_mode": "NPU3", + "quant": { + "input_configs": [ + { + "tensor_name": "input", + "calibration_dataset": "./RESIDE.tar.gz", + "calibration_format": "Image", + "calibration_size": 32, + "calibration_mean": [127.5, 127.5, 127.5], + "calibration_std": [127.5, 127.5, 127.5] + } + ], + "calibration_method": "MinMax", + "precision_analysis": true, + "transformer_opt_level":1, + // "enable_smooth_quant": true, + "precision_analysis_method": "EndToEnd", + "precision_analysis_mode": "NPUBackend", + "device": "cuda:3", + // "layer_configs": [ + // { + // "start_tensor_names": ["DEFAULT"], + // "end_tensor_names": ["DEFAULT"], + // "data_type": "U16" + // } + // ] + }, + "input_processors": [ + { + "tensor_name": "input", + "tensor_format": "RGB", + "tensor_layout": "NCHW", + "src_format": "BGR", + "src_dtype": "U8", + "src_layout": "NCHW", + "csc_mode": "NoCSC" + } + // { + // "tensor_name": "input", + // "tensor_format": "RGB", + // "tensor_layout": "NCHW", + // "src_format": "YUV420SP", + // "src_layout": "NHWC", + // "src_dtype": "U8", + // "csc_mode": "FullRange", + // "csc_mat": [1.164, 0, 1.596, -222.912, 1.164, -0.392, -0.813, 135.616, 1.164, 2.017, 0, -276.8] + // } + ], + "compiler": { + "check": 2 + } +} + diff --git a/MixDehazeNet/model_convert/axmodel/MixDehazeNet.axmodel b/MixDehazeNet/model_convert/axmodel/MixDehazeNet.axmodel new file mode 100644 index 0000000000000000000000000000000000000000..67b51610862aeb9cf379e359247240962f189a97 --- /dev/null +++ b/MixDehazeNet/model_convert/axmodel/MixDehazeNet.axmodel @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58dd507bceeae2bcd7b09473b38886c0a3e1077dee8e6bc144e32dc94ef6ecae +size 9838777 diff --git a/MixDehazeNet/pic/0003_0.8_0.2.jpg b/MixDehazeNet/pic/0003_0.8_0.2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1c9f6def0735e31ead3e5a3e253642183e5faae0 --- /dev/null +++ b/MixDehazeNet/pic/0003_0.8_0.2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b093fd7e17d27cd57327dbb9ddfae36c2db1e539320e0266f0eaa6942f086a5 +size 77365 diff --git a/MixDehazeNet/python/axmodel_infer.py b/MixDehazeNet/python/axmodel_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..cd18c609bfd7955b8a176ba13019e43e3f485407 --- /dev/null +++ b/MixDehazeNet/python/axmodel_infer.py @@ -0,0 +1,74 @@ +import argparse +import os + +import cv2 +import numpy as np +import axengine as axe + + +def read_image(path, height, width): + image = cv2.imread(path, cv2.IMREAD_COLOR) + if image is None: + raise FileNotFoundError(path) + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + # 不做均值归一化,仅转 NCHW/RGB,保存为 uint8 + image = image.astype(np.uint8) + if image.shape[:2] != (height, width): + image = cv2.resize(image, (width, height), interpolation=cv2.INTER_LINEAR) + image = np.transpose(image, (2, 0, 1))[None].copy() + return image + + +def save_comparison(hazy_orig, output, path): + """拼接原图(hazy)与去雾图(dehazed)并标注文字,还原到原图大小""" + orig_h, orig_w = hazy_orig.shape[:2] + + # output: NCHW float32 [-1, 1] → 还原到原图大小 HWC uint8 BGR + output = np.clip(output[0], -1.0, 1.0) + output = output * 0.5 + 0.5 + output = (np.transpose(output, (1, 2, 0)) * 255).astype(np.uint8) + if output.shape[:2] != (orig_h, orig_w): + output = cv2.resize(output, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR) + output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) + + h, w = hazy_orig.shape[:2] + font = cv2.FONT_HERSHEY_SIMPLEX + font_scale = max(h, w) / 512.0 + thickness = max(1, int(font_scale * 2)) + color = (255, 255, 255) + + # 加 hazy 标注 + cv2.putText(hazy_orig, 'hazy', (int(10 * font_scale), int(30 * font_scale)), + font, font_scale, color, thickness, cv2.LINE_AA) + # 加 dehazed 标注 + cv2.putText(output, 'dehazed', (int(10 * font_scale), int(30 * font_scale)), + font, font_scale, color, thickness, cv2.LINE_AA) + + compare = np.concatenate([hazy_orig, output], axis=1) + os.makedirs(os.path.dirname(path) or '.', exist_ok=True) + cv2.imwrite(path, compare) + + +def main(): + parser = argparse.ArgumentParser(description='Run MixDehazeNet axmodel inference.') + parser.add_argument('--axmodel', default='MixDehazeNet.axmodel', help='Path to axmodel model.') + parser.add_argument('--image', default='0003_0.8_0.2.jpg', help='Input hazy image.') + parser.add_argument('--height', type=int, default=256) + parser.add_argument('--width', type=int, default=256) + parser.add_argument('--output', default='./axmodel_res.png') + args = parser.parse_args() + + # 读取原图用于显示(BGR uint8) + hazy_orig = cv2.imread(args.image, cv2.IMREAD_COLOR) + if hazy_orig is None: + raise FileNotFoundError(args.image) + + input_np = read_image(args.image, args.height, args.width) + session = axe.InferenceSession(args.axmodel, providers=['AxEngineExecutionProvider']) + axmodel_output = session.run(['output'], {'input': input_np})[0] + save_comparison(hazy_orig, axmodel_output, args.output) + print(f'Saved axmodel result: {args.output}') + + +if __name__ == '__main__': + main() diff --git a/MixDehazeNet/python/onnx_infer.py b/MixDehazeNet/python/onnx_infer.py new file mode 100644 index 0000000000000000000000000000000000000000..fb1ea52e95dbcac5132017dca4a92e5153d33859 --- /dev/null +++ b/MixDehazeNet/python/onnx_infer.py @@ -0,0 +1,78 @@ +import argparse +import os + +import cv2 +import numpy as np + + +def read_image(path, height, width): + image = cv2.imread(path, cv2.IMREAD_COLOR) + if image is None: + raise FileNotFoundError(path) + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 + if image.shape[:2] != (height, width): + image = cv2.resize(image, (width, height), interpolation=cv2.INTER_LINEAR) + image = image * 2.0 - 1.0 + image = np.transpose(image, (2, 0, 1))[None].copy() + return image + + +def save_comparison(hazy_orig, output, path): + """拼接原图(hazy)与去雾图(dehazed)并标注文字,还原到原图大小""" + orig_h, orig_w = hazy_orig.shape[:2] + + # output: NCHW float32 [-1, 1] → 还原到原图大小 HWC uint8 BGR + output = np.clip(output[0], -1.0, 1.0) + output = output * 0.5 + 0.5 + output = (np.transpose(output, (1, 2, 0)) * 255).astype(np.uint8) + if output.shape[:2] != (orig_h, orig_w): + output = cv2.resize(output, (orig_w, orig_h), interpolation=cv2.INTER_LINEAR) + output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) + + h, w = hazy_orig.shape[:2] + font = cv2.FONT_HERSHEY_SIMPLEX + font_scale = max(h, w) / 512.0 + thickness = max(1, int(font_scale * 2)) + color = (255, 255, 255) + + # 加 hazy 标注 + cv2.putText(hazy_orig, 'hazy', (int(10 * font_scale), int(30 * font_scale)), + font, font_scale, color, thickness, cv2.LINE_AA) + # 加 dehazed 标注 + cv2.putText(output, 'dehazed', (int(10 * font_scale), int(30 * font_scale)), + font, font_scale, color, thickness, cv2.LINE_AA) + + compare = np.concatenate([hazy_orig, output], axis=1) + os.makedirs(os.path.dirname(path) or '.', exist_ok=True) + cv2.imwrite(path, compare) + + +def main(): + parser = argparse.ArgumentParser(description='Run MixDehazeNet ONNX inference.') + parser.add_argument('--onnx', default='onnx/MixDehazeNet-s_256x256.onnx', help='Path to simplified ONNX model.') + parser.add_argument('--image', default='0003_0.8_0.2.jpg', help='Input hazy image.') + parser.add_argument('--height', type=int, default=256) + parser.add_argument('--width', type=int, default=256) + parser.add_argument('--output', default='./onnx_res.png') + args = parser.parse_args() + + try: + import onnxruntime as ort + except ImportError as exc: + raise SystemExit('Please install ONNX Runtime first: pip install onnxruntime-gpu or onnxruntime') from exc + + # 读取原图用于显示(BGR uint8) + hazy_orig = cv2.imread(args.image, cv2.IMREAD_COLOR) + if hazy_orig is None: + raise FileNotFoundError(args.image) + + input_np = read_image(args.image, args.height, args.width) + providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if 'CUDAExecutionProvider' in ort.get_available_providers() else ['CPUExecutionProvider'] + session = ort.InferenceSession(args.onnx, providers=providers) + onnx_output = session.run(['output'], {'input': input_np})[0] + save_comparison(hazy_orig, onnx_output, args.output) + print(f'Saved ONNX result: {args.output}') + + +if __name__ == '__main__': + main() diff --git a/MixDehazeNet/res/axmodel_res.png b/MixDehazeNet/res/axmodel_res.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5e60ca137fcff6145eec5eef5c1be3932482c9 --- /dev/null +++ b/MixDehazeNet/res/axmodel_res.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:677b8fd7b0fdef15f22887c93cae26aa5fe0b689bec94ae544e8dc9dc34999c3 +size 411929 diff --git a/config.json b/config.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391