资讯详情

MATLAB Zernike波前工具箱:从波前像差到点扩散函数计算

📅 2026/9/13 23:20:35 | 华诺云谱 👁 阅读
MATLAB Zernike波前工具箱:从波前像差到点扩散函数计算
简介这是一套基于MATLAB的波前光学与Zernike像差分析工具包面向光学工程、视觉科学与成像系统研究人员用于通过Zernike多项式计算点扩散函数PSF并评估光学系统性能。资源共86个文件包含56个m脚本、11个mat数据、9个txt说明及6个pdf参考文献整体体积11.36MB支持从波前构建、像差分解到PSF仿真的完整流程。代码基于Wavefront Optics Toolbox涵盖人眼光学建模、Stiles-Crawford效应、色差补偿等模块并附有OSA标准、人眼统计模型等经典文献。已有247人学习下载。对具备一定光学基础、希望深入理解波前像差与成像质量的工程师和研究者而言这套工具包提供了可直接运行的实验脚本和清晰的验证示例便于二次开发与教学演示。1. 为什么这个Zernike波前工具箱值得拆开看在光学实验里拿到一只镜头或一只眼睛的 Zernike 波前系数之后最想回答的问题往往是一个这个波前误差最终会把点光源展成什么形状WavefrontOptics-master.zip 这个 MATLAB 工具箱做的正是这件事。它把 Zernike 多项式、瞳孔函数和点扩散函数PSF串成一条可复算的链路从 wvfCreate 创建对象、wvfSet 写入像差系数到 wvfComputePupilFunction 生成瞳孔复振幅再到 wvfComputePSF 输出点扩散函数每一步都有独立函数和验证脚本。适合做视觉光学、眼底成像、望远镜或显微镜系统分析的人阅读并套用。下载包解压后是完整源码不依赖闭源工具箱值得逐行拆看。2. 从 Zernike 系数到波前图wvfCreate 与 wvfSet 的参数约定2.1 对象化接口先建一个 wvf 结构再说这个工具箱没有选择把波前参数散落到一堆全局变量里而是统一收进 wvf 结构体用 wvfSet 写参数、wvfGet 读参数、wvfCreate 负责初始化。这样做的直接好处是同一套代码里可以维护多个不同瞳孔、不同波长、不同像差组合的光学系统不会出现参数覆盖。创建对象的典型写法是wvf wvfCreate(wave, 550, umAtInfinity, 3e3, sample interval, 1);这行代码创建了一个基础波前对象。参数含义分别是550 是波长单位 nmumAtInfinity 是用于 PSF 空间坐标换算的参考距离单位 μm工具箱用它把瞳孔面坐标映射到像面坐标sample interval 是人眼波前采样时的间隔单位 μm一般取 12。对于只做单色光模拟的场景wave 传标量即可如果要模拟彩色视觉这里可以直接传向量例如 400:50:700后面的 PSF 计算会逐波长处理。创建之后再逐项覆盖参数是更常见的用法因为 wvfCreate 的默认值往往只需要改其中几项wvf wvfSet(wvf, wave, 550); % 波长 550 nm wvf wvfSet(wvf, pupil diameter, 3); % 瞳孔直径 3 mm z zeros(1, 15); z(5) 0.1; % OSA index 4离焦 0.1 μm wvf wvfSet(wvf, zcoeffs, z);这段代码把波长、瞳孔直径和 Zernike 系数写入同一个 wvf 结构。注意 MATLAB 的 set 函数普遍采用值语义wvfSet 返回的是修改后的新对象所以左侧必须接住返回值。zcoeffs 的向量长度可以按需增长工具箱内部会自动补齐低阶项。查看当前配置时用 wvfGetwave wvfGet(wvf, wave); diam wvfGet(wvf, pupil diameter);wvfGet 返回的 wave 和 pupil diameter 会保持与 set 时一致的单位约定。这里有一个容易忽略的点如果 zcoeffs 长度不足 15 项工具箱默认把缺失项当作 0也就是说想只设置球差时不需要填满前面的倾斜项但为了可读性建议仍然保持固定长度。下表汇总了最常用的参数项参数名含义单位常见取值wave光波长nm550或 400:50:700pupil diameter瞳孔直径mm28sample interval波前采样间隔μm12umAtInfinity参考距离/像距μm3000zcoeffsZernike 系数向量μm与 OSA 索引对应2.2 Zernike 系数怎么填OSA 索引与系数向量Zernike 多项式的编号有不止一套约定WavefrontOptics 采用的是 OSA 标准索引。OSA 索引 j 从 0 开始而 MATLAB 向量从 1 开始所以向量第 i 个元素对应 OSA 索引 i-1。换句话说zcoeffs(1) 是 pistonzcoeffs(5) 才是离焦。项目里提供了两个辅助函数可以做编号互换[n, m] wvfOSAIndexToZernikeNM(4); % 输入 4得到离焦对应的径向次数和角向次数 idx wvfZernikeNMToOSAIndex(2, 0); % 输入 n2, m0得到 OSA 索引 4这两个函数适合在写脚本时用来做映射避免记错序号。实际填系数时常见的做法是直接维护一个 15 或 20 长的向量然后把对应位置赋值为微米级系数。比如设置 0.05 μm 的球差和 -0.1 μm 的离焦z zeros(1, 20); z(5) -0.1; % defocus z(13) 0.05; % spherical aberration wvf wvfSet(wvf, zcoeffs, z);简单对应关系可以记成表格。0 是 piston1 和 2 是倾斜项3 和 4 是离焦和斜向离焦5 和 6 是初级像散7 和 8 是彗差12 是初级球差。实际上 OSA 索引 4 就是 n2, m0 的 defocus 项索引 12 是 n4, m0 的 primary spherical。在使用 sampleZernikeCoeffs.txt 这类数据文件时先检查文件里第一列是否是索引如果不是建议用上述转换函数标准化后再喂给 wvfSet。2.3 采样率与空间域换算瞳孔面的采样间隔直接影响 PSF 的有效视场范围。FFT 的对偶关系决定了瞳孔面采样间隔 dp 与像面采样间隔 du 之间满足 du λ·f / (N·dp)其中 f 是等效像距N 是网格点数。采样间隔设得太大PSF 会欠采样设得太小计算量急剧上升。工具箱提供的 v_wvfSpatialSampling.m 就是专门用来检验这一点的。实际使用中我一般会先固定瞳孔直径和波长然后扫描 sample interval 从 0.5 到 2观察 PSF 的形状是否稳定for s 0.5:0.5:2 wvf wvfSet(wvf, sample interval, s); wvf wvfComputePSF(wvf); psfSampled wvfGet(wvf, psf sample interval); % 读出像面采样间隔 fprintf(sample%.1f - psf sample%.2f\n, s, psfSampled); end如果 psf sample interval 超过艾里斑半径的三分之一说明输出 PSF 基本可信如果大于艾里斑半径说明瞳孔网格太粗需要减小 sample interval 或增加网格点数。这个方法能在不打开源码的情况下快速判断参数是否合理。3. 瞳孔函数计算wvfComputePupilFunction 是怎么把相位变成复振幅的3.1 波前像差到相位延迟的计算路径PSF 计算的第一步是构造瞳孔函数。瞳孔函数是一个复振幅分布定义是 P(x,y) A(x,y) · exp(i·φ(x,y))。其中 A(x,y) 是振幅透过率在瞳孔内部通常为 1瞳孔外部为 0相位 φ(x,y) 由波前像差 W(x,y) 决定φ (2π/λ)·W。而 W(x,y) 正是由 Zernike 系数线性叠加出来的。这一步对应工具箱里的 wvfComputePupilFunction.m。调用方式很直接wvf wvfComputePupilFunction(wvf); pupil wvfGet(wvf, pupil function); phase angle(pupil);wvfComputePupilFunction 返回更新后的 wvf 对象随后 wvfGet 拿到的是包含振幅和相位的复数矩阵。angle 取出的相位单位是弧度范围通常被包在 -π 到 π 之间。如果波前像差超过一个波长相位图会出现密集的跳变条纹这时候需要靠 unwrap 或者增大采样率才能还原连续波前。这个现象本身也是检查系数量级是否合理的一个信号。再看瞳孔函数的振幅如果所有 Zernike 系数都为 0pupil 的 amplitude 在圆形孔径内是 1外部是 0对应的 PSF 就是衍射极限艾里斑。任何一个像差项的引入都会让相位图出现空间分布但不改变瞳孔外的零值区域。因此可以先看 pupil 外部的值是否严格为 0如果不为 0通常说明网格掩模和瞳孔直径没有对齐。3.2 振幅调制项与 Stiles-Crawford 效应人眼光学系统里视网膜感光细胞的方向敏感性会改变有效的光收集效率这就是 Stiles-Crawford 效应。它本质上是一种瞳孔上的振幅调制让中心区域透光率最高边缘透光率下降。这个效应不能忽略尤其在模拟大瞳孔时忽略它会导致 PSF 旁瓣偏高。工具箱里用 sceCreate 和 sceGet 两个函数管理参数sce sceCreate(density, 0.097); % 方向敏感密度 wvf wvfSet(wvf, stiles crawford, sce);sceCreate 中的 density 参数表示方向敏感效应的强度单位是 1/mm²。常见人眼测得的密度值在 0.050.12 之间Berendschot 等 1991 年的文献给出过一个统计平均值。设置完成后wvfComputePupilFunction 会把 sce 的振幅包络乘到 pupil 上。验证方法是对比设置前后的瞳孔振幅剖面sce wvfGet(wvf, stiles crawford); rho wvfGet(wvf, pupil radius vector); amp sceGet(sce, amplitude, rho); plot(rho, amp);从曲线可以看到瞳孔边缘透光率可能下降到中心值的 50% 以下。这里要留意一个常见错误有些人把 sce 当成波长相关的参数直接给一个矩阵实际上 sceCreate 生成的是参数对象后续计算时工具箱会根据波前位置自动插值。3.3 用 wvfPlot 检查波前和 PSF中间结果最好可视化确认wvfPlot 提供了比较完整的绘图入口。常用的调用包括wvfPlot(wvf, 2d, wave, 550); % 波前图 wvfPlot(wvf, psf, wave, 550); % 点扩散函数2d 模式画的是波前误差 W(x,y)单位通常为微米颜色越接近 0 表示波前越理想。psf 模式画的是点扩散函数的辐射强度分布。建议先看波前图再看 PSF这样能直观看出哪一种像差模式对应哪一种扩散图案。比如离焦项 z(5) 会产生对称的圆形扩散彗差项会产生单侧拖尾球差项则表现为中间亮斑外圈还有一个亮环。借助这些特征可以快速判断 Zernike 系数是不是写错了位置。4. 从瞳孔函数到 PSFwvfComputePSF 与傅里叶光学实现4.1 PSF 的理论基础与 FFT 计算流程点扩散函数的物理含义是光学系统对一个理想点源的响应。在相干光系统中瞳孔函数的傅里叶变换就是振幅点扩散函数而在非相干光系统中PSF 等于瞳孔函数傅里叶变换模的平方。日常光学成像分析绝大多数情况下走非相干模型所以计算公式为 PSF(u,v) |ℱ{P(x,y)}|²。工具箱把这一步封装在 wvfComputePSF.m 里。用起来只需要两行wvf wvfComputePSF(wvf); psf wvfGet(wvf, psf);内部逻辑大致是先从当前 wvf 对象拿到瞳孔函数做中心化处理再执行二维 FFT然后对结果取模平方最后按照 umAtInfinity 和采样间隔换算出 PSF 的实际坐标轴。工具箱已经处理了将 PSF 中心移到网格中心的细节所以取出的 psf 矩阵中心就是主峰位置。如果自己写 FFT 代码最容易忘记的是 fftshift 的顺序这会导致 PSF 出现在四个角落而不是中心。提取 PSF 之后第一件事是检查能量是否守恒。理想的 PSF 对全平面积分应该为 1。如果明显小于 1说明采样区域没有覆盖完整扩散区域如果大于 1说明归一化有问题。一个快速检查片段如下totalEnergy sum(psf(:)); fprintf(PSF total energy: %.4f\n, totalEnergy);对于离焦或球差很严重的系统PSF 会分散到很大区域此时网格可能截断一部分能量。出现这种情况时不应怀疑公式而应该增大采样范围也就是调小瞳孔采样间隔或增大网格。4.2 如何控制输出 PSF 的尺寸和采样输出 PSF 的支撑域尺寸由瞳孔采样间隔和 FFT 网格共同决定。从对偶关系看瞳孔面采样越密像面视场越大。实际项目中v_wvfSpatialSampling.m 会帮你验证采样是否满足需求但手工控制时要注意三个常用参数wvf wvfSet(wvf, pupil diameter, 3); wvf wvfSet(wvf, sample interval, 1); wvf wvfSet(wvf, umAtInfinity, 3000); psfSampled wvfGet(wvf, psf sample interval);这三个参数各自的作用是pupil diameter 决定物理孔径大小sample interval 决定瞳孔面的离散化步长umAtInfinity 决定像面坐标缩放。改变任何一个都会影响 PSF 的视场范围。粗略估计PSF 的有效视场宽度大约是 λ·umAtInfinity / sample interval。550 nm 波长、3 mm 瞳孔、1 μm 采样间隔下视场通常能覆盖几十微米的量级足够看到艾里斑的第一亮环。如果 PSF 边缘出现明显的周期性重复也就是混叠这时候优先减小 sample interval而不是增大瞳孔直径。很多人会先去改网格数但网格数改变只影响计算量不影响奈奎斯特采样率。保持瞳孔直径不变采样间隔减半PSF 视场扩大一倍混叠问题就会消失。4.3 验证衍射极限 PSF 与艾里斑对比一个常用的验证方式是构造一个零像差的 wvf 对象计算衍射极限 PSF然后与理论艾里斑半径对比。艾里斑的半径公式为 r 1.22·λ·f·/D其中 f 是像距D 是瞳孔直径。用 umAtInfinity 作为等效像距时验证代码可以写成wvf wvfSet(wvf, zcoeffs, zeros(1, 15)); wvf wvfComputePSF(wvf); psf wvfGet(wvf, psf); psfSampled wvfGet(wvf, psf sample interval); % 寻找第一个径向零点 [x, y] meshgrid(-size(psf,2)/2:size(psf,2)/2-1, -size(psf,1)/2:size(psf,1)/2-1); r sqrt(x.^2 y.^2) * psfSampled; profile interp2(x * psfSampled, y * psfSampled, psf, r * cos(0), r * sin(0));这里用水平方向的剖面代替全方向搜索找到第一个极小值点即可和理论值比较。如果实际零点位置和理论值偏差超过 10%多出来的误差通常来自 pupil 网格不够密导致的数值色散或者 umAtInfinity 与实际像距不一致。衍射极限 PSF 验证通过之后再逐步加入 Zernike 系数就能确认每一项像差在 PSF 上的特征。5. 光学链路实战把 Zernike 人眼模型接到场景仿真5.1 从 wvf 到 OIwvf2OI 的调用方式单纯拿到 PSF 矩阵还不算完实际视觉仿真里更关心场景经过光学系统后变成什么样子。WavefrontOptics 提供了从波前到光学图像optical image, OI的接口 wvf2OI它内部会把 wvf 计算出的 PSF 当作卷积核对场景辐射分布做卷积得到到达视网膜前的图像。基本用法oi wvf2OI(wvf, wave, 550);这里第二个参数可以传一个数组wave, 400:50:700。传入多波长时wvf2OI 会逐波长生成 PSF并对输入场景的每个波段分别卷积。返回的 oi 对象可以用其他 ISET 函数继续做传感器采样。需要注意wvf2OI 默认使用 wvf 里的瞳孔和像差参数所以调用前务必先 wvfSet、wvfComputePSF否则拿到的是旧结果。调用示例wvf wvfSet(wvf, zcoeffs, z); oi wvf2OI(wvf, wave, 400:50:700);从结构上看wvf2OI 是先取当前波前的全波段 PSF再把 PSF 集合传给光学图像结构。它和直接算单色 PSF 的区别在于多波长模式下会考虑色差带来的 PSF 展宽因此模拟彩色人眼成像时这一步不能省。如果只想快速看一个单色像传标量波长即可计算速度快很多。5.2 用锥细胞 PSF 做视网膜成像仿真人眼视网膜上有三种锥细胞它们的空间采样密度和波长响应不同。工具箱里的 wvfComputeConePSF 把波前 PSF 与锥细胞 mosaics 结合起来输出面向不同锥细胞类型的点扩散函数。常用于研究 L/M/S 锥在相同波前下的响应差异。conePSF_L wvfComputeConePSF(wvf, cone, L); conePSF_S wvfComputeConePSF(wvf, cone, S);第一个参数是已经计算过 pupil 和 PSF 的 wvf 对象第二个参数指定锥细胞类型。返回的 conePSF 矩阵可以当作成像卷积核。对比 L 和 S 锥的 PSF通常会发现短波长 S 锥受衍射影响更明显因为波长越短艾里斑半径越小同样的波前误差对短波的影响相对更大。这个信息对设计视网膜显示设备很有价值。5.3 参数调整瞳孔、离焦与色差实际仿真里最常调的三组参数是瞳孔直径、离焦量和轴向色差。离焦量用屈光度表示时需要换算成 Zernike 系数工具箱提供了便捷函数c4 wvfDefocusDioptersToMicrons(0.5, 3); % 0.5 D, 3 mm 瞳孔 z(5) c4; wvf wvfSet(wvf, zcoeffs, z);wvfDefocusDioptersToMicrons 的第一个参数是屈光度第二个参数是瞳孔直径 mm。它的物理基础是球面波前在孔径边缘与中心的光程差。调大瞳孔直径会让同样的离焦量产生更大的边缘光程差因此大瞳孔下的离焦模糊更严重。轴向色差则用 wavelength-dependent 的方式计算。人的眼睛对不同波长有不同的最佳焦面工具箱里 wvfLCAFromWavelengthDifference 提供了从波长差到色差的换算lca wvfLCAFromWavelengthDifference(589, 550); z(5) z(5) lca;这一行的含义是把 589 nm 到 550 nm 的纵向色差量加进离焦系数。模拟白场成像时可以循环不同波长分别加上对应色差再交给 wvf2OI 合成。不做这一步多波长仿真会低估视网膜图像的颜色模糊。6. 一个排错技巧用 v_wvfValidate 和内置样例定位计算误差6.1 跑通验证脚本工具箱里带了回归验证脚本 v_wvfValidate.m它会调用 v_wvfDiffractionPSF、v_wvfStilesCrawford、v_wvfComputeConePSF 等子验证函数把计算结果和 data/ver121data 里的参考数据做对比。第一次拿到 zip 包后建议直接在 MATLAB 路径下运行v_wvfValidate;正常情况下会输出一系列 OK 提示。如果某个子模块报错比如 v_wvfSpatialSampling 失败问题多半位于采样参数配置而不是 PSF 公式。这个脚本的价值是帮你区分工具箱自身没问题和我的参数写错了。6.2 对不上结果的一般检查点当自己计算的 PSF 与论文或参考数据对不上时按以下顺序排查。第一Zernike 系数向量是否从 piston 开始。如果数据文件是从第 1 项 tilt 开始直接喂给 wvfSet 会把 defocus 错放到 tilt 位置PSF 形状会完全不对。第二波长单位是不是纳米。工具箱所有 API 默认波长单位都是 nm如果输入 0.55 会被当成 0.55 nm导致相位计算完全错误。第三瞳孔直径与 umAtInfinity 是否匹配。两者不一致时PSF 的空间尺度和艾里斑半径理论值会有系统性偏差。最后一个常见错误是忘记调用 wvfComputePSF 就直接用 wvfGet 取 PSF。wvfSet 只更新参数不会触发重算必须手动调用计算函数。验证能量归一化是最快的手段if abs(sum(psf(:)) - 1) 1e-3 error(PSF energy not normalized, check sampling range); end把这段放到计算函数末尾能在第一时间发现采样截断问题。若能量偏差小于千分之一就可以放心把 PSF 用于后续的 OI 仿真或锥细胞卷积。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。