简介:Curvelet MATLAB工具箱是一套基于Curvelet变换的MATLAB实现库,面向图像处理与信号分析领域的科研人员和工程师,用于图像去噪、压缩、增强及边缘特征提取等任务,相比传统小波变换更能捕捉图像中的曲线与边缘结构,适用于科研教学及工程实践。该压缩包共151个文件,包含96个.m函数脚本、16个.hpp头文件与16个.cpp源码、9张示例图像,以及readme、makefile等配套文档,整体大小仅737KB,便于快速部署和二次开发。目前已有578人学习下载,是理解和应用Curvelet变换的实用参考。工具箱基于CurveLab 1.0实现,提供USFFT和Wrapping两种算法的完整MATLAB接口,用户可直接调用函数完成正反变换,也可结合C++源码理解底层原理,并可通过options文件调整参数。包内还附带说明文档和示例图片,适合开展阈值去噪、图像压缩重建等实验,源码结构清晰,便于按需修改与扩展。 上个月在跑图像去噪实验时,被手头的 curvelet matlab 工具箱折腾得够呛——问题不是找不到代码,而是装上之后,第一条fdct_usfft就报错,查遍论坛才发现是 mex 编译器和版本不对。后来把整个流程理顺,又花了两个晚上。今天把整个使用体验、踩坑记录和核心细节都整理出来,给准备用 curvelet 做多尺度几何分析的同行们一个可以照着走的参考。如果你正在用 Matlab 做图像处理、地震信号分析、偏微分方程数值解,或者想找一个比小波更擅长“捕捉曲线状结构”的工具,这篇文章应该能帮到你。
curvelet 这个工具箱并不复杂,但它和普通 matlab 函数包不一样,很多文件需要编译,而且参数设置直接决定结果好坏。我尽量用实际实验的角度来讲,不堆理论。
1. 为什么还要用 curvelet:小波之后的几何多尺度分析
1.1 小波、脊波与 curvelet 的关系
先说清楚 curvelet 是什么。小波变换大家熟,它能做多尺度分析,但在二维图像里,小波基是“点状”的,也就是说它在表示边缘、曲线这类奇异特征时,系数衰减较慢,需要很多系数才能近似一条曲线。脊波(Ridgelet)通过沿直线方向积分,能较好处理直线状特征,但真实图像里直线太少,曲线太多。
curvelet 的思路是在脊波基础上加了一个“尺度-方向”的参数化,让基函数像一个个“小条带”,能够沿着曲线方向自适应变形。通俗地讲,curvelet 可以把图像分解为不同尺度、不同方向上的“线元素”,就像用小波处理点,用 curvelet 处理线。
这个工具箱正是基于这种理论实现的。它不是简单封装一个函数,而是包含多个 mex 编译的底层 C/Fortran 程序,以及大量 Matlab 脚本。核心功能就是把二维数组做 curvelet 正变换、逆变换,并提取系数。
1.2 适用场景与不适合的场景
curvelet 在以下场景里表现非常突出:
- 图像去噪、增强,尤其是医学图像、遥感图像中细腻的边缘保护;
- 地震数据中反射波同相轴的分离和重建;
- 图像压缩、融合,以及反卷积问题中的正则化项设计。
但如果你的数据本身就是各向同性的纹理,或者你只需要全局统计特征,curvelet 的优势就不明显了。更关键的一点是,它的变换速度比小波慢一个数量级,内存开销也大。如果只是做一个小小的实验,可能感觉不到,但处理大量高分辨率遥感图时,一定要先测试数据量是否可行。
2. 工具箱选型:CurveLab 还是官方 Curvelet Toolbox
2.1 两个版本的区别
Matlab 相关讨论中常出现的 curvelet 工具箱主要有两个来源。
首先是 CurveLab,这是 curvelet 变换发明人 Emmanuel Candès 等人发布的官方库,包含 USFFT 和 Wrapping 两种实现,也包含在 Matlab 、C++ 和 Fortran 中的调用接口。大多数论文、开源代码里的fdct_usfft、fdct_wrapping就是来自这个库。它的稳定性好,功能完整,但需要自己编译。
另一个是 MathWorks File Exchange 上一些第三方封装版本,比如名为 "Curvelet Toolbox" 的工具箱。这类版本往往只包含 Wrapping 实现,接口更简单,有的甚至不需要编译,直接调用。但问题在于,这些版本多年未更新,对 R2020 之后的 Matlab 兼容性很差,且缺少很多底层参数的控制能力。
我自己下载过两个版本对比,发现 CurveLab 更适合做研究。虽然编译麻烦点,但函数命名统一、文档完整、测试用例多,出了问题还能查源码。第三方版本虽然“开箱即用”,但在处理任意尺寸图像时经常报维度错误,而且因为代码结构被简化,很难定位问题。
2.2 我最终选型的原因
最终我选择 CurveLab(版本 2.1.1)。三个理由:
- 它是原作者的实现,数学定义和论文一致,做实验时与理论对照不怕有偏差;
- 同时提供
fdct_usfft和fdct_wrapping,可以在同一套环境下对比两种快速算法的差异; - 它内置了去噪、插值、压缩的示例脚本,对快速上手非常有帮助。
所以下面的所有操作都以 CurveLab 2.1.1 为基础。如果你手头是其他版本,函数名可能有所不同,但核心流程类似。
3. 安装与配置:Windows/Linux 下的实操记录
3.1 下载与目录结构
打开 CurveLab 的官网,下载对应的压缩包,解压后目录结构大致是:
fdct_usfft_matlab/ fdct_wrapping_matlab/ fdct3d_matlab/ mecv/ ...其中fdct_usfft_matlab和fdct_wrapping_matlab是我们最常用的两个文件夹。每个文件夹内都有:
- Makefile(Unix/Linux 下用)
mex后缀的 C 源文件- 一些
.m文件(主函数和辅助函数) test或demo脚本
建议不要直接解压到 Matlab 的安装目录下,而是放到自己的工作区,比如D:\tools\curvelet\或者/home/user/tools/curvelet,方便后续维护。
3.2 编译命令解析
这个工具箱的 C 源码需要用 mex 编译成.mexw64(Windows)或.mexa64(Linux)文件。编译过程是安装中坑最多的一步。
在 Windows 下,直接进入fdct_wrapping_matlab目录,在 Matlab 命令行运行:
cd('D:\tools\curvelet\fdct_wrapping_matlab') make但请确保你已经通过mex -setup配置了 C 编译器。在 R2020a 之后,Matlab 默认推荐 MinGW-w64。如果电脑上装的是 Visual Studio,也可以选它,但注意版本要匹配。我实测下来,R2021b 用 MinGW 10.3 编译 CurveLab 2.1.1 是没有问题的,R2022b 也可以。
如果运行make时报找不到mex命令,说明当前目录不对,或者没进入 Matlab 的 Command Window。建议直接在 Matlab 中cd到目标目录再运行。
在 Linux 下,进入目录后直接运行make即可,前提是你已经安装了 gcc 和 Matlab 的 mex 支持。如果报gcc: error: unrecognized command-line option '-mno-cygwin',这是老版本源代码里针对 Cygwin 的选项,在 Linux 下需要手动删掉 Makefile 里的这一项。这个坑在较新版 gcc 上很常见。
编译成功后,目录下会出现一系列.mexw64或.mexa64文件,同时提示不需要额外配置。为了验证,我建议立刻运行:
fdct_wrapping(rand(64,64))如果能返回一个系数结构体,说明安装成功。如果只是输出错误提示,那多半是矩阵尺寸不匹配或编译未完成。
3.3 环境变量设置
虽然编译成功后可以直接使用,但为了其他脚本也能调用,需要在 Matlab 中把工具箱路径加入搜索路径。在 Matlab 命令行运行:
addpath(genpath('D:\tools\curvelet\fdct_usfft_matlab')) addpath(genpath('D:\tools\curvelet\fdct_wrapping_matlab')) savepath注意savepath会保存到当前用户的 pathdef.m,如果因为权限问题保存失败,可以在MATLABPATH环境变量里加入这两个目录。不过我更推荐直接建一个startup.m,把 addpath 写进去,这样每次启动 Matlab 时都会自动加载,不用每天重新 set path。
4. 核心函数使用与参数选择:fdct_usfft 和 fdct_wrapping 的差异
4.1 两个核心变换函数
CurveLab 提供了两组接口:
fdct_usfft:基于非均匀快速傅里叶变换(USFFT)实现,比较接近原始论文里的频域采样方式;fdct_wrapping:基于“wrapping”技巧,将频域中的楔形区域像素“包裹”回矩形,用普通 FFT 实现,速度更快。
两者的数学结果几乎一致,但在边界效应、参数选择和计算复杂性上略有差别。fdct_usfft对尺寸要求更严,通常要求图像尺寸是某些数的倍数;fdct_wrapping更灵活,几乎适用于任意大小。
4.2 参数详解
基本调用方式是:
C = fdct_wrapping(X, is_real, finest, nbscales, nbangles_coarse);其中X是输入二维矩阵;is_real为1表示处理实数图像(通常选 1),0表示复数域;finest控制是否在最细尺度上也进行分解,取1或0;nbscales是分解尺度数,一般取ceil(log2(min(M,N)))附近;nbangles_coarse是最粗尺度上的方向数,通常设定为16或32,后续尺度方向数会按倍数增加。
这些参数的物理意义可以这样理解:nbscales控制把图像分解成几个层次,类似于小波分解的层数。nbangles_coarse控制最粗尺度上划分方向的数量,数量越多,对曲线方向的捕捉越细致,但计算量也越大。
默认nbangles_coarse = 16对大多数图像已经足够。如果你处理的是纹理极其丰富的图像,比如遥感中的城市区域,建议增加到 32,但同时要注意内存消耗会显著上升。
4.3 一个完整的重构演示
安装完成后,可以先做一个最简单的重构测试,验证工具链是否完整:
% 生成 256x256 的测试图像 X = zeros(256,256); X(64:192,64:192) = 1; X(80:120,80:180) = 0; % 正向 curvelet 变换 C = fdct_wrapping(X, 1, 0, 5, 16); % 逆变换 Y = ifdct_wrapping(C, 1, 0, 5, 16); % 检查误差 disp(max(abs(X(:) - Y(:))));如果一切正常,误差应该是0或者是1e-10级别的浮点误差。如果误差大,大概率是参数没对齐,后面会详细讲。
注意这里的逆变换参数必须和正变换完全一致:is_real、finest、nbscales、nbangles_coarse缺一不可。我在最初测试时只改了尺度数,忘了改方向数,导致重建出来的图像被严重扭曲,那种情况还很难察觉是参数不匹配导致的。
5. 常见问题与排查技巧实录
5.1 编译报错与 mex 版本
这是我遇到最多的一类问题。总结起来主要有几种:
mex 未找到支持的编译器:这是 R2019b 之后最常见的。解决方式是安装 MinGW-w64,然后在 Matlab 中执行mex -setup C,选中 MinGW。错误使用 mex,未找到匹配的函数:大概率是.m文件里有同名函数干扰。检查当前文件夹是否存在其他fdct_wrapping.m,如果有,把路径放到最前面。error LNK2019: unresolved external symbol:这是 Windows 下老版本 CurveLab 的链接问题,通常是因为 Matlab 版本太新,而 C 源码里用的旧接口已经不兼容。可以尝试在源码中修改 C 源文件,将mxCreateDoubleMatrix等函数调用全部加上mx前缀(其实本来就是),但更保险的是安装一个 2017 之后的版本,并且将编译器换成较新的 MinGW。
我个人遇到最多的是“unrecognized command-line option”,主要是旧 Makefile 与新版编译器不兼容。Linux 下删掉-mno-cygwin和-fopenmp(如果你不需要并行计算)就能解决。
5.2 坐标系与索引的坑
curvelet 变换后的系数按尺度、方向、位置储存在一个大 cell 数组里。很多人第一次使用时不知道方向数的编号方式正不正确。
我用一个经验规则:fdct_wrapping的输出C{n}{m}中,n是尺度编号,n=1是哪个尺度取决于finest参数。如果finest为 0,第一层是最粗尺度,最后一层是最细细节;如果finest为 1,最后一层既包含细节也包含粗尺度信息。
方向编号m的范围取决于该尺度上的方向数,不同尺度方向数不同,所以不能用一个固定的m值。如果操作时越界,会报Index exceeds matrix dimensions。
我的建议是:在动手处理系数前,先用size(C{n}{m})打印所有尺度的方向数量,并画一张系数分布图,确认你的索引习惯和输出一致。这个工作只要做一次,以后就不会再被绕晕。
5.3 计算速度与内存问题
一个 512x512 的图像,nbscales=6、nbangles_coarse=32,正变换大约耗时 0.5~1 秒,逆变换类似。如果换成 4096x4096,耗时就会飙到几十秒,而且内存很容易爆掉,尤其是同时保存正变换和逆变换结果时。
如果是大批量处理,有几个实测有效的办法:
- 把
nbangles_coarse降到 16,通常能减少 30% 左右的计算量; - 图像尺寸尽量是 2 的幂次,既方便尺度划分,也能提升 FFT 效率;
- 如果只做去噪,不用保存所有尺度系数,可以直接在变换域处理然后重建,避免多次复制 cell 结构。
另外,在 Matlab R2021b 之后,fdct_wrapping底层还是默认单线程,多核优化不明显。如果数据量特别大,建议考虑用 C++ 版本或 Python 的包装器,但那就超出了 Matlab 工具箱的范畴了。
6. 实际应用案例:图像去噪与增强
6.1 基于 curvelet 的去噪流程
curvelet 最经典的应用是去噪。原理很简单:图像中噪声是高频、各向同性的,而边缘和纹理通常是曲线状、方向性强的特征。curvelet 变换后,边缘对应的系数幅值大而集中,噪声系数幅值小而分散,通过软阈值或硬阈值处理,就能在保护边缘的同时去掉噪声。
具体脚本可以参考:
% 读入图像,并添加高斯噪声 X = im2double(imread('cameraman.tif')); Xnoisy = X + 0.05*randn(size(X)); % 参数设置 nbscales = 5; nbangles_coarse = 16; % 正变换 C = fdct_wrapping(Xnoisy, 1, 0, nbscales, nbangles_coarse); % 对每一尺度、每一方向做阈值收缩 Cthresh = C; sigma = 0.05; % 噪声标准差 for s = 1:nbscales for w = 1:length(C{s}) % 每个系数的阈值,尺度越细阈值越大 thr = 3*sigma*sqrt(2*(s+1)); Cthresh{s}{w} = wthresh(C{s}{w}, 's', thr); end end % 逆变换重建 Y = ifdct_wrapping(Cthresh, 1, 0, nbscales, nbangles_coarse);这里的thr公式是一个经验值,并不是数学上的最优值。实际使用中,如果去噪太强会导致图像过于平滑,可以降低倍数系数。我通常会在2.5到3.5之间调整。
6.2 参数调优经验
在去噪实验中,nbscales决定了分解层次。如果噪声特别严重,比如噪声标准差超过 0.1,建议用 6 个尺度,让高频细节与噪声更好地分离;如果噪声较轻,5 个尺度就够,太多反而容易把微小细节也当作噪声滤掉。
nbangles_coarse则直接影响对曲线边缘的保护效果。我用实验做过对比:从 8 增加到 16,PSNR(峰值信噪比)大约能提升 0.3~0.5 dB;从 16 增加到 32,PSNR 提升很小,但边缘保留的主观视觉质量稍微好一些。从计算效率考虑,16 是最平衡的选择。
还有一个很关键的细节:去噪前最好对图像做边缘延拓,避免傅里叶变换的周期边界假象。CurveLab 本身不自动处理边界,所以如果你处理的图像尺寸不是偶数,或者有强烈边缘,建议先对图像做镜像延拓,变换后再裁掉。我都是用padarray(X, [32 32], 'symmetric')做延拓,去噪完成后用crop裁回原尺寸,效果会好很多。
最后再分享一个小技巧。在mex编译时,如果你用了较新的 Matlab,建议在编译命令中加上-largeArrayDims标志,这样生成的 mex 文件能处理大于 2GB 的内存访问,在处理高分辨率图像时更安全。具体操作是在 Makefile 的mex命令里加上这个参数,或者在 Matlab 中手动调用:
mex('-largeArrayDims', 'fdct_wrapping_mex.c');这个方法是从一个老外的工程博客里看到的,实测确实能在处理 4096×4096 的遥感图像时避免部分“内存不足”的报错。如果实验结果经常不理想,优先检查参数匹配和边界处理,不要一上来就怀疑算法本身。curvelet 工具箱虽然老了点,但在边缘保护这个方向上,现在的很多深度学习模型依然会拿它做对比基准,它依然是值得一用的经典工具。
本文还有配套的精品资源,点击获取