简介这是一份基于小波变换的图像融合MATLAB仿真资源配有可交互的图形界面和完整的代码操作视频主要面向本硕博阶段需要学习图像融合算法、完成课程设计或进行科研验证的读者。资源在MATLAB 2021a及更高版本中运行通过Runme_.m总程序启动图形界面操作视频完整演示了从读取图像、执行小波融合到查看结果的全过程能够帮助使用者快速理解程序结构与运行逻辑。包内共31个文件包括一个主程序m文件、一个图形界面fig文件、一个avi操作录屏以及28张用于算法验证的tif测试图像测试图像同时涵盖灰度图与彩色图压缩包整体约7.75MB文件结构简明清晰便于对照代码逐一理解小波分解、系数融合和图像重构等核心步骤。已有953人学习下载该资源既适合作为本科毕业设计与研究生课程的参考资料也为后续改进融合规则、扩展自己的实验数据提供了可直接修改的运行框架。1. 从“两幅图合成一张”说起小波融合仿真到底在做什么图像融合通常不是“拿两张图直接加权平均”就能交差的。红外图里目标亮但背景细节少可见光图里纹理清楚但目标不突出如果只在像素层做加法得到的往往是一张对比度下降、边缘发糊的图。小波变换的做法是把图像拆成低频近似和高频细节再在两套系数上分别执行融合规则最后逆变换回空间域。这也是标题里“小波变换”和“图像融合”连在一起的原因低频保留整体亮度结构高频负责纹理边缘两个部分各用各的策略。这套仿真在 MATLAB 里落地并不复杂核心是一个三层左右的小波分解、两个融合规则、一次逆变换加上一个能调参数的 GUI 界面。适合正在做数字图像处理课设、毕业设计或者需要快速出一版融合 demo 的工程师。关键在于把“分解到第几层”“高频系数怎么选”“GUI 回调怎么传参数”三件事理顺。下面按从原理到接口的顺序把能直接复现的代码和参数一次讲清。2. 小波分解与融合规则低频取平均、高频取最大以及层数怎么定2.1 为什么拆成低频和高频再融合图像经过小波变换后一层分解会得到四个子带LL低频近似、LH水平细节、HL垂直细节、HH对角细节。下一层继续只对 LL 做分解形成金字塔式的系数结构。低频子带本质上是原图的模糊版本包含大部分能量和亮度信息三个高频子带分别记录不同方向的边缘与纹理突变。如果直接在原图上融合像素级加权的病态问题是两个输入里亮度差异大的区域平均后出现光晕一个清晰一个模糊的区域平均后清晰度被拖低。把系数分开处理以后低频和高频可以用不同规则比如低频拉普拉斯金字塔式加权高频用绝对值取大。这也是多数小波融合方案比空域加权更稳的原因融合发生在“信息的频段”而不是“像素本身”。2.2 一个最小可用的融合函数下面这个函数用 MATLAB 自带的小波工具函数实现两层分解融合可以直接复制为waveFuse.m是后续 GUI 回调里要调用的核心计算部分。function F waveFuse(img1, img2, wname, level, rule) % 基于小波变换的图像融合 % img1/img2 : 灰度图double 类型尺寸一致 % wname : 小波基如 db2 / sym4 % level : 分解层数如 2 或 3 % rule : 高频融合规则max 绝对值取大 | avg 平均 % 对两幅图分别做 level 层小波分解 [C1, S1] wavedec2(img1, level, wname); [C2, S2] wavedec2(img2, level, wname); % S1 和 S2 应当一致因为输入尺寸相同 % 低频部分直接用两幅图的 LL 全平均 % LL 在低频系数向量最前面长度是 S1(1,1)*S1(1,2) ll_len S1(1,1) * S1(1,2); C_fuse (C1(1:ll_len) C2(1:ll_len)) / 2; % 低频系数是不需要看方向的平均能避免亮度漂移 % 高频部分按每条系数绝对值比较 % 从 ll_len1 开始到末尾都是三个方向的高频细节 idx0 ll_len 1; for k idx0 : numel(C1) if strcmp(rule, max) if abs(C1(k)) abs(C2(k)) C_fuse(k) C1(k); else C_fuse(k) C2(k); end else C_fuse(k) (C1(k) C2(k)) / 2; end end % 重构回空间域 F waverec2(C_fuse, S1, wname); F mat2gray(F); % 归一化到 [0,1]方便 imshow / imwrite end参数说明wname选db2或sym4在小尺寸图像上差异不明显db2计算量更小如果图里有细密纹理sym4的对称性更好边缘伪影略轻。课设场景固定用一种即可。level推荐 2 或 3。层数越高滤波器的下采样次数越多图像越小的子带越可能只剩几个像素融合就失去了局部性。rule的高频用max是融合里最常用的策略哪个系数绝对值大说明该处边缘强度更高直接保留它。若想平滑过渡可以选avg但清晰度会下降。这里用strcmp每次比较判断会有性能损耗单张图融合无所谓如果做视频帧融合建议把分支提到 for 循环外面。2.3 层数、小波基对结果的影响分解层数低频占比高频细节保留适用场景重构耗时512×5121小融合倾向像素级最多但边缘易出现“断裂感”纹理差异大的医学图像最短2适中边缘较完整大多数红外可见光融合中等3大整体亮度稳定细节有损需要压噪声、提目标亮度的场景略长4 及以上过大明显丢失细纹理大尺寸遥感影像不建议普通图像使用明显变慢选层数时还要考虑一个容易被忽略的点wavedec2要求图像长宽至少能被2^level整除。如果 512×512 做 3 层没问题但 300×200 做 3 层就会报错。常见做法是在预处理阶段用imresize把尺寸归一到 256×256 或 512×512或者在分解前手动裁剪。GUI 里如果开放了层数选择要同步检查输入尺寸。3. 能一次跑通的 matlab 仿真从预处理到重构的完整工程3.1 读图、灰度对齐、尺寸检查的预处理实际工程里两个输入图往往来自不同传感器尺寸、通道数、亮度范围都不一样。比如红外图经常是单通道 uint8可见光图是三通道 uint8。如果直接送进wavedec2会因为维度不一致报错。下面这段主脚本展示标准处理链路。%% 主脚本加载两幅图调用融合函数展示结果 clear; clc; close all; % 1. 读入两张图 I1 imread(infrared.png); % 红外图 I2 imread(visible.jpg); % 可见光图 % 2. 统一转为灰度 double并归一化 if size(I1, 3) 3 I1 rgb2gray(I1); end if size(I2, 3) 3 I2 rgb2gray(I2); end I1 im2double(I1); I2 im2double(I2); % 3. 尺寸不一致时以第一张为准做裁剪而不是直接拉伸 % 直接 imresize 会改变空间分辨率融合出来的边缘有“橡皮感” [h, w] size(I1); I2 I2(1:min(h,size(I2,1)), 1:min(w,size(I2,2))); I1 I1(1:size(I2,1), 1:size(I2,2)); % 4. 调用融合函数这里用 db2、2 层、高频绝对值取大 F waveFuse(I1, I2, db2, 2, max); % 5. 显示原图与融合结果 figure(Name, 小波图像融合结果); subplot(1, 3, 1); imshow(I1); title(Input 1); subplot(1, 3, 2); imshow(I2); title(Input 2); subplot(1, 3, 3); imshow(F); title(Fused); % 6. 保存结果imwrite 需要 uint8 imwrite(uint8(F * 255), fused_result.png);这段脚本的逻辑说明第二步的im2double把 uint8 转到 [0,1] 浮点避免后面abs、除法出现整数截断。很多人融合结果发灰往往是 uint8 计算除二之后小数被舍弃。第三步的“裁剪而非拉伸”是关键imresize到同一尺寸虽然简单但插值会引入虚假纹理高频系数里会混入不属于任何一张输入图的信息。对图像融合的仿真实验来说裁剪到公共区域是更严谨的做法。第五步用同一个 figure 分三个 subplot 对比便于在 GUI 外先验证算法本身是否正确。3.2 融合函数的三种调用方式与参数传递如果你的工程不需要 GUI直接改主脚本里的waveFuse参数即可。常用三种变体% 方式1默认参数固定 db2、2 层、高频取最大 F1 waveFuse(I1, I2, db2, 2, max); % 方式2换小波基观察边缘保留差异 F2 waveFuse(I1, I2, sym4, 2, max); % 方式3高频改用平均结果会更平滑但清晰度下降 F3 waveFuse(I1, I2, db2, 2, avg);这里有个实际经验level和rule不是独立参数。当level提高时高频子带数量变多但单个子带尺寸变小max规则对噪声更敏感此时在循环前加一个中值滤波预处理比在系数域做阈值更简单有效。我一般会在主脚本里放一个use_median开关为后续 GUI 里加滤波选项预留位置% 可选融合前中值滤波去噪核大小 3x3 if use_median I1 medfilt2(I1, [3 3]); I2 medfilt2(I2, [3 3]); end3.3 常见运行报错与处理对照报错信息发生原因处理办法Error using wavedec2 X must be a 2-D matrix输入是 RGB 三通道或非二维矩阵调rgb2gray或取单通道The input image is too small to decompose图像尺寸小于2^level减小level到 1 或 2或先imresize放大Index exceeds the number of array elements尺寸不一致S1、S2 行数不同按 3.1 的方式统一裁剪不要直接 resizeFused result is all gray/blank高频全被置零或低频被错误清零检查 C_fuse 初始化和ll_len是否被赋值GUI 中点击按钮卡死融合计算阻塞了回调线程用drawnow分割任务或用parfor改进见第 4 章这些错误里最容易忽略的是第二个GUI 里用户选了 3 层分解但载入的图是 200×1502^38虽小于边长但wavedec2内部要求每一层维度都是偶数200 除以两次 2 后余数不为 0 同样会报错。稳妥的做法是在进入融合前加一句检查if mod(size(I1,1), 2^level) ~ 0 || mod(size(I1,2), 2^level) ~ 0 uialert(app.UIFigure, 图像尺寸无法被 2^level 整除请裁剪或缩放, 尺寸错误); return; end4. 带 GUI 界面用 App Designer 把融合参数变成可视操作4.1 为什么选 App Designer 而不是 GUIDE老版本的 GUIDE 在 MATLAB R2016a 之后就不再推荐新工程使用新安装的 R2023b、R2024a 里 GUIDE 功能虽然还在但组件库和回调管理都比较旧。App Designer 的受益点有两个一是回调函数自动生成骨架不用手动写handles传递二是坐标轴控件 UI 与绘图分离不会出现在 GUIDE 里常见的“图被 figure 覆盖”问题。对“带 GUI 界面”这个交付需求App Designer 生成的是.mlapp文件双击即可运行不需要额外脚本引导。代码操作视频里演示起来也更直观。整体布局建议如下控件类型名称Tag作用按钮LoadImage1Button读取第一张输入图按钮LoadImage2Button读取第二张输入图下拉框WaveletDropDown选择小波基db1/db2/sym2/sym4旋钮LevelKnob选择分解层数 1~4下拉框RuleDropDown选择高频融合规则max/avg坐标轴FusionAxes显示融合结果标签StatusLabel显示运行状态与报错信息按钮ExportButton保存融合结果到文件4.2 回调函数与数据传递的核心写法App Designer 里不同回调共享数据的方式有三种存储到app的公开属性、用handles结构体、嵌套函数直接访问。推荐在properties (Access private)里声明两个变量用于存图properties (Access private) img1 [] % 第一张输入图存 double 灰度图 img2 [] % 第二张输入图 end读取图片的回调function LoadImage1ButtonPushed(app, event) [file, path] uigetfile({*.png;*.jpg;*.bmp, Image Files}); if isequal(file, 0) return; % 用户取消选择 end fullpath fullfile(path, file); tmp imread(fullpath); % 统一转灰度 double 存到 app 属性后续融合函数直接用 if size(tmp, 3) 3 tmp rgb2gray(tmp); end app.img1 im2double(tmp); app.StatusLabel.Text [已加载: , file]; end融合按钮的回调是重头戏。注意这里要从两个下拉框和旋钮拿参数再调用第 2 章的waveFusefunction FuseButtonPushed(app, event) % 检查两张图是否都已加载 if isempty(app.img1) || isempty(app.img2) app.StatusLabel.Text 请先加载两张输入图; return; end % 尺寸对齐统一裁剪到公共区域 [h, w] size(app.img1); app.img2 app.img2(1:min(h,size(app.img2,1)), 1:min(w,size(app.img2,2))); app.img1 app.img1(1:size(app.img2,1), 1:size(app.img2,2)); % 从控件读参数 wname app.WaveletDropDown.Value; level app.LevelKnob.Value; rule app.RuleDropDown.Value; % 调核心融合函数 F waveFuse(app.img1, app.img2, wname, level, rule); % 显示到坐标轴控件 imshow(F, Parent, app.FusionAxes); app.StatusLabel.Text [融合完成: , wname, , level, num2str(level)]; end参数说明uigetfile的返回值file是文件名、path是文件夹路径用fullfile拼接后才能传给imread。直接imread(file)只在当前工作目录下有效用户从其他文件夹选图就会报错。回调函数名字里的Pushed是控件事件名的固定后缀不能改。app是 App Designer 自动生成的实例对象所有组件都能通过app.组件Tag访问。imshow(F, Parent, app.FusionAxes)是指定在 UI 的坐标轴里绘图的唯一方式。如果写成imshow(F)图会弹到独立 figure 窗口而不是嵌在 GUI 界面内。4.3 在 GUI 里加入小波分解过程可视化这是进阶但常见的要求不只显示最终融合结果还要展示分解后的各层系数。实现方式在 App Designer 里要新增一个坐标轴数组或一个标签页组件。代码核心是function ShowDecomposeButtonPushed(app, event) [C, S] wavedec2(app.img1, 2, db2); % 用 wrcoef2 提取各层细节进行显示 figure(Name, Wavelet Coefficients); for k 1:2 subplot(2, 2, k); A wrcoef2(a, C, S, db2, k); % 第 k 层低频重构 imshow(mat2gray(A)); title([Level , num2str(k), Approx]); subplot(2, 2, k 2); D wrcoef2(d, C, S, db2, k); % 第 k 层高频重构合成 imshow(mat2gray(abs(D))); title([Level , num2str(k), Detail]); end end这段代码里的wrcoef2参数中a表示重构近似分量d表示重构细节分量后面的k是层数。注意 GUI 里弹出独立 figure 并不破坏 App Designer 结构因为它是临时查看用的不是主界面的一部分。5. 融合质量怎么验证、参数陷阱与操作视频录制细节5.1 用指标替代肉眼看图熵、标准差与互信息很多人在 GUI 里调完参数只看“感觉更亮了一点”就交差了。但融合算法是否真的优于原图或优于另一组参数需要数值指标支撑。三个在论文和课设报告里最常用的指标function [ent, stdv, mi] fusionMetrics(F, I1, I2) % 计算融合图像常用质量指标 % ent : 信息熵越大表示信息量越丰富 % stdv : 标准差反映对比度 % mi : 融合图与两张原图的互信息之和越大表示保留信息越多 % 1. 信息熵 p imhist(uint8(F * 255)) / numel(F); p(p 0) []; % 去掉 0 概率项避免 log2(0) 警告 ent -sum(p .* log2(p)); % 2. 标准差 stdv std2(F); % 3. 互信息简单版本分别算 F 与 I1、I2 的互信息后相加 mi mutualInfo(F, I1) mutualInfo(F, I2); end function m mutualInfo(X, Y) % 离散化到 256 级后算联合直方图 Xq uint8(X * 255) 1; Yq uint8(Y * 255) 1; joint accumarray([Xq(:), Yq(:)], 1, [256 256]); pXY joint / sum(joint(:)); pX sum(pXY, 2); pY sum(pXY, 1); % 逐元素计算 p * log(p / (px * py)) m sum(sum(pXY .* log((pXY eps) ./ (pX * pY eps)))); end使用建议在 GUI 里增加一个“计算指标”按钮回调里调fusionMetrics把结果显示到StatusLabel或专门的文本框。对比不同level参数时记录ent和stdv的数值变化比主观描述有说服力得多。注意互信息的单位是 bit两个图像都被归一化到 [0,1] 后计算才有可比性。5.2 参数陷阱三处容易让结果“翻车”的地方第一处是低频平均处理。很多新手自然以为“低频取平均”就是C_fuse(1:ll_len) (C1(1:ll_len) C2(1:ll_len)) / 2这在两张图亮度接近时没问题。但当红外图整体偏亮时平均后融合图会在亮区发白暗区细节丢失。更稳的做法是低频用“基于局部能量的加权平均”将 LL 子带切块计算每个块的能量占比用占比作为权重。代码改动不大但融合质量有明显提升。第二处是abs取最大时没有考虑符号。高频系数有正有负绝对值最大不一定代表结构信息最丰富某些情况下它只是噪声尖峰。一个折中的办法是比较前先做个 3×3 邻域均值滤波判断即abs(conv2(C1(k), ones(3)/9))和abs(conv2(C2(k), ones(3)/9))比较能降低孤立噪点被选中。第三处是 GUI 的旋钮返回值不是整数。app.LevelKnob.Value在用户旋转时返回小数比如 2.7直接传给wavedec2会报错。回调里必须加round()level round(app.LevelKnob.Value);这个细节在实际演示时最容易暴露因为手动旋钮很难精确停在整数值上。5.3 代码操作视频怎么录出“能跟着做”的效果标题里包含“代码操作视频”交付的往往是一个录屏或讲解视频。录制时有一个原则先录完整流程再补录关键局部不要一条视频从头录到尾。推荐流程是第一段30 秒打开 App Designer运行.mlapp文件加载两张样例图演示融合按钮。第二段2 分钟修改下拉框的wname、旋钮的level展示结果差异并配合 imshow 对比。这一步直接展示了 GUI 参数与算法结果的关系。第三段1 分钟打开waveFuse.m源码指向abs取最大的那一段代码口头补充一句“这里如果改成平均值边缘就会发糊”对应视频文字里写“小波变换图像融合规则对比”。第四段30 秒点击导出按钮保存结果展示目标文件夹里生成的fused_result.png。录制工具用 MATLAB 自带record功能或者系统录屏都可以但建议输出分辨率保持 1920×1080码率不低于 8Mbps否则代码区的文字在压缩后看不清。代码字体在 MATLAB 的“预设-字体”里调成等宽字体字号 14 以上方便手机端观看。最后在视频里每一步鼠标操作之间停顿 1 秒方便观众跟按。如果截图说明和画面标注也可选。操作视频的价值不在画面精美而在于让一个只装了 MATLAB 的人能看着你的操作顺序自己把.mlapp跑起来这就达到了交付要求。本文还有配套的精品资源点击获取