基于深度学习的红外与可见光图像融合:自编码器方案与PyTorch实践

发布时间:2026/9/12 19:55:00
基于深度学习的红外与可见光图像融合:自编码器方案与PyTorch实践 简介面向需要完成课程设计或期末大作业的高校学生这是一份基于深度学习的红外与可见光图像融合Python源码。项目已通过导师指导并获得97分高分压缩包下载后可直接运行无需修改。资源体积非常精简仅7KB包含3个Python文件分别承担图像预处理、直方图均衡化和Otsu阈值分割等关键任务预处理脚本完成图像配准与归一化直方图均衡化增强对比度Otsu分割生成显著目标掩膜为后续深度融合网络提供高质量输入。红外与可见光图像融合常用于夜间监控、目标识别等场景该源码可作为入门级参考模板也可在此基础上替换或增加深度网络结构便于二次开发。目前已有601人学习下载代码结构清晰、注释完整尤其适合课程设计、期末大作业的快速启动与对比学习。1. 基于深度学习的红外与可见光图像融合课程设计的正确打开方式夜间监控摄像头拍到的红外图像里有清晰的人形热源但背景路牌、车牌、墙面纹理全是糊的可见光图恰好相反细节锐利却看不清暗处的目标。这两种图像的融合不是把两张图叠起来而是在像素层面决定哪里信红外、哪里信可见光。基于深度学习的方案把这套决策交给了网络自己去学这也是红外与可见光图像融合在课程设计里被反复选择的原因模型结构不复杂训练数据不需要标注而且用 Python 源码复现时灵活度高。下面这套从模型架构、训练管道到损失函数调整的路径能让新手按步骤跑通也能让有经验的人看出哪些参数值得改。2. 融合模型的架构选型DenseFuse 自编码器的设计逻辑与代码骨架2.1 为什么编码器-解码器结构适合红外与可见光融合从主流论文和公开代码仓库看基于深度学习的红外与可见光图像融合实现路径主要分为端到端生成式和自编码器式。端到端生成式以 FusionGAN 为代表用生成器和判别器互相对抗来生成融合图自编码器式则以 DenseFuse、NestFuse 为代表用编码器提取特征、融合层合并特征、解码器重建图像。课程设计选自编码器式的理由非常实际训练稳定、显存占用低、可解释性好融合策略可以很方便地替换和对比。自编码器把融合任务拆成压缩—合并—重建三段编码器分别处理红外图和可见光图融合层在特征空间做信息选择解码器只负责把特征恢复到图像域。训练时不需要成对的有监督标签让解码器重建原图就够了。这意味着只需要用可见光图完成编码器和解码器的训练红外图纯粹在推理阶段参与融合计算。2.2 密集连接编码器的设计意图编码器里用的密集块是把每一层卷积的输出都拼接到后续层的输入每一层都能看到前面所有层的特征。这个设计在图像融合里特别有用因为红外特征与可见光特征的形态差异很大前者是热辐射梯度后者是反射纹理。如果用普通卷积串联浅层细节经过几层非线性变换后衰减明显密集连接保证在第3层输出里依然保留第1、2层的边缘和纹理成分融合层拿到的是更完整的特征集。此外密集块在增加网络容量的同时控制了参数量。每个卷积输出固定16通道三层共48个新增通道参数量约等于一个普通三层网络但特征复用的效率高得多。2.3 融合层的两种策略与取舍融合层放在编码器和解码器之间是决定融合质量最关键的部分。常见做法有两种加法融合和L1范数加权融合。加法融合是直接把特征相加计算极简但会把红外特征和可见光特征平均化当边缘强度与热辐射强度差异大时弱特征容易被强特征掩盖。L1加权融合先用每个位置的特征绝对值之和衡量活跃度再对两组特征加权合成本质上是逐像素的注意力机制。推理时只要有差别的区域权重会自动向特征更突出的一侧倾斜。加法融合的实现非常简单一句feat_fused feat_ir feat_vis就够了而加权融合值得单独写一个函数课程设计报告里也建议把两种策略的实验结果各放一张图对比。2.4 骨干网络的 PyTorch 实现与参数表以下是 DenseFuse 风格骨干网络的 PyTorch 实现结构上直接对应上面讨论的密集连接编码器和解码器import torch import torch.nn as nn class DenseBlock(nn.Module): def __init__(self, in_ch, growth16, depth3): super().__init__() self.growth growth self.convs nn.ModuleList() for i in range(depth): ch in_ch i * growth self.convs.append(nn.Sequential( nn.Conv2d(ch, growth, kernel_size3, padding1), nn.BatchNorm2d(growth), nn.ReLU(inplaceTrue) )) def forward(self, x): acc [x] out x for i, layer in enumerate(self.convs): concat torch.cat(acc, dim1) out layer(concat) acc.append(out) return torch.cat(acc, dim1) class FusionEncoder(nn.Module): def __init__(self, in_ch1, mid_ch32, growth16, depth3): super().__init__() self.conv_in nn.Sequential( nn.Conv2d(in_ch, mid_ch, kernel_size3, padding1), nn.ReLU(inplaceTrue) ) self.dense DenseBlock(mid_ch, growth, depth) def forward(self, x): return self.dense(self.conv_in(x)) class FusionDecoder(nn.Module): def __init__(self, in_ch80, mid_ch32, out_ch1): super().__init__() self.conv1 nn.Sequential( nn.Conv2d(in_ch, mid_ch, kernel_size3, padding1), nn.ReLU(inplaceTrue) ) self.conv2 nn.Conv2d(mid_ch, out_ch, kernel_size3, padding1) def forward(self, x): return self.conv2(self.conv1(x))代码逻辑说明DenseBlock里每次循环先把已有输出拼接成concat再把concat送入当前卷积层新输出追加进列表。第0层输入是 conv_in 的32通道输出16通道第1层输入是48通道第2层输入是64通道。最终把3216×380通道的特征一次性返回。所有卷积的 padding 都设为1因此整个编码过程不改变特征图的空间尺寸解码器同样用 padding1 保持尺寸不变这让输入输出可以直接逐像素比较损失也方便融合后叠加别的高级任务。growth参数影响特征通道增量调大能增强细节恢复能力但显存占用会明显上涨课程设计的单卡环境建议保持16不变。模块输入通道输出通道卷积核padding说明conv_in1323×31灰度图到特征映射DenseBlock32 → 8016/层3×31三层的稠密拼接decoder.conv180323×31降维和重建decoder.conv23213×31最终融合图输出到这一步模型骨架可以跑通 forward但离真正出结果还差训练管道和损失函数两件事。3. 在 Python 里搭起训练管道数据集载入、训练循环与融合推理3.1 数据组织公开数据集与单模态重建训练策略红外与可见光融合的公开数据集常用 TNO 和 RoadSceneTNO 场景覆盖面较广RoadScene 的可见光图清晰度更高课程设计优先选 TNO 就行。如果只是想演示效果对图像数量要求不高40到50对图足够做验证。数据集的存储结构一般按模态分目录放dataset/ ├── infrared/IR_001.png ├── infrared/IR_002.png ├── visible/VIS_001.png └── visible/VIS_002.png训练阶段不需要成对读图原因前面说过编码器、解码器只做单图重建。一个常见做法是把可见光目录中每一张图都当作独立训练样本红外图留到推理步骤使用。这样处理的好处是天然回避了红外、可见光图像对齐不准的问题也让数据规模直接扩大一倍以上。3.2 PyTorch Dataset 与 DataLoader 实现import os import cv2 import torch from torch.utils.data import Dataset class GrayscaleDataset(Dataset): 只读单模态灰度图训练阶段不需要配对 def __init__(self, image_dir, size(256, 256)): self.paths [ os.path.join(image_dir, f) for f in sorted(os.listdir(image_dir)) if f.lower().endswith((.png, .jpg)) ] self.size size def __len__(self): return len(self.paths) def __getitem__(self, idx): img cv2.imread(self.paths[idx], cv2.IMREAD_GRAYSCALE) img cv2.resize(img, self.size) t torch.from_numpy(img.astype(float32)) / 255.0 return t.unsqueeze(0) # 返回 [1, H, W]逻辑说明cv2.IMREAD_GRAYSCALE直接读成单通道/255.0归一化到 0 到 1unsqueeze(0)补上通道维。size统一缩放到 256×256这个尺寸在消费级显卡上显存压力较小也能保证红外目标轮廓不被过度压缩。如果实验室显卡只有 4G 显存考虑改成 224 或 192。3.3 训练循环主体代码与配置接下来是训练主循环。这里把训练时的损失函数简化成 MSE 先跑通流程第4章再替换成混合损失。import torch.optim as optim from torch.utils.data import DataLoader device torch.device(cuda if torch.cuda.is_available() else cpu) encoder FusionEncoder().to(device) decoder FusionDecoder(in_ch80).to(device) optimizer optim.Adam( list(encoder.parameters()) list(decoder.parameters()), lr1e-4 ) ds GrayscaleDataset(dataset/visible, size(256, 256)) loader DataLoader(ds, batch_size4, shuffleTrue, num_workers2) def mse_loss(recon, target): return torch.mean((recon - target) ** 2) encoder.train() decoder.train() for epoch in range(30): total_loss 0.0 for batch in loader: x batch.to(device) feat encoder(x) recon decoder(feat) loss mse_loss(recon, x) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() print(fepoch {epoch 1}: loss {total_loss / len(loader):.6f})这段代码做的事情很直接输入一张可见光灰度图编码器提特征解码器还原图像通过比较重建图和原图的均方误差来更新网络。训练完成后encoder 学到的是如何把图像信息压缩进特征decoder 学到的是如何从特征还原图像。二者合起来的特征空间就具备了跨模态融合的基础。一个值得注意的细节解码器的输入通道必须和编码器输出通道严格一致这里编码器输出是80所以FusionDecoder(in_ch80)。如果中途改了 growth 参数这里也要跟着改很多新手在这一步容易把通道数写错导致维度不匹配。配置项设定值输入尺寸256×256训练轮数30批大小4优化器Adam学习率1e-43.4 融合推理与可视化代码训练结束后进入推理阶段。先补上 L1 加权融合函数再写完整的推理流程def l1_weighted_fusion(feat_ir, feat_vis, eps1e-8): # 逐通道绝对值求和得到每个空间位置的活跃度 act_ir torch.sum(torch.abs(feat_ir), dim1, keepdimTrue) act_vis torch.sum(torch.abs(feat_vis), dim1, keepdimTrue) w_ir act_ir / (act_ir act_vis eps) w_vis act_vis / (act_ir act_vis eps) return w_ir * feat_ir w_vis * feat_vis def run_fusion(inf_path, vis_path, encoder, decoder, model1, size(256, 256)): def _read(path): img cv2.imread(path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, size) t torch.from_numpy(img.astype(float32) / 255.0).unsqueeze(0).unsqueeze(0) return t.to(device) inf_t _read(inf_path) vis_t _read(vis_path) encoder.eval() decoder.eval() with torch.no_grad(): f_ir encoder(inf_t) f_vis encoder(vis_t) if mode l1: f_fused l1_weighted_fusion(f_ir, f_vis) else: f_fused f_ir f_vis # 加法融合作为对比 fused decoder(f_fused).squeeze().cpu().numpy() return fused这个函数的输出是 256×256 的 numpy 数组直接用 matplotlib 的imshow显示即可。建议把红外原图、可见光原图、融合图三张图放在同一行出图这样视觉效果对比最强也是课程设计报告里的标配展示方式。到这里一个可以跑通的完整流程已经成型模型定义、数据加载、训练、推理都有了。下一步的关键是把损失函数换成感知质量更好的混合损失。4. 损失函数与训练调参用 SSIM 约束提升融合图像质量4.1 为什么只用 MSE 训练会导致融合图像过度平滑只用 MSE 训练的模型重建图往往存在过度平滑的问题。MSE 对每个像素独立计算平方差它惩罚的是整体数值偏差但并不关心局部结构比如边缘保持、对比度变化、亮度一致性。红外图像里的弱目标区域在红外模态里可能只有一个模糊边缘MSE 训练时该区域权重与背景相同重建出来就是一团糊。SSIM 在局部窗口内对比亮度、对比度和结构三个分量输出范围 0 到 1越接近 1 表示两幅图结构越相似。在融合任务中把 1 减去 SSIM 作为损失项可以显著提升边缘锐度。pytorch_msssim 库提供了现成的实现课程设计里直接pip install pytorch-msssim即可不需要自己从头实现滑动窗口。4.2 混合损失函数 PyTorch 实现与 alpha 参数效果from pytorch_msssim import ssim def fusion_loss(recon, target, alpha0.8): # 亮度保真项MSE mse torch.mean((recon - target) ** 2) # 结构保真项1 - SSIM ssim_loss 1.0 - ssim(recon, target, data_range1.0, size_averageTrue) return alpha * mse (1 - alpha) * ssim_lossalpha控制两项的权重比例。alpha越接近 1结果越偏向像素级精度越接近 0越偏向结构完整度。课程设计调参时可以固定 alpha0.8 跑一组alpha0.5 跑一组对比融合结果的差异后在报告里给出结论。4.3 从学习率到 batch size 的推荐参数表给出一组经多次验证可用的起点参数方便直接抄参数推荐值调整方向图像尺寸256×256显存不够时降为 192 或 224batch size4显存小换 2损失不稳定时适当增大学习率1e-4loss 震荡改为 5e-5epochs3060重建训练收敛快一般 30 轮足够optimizerAdam默认 betas(0.9, 0.999)alpha0.8边缘模糊调低到 0.5growth16想要更多细节但显存充足可加到 20重点说两个最影响结果的参数。第一个是学习率1e-4 是大多数公开实现的默认选择如果 loss 曲线不停抖动毫不犹豫降到 5e-5第二个是 alpha它在可视化效果里体现得最直观alpha0.8 平衡性最好不用再额外做任何后处理就能达到上课展示的水准。4.4 跨模态特征分布的对齐与推理阶段处理单模态重建 loss 降到 0.001 并不代表融合图一定感知优秀因为训练阶段看到的只有可见光图融合阶段进入解码器的是两种模态混合特征解码器对这个特征统计分布并不完全熟悉。最简单的缓解手段是推理时给红外特征和可见光特征做一次标准化让两者统计分布对齐def normalize_feature(feat): mean feat.mean(dim[2, 3], keepdimTrue) std feat.std(dim[2, 3], keepdimTrue) 1e-5 return (feat - mean) / std f_ir normalize_feature(encoder(inf_t)) f_vis normalize_feature(encoder(vis_t)) f_fused l1_weighted_fusion(f_ir, f_vis)这件事属于课程设计里的加分项在报告里写清楚这是跨模态特征分布对齐然后给出标准化前后的融合效果对比图比堆叠训练 epoch 次数更有说服力。5. 课程设计交付用三个指标量化融合效果并完成验证5.1 PSNR、SSIM、SD 三个指标的适用场景课程设计答辩环节光靠三张图说看起来不错不够最好配合定量指标。常用三个PSNR 峰值信噪比、SSIM 结构相似度、SD 图像标准差。PSNR 衡量融合图与参考图之间的像素重建质量SSIM 衡量结构保留程度SD 则是无参考指标反映融合图的对比度和信息丰富度。5.2 一个可复用的评估脚本在推理代码的基础上补一段评估代码对测试集所有图像对计算三组平均值import numpy as np from pytorch_msssim import ssim as ssim_fn def calculate_metrics(fused, ref): # fused 与 ref 都是 0~1 的灰度 numpy 数组 mse np.mean((fused - ref) ** 2) psnr 20 * np.log10(1.0 / np.sqrt(mse 1e-8)) ss ssim_fn( torch.tensor(fused).unsqueeze(0).unsqueeze(0).float(), torch.tensor(ref).unsqueeze(0).unsqueeze(0).float(), data_range1.0 ).item() sd np.std(fused) return psnr, ss, sd # 遍历测试集图像对累计平均最后输出结果表calculate_metrics的输入是 0 到 1 之间的灰度数组三个返回值分别对应 PSNR、SSIM、SD。遍历测试集时对每组图像对分别计算最后取平均就得到一组可以写进报告的数字。5.3 实验对比表与一个演示技巧指标红外原图可见光原图融合图PSNR——17.82SSIM——0.71SD0.180.250.31这种展示结构可以直接放进课程设计报告的结果章节把数字替换成自己跑出的结果答辩时对照三张图逐项解释老师能立即看到融合图在 SD 上比两个源图都高说明信息量确有提升。指标计算完以后如果还想让演示效果更突出可以对融合结果做 CLAHE 局部直方图均衡化增强对比度但注意指标必须基于增强前的原始融合图计算否则前后就不一致了。本文还有配套的精品资源点击获取