简介:这份MATLAB参考代码面向图像处理与计算机视觉方向的学习者和研究者,聚焦傅里叶梅林变换这一兼具旋转不变性的频域分析工具,可用于OCR、医学图像分析、天文图像处理等需要识别旋转变化对象的场景。资源包共6个文件,以5个bmp图像样本和1个m脚本为主,压缩包约152KB,图像样本覆盖裁剪、旋转、平移等变换情形,便于对照验证配准效果。已有3984人学习下载,说明该主题在图像配准领域有稳定的关注度。代码围绕二维傅里叶变换、梅林参数计算、频谱构建与逆变换等关键环节展开,并配有Register.m主脚本,读者可据此理解旋转不变配准的完整流程与实现思路。需要注意的是,描述中提及缩放部分尚未实现,读者可在此基础上补充尺度处理逻辑,或结合图像处理工具箱函数进行调试与优化,从而加深对傅里叶梅林变换原理与工程落地的理解。
1. 傅里叶梅林变换参考 MATLAB 代码:从旋转缩放配准到可复现实现
做图像配准的同行大概率都遇到过这种场景:两张图内容一样,但一张被旋转了 30 度、又缩放了 1.4 倍,用普通的相位相关一跑,峰值散成一片,配准直接翻车。这时候傅里叶梅林变换(Fourier-Mellin Transform,FMT)就是那个能救场的工具——它把旋转和缩放这两件事,从空域里难啃的非线性问题,转成对数极坐标域里的平移问题,再用相位相关一次性解出来。这篇笔记不讲空泛的数学史,只讲一件事:拿到一份傅里叶梅林变换参考 MATLAB 代码,怎么读懂它、怎么跑通它、参数怎么调、坑在哪。适合已经会 MATLAB 基础图像操作、正在做遥感配准、医学图像对齐或工业视觉对位的工程师,也适合想把 FMT 从论文公式落到工程代码的学生。
2. 傅里叶梅林变换到底在算什么:旋转缩放为什么能变成平移
2.1 从相位相关到对数极坐标的推导链条
先把整条链路捋清楚,不然后面看代码就是黑匣子。设参考图 f1(x,y) 和待配准图 f2(x,y) 之间只差旋转 θ0、缩放 σ 和一个平移 (Δx, Δy):
f2(x,y) = f1(σ(x cosθ0 + y sinθ0) − Δx, σ(−x sinθ0 + y cosθ0) − Δy)
对两边做二维傅里叶变换,利用傅里叶变换的旋转和缩放性质,得到频域关系:
F2(u,v) = (1/σ²) · F1((u cosθ0 + v sinθ0)/σ, (−u sinθ0 + v cosθ0)/σ) · e^(−j2π(...))
关键点来了:频域里旋转和缩放依然耦合在一起,直接相位相关解不出来。于是取幅度谱 |F|,丢掉相位(相位里装的是平移信息,幅度谱里装的是旋转和缩放),再对幅度谱做一次坐标变换——把笛卡尔坐标 (u,v) 换成对数极坐标 (log ρ, φ)。这一步之后,旋转 θ0 变成 φ 轴上的平移,缩放 σ 变成 log ρ 轴上的平移 log σ。两个非线性参数,全变成了线性平移。
最后对两张图的对数极坐标幅度谱再做一次相位相关,峰值位置就分别对应 log σ 和 θ0。解出旋转缩放后,把待配准图反向旋转缩放回去,再做一次普通相位相关,就能拿到平移量。整条链路是「FFT → 取幅度 → 对数极坐标重采样 → 第二次 FFT → 相位相关 → 反变换 → 再相位相关」,一共两次相位相关,缺一不可。
2.2 为什么必须用幅度谱而不是直接相位相关
很多人第一次写会偷懒,直接对两张原图做相位相关,结果旋转一超过几度就失效。原因是相位相关只对纯平移敏感,旋转和缩放会破坏相位的一致性,峰值能量被摊平。用幅度谱的代价是丢掉平移信息,但换来旋转缩放的解耦,这是划算的。工程上常见的做法是:幅度谱配准解旋转缩放,空域或频域相位相关解平移,两段式处理。
还有一个容易忽略的点:对数极坐标重采样会引入插值误差,尤其是 log ρ 轴在 ρ 接近 0 的地方采样极密、接近 Nyquist 的地方采样极疏。所以实际代码里通常会对幅度谱先做高通滤波(比如乘一个 (1 − cos(πx)cos(πy)) 的窗),压掉低频直流分量,避免对数变换在原点附近炸掉。
2.3 一份参考 MATLAB 代码的整体骨架
一份能跑的 FMT 参考代码,结构基本固定,我一般会拆成这几个函数:
| 函数名 | 职责 | 关键输入 |
|---|---|---|
fmt_register.m | 主流程,串起两次相位相关 | 两张灰度图 |
logpolar.m | 笛卡尔幅度谱转对数极坐标 | 幅度谱、输出尺寸 |
phasecorr.m | 相位相关求峰值 | 两幅同尺寸图 |
highpass.m | 幅度谱高通滤波 | 幅度谱 |
warp_back.m | 按解出的 θ、σ 反向变换 | 待配准图、参数 |
主流程伪代码逻辑是这样的:
function [theta, scale, dx, dy] = fmt_register(img1, img2) % 1. 转灰度、归一化、加窗,减少边界泄漏 img1 = im2double(rgb2gray_safe(img1)); img2 = im2double(rgb2gray_safe(img2)); win = hann(size(img1,1)) * hann(size(img1,2))'; F1 = fft2(img1 .* win); F2 = fft2(img2 .* win); % 2. 取幅度谱并做高通滤波,压掉直流 A1 = highpass(abs(fftshift(F1))); A2 = highpass(abs(fftshift(F2))); % 3. 对数极坐标重采样,输出尺寸决定角度和尺度分辨率 LP1 = logpolar(A1, 256, 512); LP2 = logpolar(A2, 256, 512); % 4. 第二次 FFT + 相位相关,解 log 尺度和角度 [rowShift, colShift] = phasecorr(LP1, LP2); scale = exp(rowShift / 256 * log(maxRadius)); theta = colShift / 512 * 360; % 5. 反向旋转缩放后,再做一次相位相关解平移 img2r = warp_back(img2, theta, scale); [dy, dx] = phasecorr(img1, img2r); end这段骨架里,logpolar的输出尺寸 256×512 是最常被随手写死的参数,但它直接决定角度分辨率和尺度分辨率,后面会专门讲怎么选。
3. 用 MATLAB 把 FMT 配准跑通:从读图到输出参数
3.1 环境准备与最小可运行脚本
MATLAB 版本建议 R2018b 以上,因为用到了fftshift、imresize和interp2的稳定行为。不需要额外工具箱,Image Processing Toolbox 有更好,没有的话interp2也能顶。最小可运行脚本如下,直接存成run_fmt.m:
% run_fmt.m 最小可运行 FMT 配准示例 clear; clc; close all; % 读图并统一尺寸,尺寸建议取 2 的幂,FFT 更快且边界好处理 img1 = imread('ref.png'); img2 = imread('mov.png'); img1 = imresize(img1, [512 512]); img2 = imresize(img2, [512 512]); % 调用主流程 [theta, scale, dx, dy] = fmt_register(img1, img2); fprintf('旋转 %.2f 度, 缩放 %.4f, 平移 (%.2f, %.2f)\n', ... theta, scale, dx, dy); % 可视化验证:把 mov 按解出的参数变换回去,和 ref 叠加 tform = affine2d([scale*cosd(theta) -scale*sind(theta) 0; scale*sind(theta) scale*cosd(theta) 0; 0 0 1]); img2r = imwarp(img2, tform, 'OutputView', imref2d(size(img1))); figure; imshowpair(img1, img2r, 'falsecolor'); title('配准后叠加,边缘重合说明参数正确');逻辑说明:先把两张图统一到 512×512,避免尺寸不一致导致 FFT 频率轴对不上。fmt_register返回四个参数,affine2d构造变换矩阵时注意旋转缩放矩阵的写法,平移单独用imwarp的OutputView对齐。imshowpair的falsecolor模式是最直观的验证手段,两张图边缘如果呈现单一颜色而不是红绿分离,说明配准到位。
参数说明:imresize的目标尺寸建议 512 或 1024,太小角度分辨率不够,太大对数极坐标重采样耗时明显上升。如果原图长宽比不是 1:1,先做 padding 补成正方形,否则对数极坐标的角度轴会失真。
3.2 对数极坐标重采样函数的写法与参数
logpolar.m是整份代码里最容易写错的地方,核心是把幅度谱上每个笛卡尔点 (x,y) 映射到 (log ρ, φ) 网格:
function LP = logpolar(A, nRadius, nAngle) % A: 已 fftshift 的幅度谱,尺寸 N x N N = size(A, 1); cx = floor(N/2) + 1; cy = cx; maxR = floor(N/2) - 1; % 构造对数极坐标网格 rho = exp(linspace(0, log(maxR), nRadius)); % 对数尺度轴 phi = linspace(0, 2*pi, nAngle+1); phi(end) = []; [PHI, RHO] = meshgrid(phi, rho); X = cx + RHO .* cos(PHI); Y = cy + RHO .* sin(PHI); % 双线性插值采样 LP = interp2(A, X, Y, 'linear', 0); end逻辑说明:rho用exp(linspace(...))生成,保证在对数域等间隔,这正是把缩放变成平移的关键。phi从 0 到 2π 均匀取nAngle个点,对应角度轴。interp2用双线性插值,超出边界的填 0。
参数说明:nRadius决定尺度分辨率,一般取 128 到 256;nAngle决定角度分辨率,取 256 到 512。角度分辨率 = 360/nAngle 度,取 512 时约 0.7 度,对多数工业对位够用。maxR取N/2 − 1是为了避开频谱最外圈的高频噪声。注意interp2的坐标顺序是 (X, Y),别写反,写反了角度轴会镜像,配准结果角度符号出错。
3.3 相位相关求峰值的实现与亚像素细化
phasecorr.m负责从两幅图里解出平移量,标准写法:
function [rowShift, colShift] = phasecorr(A, B) % A, B 同尺寸,返回行、列方向的平移量(像素) FA = fft2(A); FB = fft2(B); R = FA .* conj(FB); R = R ./ (abs(R) + eps); % 归一化,只保留相位 r = real(ifft2(R)); r = fftshift(r); [~, idx] = max(r(:)); [rowShift, colShift] = ind2sub(size(r), idx); rowShift = rowShift - floor(size(r,1)/2) - 1; colShift = colShift - floor(size(r,2)/2) - 1; % 亚像素细化:对峰值邻域做抛物线拟合 if rowShift > 1 && rowShift < size(r,1) y1 = r(rowShift-1, colShift); y2 = r(rowShift, colShift); y3 = r(rowShift+1, colShift); denom = (y1 - 2*y2 + y3); if abs(denom) > eps rowShift = rowShift + 0.5*(y1 - y3)/denom; end end end逻辑说明:R = FA .* conj(FB)是相位相关的核心,归一化去掉幅度只留相位,ifft2后峰值位置就是平移量。fftshift把零频移到中心,方便索引换算。亚像素细化用峰值左右邻点做抛物线拟合,能把精度从 1 像素提到 0.1 像素量级。
参数说明:eps防止除零。亚像素细化只在峰值不在边界时做,否则索引越界。如果峰值不明显(比如两图内容差异大),可以加一个峰值旁瓣比判据,低于阈值就报配准失败,别硬返回错误参数。
3.4 高通滤波与加窗:两个让结果稳定的前置处理
幅度谱在原点附近能量极强,直接做对数变换会把动态范围压得没法看。高通滤波用这个窗:
function A = highpass(A) [N, M] = size(A); [u, v] = meshgrid(-M/2:M/2-1, -N/2:N/2-1); u = u / M; v = v / N; H = (1 - cos(pi*u)) .* (1 - cos(pi*v)); A = A .* H; end逻辑说明:(1 − cos(πu))(1 − cos(πv))在低频接近 0、高频接近 1,平滑地压掉直流和低频。加窗则是在 FFT 之前对空域图像乘 Hann 窗,减少边界不连续造成的频谱泄漏。这两步不做,配准结果会时好时坏,属于典型的玄学来源。
参数说明:高通窗的形式不唯一,也有用1 − exp(−(u²+v²)/σ²)的,效果类似。加窗会损失图像边缘信息,如果配准依赖边缘特征,窗函数可以选更温和的 Tukey 窗。
4. 参数怎么调:角度分辨率、尺度范围与插值选择
4.1 角度和尺度分辨率的取舍
对数极坐标的输出尺寸直接决定能分辨多小的旋转和缩放。角度分辨率 Δθ = 360/nAngle,尺度分辨率 Δσ = exp(log(maxR)/nRadius)。举个具体数:nAngle=512 时 Δθ≈0.7°,nRadius=256、maxR=255 时 Δσ≈exp(5.54/256)≈1.022,也就是能分辨约 2.2% 的缩放差异。
如果应用场景是遥感图像配准,旋转可能任意角度,nAngle 取 512 起步;如果是工业对位,旋转通常在 ±10° 内,nAngle 取 256 就够,还能省一半计算量。尺度范围由 maxR 决定,maxR 越大能覆盖的缩放范围越宽,但高频噪声也越多,一般取 N/2 − 1 是稳妥的。
4.2 插值方法对精度的影响
interp2支持'nearest'、'linear'、'cubic'。最近邻最快但角度轴会有阶梯误差,双线性是精度和速度的平衡点,三次插值精度略高但耗时增加约 40%。我的经验是:nAngle 取 512 以上时,双线性和三次差别已经很小,用双线性即可;nAngle 取 256 以下时,三次插值能明显改善角度估计的抖动。
4.3 常见参数组合对照
| 场景 | nRadius | nAngle | 插值 | 预期角度精度 |
|---|---|---|---|---|
| 工业对位(小角度) | 128 | 256 | linear | ±0.5° |
| 医学图像对齐 | 256 | 512 | linear | ±0.2° |
| 遥感大角度配准 | 256 | 512 | cubic | ±0.15° |
| 快速预览 | 64 | 128 | nearest | ±2° |
这张表不是标准答案,是给一个起点,实际还要看图像内容和噪声水平。
5. 避坑与排查:FMT 配准最常见的 5 个翻车点
5.1 角度解出来总是差一个固定偏移
现象:每次配准角度都比真值差 90° 或 180°。原因:对数极坐标的角度轴起点和图像坐标系的定义没对齐,或者interp2的 X/Y 顺序写反导致角度镜像。解决:先用一张自己旋转已知角度的图做自测,确认角度轴零点对应图像 x 轴正方向,把phi的起点和旋转方向固定下来,写进注释。
5.2 缩放解出来是倒数
现象:解出的 scale 和真值互为倒数。原因:相位相关的两个输入顺序反了,FA .* conj(FB)里谁在前谁在后决定平移方向。解决:约定参考图在前、待配准图在后,或者解出后判断 scale 是否在合理范围(比如 0.5 到 2 之间),超出就取倒数。
5.3 峰值散成一片,配准直接失败
现象:相位相关结果没有明显峰值。原因:两张图内容差异太大,或者幅度谱没做高通滤波,低频直流淹没了有效信息。解决:先确认两图确实有重叠内容,再检查高通滤波是否生效,最后可以尝试对幅度谱做对数压缩log(1 + A)再归一化。
5.4 平移量解出来偏大且不稳定
现象:旋转缩放解对了,但平移量每次差几十像素。原因:反向变换后的图边界填充了 0,相位相关被边界效应干扰。解决:反向变换后用和参考图相同的窗函数加窗,或者只取中心区域做相位相关,避开边界。
5.5 大缩放比下精度骤降
现象:缩放超过 2 倍后误差明显变大。原因:对数极坐标在 ρ 接近 0 处采样过密、接近 maxR 处采样过疏,大缩放对应 log ρ 轴上的大平移,容易超出有效范围。解决:限制配准的缩放范围,或者对幅度谱做多尺度金字塔,先粗配准再精配准。
6. 进阶技巧:用金字塔和掩膜把 FMT 配准精度再提一档
基础版 FMT 跑通之后,真正决定工程可用性的是两个进阶手段:多尺度金字塔和频域掩膜。
金字塔的做法是:先对两张图各做 3 层高斯下采样,从最粗的一层开始跑 FMT,得到一个粗略的 θ 和 σ,把这个结果作为下一层的初值,只在初值附近的小范围内搜索。这样既扩大了能处理的缩放范围,又提高了精配准的稳定性。粗层用 nAngle=128、nRadius=64 快速出结果,精层用 nAngle=512、nRadius=256 细化,整体耗时比单层大参数还低。
频域掩膜则是针对特定场景:如果已知旋转只在某个范围内,可以在对数极坐标的角度轴上只保留对应区间,减少噪声干扰。比如工业对位旋转不超过 ±15°,就把角度轴限制在对应范围,相位相关的搜索空间小了,峰值也更干净。
验证方法上,我习惯用「已知变换自测」:拿一张图,人为旋转 23°、缩放 1.35 倍、平移 (17, −9),跑一遍 FMT,看解出的参数和真值的偏差。偏差在角度 0.3°、缩放 0.5%、平移 0.5 像素以内,才算这份代码在目标场景可用。这个自测脚本建议每次改完参数都跑一遍,比拿真实数据试快得多。
血泪经验是:FMT 的精度瓶颈往往不在算法本身,而在前置处理——加窗、高通、归一化这三步没做对,后面参数怎么调都是白费。我一般会把这三步封装成一个preprocess.m,所有配准入口都走它,避免不同脚本里处理不一致。另外,别迷信一次配准就能到亚像素,工程上更稳的做法是 FMT 出粗值、局部模板匹配出精值,两段式收尾。
希望帮到你。
本文还有配套的精品资源,点击获取