拓冰建站拓冰建站
首页 / 资讯中心 / 正文

视觉显著性检测必备:PQFT四元数傅里叶变换MATLAB实现与调参指南

简介PQFT四元傅里叶变换模型的MATLAB代码是一套用于图像显著性检测与目标提取的完整实现适合具备基础MATLAB与图像处理知识的开发者学习和复用。压缩包共45个文件以15个.m源码为核心涵盖主程序、四元数傅里叶变换与逆变换、四元数运算、显著图生成等模块并附带26张jpg测试图像、1张png示例、1个fig可视化界面和2个asv自动保存文件总大小仅1.49MB。已有573人学习/下载算法流程按预处理、四元数构造、频域变换、滤波后处理、逆变换和检测定位完整展开其中实现了图像归一化、灰度到四元数映射、fft2变换及ifft2反变换等关键步骤注释清晰目录直观直接运行入口脚本即可观察PQFT谱与显著性图的生成过程也可基于fig界面调整参数。这套实现便于理解四元数频域分析原理适合作为显著性检测实验的参考实现或二次开发基础亦可进一步扩展至特征提取与目标检测等视觉任务。 做视觉显著性检测的人十有八九都绕不过PQFT这个模型。我第一次接触它是在做一个视频监控中的感兴趣区域提取模块当时需要一种既快又不依赖训练数据的显著性算法作为baseline查了一圈资料最后锁定了Phase Quaternion Fourier Transform也就是相位四元数傅里叶变换。这套MATLAB代码我前前后后改了三个版本从照抄论文公式翻车到理解辛分解之后自己手写实现整个过程踩了不少坑。这篇文章把我最终验证过的PQFT MATLAB完整实现、每一行代码背后的原理以及哪些参数需要调、哪些坑必须避开一次性讲清楚。不管你是刚接触显著性检测的新手还是想快速拿一个频域显著性baseline来做对比的老手这篇都应该能帮你省下不少时间。1. 视觉显著性检测里PQFT为什么值得作为首选Baseline1.1 显著性检测到底在检测什么视觉显著性检测Visual Saliency Detection的目标很简单让计算机像人眼一样在扫到一张图的瞬间就锁定最抓眼球的那块区域。人眼在200毫秒左右就能完成对复杂场景的初步扫描这种能力来自生物视觉系统底层的自底向上机制——不需要任何语义理解纯粹依靠颜色、亮度、边缘、运动这些低层特征的对比度来决定注意力分配。PQFT就是这种自底向上机制的算法化实现。它输出的显著图Saliency Map是一张与原图同尺寸的灰度图亮度越高的像素代表该位置越“显眼”。这套东西在工程上的应用非常广图像压缩时优先保留显著区域、视频摘要提取关键帧、广告布放选位置、机器人视觉导航找目标、图像检索里的区域权重计算。只要是涉及“先找重点”的场景显著图都能当第一步的预处理。我之所以强调“自底向上无需训练”是因为在真实项目里标注数据往往是最贵的资源。PQFT不需要训练不需要标注一张图丢进去直接出结果这对冷启动来说太友好了。1.2 跟ITTI、谱残差比PQFT强在哪在频域显著性检测这条技术路线上有几位绕不开的前辈。Itti-Koch模型是神经科学启发的经典方法用高斯金字塔构造多尺度图像在每个尺度上提取颜色、亮度、方向特征图再做中央-周边差分最后跨尺度融合归一化。效果确实好但计算链路长参数多金字塔层数、特征权重、归一化方式在实时场景下基本跑不动。谱残差Spectral Residual, SR是侯晓迪早先提出的频域方法做法比较暴力对灰度图做FFT取对数幅度谱减去均值滤波后的平滑幅度谱得到“谱残差”再exp回去做IFFT取模得到显著图。SR胜在快但它只用了一个亮度通道完全丢掉了颜色信息对彩色场景的显著性刻画比较弱。PQFT正好在两者之间找到了平衡点。它用四元数把亮度、颜色甚至运动通道捆成一个整体一次性做傅里叶变换既保留了多通道特征融合的能力又只有一个FFT的计算量天生适合实时系统。2007年侯晓迪他们在CVPR上发那篇PQFT论文时用普通PC就能跑到几十帧每秒这在当时是很惊艳的。我没必要在这里写大段比较直接给个结论如果你只想用一个又快又稳的显著性baselinePQFT是性价比最高的选择如果要追求极致的检测精度后面可以换深度学习方法但PQFT作为对比实验的下限永远有它的位置。1.3 核心直觉为什么只看相位谱就能找到目标用过FFT的人都知道一个二维信号做傅里叶变换后会得到幅度谱和相位谱两个部分。教科书上通常说幅度谱反映频率成分的强弱相位谱反映各频率分量的位置关系。但很多人忽略了一个更直观的结论——相位谱携带了图像绝大部分的“结构信息”。有个经典实验把A图的幅度谱跟B图的相位谱组合反变换出来的图像观感上几乎完全就是B图的样子。换句话说决定“图里有什么形状、目标在哪个位置”的主要是相位谱幅度谱更多决定纹理的粗糙程度和能量分布。PQFT抓住了这一点既然目标的位置信息藏在相位里那我干脆把幅度谱全部归一化只保留相位再做逆变换。逆变换得到的值在结构突变强烈的地方比如目标边缘、目标和背景交界的区域就会特别大这些位置叠加起来就是显著区域。实现上更简单粗暴——频域每个点除以其模长相当于把幅度缩放到1相位完整保留。这个方法听起来有点“乱来”但实测效果出奇地稳定。原因在于自然图像的光谱分布遵循1/f规律幅度谱天然集中在低频直接看幅度谱反而会被背景主导把幅度拍平成1相当于对所有频率一视同仁高频的细节和低频的轮廓都会被纳入考量显著目标的结构信息就凸显出来了。2. 四元数傅里叶变换的工程实现从数学到MATLAB2.1 四元数怎么把多路特征“叠”成一个整体四元数这东西学过但没怎么用过的人容易觉得它神秘。其实可以把它理解成一个四维的扩展复数一个实部加三个虚部写作q w x·i y·j z·k满足i² j² k² ijk -1。一个四元数天然有四个“坑位”正好可以把图像的四个特征通道分别填进去。在PQFT的静态图像版本里我填的是这三路特征亮度通道 I (RGB)/3颜色对手通道 RG R-G颜色对手通道 BY B-(RG)/2如果是视频场景还会加一路运动特征 M |I(t) - I(t-τ)|这第四个通道就填进k分量。四元数的实部我通常空出来或者放一些辅助数据因为三个虚部已经足够承载静态图的特征。有人可能会问为什么不直接对三个通道分别做FFT再把结果加起来这恰恰是四元数的关键价值。四元数傅里叶变换是在超复数域内做整体运算三个通道在变换过程中会通过四元数乘法互相影响这种耦合关系等价于一种隐式的跨通道特征融合。简单的“分别FFT再相加”很难模拟这种交互效果。你可以这么理解普通FFT是单通道信号在频域的分析四元数FFT是多个通道作为整体在频域做联合分析它保留了通道之间的相位关系。2.2 两个fft2等价于一个四元数FFT辛分解的秘密在MATLAB里我们没有现成的四元数FFT函数除非装了某些特定工具箱但这并不妨碍实现。方法就是辛分解Symplectic Decomposition。核心思路是这样任何一个四元数q都可以拆成“第一个复数 第二个复数乘j”的形式q (w x·i) (y z·i)·j因为i和j在四元数代数里是正交的乘上j之后y z·i这个复数整体的虚部就会落在k方向上。拆开之后四元数FFT的线性性允许我们把它拆成两个普通的复数FFT对第一部分做fft2得到Q1对第二部分做fft2得到Q2频域的四元数结果就是 F Q1 Q2·j。这个操作看着不起眼但它是整段PQFT代码能落地的基础。没有这个分解你大概率得自己实现四元数矩阵乘法那代码复杂度会翻好几倍而且很难调通。我第一版代码就是试图用for循环逐像素构造四元数再手写FFT结果速度惨不忍睹后来才发现辛分解这个标准操作。在实际代码里我会把w和x合成一个复数矩阵q1 complex(I, RG)把y和z合成q2 complex(BY, zeros(size(BY)))。注意这里的BY放在实部虚部填0因为我们静态图只用三个通道第四路留空。2.3 相位谱归一化与逆变换取模的原理四元数FFT做完之后频域里每个频点是一个四元数。要让幅度归一化就得先算出这个四元数的模。在辛分解表示下四元数F Q1 Q2·j的模有一个非常简洁的计算方法|F| sqrt(|Q1|² |Q2|²)其中|Q1|和|Q2|就是普通复数的模。因为Q1包含了实部w和虚部xQ2包含了y和z四者的平方和恰好就是四元数的模平方。归一化就是F除以|F|即Q1/mag和Q2/mag。做完这个操作后频域中所有频点的幅度都是1只剩相位信息。逆变换同样用辛分解对Q1/mag做ifft2对Q2/mag做ifft2再组合。逆变换的结果是空间域的四元数矩阵我们关心的只是它的模——因为相位谱“全通”之后逆变换会产生很多高频振荡在目标结构突变处模值会特别大背景平坦区域接近零这个模值天然就是一张显著的响应图。这里有个关键细节逆变换出来的模值必须做高斯平滑。原因是相位谱归一化相当于一个全通滤波器会让能量扩散到所有频点直接取模会看到大量椒盐状的噪点。高斯平滑能把邻域内的响应连成片让显著区域更完整。3. PQFT的MATLAB代码完整实现与逐段注释3.1 特征提取亮度与颜色对手通道的构造我先把特征提取这段单独拎出来说。很多人直接拿RGB三个通道用效果会打折扣。PQFT论文里用的是颜色对手空间Color Opponent Space这个设计模仿了人眼视觉通路中的颜色拮抗机制——视网膜神经节细胞对“红-绿”和“蓝-黄”的差异最敏感而不是直接响应RGB绝对值。% 输入图像归一化到[0,1] if isa(img, uint8) img double(img) / 255; else img double(img); end % 提取亮度通道 I mean(img, 3); % 提取颜色对手特征 r img(:,:,1); g img(:,:,2); b img(:,:,3); RG r - g; BY b - (r g) / 2;这段代码的关键点有两个。一是归一化。如果不先归一化RG的数值范围在-255到255之间而亮度I的范围在0到255之间量纲不一致会导致四元数傅里叶变换时某个通道主导显著性计算。归一化之后大家都在同一个尺度上。二是BY通道的计算方式。很多文章里写的是B-Y实际上Y是黄色通道由R和G混合而来简化为(rg)/2。这样BY能捕捉蓝色与黄绿色的对抗信息。直接用B减去一个常数得不到这个效果。3.2 构造四元数矩阵与QFFT等价实现特征提取完之后就要把三路特征塞进四元数实部和虚部。我前面说了用辛分解所以构造的是两个复数矩阵% 构造四元数 q I RG*i BY*j 0*k % 辛分解: q (I RG*i) (BY 0*i)*j q1 complex(I, RG); q2 complex(BY, zeros(size(BY))); % 二维四元数傅里叶变换 (等价于两个复数fft2) Q1 fft2(q1); Q2 fft2(q2);这里我用complex函数直接合成复数矩阵比用realimag*1i更高效也不会写出容易看花眼的表达式。Q1实部是亮度虚部是RGQ2实部是BY虚部是0。两个fft2跑完频域的四元数F Q1 Q2·j就构造完成了。如果你后面要加运动通道M只需要把Q2的虚部从0改成M就行。3.3 相位谱归一化与逆变换取模频域处理的核心代码就几行% 计算四元数模长并防除零 mag sqrt(abs(Q1).^2 abs(Q2).^2); mag(mag eps) eps; % 提取相位谱 F / |F| P1 Q1 ./ mag; P2 Q2 ./ mag; % 逆四元数傅里叶变换 p1 ifft2(P1); p2 ifft2(P2); % 显著图 逆变换结果取模 smap sqrt(abs(p1).^2 abs(p2).^2);这里面最容易翻车的就是除零。FFT结果里如果某个频点的所有通道能量都为零比如纯色图像的DC附近某些频点直接除会得到NaN。NaN会像病毒一样在后续的滤波和归一化中扩散最后显著图会变成一张布满白色噪点的废图。mag(mag eps) eps这行是保命代码。逆变换后的模值就是初步显著图。注意p1和p2都是复数矩阵abs(p1).^2 abs(p2).^2相当于四元数四个分量的平方和开根号后就是四元数模。这一步不能只用abs(p1)会丢掉Q2里的信息。3.4 完整函数与demo调用把上面的代码拼装起来再加上高斯平滑和归一化就是一个可以直接用的函数function smap pqft_static(img, sigma) % PQFT_STATIC Phase Quaternion Fourier Transform for image saliency % 输入: % img - RGB彩色图像uint8(0~255)或double(0~1)均可 % sigma - 高斯平滑标准差默认4 % 输出: % smap - double型显著图范围[0,1]与原图同尺寸 if nargin 2 sigma 4; end % 转为double并归一化 if isa(img, uint8) img double(img) / 255; else img double(img); end % 特征提取 I mean(img, 3); r img(:,:,1); g img(:,:,2); b img(:,:,3); RG r - g; BY b - (r g) / 2; % 构造四元数矩阵并进行QFFT q1 complex(I, RG); q2 complex(BY, zeros(size(BY))); Q1 fft2(q1); Q2 fft2(q2); % 相位谱归一化 mag sqrt(abs(Q1).^2 abs(Q2).^2); mag(mag eps) eps; P1 Q1 ./ mag; P2 Q2 ./ mag; % 逆变换并取模 p1 ifft2(P1); p2 ifft2(P2); smap sqrt(abs(p1).^2 abs(p2).^2); % 高斯平滑 hsize round(3*sigma) * 2 1; h fspecial(gaussian, hsize, sigma); smap imfilter(smap, h, replicate); % 归一化到[0,1] smin min(smap(:)); smax max(smap(:)); if smax smin smap (smap - smin) / (smax - smin); else smap zeros(size(smap)); end end这里有两个依赖工具箱的函数fspecial和imfilter在Image Processing Toolbox里。如果你的MATLAB没有这个工具箱可以用基础版的conv2替代smap conv2(smap, h, same);效果几乎一样只是边界处理方式略有差异。这个替换能避免一部分人因为工具箱问题跑不起来。调用和验证代码如下% 生成一个简单测试图深色背景上一个亮色方块 img zeros(256, 256, 3); img(80:170, 100:200, 1) 1; img(80:170, 100:200, 2) 0.2; img(80:170, 100:200, 3) 0.2; smap pqft_static(img, 4); figure; subplot(1,2,1); imshow(img); title(Input); subplot(1,2,2); imshow(smap); title(PQFT Saliency); colormap(gca, jet);这个测试图是深灰背景上的一个偏红色亮方块理论上PQFT应该在方块位置产生高响应背景低响应。跑完如果看到显著图牢牢锁定在方块区域说明基本流程通了。4. 实测效果与调参经验别让这几个坑毁掉你的显著图4.1 合成图和自然图的实测效果我拿上面那个红方块测试图跑了一遍效果符合预期显著图中心正好落在方块区域背景几乎全黑。这说明PQFT对颜色对比明显的场景非常敏感RG通道里的红绿对抗信息被充分用上了。我又拿MATLAB自带的peppers.png辣椒图做测试。这张图的特点是色彩丰富红色、绿色、黄色辣椒密集分布背景纹理复杂。跑出来的显著图把几个色彩饱和度高的辣椒区域标亮同时背景的暗部几乎不响应。这个结果比只做灰度SR要明显好因为SR完全靠亮度对比遇到颜色丰富但亮度接近的场景容易翻车。如果你想量化验证效果可以计算显著图的质心坐标和目标区域中心之间的距离或者用固定阈值把显著图二值化后计算IoU。做对比实验的时候我习惯把PQFT、SR、ITTI三种算法的显著图放在同一尺度下归一化再比较避免因为显示范围不同而产生误判。4.2 sigma参数怎么选sigma是高斯平滑的标准差这个参数直接决定显著图的“颗粒感”。太小显著图会有大量高频噪点目标区域不连续太大目标边缘模糊定位精度下降。我自己的经验值是这样的输入图像尺寸短边推荐sigma说明64x643~4小图本身分辨率有限稍大核可连片256x2564~6常用图像尺寸sigma4是比较稳的起点512x512以上6~8大图建议先缩放再算显著图一个比较实用的自适应策略是sigma_pix 4 * min(rows, cols) / 256。这样无论图像多大平滑的“相对尺度”保持一致。我在视频场景里就用这个公式自动计算sigma省得换视频分辨率时手动调参。4.3 四个常见的坑第一个坑就是除零前面已经强调过了。这里再说一个重要细节eps在MATLAB里大约是2.2e-16但我实际测试时发现对于图像FFT这种量级的数据mag小于1e-10就已经可以视为零了。用eps做阈值不会错但如果你追求极致的数值稳定性可以写成mag(mag 1e-6) 1e-6。第二个坑是灰度图退化。如果输入是单通道灰度图RG和BY全部为零四元数就只剩一个亮度通道这本质上退化成了SR算法颜色信息完全丢失。处理办法有两个要么在输入层做伪彩色映射要么在代码里检测通道数如果是灰度图就只跑SR逻辑。我在实际工程中遇到灰度视频流时会先用一个简单的颜色映射把灰度转成伪彩色再送进PQFT效果比直接退化要好。第三个坑是大图性能。高清视频帧动辄1920x1080直接做fft2虽然能跑但每帧几十毫秒的耗时在实时系统里不可忽视。论文原版的做法是把每一帧先imresize到64x64算完显著图再imresize回原始尺寸。64x64的FFT在普通PC上是微秒级别的配合插值上采样总耗时可以控制在5毫秒以内。实测下来降采样对显著图质量的影响很小因为显著性本身是一个“全局对比”属性小尺度上已经能算得很准。第四个坑是显式归一化的边界情况。当图像内容极为平坦时smap里所有像素值都相同min等于max直接除会得到NaN。我在代码里加了if smax smin的判断就是为了兜底这种极端情况。5. 从静态图到视频加一路运动通道PQFT秒变实时显著性检测器5.1 运动通道M的构造PQFT最秀的地方在于它从设计之初就是为视频准备的。静态图用三个通道视频只需要增加一路运动特征M就能把“动的物体”也标成显著区域。运动通道最朴素的构造方式是帧间差分取当前帧和上一帧的亮度图做差取绝对值。亮度差大的位置说明有物体在移动或者场景发生变化这些位置天然就是注意力的焦点。% 假设 frame_current 和 frame_prev 是已经转成double并归一化的RGB帧 I_curr mean(frame_current, 3); I_prev mean(frame_prev, 3); M abs(I_curr - I_prev);把M放进四元数第四通道也就是辛分解的Q2虚部q1 complex(I_curr, RG_curr); q2 complex(BY_curr, M); % 运动通道放在虚部注意M的取值范围是[0,1]跟其他特征一致不需要额外归一化。如果视频帧率比较高帧间差分信号会非常稀疏M大部分区域接近0这正好符合“只有少数区域在动”的稀疏性假设。5.2 视频循环的完整改动完整的视频PQFT循环代码大概是这个样子videoReader VideoReader(input.mp4); videoWriter VideoWriter(saliency_output.avi); open(videoWriter); prevI []; while hasFrame(videoReader) frame readFrame(videoReader); if isa(frame, uint8) frame double(frame) / 255; end I mean(frame, 3); % 第一帧没有上一帧运动通道置零 if isempty(prevI) M zeros(size(I)); else M abs(I - prevI); end prevI I; % 颜色对手特征 r frame(:,:,1); g frame(:,:,2); b frame(:,:,3); RG r - g; BY b - (r g) / 2; % 四元数构造与QFFT q1 complex(I, RG); q2 complex(BY, M); Q1 fft2(q1); Q2 fft2(q2); % 相位谱 逆变换 mag sqrt(abs(Q1).^2 abs(Q2).^2); mag(mag eps) eps; p1 ifft2(Q1 ./ mag); p2 ifft2(Q2 ./ mag); smap sqrt(abs(p1).^2 abs(p2).^2); % 自适应sigma平滑 sigma 4 * min(size(I)) / 256; hsize round(3*sigma) * 2 1; h fspecial(gaussian, hsize, sigma); smap imfilter(smap, h, replicate); % 归一化并写入视频 smap (smap - min(smap(:))) / (max(smap(:)) - min(smap(:)) eps); writeVideo(videoWriter, smap); end close(videoWriter);这套代码在监控视频上实测移动的人或者车辆会持续被高亮。如果目标静止不动运动通道会衰减显著图会慢慢回落到静态显著性的水平这是符合预期的——静止目标靠颜色/亮度对比来吸引注意运动目标靠运动通道来吸引注意两者互补。有一点需要提醒如果视频本身有镜头抖动或者全局光照变化帧间差分会产生大面积的虚假运动响应。这时候建议先做全局运动补偿比如用相位相关法算出全局平移量对齐后再做差分或者对M做阈值处理把小于某个值的差分信号直接清零。5.3 从PQFT还能往哪些方向继续玩PQFT作为baseline已经够用但它也有明显短板相位谱对噪声敏感、没有多尺度机制、对纹理复杂背景容易产生大量虚假显著点。学术界在它之后又发展了不少改进方向你可以按需取用。一个是Hypercomplex Fourier TransformHFT在四元数频域引入多尺度显著性特征融合用不同尺度的频域滤波器分离出“大目标”和“小目标”的显著响应显著图质量比原始PQFT更高。另一个是把运动通道换成光流场用光流幅值作为M能更好地处理相机运动和复杂运动模式比简单帧差更鲁棒。还有一个实际工程中很有用的技巧把PQFT显著图作为权重图叠加到传统目标检测器的候选区域生成上可以抑制背景误检。这也是我当初做监控模块时选它的原因——它不抢目标检测器的饭碗但能让检测器活得更轻松。补充一点个人体会如果你想把PQFT用到自己的数据集上又希望效果更稳可以在第一帧先跑一遍静态PQFT把显著图预存起来这样视频循环一开始就有一个完整的静态显著图运动通道再从零开始积累不会出现前几帧显著图“冷启动”偏弱的问题。这个小技巧在真实项目中非常实用。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门