
NTIRE 系列比赛一直是图像修复与增强方向的“风向标”每年 CVPR 期间都会集中放出大量高质量赛道。这次 NTIRE 2026 的低光增强赛道名字很有意思Twilight Cowboy Challenge。从命名来看重点大概率落在黄昏、夜晚这类真实弱光场景的恢复与增强上而不是简单地把暗图拉亮。低光增强一直是竞赛和工程落地的双热点手机夜景模式、监控视频增强、老照片修复、医学内窥成像全都依赖这个方向。这篇文章就来拆一下NTIRE 2026 低光增强挑战赛可能考什么、需要准备哪些技术栈、本地怎么复现和验证以及直接把模型接到批量任务和 API 服务时要注意什么。先说结论如果你已经有图像复原模型的基础这次挑战赛的技术门槛不算特别高真正的难点在于数据分布、指标博弈和极端暗光下的稳定性。本文会围绕低光增强的常用技术路线、训练验证流程、显存优化、批量推理和接口部署给出一套完整的实操思路。没有官方赛题细节的部分我会明确标出“需以官方发布为准”不做过度推测。1. NTIRE 2026 低光增强挑战赛概览NTIRE全称 New Trends in Image Restoration and Enhancement是计算机视觉顶级会议 CVPR 上的常驻研讨会和挑战赛品牌。每年的 NTIRE 挑战赛都会设置多个图像复原赛道低光增强 Low-light Enhancement 是其中最稳定的赛道之一。2026 年这届赛题叫 Twilight Cowboy Challenge从名称上判断场景设计会更贴近“黄昏到夜晚”的过渡光环境可能包含城市夜景、室内弱光、逆光人像等复杂子任务。下面用一张表把赛前需要关注的信息列清楚。项目说明赛事背景NTIRE 系列挑战赛通常在 CVPR 期间发布结果赛题名称NTIRE 2026 Low-light Enhancement: Twilight Cowboy Challenge任务类型低光图像增强、暗光恢复、色彩还原、去噪核心难点极端暗光、光照不均匀、噪声放大、颜色偏移、细节保持主流评估指标PSNR、SSIM、LPIPS、NIQE以及主观视觉质量参赛常用模型Retinex 类、CNN 类、Transformer 类、扩散模型类训练数据形式成对低光/正常光图像或真实场景无参考对官方 baseline以挑战赛官网发布为准本文不臆测具体数值从以往 NTIRE 低光赛道来看比赛通常会提供一批成对训练数据让参赛者通过监督学习让模型学习“低光图到正常光图”的映射。对于这类任务模型的亮度提升能力只是底线真正拉开差距的是噪声抑制、颜色恢复、纹理保留和极端暗部的可用性。“Twilight”这个关键词提示我们需要特别关注低照度下的色彩层次和边缘细节而不是单纯追求高 PSNR。对参赛者来说需要准备的工程能力包括数据处理、模型训练、指标评估、推理加速、结果可视化。任何一个环节做得不够扎实最后的测试集成绩都可能出现明显波动。这篇文章后面会按这条链路逐步展开。2. 低光增强的主流技术路线低光增强不是单一模型问题而是一个“感知重建”的综合问题。目前主流方案可以分为四类基于物理模型的传统方法、基于 CNN 的监督方法、基于 Transformer 的全局建模方法、以及基于扩散模型的生成式方法。2.1 基于物理模型的方法这类方法以 Retinex 理论为核心认为图像可以分解成反射分量和光照分量。早期方法通过估计光照图、增强光照、再合成增强图像来完成低光增强。优点是无需大量训练数据计算速度快缺点是在复杂噪声场景下容易产生局部过曝、颜色失真和伪影。很多竞赛方案会把 Retinex 作为预处理或后处理模块用来做曝光校正和颜色校正而不是直接作为最终模型。2.2 基于 CNN 的监督方法这是竞赛中最常见的做法。直接让网络学习“低光图像 - 正常图像”的端到端映射常用 U-Net 结构、残差连接和注意力机制。CNN 类方法训练稳定、显存可控、推理速度快适合作为 Baseline 和后期微调基础。问题是纯 CNN 感受野有限对大面积暗部和全局光线变化建模能力偏弱。要改善这一点通常会在网络中间层加入通道注意力或空间注意力让模型更关注暗部区域的信息传播。2.3 基于 Transformer 的方法Transformer 类模型通过自注意力机制捕获全局依赖关系在低光增强中往往能更好处理光照不均匀和场景结构恢复。Restormer、RetinexFormer 这类模型在图像复原任务中效果明显但它们对显存和计算资源要求更高训练时通常需要使用 Patch 训练策略。如果你的显卡显存是 8GB 或 12GB可以考虑用小 Patch 加梯度累积的方式训练如果是 24GB 以上就可以尝试较大分辨率训练。2.4 基于扩散模型的生成式方法扩散模型在低光增强里是最近几年的热门方向优势是生成图像细节丰富、整体自然度更高缺点是推理速度慢、显存占用大、训练难度高。在竞赛中扩散模型常用于“精修阶段”先用轻量模型做粗增强再用扩散模型恢复纹理和抑制伪影。这种两阶段方案在主观评分和 LPIPS 这类感知指标上往往有优势但需要赛方提供的数据量和官方评测标准来权衡。若赛题强调推理效率或可部署性扩散模型可能不是最优选择。2.5 你的 baseline 应该怎么选最稳妥的策略是准备两套方案一套 CNN 或 Transformer 模型作为快速迭代基线保证 PSNR 和 SSIM 稳步提升另一套用扩散模型或两阶段精修方案作为主观质量和感知指标冲刺方案。竞赛中客观指标和主观评分往往都要看过度追求 PSNR 可能导致图像过于平滑过度追求感知质量又可能产生不真实纹理。两者之间的平衡需要大量实验。3. 评估指标别让分数骗了你低光增强的评估是竞赛最容易被忽略的部分。常见的客观指标有 PSNR、SSIM、LPIPS无参考指标有 NIQE、BRISQUE、LOE 等。这些指标各有偏向理解它们的特性才能避免调错方向。指标评估内容特点使用建议PSNR像素级误差对像素差异敏感偏好平滑结果用于训练收敛参考SSIM结构相似度更关注亮度、对比度、结构配合 PSNR 一起看LPIPS感知相似度基于深度特征比较主观质量强相关但计算较慢NIQE无参考自然度不需要 GT评估图像自然度验证真实测试时有用LOE光照顺序误差衡量增强后自然光照保持低光增强特有指标在实际操作中建议每训练几个 epoch 就保存一次验证集结果把 PSNR、SSIM、LPIPS 全部计算出来。很多选手只盯 PSNR结果模型在验证集分数很高一到真实夜景就暴露问题颜色发灰、暗部噪声依然明显、高光区域过曝。所以无论官方采用什么指标自己都要建立一个“主观客观”双轨评估流程。如果要评估极暗光下的表现可以专门构造一组“低光强噪声”测试图观察模型在高 ISO 场景下的去噪和颜色恢复能力。这个测试集不参与训练只用于局部对比。对于 Twilight Cowboy Challenge 这类强调黄昏夜景场景的赛题暗部细节和颜色层次的主观验证尤其重要。4. 本地复现环境与数据准备不管参赛还是学习复现第一步都是搭建可重复运行的本地环境。这里给出一套通用流程具体版本号需要按模型和赛方数据要求调整。4.1 硬件预期低光增强模型训练显卡建议至少 8GB 显存起步12GB 会更宽裕。如果只能用 CPU 训练建议只跑极小规模消融实验训练主模型还是要用 GPU。推理阶段的显存占用一般比训练低8GB 显卡通常可以跑 1080p 分辨率的小型网络。如果使用扩散模型或 Transformer 大模型高分辨率推理需要分块处理或者依赖更大显存。这里要强调一句显存占用取决于模型参数量、输入分辨率、Batch Size 和是否使用梯度检查点不能只凭模型类型判断。新手第一次跑模型前先用nvidia-smi盯一下显存曲线确认占用不会把显存打满。4.2 环境搭建建议使用 Conda 管理独立环境避免依赖冲突。以下是通用模板conda create -n lowlight python3.10 -y conda activate lowlight pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python pillow numpy tqdm wandb pip install scikit-image lpips如果你需要训练 Transformer 类模型再安装对应的模型库pip install einops timmCUDA 版本和 PyTorch 版本必须与显卡驱动匹配。安装前可以先执行nvidia-smi查看驱动最高支持的 CUDA 版本再选择对应的 PyTorch 安装源。如果驱动版本较低直接安装最新版 PyTorch 可能会导致CUDA unavailable。4.3 数据目录组织参赛过程会产生大量实验数据建议从一开始就按目录管理lowlight_project/ ├── configs/ ├── data/ │ ├── train/ │ └── val/ ├── models/ ├── scripts/ ├── experiments/ │ ├── exp001/ │ └── exp002/ └── outputs/训练前先用脚本统计一下数据集的亮度分布、图像分辨率和文件数量判断数据是否平衡。如果训练集中低光图的暗部区域占比差异很大可以考虑对数据做采样加权避免模型偏向过暗或过亮样本。5. 训练与验证流程数据准备好后可以开始训练。下面给出一套图像到图像的训练循环模板需要根据具体模型替换网络结构和损失函数。5.1 数据加载低光增强的成对数据一般是一张低光图和一张对应的正常光图。读取图像后先用cv2.cvtColor把它从 BGR 转成 RGB再归一化到[0,1]。训练时通常使用随机裁剪增强数据多样性例如裁剪成256x256或512x512的 Patch验证时保持原图或按固定分辨率缩放。import cv2 import torch from torch.utils.data import Dataset class LowLightDataset(Dataset): def __init__(self, file_list, patch_size256, is_trainTrue): self.file_list file_list self.patch_size patch_size self.is_train is_train def __len__(self): return len(self.file_list) def __getitem__(self, idx): input_path, gt_path self.file_list[idx] low cv2.imread(input_path) high cv2.imread(gt_path) low cv2.cvtColor(low, cv2.COLOR_BGR2RGB) high cv2.cvtColor(high, cv2.COLOR_BGR2RGB) if self.is_train: # 随机裁剪相同位置 h, w, _ low.shape x torch.randint(0, w - self.patch_size 1, (1,)).item() y torch.randint(0, h - self.patch_size 1, (1,)).item() low low[y:yself.patch_size, x:xself.patch_size] high high[y:yself.patch_size, x:xself.patch_size] low torch.from_numpy(low).permute(2, 0, 1).float() / 255.0 high torch.from_numpy(high).permute(2, 0, 1).float() / 255.0 return low, high5.2 训练循环训练时建议先用小分辨率和小 Batch Size 做一个 20 到 30 轮的冒烟测试确认 Loss 能下降、验证集指标有变化再跑正式训练。Loss 部分可以使用 L1 Loss 加感知 Loss 的组合。L1 Loss 保证像素收敛感知 Loss 提升视觉质感。不要一上来就堆大量 Loss 项否则很难定位问题。import torch import torch.nn as nn def l1_loss(pred, target): return nn.L1Loss()(pred, target) def train_one_epoch(model, dataloader, optimizer, device): model.train() total_loss 0.0 for low, high in dataloader: low low.to(device) high high.to(device) pred model(low) loss l1_loss(pred, high) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(dataloader)训练时要注意学习率策略。低光增强任务常用的做法是先用 1e-4 到 5e-4 的学习率训练验证集指标平稳后再用余弦退火或 ReduceLROnPlateau 降低学习率。如果训练过程出现 Loss 震荡优先降低学习率而不是调整模型结构。5.3 验证与指标计算每个 Epoch 结束后在验证集上计算 PSNR 和 SSIM。为了节省时间可以每 5 个 Epoch 完整计算一次 LPIPS。保存模型时不要只保存最后一个 Epoch最好保留验证集指标最好的权重。判断实验是否有效的标准是验证集 PSNR 和 SSIM 上升、输出图像没有明显偏色和伪影、训练 Loss 没有突然发散。import torch import numpy as np from skimage.metrics import structural_similarity as ssim import cv2 def calculate_psnr_ssim(pred, gt): pred pred.clamp(0, 1).cpu().numpy().transpose(1, 2, 0) gt gt.cpu().numpy().transpose(1, 2, 0) pred (pred * 255.0).astype(np.uint8) gt (gt * 255.0).astype(np.uint8) pred_y cv2.cvtColor(pred, cv2.COLOR_RGB2GRAY) gt_y cv2.cvtColor(gt, cv2.COLOR_RGB2GRAY) psnr cv2.PSNR(pred, gt) ssim_value ssim(pred_y, gt_y) return psnr, ssim_value这里有一个容易踩的坑PSNR 和 SSIM 对数据范围非常敏感。如果预测值没有收敛到[0,1]或者 GT 的归一化方式不一致指标会直接失真。建议统一在计算指标前把预测值clamp(0,1)。6. 推理、批量处理与接口服务竞赛之外低光增强模型经常要落地到批量任务或服务接口。训练完成后可以把模型导出为 TorchScript 或 ONNX也可以直接用 PyTorch 做推理服务。这里给出一套批量推理和 FastAPI 接口的通用实现。6.1 批量推理脚本批量推理的核心是读图、推理、写图。为了提高效率建议使用torch.no_grad()并对图片做保持长宽比的缩放。批量处理时要注意显存波动最好设置一个进程内的最大 Batch Size避免一次加载过多图片导致显存溢出。import torch import cv2 import glob import os def inference_folder(model, input_dir, output_dir, device, size1024): model.eval() os.makedirs(output_dir, exist_okTrue) image_paths glob.glob(os.path.join(input_dir, *.jpg)) \ glob.glob(os.path.join(input_dir, *.png)) with torch.no_grad(): for idx, path in enumerate(image_paths): img cv2.imread(path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, _ img.shape scale min(size / w, size / h) if scale 1.0: new_w, new_h int(w * scale), int(h * scale) img cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_AREA) tensor torch.from_numpy(img).permute(2, 0, 1).float() / 255.0 tensor tensor.unsqueeze(0).to(device) pred model(tensor).squeeze(0) pred pred.clamp(0, 1).cpu().numpy().transpose(1, 2, 0) pred (pred * 255.0).astype(uint8) pred cv2.cvtColor(pred, cv2.COLOR_RGB2BGR) if scale 1.0: pred cv2.resize(pred, (w, h), interpolationcv2.INTER_LINEAR) name os.path.basename(path) cv2.imwrite(os.path.join(output_dir, name), pred) print(f[{idx1}/{len(image_paths)}] {name} done)批量任务失败时不要整个流程重跑。更好的做法是记录失败文件清单后续单独重试。批量推理脚本里建议加入try-except遇到单张图片损坏或格式异常时跳过并记录日志。6.2 FastAPI 接口服务如果要给其他系统提供低光增强能力直接用 FastAPI 起一个服务。接口设计成 POST请求体带image_base64或图片 URL返回处理后的 base64 结果。这里用uvicorn启动端口可以按需调整。pip install fastapi uvicorn python-multipartimport base64 import io import torch import cv2 import numpy as np from fastapi import FastAPI, UploadFile, File from fastapi.responses import JSONResponse app FastAPI() device cuda if torch.cuda.is_available() else cpu # 初始化模型实际使用时替换为你的网络结构 # model YourModel().to(device) # model.eval() app.post(/enhance) async def enhance(file: UploadFile File(...)): raw await file.read() img cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_COLOR) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) tensor torch.from_numpy(img).permute(2, 0, 1).float() / 255.0 tensor tensor.unsqueeze(0).to(device) with torch.no_grad(): pred model(tensor).squeeze(0) pred pred.clamp(0, 1).cpu().numpy().transpose(1, 2, 0) pred (pred * 255.0).astype(uint8) pred cv2.cvtColor(pred, cv2.COLOR_RGB2BGR) _, encoded cv2.imencode(.jpg, pred) result_base64 base64.b64encode(encoded.tobytes()).decode(utf-8) return JSONResponse(content{success: True, image_base64: result_base64})启动服务的命令uvicorn server:app --host 127.0.0.1 --port 8000接口测试时用 curl 上传一张图curl -X POST http://127.0.0.1:8000/enhance \ -H Content-Type: multipart/form-data \ -F file./test_lowlight.jpg \ -o response.json接口服务部署后一定要加访问控制。这个服务如果暴露到公网很容易被滥用。最简单的做法是绑127.0.0.1或者加一层 Token 校验。涉及真实人脸或隐私照片更要控制访问范围并做好日志脱敏。7. 性能观察与显存优化低光增强模型在训练和推理时的资源占用主要有三个变量输入分辨率、Batch Size、模型参数量。分辨率越高、Batch Size 越大显存占用越高。下面是一套通用的观察和优化方法。7.1 如何观察显存占用训练时另开一个终端执行watch -n 1 nvidia-smi重点看Memory-Usage和Power Usage。如果显存占用接近上限说明 Batch Size 或分辨率需要下调如果 GPU 利用率极低但显存很高说明模型可能等待数据加载需要检查 DataLoader 的num_workers和pin_memory设置。7.2 降低显存占用的策略显存不足时按优先级尝试以下几种方式减小输入 Patch 尺寸例如从512x512改成256x256。降低 Batch Size配合梯度累积保证训练稳定。开启梯度检查点torch.utils.checkpoint用计算换显存。使用混合精度训练torch.cuda.amp。推理时使用分块预测对高分辨率图做 Overlap 切块处理拼回时用加权融合避免接缝。如果使用 Transformer 类模型显存占用通常比 CNN 高很多。建议先在小分辨率 Patch 上验证模型能否正常收敛再逐步增大输入尺寸。不要一上来就用完整分辨率训练很容易把显卡显存打满并导致训练中断。7.3 CPU 推理与 GPU 推理的差异CPU 推理适合做单张图片的简单测试和调试速度慢但不会报显存错误。GPU 推理速度快但需要 CUDA 环境配置正确。批量任务场景下如果每张图只有几百 KB可以用 GPU Batch 推理提升吞吐如果是超高清图瓶颈可能不在模型而在图像编解码建议先用 OpenCV 做压缩和缩放再交给模型处理。8. 常见问题与排查方法低光增强的复现和调参过程中问题通常集中在环境、数据、显存和效果四个层面。下面整理了一张排查表格。问题现象可能原因排查方式解决方案训练时出现 CUDA out of memoryBatch Size 过大或分辨率过高nvidia-smi查看显存占用减小 Batch Size、削减 Patch、开启梯度检查点PyTorch 检测不到 GPUCUDA 版本与驱动不匹配执行torch.cuda.is_available()重装对应版本的 PyTorch 或升级驱动训练 Loss 不下降学习率设置不合理或数据归一化错误打印输入输出范围调低学习率确认低光图和 GT 归一化一致输出图像偏色模型没有学到颜色映射检查训练数据颜色空间统一使用 RGB 或 BGR避免混用 cv2 默认格式暗部噪声明显数据中噪声样本不足增强训练集的暗光噪声加入真实噪声数据或使用噪声增强策略高光区域过曝模型过度提升亮度观察低光图的高光分布在 Loss 中加入感知 Loss降低像素级亮度权重批量推理时单张图失败导致中断图片格式异常添加 try-except 和日志单张失败跳过记录失败路径并重试接口返回超时推理时间过长或并发过高检查模型推理耗时缩小输入尺寸、使用异步推理、限制并发数这类问题在 NTIRE 低光增强挑战赛中非常常见。尤其是“输出图像偏色”和“暗部噪声明显”几乎每个低光增强项目都会遇到。解决偏色问题第一步是检查数据加载时是不是 RGB 和 BGR 混用了第二步是检查 Loss 是否对颜色通道一视同仁。很多情况下问题根源不是模型结构而是数据管线的细节。9. 参赛与部署的最佳实践竞赛和工程部署的目标不完全一样。如果目标是拿一个好名次建议把精力集中在指标优化和结果对比上如果目标是落地建议把模型轻量化、接口化和工程化。无论哪种场景以下几条最佳实践都可以直接套用。9.1 建立最小可运行基线不要一开始就追求大模型。先用一个小网络跑通训练、验证、推理、指标计算全流程再逐步替换为复杂模型。这样可以快速判断问题出在代码还是模型避免浪费时间在无效实验上。9.2 实验版本管理低光增强模型实验变量非常多数据增强方式、Loss 组合、分辨率、Batch Size、学习率、模型结构。建议命名规则明确例如exp001_res512_patch256_l1_lpips。同时每轮实验都保存最优权重和对应指标方便复盘和回滚。9.3 指标与可视化同步记录只记录指标还不够最好每个实验都保存一组固定验证集的可视化结果。很多时候指标提升并不等于视觉效果提升反过来也一样。可视化结果可以快速判断模型是否出现偏色、伪影、过曝等问题。9.4 隐私与合规边界如果你的训练数据或测试数据包含人脸、车牌、医疗图像或者未授权的摄影作品必须严格遵守数据来源协议和版权规定。低光增强模型往往会大幅度改变图像亮度也可能让原本难以辨认的细节变得清晰这带来隐私风险。在分享 demo、参赛提交或部署服务时应确认素材来源合法、用途可用避免用真实人物照片做无授权演示。如果模型最终要商用还要考虑输出内容的可解释性和质检流程。低光增强不是“所有图片都能顺利增强”有些极端暗光图可能产生色块或伪影上线前需要人工抽检。10. 总结与下一步建议NTIRE 2026 Low-light Enhancement: Twilight Cowboy Challenge 是一次很好的技术验证机会既要处理极端低照度下的噪声和颜色恢复又要保证模型的泛化能力和可部署性。从准备工作来看第一阶段建议先复现一个简单的端到端基线模型准备好训练脚本、验证脚本和指标计算脚本第二阶段再根据验证集效果决定是换更强的模型结构还是针对特定场景做数据增广第三阶段如果有余力可以做模型轻量化或用蒸馏方式提升推理速度。这篇文章里提到的代码都是通用模板具体到赛题时需要根据官方发布的数据格式、评估方式和模型结构做替换。最容易踩的坑有三个数据加载时颜色空间混乱、训练和推理的归一化不一致、以及只看 PSNR 忽略 LPIPS 和主观效果。低光增强是一个下限低、上限也很高的方向。简单方法能快速看到效果但要达到 NTIRE 级水准需要在数据、模型结构、损失函数和推理细节上做大量实验。建议把这篇的流程保存起来作为后续复现和调参的检查清单。拿到赛题数据集后先跑通一轮完整流程再决定投入方向。