LYT-Net低光增强模型ONNX部署:PyTorch转ONNX与INT8量化实战
发布时间:2026/10/3 21:20:40 锦皓数字建站

简介这份资源面向计算机相关专业学生、高校教师与算法工程师聚焦低光图像增强模型的工程化落地提供基于Transformer架构的LYT-Net算法Python与C双语言部署源码及配套ONNX模型可用于毕设课设、二次开发或实际项目集成。压缩包共21个文件约3.09MB包含9个ONNX模型文件覆盖LOLv1、LOLv2合成与真实场景的多种分辨率、1个Python脚本、1个C源文件以及若干png、jpg测试图像和说明文档方便直接验证推理效果。已有240人学习下载。资源核心价值在于打通从算法到部署的完整链路ONNX模型可直接加载运行Python与C示例分别展示不同工程环境下的调用方式测试图片便于快速对比增强前后效果说明文档降低上手门槛。适合希望理解Transformer低光增强原理、掌握ONNX跨平台部署技巧的读者也可作为课程设计或毕业设计的可运行基础方案。1. 低光增强模型上端侧LYT-Net 的 ONNX 部署到底在解决什么夜里十一点监控画面糊成一片人眼都分不清是猫还是影子这是低光图像增强最典型的落地场景。标题里的 LYT-Net 是一套基于 Transformer 的低亮度增强算法核心思路是用轻量化的注意力结构在极暗条件下恢复亮度和细节而这份方案真正要解决的不是「模型能不能跑出好看的效果图」而是「怎么把它从 PyTorch 训练权重变成能在 Python 和 C 里直接调用的 ONNX 推理引擎」。适合谁看手里已经有或准备复现 LYT-Net 权重、需要在边缘设备或本地服务里做实时增强的工程师以及想把 transformer 类模型塞进 C 推理管线的人。整条链路的关键词就三个pytorch 转 onnx、onnx 量化 int8、onnx 怎么运行。下面按「先立住原理、再动手复现、最后踩坑收尾」的顺序讲透。2. LYT-Net 为什么适合转 ONNX结构拆解与导出前的三个判断2.1 Transformer 低光增强的计算瓶颈在哪低光增强和普通图像分类不一样它是逐像素回归任务输出和输入同分辨率这意味着网络里任何一次下采样都要在解码端补回来。LYT-Net 这类结构通常走「浅层卷积提特征 轻量 Transformer 块做全局建模 上采样重建」的路子。Transformer 块负责在暗区建立长距离依赖把噪点和真实纹理区分开卷积负责保留高频边缘。问题在于自注意力的计算量随特征图尺寸平方增长如果直接在原分辨率上做注意力端侧根本跑不动。所以 LYT-Net 的轻量化设计一般会把注意力放在降采样后的低分辨率特征上或者用窗口注意力把计算限制在局部窗口内。导出 ONNX 之前你必须先搞清楚三件事注意力是在哪个尺度做的、有没有动态 shape 的分支、有没有 PyTorch 特有的算子。这三点直接决定导出会不会翻车。常见做法是先打印一遍模型的 forward 流程把每个模块的输入输出尺寸记下来再决定导出时的固定分辨率。2.2 导出前必须确认的算子兼容性ONNX 的算子集是有限的PyTorch 里很多写法在导出时会触发 fallback 或者直接报错。低光增强模型里最容易出问题的是这几类自定义的归一化层、动态索引、以及某些版本的grid_sample。Transformer 里常见的MultiheadAttention如果用了 PyTorch 原生实现导出时通常没问题但如果自己手写了带 mask 的注意力就要检查 mask 的广播方式是否被 ONNX 支持。一个稳妥的做法是先把模型切到 eval 模式用一组固定输入跑一次torch.onnx.export把 opset 设到 17 或更高。opset 太低会缺算子太高部分推理引擎还没跟上。导出后用 onnxruntime 加载一遍对比 PyTorch 和 ONNX 的输出误差如果 max abs diff 超过 1e-3说明某个算子被近似了得回去查。2.3 输入输出张量的命名与动态轴设置导出时给输入输出起明确的名字后面 C 里按名字取张量会省很多事。低光增强的输入一般是[1, 3, H, W]的 float32值域 0 到 1。如果你希望支持不同分辨率可以把 H、W 设成动态轴但要注意一旦设了动态轴某些推理引擎会退回到较慢的执行路径而且部分量化工具对动态 shape 支持不好。我的建议是端侧部署优先固定分辨率比如 256x256 或 512x512需要处理大图就先缩放再增强最后再放大回去。import torch import torch.onnx model LYTNet().eval() # 假设已定义好结构并加载权重 dummy torch.randn(1, 3, 256, 256) torch.onnx.export( model, dummy, lytnet.onnx, input_names[input], output_names[output], opset_version17, dynamic_axesNone, # 端侧固定分辨率不设动态轴 do_constant_foldingTrue, # 常量折叠减小图体积 )这段代码做了四件事切 eval 模式关掉 dropout 和 batchnorm 的训练行为构造固定尺寸的 dummy 输入指定输入输出名方便后续按名取张量opset 17 兼顾算子覆盖和引擎兼容。do_constant_folding会把能提前算的常量合并减小模型体积对端侧友好。导出完别急着高兴先用 onnxruntime 跑一遍数值对比。3. pytorch 转 onnx 实操从权重到可推理文件的完整命令3.1 环境准备与依赖版本对齐这一步的坑最多。PyTorch、onnx、onnxruntime 三个包的版本要大致对齐否则会出现导出成功但加载失败的情况。我一般用 conda 建独立环境先装 PyTorch再装 onnx 和 onnxruntime。如果你还要做 int8 量化额外装 onnxruntime 的量化工具。注意 onnxruntime 分 CPU 版和 GPU 版端侧部署基本用 CPU 版就够GPU 版在部分嵌入式平台上反而装不上。conda create -n lytnet python3.10 -y conda activate lytnet pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install onnx onnxruntime pip install onnxruntime-tools # 量化会用到装完先验证python -c import torch, onnx, onnxruntime; print(torch.__version__, onnx.__version__, onnxruntime.__version__)。三个版本号都打出来才算环境通了。如果 onnxruntime 导入报 DLL 相关错误多半是缺 Visual C 运行库装一下 Microsoft Visual C Redistributable 即可这在 Windows 上尤其常见。3.2 导出脚本与数值一致性校验导出只是第一步真正要确认的是 ONNX 和 PyTorch 输出一致。下面这段校验脚本是必写的别省。import numpy as np import onnxruntime as ort import torch # PyTorch 输出 model LYTNet().eval() x torch.randn(1, 3, 256, 256) with torch.no_grad(): y_torch model(x).numpy() # ONNX 输出 sess ort.InferenceSession(lytnet.onnx, providers[CPUExecutionProvider]) y_onnx sess.run([output], {input: x.numpy()})[0] diff np.abs(y_torch - y_onnx).max() print(max abs diff:, diff) assert diff 1e-3, 导出误差过大检查算子兼容性providers指定 CPU 执行端侧基本都用这个。sess.run的第一个参数是输出名列表第二个是输入字典键名必须和导出时input_names一致。误差阈值 1e-3 是个经验值低光增强输出值域在 0 到 1 之间超过这个数说明有算子被替换成了近似实现常见于某些激活函数和归一化层。如果误差大先把 opset 调高再试还不行就定位到具体层手动替换。3.3 用 onnxsim 做图优化导出的原始 ONNX 图里往往有一堆冗余节点比如恒等变换、可以合并的连续 transpose。onnxsim 能把这些消掉减小体积、提升推理速度。pip install onnxsim onnxsim lytnet.onnx lytnet_sim.onnx跑完对比两个文件的节点数和体积。一般能减 10% 到 30% 的节点。注意 onnxsim 偶尔会把某些动态分支简化错所以简化后必须再跑一遍 3.2 的数值校验确认输出没变。这一步是很多人的血泪经验图小了结果也变了上线才发现。4. onnx 量化 int8端侧提速的关键与精度代价4.1 静态量化与动态量化的选择int8 量化分动态和静态两种。动态量化只量化权重激活值在推理时动态算 scale实现简单但提速有限静态量化需要一批校准数据把激活值的 scale 也提前定下来提速更明显但精度损失风险更大。低光增强是回归任务输出对数值敏感我一般先试动态量化精度掉太多再考虑静态量化加精细校准。from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic( model_inputlytnet_sim.onnx, model_outputlytnet_int8.onnx, weight_typeQuantType.QInt8, )weight_type选 QInt8 是常见做法部分平台对 QUInt8 支持更好可以两个都试。量化完必须重新跑数值校验这次阈值可以放宽到 5e-2因为 int8 本身有量化误差。如果误差超过这个数说明模型对量化太敏感得考虑只量化部分层或者放弃 int8 改用 fp16。4.2 校准数据的准备与静态量化静态量化需要校准集一般从训练集或真实场景里抽 100 到 500 张低光图。校准数据要覆盖不同亮度分布否则 scale 会偏。from onnxruntime.quantization import quantize_static, CalibrationDataReader class LowLightCalib(CalibrationDataReader): def __init__(self, img_dir): self.files [f for f in os.listdir(img_dir) if f.endswith(.png)] self.idx 0 self.img_dir img_dir def get_next(self): if self.idx len(self.files): return None img load_and_preprocess(os.path.join(self.img_dir, self.files[self.idx])) self.idx 1 return {input: img} quantize_static( model_inputlytnet_sim.onnx, model_outputlytnet_int8_static.onnx, calibration_data_readerLowLightCalib(./calib), quant_formatQuantFormat.QDQ, )QuantFormat.QDQ会在图里插入 Quantize/Dequantize 节点兼容性好但图会变大。校准数据预处理必须和推理时完全一致包括归一化方式否则 scale 全错。这一步翻车的人特别多校准用 0 到 255推理用 0 到 1结果量化后输出一片黑。4.3 量化后的精度与速度权衡量化不是免费的。int8 在 CPU 上通常能带来 2 到 4 倍提速但低光增强的暗区细节容易在量化后丢失表现为噪点变多或暗部出现色块。我的做法是准备一组固定测试图量化前后各跑一遍用 PSNR 和 SSIM 对比PSNR 掉超过 1dB 就要警惕。如果业务对暗部细节要求高可以考虑混合精度注意力层保持 fp16卷积层走 int8但这需要手动改图工作量不小。5. Python 与 C 双端推理onnx 怎么运行才不翻车5.1 Python 端推理封装Python 端适合做验证和批处理。封装一个类把 session 初始化、预处理、推理、后处理串起来。import cv2 import numpy as np import onnxruntime as ort class LYTNetRunner: def __init__(self, model_path): self.sess ort.InferenceSession(model_path, providers[CPUExecutionProvider]) self.input_name self.sess.get_inputs()[0].name def preprocess(self, img_bgr): img cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) img cv2.resize(img, (256, 256)) img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1))[None, ...] return np.ascontiguousarray(img) def infer(self, img_bgr): x self.preprocess(img_bgr) y self.sess.run(None, {self.input_name: x})[0] y np.clip(y[0].transpose(1, 2, 0), 0, 1) return (y * 255).astype(np.uint8)np.ascontiguousarray很关键ONNX Runtime 要求输入内存连续不连续会报错或悄悄变慢。sess.run(None, ...)里第一个参数传 None 表示取所有输出。后处理要 clip 再转 uint8否则溢出会出彩色噪点。5.2 C 端集成与内存管理C 端用 onnxruntime 的 C API核心是Ort::Session和Ort::Value。跨平台编译时注意运行库版本要和 Python 端一致。#include onnxruntime_cxx_api.h #include opencv2/opencv.hpp Ort::Env env(ORT_LOGGING_LEVEL_WARNING, lytnet); Ort::SessionOptions opts; opts.SetIntraOpNumThreads(4); Ort::Session session(env, lytnet_int8.onnx, opts); std::vectorint64_t shape {1, 3, 256, 256}; auto mem_info Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input Ort::Value::CreateTensorfloat( mem_info, blob.data(), blob.size(), shape.data(), shape.size()); const char* input_names[] {input}; const char* output_names[] {output}; auto outputs session.Run(Ort::RunOptions{nullptr}, input_names, input, 1, output_names, 1);SetIntraOpNumThreads控制算子内并行线程数端侧一般设成物理核数。输入 blob 必须是连续内存OpenCV 的 Mat 转过来时要确保isContinuous()。C 里最容易出的问题是 access violation c0000005九成是输入张量生命周期或内存对齐问题检查 blob 是否在 Run 返回前被释放。5.3 双端输出一致性验证Python 和 C 跑同一张图输出应该几乎一致。如果差异大先查预处理BGR/RGB 顺序、归一化系数、resize 插值方式。这三项任何一项不一致都会导致明显色偏。建议把预处理后的张量存成 npy两端各跑一次对比定位到具体环节。6. 部署避坑与排查五个真实踩过的坑6.1 导出成功但推理输出全黑现象ONNX 加载正常推理结果全 0 或全黑。原因导出时模型没切 eval 模式batchnorm 用了训练时的统计量或者输入归一化方式和训练不一致。解决确认model.eval()核对训练时的归一化参数用 3.2 的脚本对比 PyTorch 输出。6.2 int8 量化后暗部出现色块现象量化后亮区正常暗区出现规则色块。原因校准数据亮度分布太单一激活 scale 偏大暗区被量化到同一档。解决校准集加入更多暗图或改用动态量化或对暗区敏感层保持 fp16。6.3 C 端加载报找不到算子现象Python 能加载C 报 unsupported operator。原因C 用的 onnxruntime 版本比导出时低缺新算子。解决对齐两端 onnxruntime 版本或导出时降低 opset。6.4 动态 shape 导致推理变慢现象设了动态轴后单帧耗时翻倍。原因动态 shape 触发引擎重新分配内存和选择 kernel。解决端侧固定分辨率大图先缩放。6.5 多线程推理结果错乱现象单线程正常多线程输出串图。原因多个线程共用同一个 Session 的输入输出缓冲区。解决每个线程独立 Session或用 Session 的线程安全 Run 接口并确保输入张量独立分配。7. 把 LYT-Net 塞进视频流的进阶技巧单帧推理跑通只是起点真实场景是视频流。我一般用「跳帧增强 缓存复用」的策略低光增强对时间连续性要求没那么高每两帧增强一帧中间帧用上一帧的结果做引导能省近一半算力。具体做法是把增强结果缓存下一帧先做亮度估计如果和上一帧差异小于阈值就直接复用差异大才重新推理。另一个技巧是分辨率分级。预览流用 256x256 快速增强关键帧或抓拍时切到 512x512 精细增强。这需要在导出时准备两个 ONNX 文件C 端按场景切换 Session。切换本身有开销所以切换频率不能太高我一般按秒级切换。验证方法上别只看 PSNR。准备一段真实夜间视频增强后人工看暗部细节和噪点再统计端到端延迟的 P99P99 比平均值更能反映卡顿。我自己的习惯是每次改完量化或分辨率都跑一遍固定测试集加一段视频两个都过才上线。这套流程踩过的坑够多了希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。