news 2026/10/16 9:54:59

数字全息MATLAB重建:CONV、Fresnel与角谱法对比

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数字全息MATLAB重建:CONV、Fresnel与角谱法对比

简介:这份资源面向光学、光电信息与图像处理方向的学习者和研究者,提供数字全息图重构的MATLAB实现代码,帮助理解从全息图恢复原始光场信息的核心流程。包内共3个m脚本文件,压缩包约1KB,分别对应基于卷积(CONV)、Fresnel变换(Fresnel_diff)与角谱法(Angular_spectrum)三种重构思路:卷积法借助光学传递函数做近场重建,计算相对简单;Fresnel变换法考虑光源到探测器的传播距离,适合中等距离重构;角谱法基于傅里叶光学,在频域完成位相恢复与逆变换,理想条件下可实现无失真重建。三种方法各有适用距离、计算复杂度与精度取舍,读者可对照运行脚本,比较不同重构结果,进而掌握数字全息的基本理论与光学信号处理的实践方法。目前已有1643人学习下载,适合作为课程实验、课题入门或算法对比的参考素材。

1. 从一张全息图到三种重建结果:这个 MATLAB 项目到底在做什么

如果你手头有一张数字全息图,想把它还原成物体的振幅和相位,最直接的问题就是:用哪种重构算法?卷积法(CONV)和菲涅耳衍射法(Fresnel)到底差在哪?这个 MATLAB 项目就是冲着这个问题来的——它把数字全息里最常用的三种重构路径打包成可运行的脚本,输入一张全息图,输出重建后的复振幅分布。适合正在做光学测量、显微成像或者干涉计量方向的研究生和工程师,尤其是那些不想从零推导衍射公式、只想先把结果跑出来看看的人。项目本身不依赖任何工具箱之外的第三方库,纯 MATLAB 实现,拿到手改几个路径参数就能出图。

2. 三种重构方法的代码结构:CONV、Fresnel 和角谱怎么选

2.1 目录组织与入口脚本

这个项目的文件组织方式很直白,没有搞复杂的包管理。根目录下通常能看到这样几类文件:主入口脚本(比如main.m或run_reconstruction.m)、三个重构函数文件(recon_conv.m、recon_fresnel.m、recon_angular.m)、以及一个放全息图的data/文件夹。主脚本负责读图、设参数、调函数、画结果,重构函数各自独立,互不干扰。

我一般会先看主脚本里的参数区,因为那里决定了整个重建的物理尺度。典型的参数包括:波长lambda、像素尺寸pixel_size、重建距离z、以及全息图的尺寸。这些参数不是随便填的,它们直接决定了重建平面上的采样间隔和视场大小。

% main.m 参数设置区(常见写法) lambda = 532e-9; % 波长,单位米 pixel_size = 3.45e-6; % 相机像素尺寸,单位米 z = 0.15; % 重建距离,单位米 hologram = imread('data/holo.bmp'); hologram = double(hologram(:,:,1)); % 取单通道

这里lambda和pixel_size决定了衍射计算的尺度,z是物平面到全息面的距离。如果z设错了,重建出来的像会模糊或者尺寸不对。常见做法是先用一个已知的校准样本跑一遍,确认z的符号和大小。

2.2 CONV 重构:卷积法的实现细节

卷积法的核心思想是把菲涅耳衍射积分写成卷积形式,然后用 FFT 加速。在离散域里,它等价于对全息图乘以一个二次相位因子,再做一次卷积。代码里通常这样写:

function U = recon_conv(hologram, lambda, pixel_size, z) [M, N] = size(hologram); k = 2*pi/lambda; % 空间坐标 x = (-N/2:N/2-1) * pixel_size; y = (-M/2:M/2-1) * pixel_size; [X, Y] = meshgrid(x, y); % 卷积核:二次相位因子 h = exp(1j*k/(2*z) * (X.^2 + Y.^2)); % 频域卷积 H = fft2(fftshift(h)); U = ifft2(fft2(hologram) .* H); U = fftshift(U); end

这段代码里,h是菲涅耳衍射的脉冲响应,fftshift把零频移到中心。卷积法的一个特点是重建平面的采样间隔和全息面一致,都是pixel_size。这意味着如果你想要更大的视场,卷积法不会自动帮你放大,它保持原尺寸。参数z在这里出现在分母上,z越小,二次相位因子的曲率越大,卷积核越“尖锐”。

注意:卷积法在z很小时会出现混叠,因为脉冲响应的相位变化太快,离散采样跟不上。这时候要么增大采样率,要么换方法。

2.3 Fresnel 重构:单次 FFT 的轻量路径

菲涅耳衍射法(也叫单傅里叶变换法)是另一种常见实现。它把衍射积分近似成一次傅里叶变换,重建平面的采样间隔会随z变化。代码通常长这样:

function U = recon_fresnel(hologram, lambda, pixel_size, z) [M, N] = size(hologram); k = 2*pi/lambda; x = (-N/2:N/2-1) * pixel_size; y = (-M/2:M/2-1) * pixel_size; [X, Y] = meshgrid(x, y); % 二次相位因子 phase = exp(1j*k/(2*z) * (X.^2 + Y.^2)); % 单次 FFT U = fftshift(fft2(fftshift(hologram .* phase))); end

这里只做了一次 FFT,计算量比卷积法小。但它的重建采样间隔变成了lambda*z/(N*pixel_size),也就是说z越大,重建出来的像素间隔越大,视场越大但分辨率越低。这个特性在需要大视场观察时很有用,但如果你要精确测量微小结构,就得注意分辨率损失。

2.4 角谱法:第三种重构的补充

虽然标题里只提了 CONV 和 Fresnel,但很多数字全息项目会带上角谱法作为对比。角谱法在频域里传播,不做近轴近似,适合短距离或者大角度的情况。代码结构通常是:

function U = recon_angular(hologram, lambda, pixel_size, z) [M, N] = size(hologram); k = 2*pi/lambda; fx = (-N/2:N/2-1) / (N*pixel_size); fy = (-M/2:M/2-1) / (M*pixel_size); [FX, FY] = meshgrid(fx, fy); % 传递函数 H = exp(1j*k*z*sqrt(1 - (lambda*FX).^2 - (lambda*FY).^2)); H(lambda*FX > 1 | lambda*FY > 1) = 0; % 倏逝波截止 U = ifft2(fft2(hologram) .* H); end

角谱法的传递函数里有个根号项,当lambda*FX超过 1 时变成倏逝波,代码里直接置零。这个方法在z很小的时候比 Fresnel 更准,但计算量稍大。

三种方法放在一起,你可以根据z的大小和需要的分辨率来选。一般经验是:z小、要求精度高,用角谱;z中等、想快速看结果,用 Fresnel;z大、想保持采样间隔不变,用 CONV。

3. 跑通第一个重建:从读图到出图的完整操作

3.1 环境准备与数据放置

这个项目对 MATLAB 版本要求不高,R2016b 以上都能跑,因为用到的都是基础函数。不需要额外工具箱,fft2、ifft2、fftshift都是自带的。唯一要注意的是图像读取,如果全息图是彩色相机拍的,记得转灰度。

把全息图放到data/目录下,命名成holo.bmp或者改主脚本里的路径。我一般会先确认图像尺寸,因为后面所有坐标计算都依赖[M, N]。

% 检查图像尺寸和数据类型 info = imfinfo('data/holo.bmp'); disp(['图像尺寸: ', num2str(info.Width), ' x ', num2str(info.Height)]);

如果图像太大,比如 4000x3000,FFT 会吃内存。常见做法是裁剪到 1024x1024 或者 2048x2048 再跑。裁剪的时候注意把感兴趣的区域放在中心,因为后面fftshift会假设零频在中心。

3.2 参数配置与第一次运行

主脚本里通常有一个参数区,你需要根据实际光路填三个值:波长、像素尺寸、重建距离。波长看激光器标签,像素尺寸看相机手册,重建距离用卷尺量或者用自动对焦算法找。

% 第一次运行建议先用小图测试 lambda = 532e-9; pixel_size = 3.45e-6; z = 0.15; hologram = imread('data/holo.bmp'); hologram = double(hologram(:,:,1)); % 调用三种方法 U_conv = recon_conv(hologram, lambda, pixel_size, z); U_fresnel = recon_fresnel(hologram, lambda, pixel_size, z); U_angular = recon_angular(hologram, lambda, pixel_size, z); % 显示振幅 figure; subplot(1,3,1); imagesc(abs(U_conv)); title('CONV'); colormap gray; axis image; subplot(1,3,2); imagesc(abs(U_fresnel)); title('Fresnel'); colormap gray; axis image; subplot(1,3,3); imagesc(abs(U_angular)); title('Angular'); colormap gray; axis image;

跑完这段,你会看到三张振幅图。如果三张图都模糊,大概率是z不对;如果只有某一张模糊,那可能是该方法在这个距离下不适用。比如z很小的时候,Fresnel 的近似误差会变大,角谱和 CONV 更可靠。

3.3 结果对比与参数扫描

想确认哪个方法更适合你的数据,可以做一个z扫描。把z从 0.05 到 0.5 步进 0.01,每个距离算一遍,看哪个距离下图像最锐利。这个操作在数字全息里叫自动对焦,常用评价函数是梯度平方和或者拉普拉斯算子。

z_range = 0.05:0.01:0.5; sharpness = zeros(size(z_range)); for i = 1:length(z_range) U = recon_conv(hologram, lambda, pixel_size, z_range(i)); sharpness(i) = sum(sum(gradient(abs(U)).^2)); end [~, idx] = max(sharpness); disp(['最佳重建距离: ', num2str(z_range(idx)), ' m']);

这段代码用梯度平方和作为清晰度指标,峰值对应的z就是最佳距离。注意不同方法得到的最佳z可能略有差异,因为它们的近似条件不同。我一般会以角谱法的结果作为基准,因为它的近似最少。

4. 避坑与排查:数字全息重建里最容易翻车的几个点

4.1 重建距离符号搞反,像面跑到反方向

现象:重建出来的图像完全模糊,怎么调z都不清晰。原因:z的符号决定了衍射传播方向,如果物平面和全息面的相对位置搞反了,相当于往反方向传播。解决:把z取负再试一次,或者检查光路里物和相机的先后顺序。常见做法是先用一个已知距离的样本标定符号。

4.2 像素尺寸填错,重建尺度全乱

现象:重建出来的物体尺寸和实际对不上,比如一个 100 微米的球重建出来变成 200 微米。原因:pixel_size填的是相机像素尺寸,但如果你用了显微物镜或者缩束系统,实际物平面的采样间隔是pixel_size / magnification。解决:确认光路里有没有放大或缩小,把放大倍率算进去。我一般会在代码里单独定义一个mag变量,避免直接改pixel_size。

4.3 FFT 混叠导致高频伪影

现象:重建图里出现周期性条纹或者网格状伪影。原因:全息图的高频信息超过了采样定理,或者fftshift用错了位置。解决:先检查全息图本身有没有欠采样,如果条纹太密,说明载频太高,需要调整光路或者用更小的像素尺寸。另外,fft2之前记得fftshift,之后记得ifftshift,顺序错了会引入相位斜坡。

4.4 零级项和共轭项没滤掉

现象:重建图里除了物体像,还有一团亮斑或者对称的鬼影。原因:全息图里的零级衍射和共轭项没有分离,直接做重建会把它们一起传过去。解决:在频域里加一个带通滤波器,把载频附近的一个边带选出来。常见做法是手动找载频峰值,然后用一个圆形窗口滤出来。

% 频域滤波示例 H = fftshift(fft2(hologram)); [rows, cols] = size(H); [X, Y] = meshgrid(1:cols, 1:rows); center = [cols/2, rows/2]; radius = 50; % 根据载频位置调整 mask = ((X - center(1)).^2 + (Y - center(2)).^2) < radius^2; H_filtered = H .* mask; hologram_filtered = ifft2(ifftshift(H_filtered));

这段代码在频域里选了一个圆形区域,只保留一个边带。radius需要根据实际载频的展宽来调,太小会丢信息,太大会把零级带进来。

4.5 内存不够导致 FFT 报错

现象:跑大图的时候 MATLAB 提示内存不足。原因:fft2对 4000x4000 的复数矩阵会占用几百 MB,加上中间变量很容易爆。解决:分块处理或者降采样。我一般会先把图裁到 2048x2048 以内,如果非要大视场,就用single精度代替double,能省一半内存。

5. 二次开发与改造:把三种重构封装成可复用的工具链

5.1 统一接口与参数校验

原始项目里三个函数各自独立,参数传递靠位置。如果你想把它集成到更大的 pipeline 里,建议统一接口,加一个method参数来切换。同时加上参数校验,避免传错顺序导致玄学错误。

function U = reconstruct(hologram, lambda, pixel_size, z, method) % 统一入口,method 可选 'conv'、'fresnel'、'angular' validateattributes(lambda, {'double'}, {'positive'}); validateattributes(pixel_size, {'double'}, {'positive'}); validateattributes(z, {'double'}, {'nonzero'}); switch lower(method) case 'conv' U = recon_conv(hologram, lambda, pixel_size, z); case 'fresnel' U = recon_fresnel(hologram, lambda, pixel_size, z); case 'angular' U = recon_angular(hologram, lambda, pixel_size, z); otherwise error('不支持的重构方法: %s', method); end end

这样改完之后,上层代码只需要调reconstruct,换方法不用改调用方式。参数校验能挡住大部分低级错误,比如把z传成 0 导致除零。

5.2 自动对焦与批量处理

如果你有一批全息图要处理,手动调z不现实。可以把第 3 章里的对焦扫描封装成函数,自动返回最佳距离和重建结果。再配合dir遍历文件夹,就能批量出结果。

function [U_best, z_best] = autofocus_reconstruct(hologram, lambda, pixel_size, z_range, method) sharpness = zeros(size(z_range)); for i = 1:length(z_range) U = reconstruct(hologram, lambda, pixel_size, z_range(i), method); sharpness(i) = sum(sum(gradient(abs(U)).^2)); end [~, idx] = max(sharpness); z_best = z_range(idx); U_best = reconstruct(hologram, lambda, pixel_size, z_best, method); end

这个函数返回最佳距离和对应的复振幅。批量处理的时候,把每个全息图的结果存成 MAT 文件或者 PNG,方便后续分析。

5.3 相位解包裹的衔接

数字全息重建出来的相位是包裹在[-pi, pi]之间的,直接看会有跳变。如果你要做定量相位测量,需要接一个解包裹算法。MATLAB 里可以用unwrap函数做一维解包裹,二维的话常见做法是用质量引导或者最小二乘法。我一般会在重建之后加一步相位解包裹,再转成高度或者厚度。

phase = angle(U_best); phase_unwrapped = unwrap(unwrap(phase, [], 1), [], 2); % 转成高度(反射式) height = phase_unwrapped * lambda / (4*pi);

这段代码先按行解包裹再按列解包裹,适用于连续变化的相位。如果噪声大,解包裹会出错,这时候需要先做相位滤波,比如用均值滤波或者中值滤波。

5.4 性能优化:GPU 加速与内存复用

如果数据量大,FFT 是瓶颈。MATLAB 支持gpuArray,把全息图传到 GPU 上做 FFT 能快好几倍。但要注意显存限制,大图还是会爆。另一个技巧是复用 FFT 结果,比如 CONV 和角谱都需要fft2(hologram),可以算一次存起来。

if canUseGPU hologram_gpu = gpuArray(hologram); U = gather(ifft2(fft2(hologram_gpu) .* H_gpu)); else U = ifft2(fft2(hologram) .* H); end

这段代码先判断有没有 GPU,有就传上去算,算完再取回来。H_gpu是提前算好的传递函数,避免每次重复计算。

5.5 从脚本到函数库的改造习惯

我刚开始写这类项目的时候,喜欢把所有代码堆在一个脚本里,改一个参数要翻半天。后来强迫自己把每个重构方法拆成独立函数,参数用结构体传,主脚本只负责调度和画图。这样改的好处是,下次遇到新的重构算法,只需要加一个函数文件,不用动主逻辑。从那以后我每次拿到新数据,都先跑一遍三种方法的对比图,确认哪种最适合当前的光路参数,再往下做定量分析。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 9:51:19

IM安卓ROM级调试工具imakit9.13:协议热修复与固件差分分析

简介&#xff1a;IM安卓开发工具箱imakit 9.13是面向Android ROM开发者、刷机爱好者及系统定制工程师的专业级工具集&#xff0c;专为高效制作、备份与转换刷机包而设计&#xff0c;解决ROM打包、img镜像管理、多格式兼容等核心开发痛点。资源包共283个文件&#xff0c;涵盖54个…

作者头像 李华
网站建设 2026/10/16 9:51:13

铁打的员工,流水的领导:领导更替期如何稳住职业底盘

1. 一个我观察了很久的职场现象1.1 “铁打的员工”到底铁在哪我进某家大厂那一年&#xff0c;隔壁工位的前辈已经待了六年。后来我亲眼看着他送走了三任直属领导、两任部门负责人&#xff0c;甚至见证了一整个事业部被重组两次。每次新领导上任&#xff0c;他都会被重新介绍一遍…

作者头像 李华
网站建设 2026/10/16 9:51:10

双目立体标定与校正:从棋盘格采集到Q矩阵验证的完整工程实践

简介&#xff1a;本资源是一套基于OpenCV 3.0与Visual Studio 2013实现的双目视觉立体标定与校正完整工程&#xff0c;面向计算机视觉初学者、机器人感知方向学习者及三维重建实践者&#xff0c;解决双目相机系统参数标定、极线校正等核心问题&#xff0c;为后续立体匹配与深度…

作者头像 李华
网站建设 2026/10/16 9:51:07

计组课设实战指南:从Verilog设计到FPGA烧录的全流程交付

简介&#xff1a;本资源是山东大学20级计算机科学与技术专业《计算机组成原理》课程的大课设实践成果&#xff0c;面向高校计组初学者及硬件设计入门者&#xff0c;聚焦CPU核心模块的电路实现与指令系统设计&#xff0c;有效弥合理论教学与动手实践之间的鸿沟。压缩包共150个文…

作者头像 李华
网站建设 2026/10/16 9:50:52

Unity对话系统设计:数据驱动、状态机与AI接入实战

做游戏只要碰到剧情&#xff0c;就绕不开对话系统。我入行那几年&#xff0c;项目里最常见的做法是给每个 NPC 写一个状态枚举&#xff0c;再用一长串 if/switch 把对白文本拼出来。界面简陋还好说&#xff0c;最怕策划在评审会上提一句“这里加个分支、那里加个选项”&#…

作者头像 李华
网站建设 2026/10/16 9:50:10

模板元编程性能分析:编译期、运行期与体积三大账本

模板元编程这个话题&#xff0c;在 C 圈子里几乎聊一次火一次。它能让你在编译期把该算的全算完&#xff0c;能生成手写循环级别的代码&#xff0c;也能把一个库的接口打磨得漂亮到不像 C。但只要一提性能&#xff0c;讨论就立刻变得两极分化&#xff1a;有人说它零成本抽象、快…

作者头像 李华