MATLAB深度学习框架下的红外与可见光图像融合实战解析
简介这是一份基于MATLAB与深度学习框架的红外和可见光图像融合项目资料面向具备一定图像处理基础、希望掌握融合算法落地实践的开发者和研究人员。内容覆盖数据预处理、特征提取、融合策略、模型训练与结果评估等完整流程并配有M脚本源码、示例图像、ZBAK备份文件及说明文档。压缩包共75个文件包含46个PNG示例图、5个M脚本、多个ZIP算法包和备份文件大小约12.17MB目录组织清晰便于按模块检索学习。目前已有54人学习下载。借助这些素材读者可了解多层级融合策略、低通滤波等方法的实现细节同时可结合附赠的多种对比算法包进行效果验证对开展相关课题实验或工程应用具有较高参考价值。1. 深度学习框架做红外与可见光融合MATLAB 是更适合新手起步的地方拿到红外和可见光两路图像最直接的诉求是红外把热目标点亮可见光把背景纹理讲清楚融合结果要两者兼顾。用深度学习框架做这件事大家第一反应是去开 Python 环境但实际上 MATLAB 的 Deep Learning Toolbox 就能把“加载预训练网络、定义融合网络、做数据增强、训练和评估”整条链路走完对没有独立 GPU 服务器、不想折腾环境的工科生来说反而更省事。本文围绕“深度学习框架 红外和可见光图像融合 MATLAB”这个组合讲清选型理由、一套能跑通的最小方案以及我从数据准备到出图验证踩过的真实坑位。适合课程项目、课题预研和想快速出效果图的算法工程师。2. 框架选型与数据准备把红外可见光数据集变成可训练的 datastore2.1 框架选型全 MATLAB 训练还是 Python 训练后导入红外和可见光融合任务里深度学习框架的价值不在于“模型有多深”而在于它替你解决了三件事自动求导、批量训练、层与参的管理。MATLAB 的 Deep Learning Toolbox 对这三件事的支持已经非常成熟VGG16、ResNet 这类预训练模型也能直接加载。常见的实现路线有两条。路线训练环境推理环境适合场景A全 MATLABDeep Learning Toolbox trainnet/trainNetworkMATLAB 内直接调用中小规模数据、快速出融合图、与图像处理工具箱无缝配合BPython 训练后导入PyTorch/TensorFlow导出 ONNX 到 MATLAB 推理已有现成 Python 模型、要在 MATLAB 里做后处理和指标对比我一般会建议第一次做的人选路线 A。理由很实际融合任务没有标准答案ground truth网络设计、数据增强、损失函数都要反复调路线 A 改一行代码就能重跑路线 B 每次都要在 Python 和 MATLAB 之间来回导文件时间全耗在格式转换上了。而且 MATLAB 自带的 imageDatastore、augmentedImageDatastore 处理配对图像非常顺手图像处理工具箱里的 imhistmatch、imfuse 还能直接辅助做亮度平衡和可视化。2.2 数据准备配对检查、裁剪、归一化与亮度平衡做红外和可见光融合数据来源通常有这几类TNO 军用场景配对图像、RoadScene 道路场景、以及 LLVIP 这种红外可见光目标检测数据集。这些数据集都能在学术搜索里直接找到公开版本。值得注意的是检测数据集也能拿来当融合训练数据因为配对本身就是对齐好的但用的时候要小心它目标密集、背景单一后面我再细说。拿到数据后的第一步不是写网络而是检查配准质量。公共数据集的图像大多已经对齐但分辨率、视场角偶尔有差异。我习惯先把每对图并排叠起来看% 检查红外与可见光配准情况 im1 imread(ir_001.png); % 红外图单通道 im2 imread(vi_001.png); % 可见光图通常三通道 im2gray rgb2gray(im2); % 并排显示错位情况用 falsecolor 可以直观看出边缘是否对齐 figure; imshowpair(im1, im2gray, falsecolor); title(红外(红) vs 可见光(青) 配准检查);imshowpair 的 falsecolor 模式会把两幅图的差异用颜色叠加显示配准良好的区域呈灰色边缘错位的地方会有明显的红青色边。如果有全局偏移用 imregister 做一次刚性配准即可如果只有个别图对错位直接把这组样本从训练集里筛掉更省事。im1和im2gray必须是相同尺寸不然 imshowpair 会报错数据集中常见的问题是尺寸差一两个像素先用 imresize 统一。接下来做裁剪和归一化。网络输入不一定要用原图全尺寸裁剪成 128×128 的 patch 既能扩大样本量又不会把显存吃爆。亮度方面红外图的灰度分布通常集中在高温目标上可见光整体偏暗两者直接喂给网络会让融合结果总是偏向高亮度模态。常见做法是先把两种模态的均值方差对齐这就是常说的“亮度平衡”% 数据预览和亮度平衡 ds imageDatastore({ir_001.png, vi_001.png}); % 只是演示读取方式 ir imread(ir_001.png); vi rgb2gray(imread(vi_001.png)); % 直方图匹配把红外图的灰度分布映射到可见光分布上去 ir_match imhistmatch(ir, vi); % 也可以统一归一化到 [0,1] 并调整均值方差 ir_norm double(ir_match) / 255; vi_norm double(vi) / 255; ir_bal (ir_norm - mean(ir_norm(:))) * (std(vi_norm(:))/std(ir_norm(:))) mean(vi_norm(:));imhistmatch是图像处理工具箱里的函数直接把红外图的直方图形状拉成和可见光一致。只做归一化不够因为红外图的对比度通常远高于可见光归一化后两者方差差异依然很大。上面这段是预处理逻辑实际训练不会一张张处理而是用 imageDatastore 加 transform 把预处理写进数据流里这样训练时每个 batch 自动完成读取、裁剪、亮度平衡。关于 transform 的写法第四章训练流程里会给完整代码。2.3 红外可见光目标检测数据集迁移到融合任务偏差要心里有数LLVIP 这类红外可见光目标检测数据集经常被当成融合训练数据因为它配对质量高、数量大。但有两个坑第一检测数据集的场景多为夜间监控目标密集且以行人车辆为主纹理信息比 TNO 那种军事场景少训练出来的模型在白天户外图上效果会打折扣第二检测标注里的边界框是目标定位信息不是融合期望输出不能拿它当融合的监督信号。融合训练的本质是“从两幅源图重建出信息更全的一幅图”监督信号只能来自源图本身的重建一致性而不是任何语义标注。这一点新手很容易想歪以为检测框能帮网络聚焦目标区域实际加上去反而让非目标区域的纹理被丢掉。3. 网络设计与融合策略VGG16 提取特征轻量解码器完成重建3.1 为什么选“预训练特征提取 可训练解码器”而不是端到端黑匣子红外和可见光融合的网络结构有很多种端到端的生成对抗网络、基于 Transformer 的融合模型、自编码器。但对 MATLAB 用户来说最可靠、最容易复现的是“共享权重的双分支自编码器 测试时特征融合”结构代表思路与 DenseFuse 这类论文一脉相承。设计逻辑分两步。训练阶段把红外图和可见光图分别输入同一个编码器得到深度特征再用解码器把特征重建回原始图像训练目标是“重建得越像越好”。测试阶段红外和可见光各过一遍编码器得到两组特征图在特征层面做融合加权、取最大、L1 范数引导然后把融合特征交给解码器生成最终融合图像。这样设计的好处是训练阶段只需要单张图像就能做自重建不需要配对的融合标签测试阶段插在中间的那个“融合层”可以自由替换成各种策略不用重新训练网络。预训练 VGG16 在这里充当特征提取器它 ImageNet 上学会的通用纹理和边缘表征对红外和可见光都有一定迁移性尤其是可见光分支受益明显。VGG16 也可以换成 ResNet18但 VGG 结构规整、层名好记调试时少踩坑。网络整体结构模块构成输出尺寸128×128 输入编码器卷积 3×3×16 ReLU 卷积 3×3×16 ReLU 池化64×64×16融合层测试时插入L1 范数加权 / 取最大 / 加法64×64×16解码器反卷积 2×2×16 ReLU 卷积 3×3×32 ReLU 卷积 3×3×1128×128×13.2 MATLAB 搭建编码器与解码器层定义和参数说明用 Deep Learning Toolbox 的 layer 定义网络代码很直白。下面是训练阶段用的自编码器结构% 定义自编码器输入单通道灰度图输出重建单通道灰度图 layers [ imageInputLayer([128 128 1], Name, input, Normalization, none) % 编码器两轮 3x3 卷积通道数从 1 扩到 16 convolution2dLayer(3, 16, Padding, same, Name, enc_conv1) reluLayer(Name, enc_relu1) convolution2dLayer(3, 16, Padding, same, Name, enc_conv2) reluLayer(Name, enc_relu2) maxPooling2dLayer(2, Stride, 2, Name, enc_pool) % 解码器反卷积升采样再卷积重建 transposedConv2dLayer(2, 16, Stride, 2, Name, dec_deconv1) reluLayer(Name, dec_relu1) convolution2dLayer(3, 32, Padding, same, Name, dec_conv1) reluLayer(Name, dec_relu2) convolution2dLayer(3, 1, Padding, same, Name, dec_conv2) ]; % 把 layer 数组转成网络并检查是否有维度错误 lgraph layerGraph(layers); analyzeNetwork(lgraph);卷积核大小选 3×3这是特征提取的经典配置感受野够用且参数量小。第一层卷积把通道从 1 扩到 16不需要一开始就扩到 64因为输入本身只有单通道灰度图信息量有限。maxPooling2dLayer(2, Stride, 2)把 128×128 降采样到 64×64解码器里用transposedConv2dLayer(2, Stride, 2)升采样回 128×128。这里的反卷积核大小固定为 2与池化窗口一致避免棋盘效应。analyzeNetwork一定要跑一遍维度不匹配会直接报错比训练到一半再崩省时间。3.3 测试阶段的融合策略L1 范数加权与更激进的变体训练好的自编码器里编码器输出的特征图承载了源图的显著信息。测试阶段把红外和可见光特征送进融合层最常用的是 L1 范数引导的加权平均。原理是某个空间位置的红外特征 L1 范数大说明该位置的信息强度高融合时应该给红外更高权重反之则给可见光更高权重。% 假设 enc_ir 和 enc_vi 分别是红外和可见光过编码器后的特征图H×W×C % 计算每个通道组的 L1 范数作为显著度 ir_l1 sum(abs(enc_ir), 3); % 在通道维度上求和得到 H×W 的显著图 vi_l1 sum(abs(enc_vi), 3); % 权重归一化 ir_w ir_l1 ./ (ir_l1 vi_l1 eps); vi_w vi_l1 ./ (ir_l1 vi_l1 eps); % 融合特征按权重叠加 enc_fused enc_ir .* ir_w enc_vi .* vi_w;sum(abs(x), 3)是在通道维度上做绝对值和输出的是每个空间位置的总体激活强度。加上eps防止某位置两幅图特征都为零时除以零。这个融合层接管了原来编码器到解码器的通路测试时将enc_fused喂给解码器得到的就是融合图像。比 L1 加权更简单的方案是逐元素取最大max(enc_ir, enc_vi)它保留最强烈的响应适合红外目标突出、可见光纹理平淡的场景。L1 加权整体过渡更自然缺点是红外小目标的权重可能被周围大范围背景稀释。实际哪个好用第 6 章的指标对比一下就知道不要凭感觉定。4. 端到端训练与参数调优用重建任务训练测试时插入融合层4.1 训练数据流imageDatastore 与 transform 预处理训练自编码器不需要配对数据只需要大量“单张灰度图”。我把红外和可见光的全部训练图像混合在同一个 datastore 里让网络同时见过两种模态的分布。这样解码器不会偏向某一种模态的灰度习惯。% 数据准备红外图和可见光图分别放在两个文件夹 ds_ir imageDatastore(data/ir/*.png); ds_vi imageDatastore(data/vi/*.png); % 合并两个 datastore 的底层文件列表 allFiles vertcat(ds_ir.Files, ds_vi.Files); ds_all imageDatastore(allFiles); % 预处理统一尺寸 单通道 归一化到 [0,1] ds_aug transform(ds_all, (x) preprocessPatch(x)); % 划分训练集和验证集 numFiles numel(ds_aug.Files); idx randperm(numFiles, round(numFiles * 0.1)); ds_val subset(ds_aug, idx); ds_train subset(ds_aug, setdiff(1:numFiles, idx));预处理函数preprocessPatch定义如下function patch preprocessPatch(img) % 统一缩放到 128x128转灰度转 double 并归一化 if size(img, 3) 3 img rgb2gray(img); end img imresize(img, [128 128]); patch double(img) / 255; endtransform的返回值在 datastore 层面惰性执行不占用额外内存训练时才逐个处理。验证集数量取 10% 是经验值如果整体样本量只有几百张验证集 10% 可能只有几十张这时把验证集改成五折交叉里的一折意义不大更多是看训练曲线是否过拟合。如果原图很大比如 640×512直接 resize 到 128×128 确实会丢失红外小目标细节。保留原尺寸训练太耗显存折中方案是随机裁剪 128×128 patch 而不是整体缩放。但随机裁剪要求输入输出对齐写预处理时要保证每次裁剪的坐标一致这里为了简洁没有展开。4.2 损失函数配置MSE 重建损失为什么不够怎么加结构约束自编码器训练目标是重建源图最基础的损失是 MSE。但只用 MSE 容易让融合结果发糊因为 MSE 对高频边缘不敏感网络倾向于输出“平均化”的图像。一个有效的改进是在重建损失基础上叠加结构相似度损失SSIM。MATLAB 的trainnet支持自定义损失函数写法很简洁% 定义训练损失MSE (1 - SSIM)两者都在 [0,1] 量级 lossFcn (Y, T) mse(Y, T) (1 - ssim(Y, T)); % 训练选项Adam 优化器初始学习率 1e-3 options trainingOptions(adam, ... InitialLearnRate, 1e-3, ... MaxEpochs, 50, ... MiniBatchSize, 16, ... ValidationData, ds_val, ... ValidationFrequency, 100, ... Plots, training-progress, ... OutputNetwork, best-validation, ... Shuffle, every-epoch, ... Verbose, false); % 执行训练 net trainnet(ds_train, lgraph, lossFcn, options);trainnet是 R2023a 之后推荐的训练接口损失函数可以是任意接受(Y, T)两个参数的函数句柄。如果你的 MATLAB 版本还在用trainNetwork自定义损失就得写成自定义输出层代码量翻倍建议升级。mse惩罚像素误差ssim保住结构相似性两者相加时要确认量级匹配。SSIM 的值域是 [-1,1] 或 [0,1]按默认参数MSE 归一化后在 [0,1] 附近直接相加不会出现一项压过另一项。验证集在这里承担两个职责一是每 100 次迭代看一次验证损失有没有跟着训练损失下降二是OutputNetwork, best-validation让训练结束后自动保留验证损失最小的那版权重而不是最后一个 epoch 的权重。如果训练到一半验证损失上升、训练损失还在降就是过拟合了优先加数据增强随机翻转、旋转 90°而不是减少网络深度——编码器只有两层卷积本身容量不大。4.3 训练参数怎么调学习率、批大小、epoch 与显存边界参数选择直接决定这个网络是能收敛还是疯狂震荡。以下是我常用的初始值及调整依据参数初始值调整方向失败时看什么InitialLearnRate1e-3损失震荡时降为 1e-4训练曲线是否呈锯齿状MiniBatchSize16显存不足时降为 8 或 4GPU 利用率是否接近 100%MaxEpochs50损失收敛慢时加 50验证损失是否还在平台期Shuffleevery-epoch固定为 every-epoch不洗牌会让模型记住顺序ValidationFrequency100数据少时增大太频繁会拉慢训练用gpuDevice查看 GPU 显存占用输入 128×128、批大小 16 的显存开销大约在 2~4 GB这基本覆盖了大多数笔记本独显。如果报“out of memory on GPU”先降 MiniBatchSize再考虑裁剪尺寸降到 96×96。batch size 降到 4 还爆显存那大概率是 GPU 太老直接options.ExecutionEnvironment cpu吧128×128 的小网络跑 CPU 也就慢两三倍不会等死人。调参有个易被忽略的点MaxEpochs不等于训练次数。如果数据集只有 400 张图批大小 16一个 epoch 只有 25 次迭代50 个 epoch 总共 1250 次迭代这个量级对自编码器来说偏少。我一般会同时观察训练曲线横轴的迭代次数而不是 epoch 数保证有效迭代在 3000 次以上再谈效果。5. 避坑清单红外-可见光融合在 MATLAB 里最容易翻车的 5 个位置5.1 单通道图复制成三通道后特征漂移融合图偏色现象融合结果出现不自然的红绿边或者整体偏色红外目标边缘尤其明显。原因VGG16 的输入设计为 RGB 三通道很多教程直接把红外单通道图用repmat复制成三通道再送进去。问题在于VGG16 的 ImageNet 预训练权重对 RGB 三个通道有不同的通道级统计特性复制后的红外图相当于人为构造了一个“伪 RGB 图”卷积特征会发生偏移解码器拿到这种特征重建融合图就带上了颜色伪影。解决融合框架里的编码器用自己训练的自编码器而不是直接套 VGG16 做编码。如果一定要用 VGG16 提特征就把红外和可见光分别送入网络后取activations的中间层特征且不要对融合特征做跨通道的加权操作只在同语义通道上做融合最后重建时固定映射到灰度输出。我的经验是自编码器的编码器虽然不如 VGG16 表征能力强但它是针对单通道灰度图训练的特征分布干净后期处理省心得多。5.2 imresize 之后忘记归一化融合图整体发黑现象网络输出的融合图显示出来是黑的用imhist查看直方图发现像素值集中在 0 附近。原因训练时输入归一化到 [0,1]但测试阶段如果先imresize得到 [0,255] 范围的数据直接喂给网络会出现分布偏移。更隐蔽的一个场景是imresize默认插值会把原有的 uint8 数据转成 double但数值范围仍是 0~255网络输出的 Sigmoid 激活范围是 0~1输出自然全黑。解决测试阶段统一走训练时同一个预处理函数出门前先double(img) / 255。我自己的习惯是写一个preprocessPatch和postprocessPatch配套函数前者负责训练和测试输入的归一化后者负责把网络输出乘 255 并转 uint8两头都卡标准。function out postprocessPatch(x) out uint8(round(x * 255)); out imresize(out, [origH origW]); % 测试前先保存原图尺寸 end5.3 GPU 显存不足先别急着换显卡现象训练刚开始就报out of memory on GPU或者跑几个 batch 之后报错。原因最常见的是批大小太大其次是输入 patch 尺寸偏大。另一个经常被忽略的原因是没有限制 GPU 显存增长MATLAB 默认会一次性把整个网络结构搬到 GPU如果网络里有多条并行分支显存开销会高于直觉预期。解决按顺序排查。第一步把 MiniBatchSize 从 16 降到 8第二步把输入尺寸从 128 降到 96第三步在训练选项里加DispatchInBackground, true让数据加载和计算并行。如果都没解决用gpuDevice查看“AvailableMemory”是不是已经被其他进程占满关掉其他占用显存的应用再试。实在不行换 CPU 训练128×128 的小网络 CPU 也熬得住。5.4 vgg16 加载报错与版本行为差异训练好网络后推理结果却不对现象第一次调用vgg16提示需要下载支持包下载后网络能加载但activations提取的特征和论文里描述的对不上或者融合效果明显退化。原因MATLAB 的预训练模型以支持包的形式提供R2023a 前后下载方式和模型版本有差异。另一个更隐蔽的问题是VGG16 的activations默认对输入做 zerocenter 归一化减 ImageNet 均值如果你在调用前手动减了一次均值特征就错位了。解决第一次使用vgg16时按提示安装支持包装完用analyzeNetwork(net)确认输入层大小。提取特征时不要手动减均值直接传原始 uint8 图给activations。如果发现融合效果不对最直接的排查方法是先跑一张可见光图的重建看解码器能否还原原图重建没问题问题就在融合策略上和网络版本无关。5.5 MATLAB 中文注释乱码一个浪费一小时的环境问题现象打开项目.m文件中文注释全部变成乱码运行脚本时警告信息也是乱码。原因MATLAB R2021b 之前默认使用系统本地编码打开.m文件如果你的脚本保存在 UTF-8 编码下在中文版 Windows 上打开就是 GBK 解码乱码几乎是必然。R2021b 之后默认切换为 UTF-8但老版本升级后旧文件编码不会自动转换。解决在 MATLAB 预设里找到“MATLAB 常规 文件格式编码”把编码改为 UTF-8然后重新打开文件。如果乱码已经出现用记事本把.m文件另存为 UTF-8 编码覆盖原文件。后续所有脚本统一用 UTF-8 保存团队协作时能够避免同一份代码在不同机器上出现编码行为不一致。这个问题跟算法无关但处理不好能卡你一下午。6. 验证与进阶用指标和三图对比把融合效果讲清楚6.1 量化指标PSNR、SSIM、互信息一个都别少融合效果不能只靠肉眼评审和论文里都需要量化指标。最常用的三个指标是PSNR峰值信噪比看融合结果与源图间的像素保真度、SSIM结构相似度看纹理结构保留程度、互信息看融合结果包含了多少源图的信息量。MATLAB 图像处理工具箱里直接有psnr和ssim互信息需要自己算下面给一个可用的实现。% 计算融合结果与红外源图的 PSNR 和 SSIM psnr_ir psnr(fused, ir); ssim_ir ssim(fused, ir); % 互信息用联合直方图估计 function mi mutualInfo(a, b) % 将灰度量化到 64 级减少计算量 a round(double(a) / 255 * 63) 1; b round(double(b) / 255 * 63) 1; % 二维联合直方图归一化为联合概率 jhist histcounts2(a, b, 64, 64, Normalization, probability); % 分别计算边缘概率 pa sum(jhist, 2); pb sum(jhist, 1); % 计算互信息sum(p * log(p / (pa*pb))) [Pa, Pb] meshgrid(pa, pb); valid jhist 0; mi sum(jhist(valid) .* log(jhist(valid) ./ (Pa(valid) .* Pb(valid)))); end指标对比时要注意两点PSNR 和 SSIM 都要分别与红外和可见光各算一次因为融合结果不可能同时超过两幅源图的所有指标合理的融合结果是“红外指标略降、可见光指标明显提升”或反过来两者都大幅低于源图说明融合策略有问题互信息则是在像素级信息维度上衡量融合是否把两边的信息都吸纳进来通常融合后的互信息应该高于任意一幅源图与自身的互信息也就是等于该图的熵。6.2 一张图画完红外、可见光、融合结果不用再导到 Origin 拼图很多项目的汇报要求把红外图和融合图并排展示不少人习惯把图导出到 Origin 再排版多此一举。MATLAB 里montage一行就能把多张图拼在一张图上还能保持坐标对齐。% 三图并排红外、可见光、融合 figure(Color, w); montage({ir, vi, fused}, Size, [1 3]); % 给每张图加标题 title(红外 / 可见光 / 融合结果); % 保存高清图 exportgraphics(gcf, fusion_result.png, Resolution, 300);如果要在融合图上标注局部区域比如某个红外目标是否保留清晰用imshow加rectangle手动画框比 montage 更灵活。对比时有个习惯值得养成把源图、中间特征显著图、最终融合结果三行放一起这样调参时能一眼看出问题是出在特征提取还是融合权重上不用每次都靠猜。我的习惯是每次只改融合策略里的一个参数比如把ir_w的权重从 0.5 调到 0.7跑一遍生成融合图同时记录三个指标这样积累几次之后就能摸清自己的数据分布适合加性融合还是取最大融合。千万不要同时改融合策略和网络结构不然效果变好都不知道是哪一步起作用。融合这个领域没有银弹红外目标检测数据集上表现好的策略到白天可见光场景可能直接翻车唯一可靠的做法是保留一套自动化指标脚本每次实验跑完立刻出数让数字说话而不是靠记忆里的视觉印象。希望帮到你。本文还有配套的精品资源点击获取