
簡介這份MATLAB仿真資源面向圖像處理與計算成像方向的學習者和研究者聚焦傅里葉疊層成像FPM的圖像重建算法實現(xiàn)。FPM通過多角度照明采集低分辨率圖像在傅里葉域疊加部分頻譜并迭代優(yōu)化從而恢復高分辨率相位與振幅信息是理解相位恢復與迭代優(yōu)化的典型平臺。資源包為zip格式僅含1個m腳本文件體積約5KB輕量易讀可直接在MATLAB環(huán)境中運行與修改。目前已有2600人學習下載說明其在相關(guān)課程與科研入門中具有一定參考價值。通過閱讀和調(diào)試該腳本讀者可掌握低分辨率圖像生成、離散傅里葉變換與逆變換、頻譜拼接及收斂判據(jù)等關(guān)鍵環(huán)節(jié)并借助圖像顯示函數(shù)直觀對比重建前后效果為生物醫(yī)學、材料科學等領(lǐng)域的實際成像實驗設計與數(shù)據(jù)分析打下基礎(chǔ)。1. 傅里葉疊層成像從一張低分辨率圖到超分辨重建的 MATLAB 仿真全流程如果你手頭只有一張低分辨率強度圖卻想恢復出超越物鏡數(shù)值孔徑限制的高頻細節(jié)傅里葉疊層成像Fourier Ptychography簡稱 FP就是繞不開的算法框架。這個 MATLAB 仿真項目做的正是這件事用一組不同角度照明下的低分辨率圖像在頻域里迭代拼接、相位恢復最終合成一張高分辨率復振幅圖像。它適合光學成像、計算成像方向的研究生和工程師也適合想搞懂“頻域疊層”到底怎么落地的 MATLAB 用戶。項目不依賴任何硬件純軟件仿真跑通后你能清楚看到頻譜如何一步步被“填滿”以及重建分辨率是怎么被合成孔徑效應推上去的。2. 算法架構(gòu)與核心模塊拆解頻譜拼接到底在拼什么2.1 傅里葉疊層的物理模型與數(shù)學表達FP 的核心思想并不復雜用不同角度的平面波照明樣品等效于把樣品頻譜的不同區(qū)域平移到系統(tǒng)的通帶內(nèi)。每個角度對應一個子孔徑所有子孔徑疊加起來就形成了一個合成的大孔徑。數(shù)學上第 (i) 個照明角度下相機采集到的低分辨率強度圖為[ I_i(\mathbf{r}) \left| \mathcal{F}^{-1} \left{ O(\mathbf{k}) \cdot P(\mathbf{k} - \mathbf{k}_i) \right} \right|^2 ]其中 (O(\mathbf{k})) 是樣品的高分辨率頻譜(P(\mathbf{k})) 是物鏡的相干傳遞函數(shù)一個低通濾波器(\mathbf{k}_i) 是第 (i) 個照明角度對應的頻移量。仿真項目要做的就是已知 (I_i) 和 (P)反推出 (O)。這個反問題是非凸的所以 FP 采用交替投影的思路先猜一個 (O)對每個角度做“低通濾波 逆變換 用實測強度替換振幅 正變換 更新頻譜”的循環(huán)。每輪迭代都會讓頻譜的高頻區(qū)域逐漸被正確信息填充。在 MATLAB 仿真里你需要先構(gòu)造一個高分辨率復振幅樣品比如用隨機相位加振幅圖案再生成對應的低分辨率強度圖集。這一步是“正問題”用來產(chǎn)生算法輸入。很多初學者直接拿真實相機圖跑結(jié)果因為照明角度標定不準、噪聲模型不匹配重建出來一團糊。仿真環(huán)境的好處是你可以精確控制每一個參數(shù)先驗證算法本身對不對。2.2 仿真數(shù)據(jù)生成從高分辨率樣品到低分辨率圖集先看正問題代碼。下面這段生成一個高分辨率復振幅樣品并模擬不同角度照明下的低分辨率強度圖。% 參數(shù)設置 N_high 256; % 高分辨率網(wǎng)格 N_low 64; % 低分辨率相機采樣 mag N_high / N_low; % 放大倍率 NA 0.1; % 物鏡數(shù)值孔徑 lambda 0.5; % 波長歸一化單位 k0 2 * pi / lambda; % 波數(shù) % 構(gòu)造高分辨率復振幅樣品振幅相位 rng(0); amplitude imresize(imread(cameraman.tif), [N_high, N_high]); amplitude double(amplitude) / 255; phase 0.5 * randn(N_high, N_high); % 隨機相位模擬厚度變化 O_high amplitude .* exp(1i * phase); % 物鏡相干傳遞函數(shù)圓形低通 [fx, fy] meshgrid(linspace(-1, 1, N_high), linspace(-1, 1, N_high)); fr sqrt(fx.^2 fy.^2); P double(fr NA / lambda); % 理想圓形孔徑 % 照明角度列表對應頻移 NA_illum 0.08; % 照明最大角度對應的數(shù)值孔徑 n_angles 7; % 每維角度數(shù) angles linspace(-NA_illum, NA_illum, n_angles); [Kx, Ky] meshgrid(angles, angles); Kx Kx(:); Ky Ky(:); n_led length(Kx); % 生成低分辨率強度圖集 I_low zeros(N_low, N_low, n_led); for idx 1:n_led % 頻移 kx_shift Kx(idx) / lambda; ky_shift Ky(idx) / lambda; % 在頻域平移頻譜 O_shift circshift(O_high, round([ky_shift, kx_shift] * N_high)); % 低通濾波 O_low O_shift .* P; % 逆變換到空域 field ifft2(ifftshift(O_low)); % 下采樣到相機分辨率 field_low field(1:mag:end, 1:mag:end); % 記錄強度 I_low(:, :, idx) abs(field_low).^2; end這段代碼的邏輯鏈條是先造一個已知的高分辨率復振幅樣品然后對每個照明角度在頻域把樣品頻譜平移、用物鏡孔徑截斷、逆變換回空域、下采樣到低分辨率相機網(wǎng)格、取強度。最終得到的I_low就是算法要處理的輸入數(shù)據(jù)。參數(shù)說明NA決定物鏡截止頻率NA_illum決定合成孔徑能擴展到多大。n_angles越大頻譜覆蓋越密但計算量也越大。mag是高低分辨率網(wǎng)格比通常取 4 或 8。注意circshift的平移量要取整否則頻譜會引入插值誤差——這是仿真里第一個容易翻車的地方。2.3 重建主循環(huán)交替投影與頻譜更新重建階段的核心循環(huán)如下。每一輪迭代遍歷所有照明角度對當前頻譜估計做一次“投影-替換-反投影”。% 初始化高分辨率頻譜估計 O_est fft2(ifftshift(imresize(I_low(:, :, 1), [N_high, N_high]))); O_est sqrt(abs(O_est)) .* exp(1i * angle(O_est)); % 保留相位 n_iter 30; % 迭代輪數(shù) for iter 1:n_iter for idx 1:n_led kx_shift Kx(idx) / lambda; ky_shift Ky(idx) / lambda; % 從當前估計中提取對應子孔徑區(qū)域 O_shift circshift(O_est, -round([ky_shift, kx_shift] * N_high)); O_low O_shift .* P; % 變換到空域 field ifft2(ifftshift(O_low)); field_low field(1:mag:end, 1:mag:end); % 用實測強度替換振幅保留相位 measured_amp sqrt(I_low(:, :, idx)); field_low_new measured_amp .* exp(1i * angle(field_low)); % 上采樣回高分辨率網(wǎng)格 field_new zeros(N_high, N_high); field_new(1:mag:end, 1:mag:end) field_low_new; % 變換回頻域 O_low_new fftshift(fft2(field_new)); % 更新對應子孔徑區(qū)域 O_shift_new O_shift .* (1 - P) O_low_new .* P; % 反平移回原頻譜位置 O_est circshift(O_shift_new, round([ky_shift, kx_shift] * N_high)); end % 可選施加頻域支撐約束已知孔徑外置零 O_est O_est .* (P 0 | abs(O_est) 0); % 計算誤差 if mod(iter, 5) 0 fprintf(Iteration %d completed.\n, iter); end end % 最終重建結(jié)果 O_recon fftshift(ifft2(ifftshift(O_est))); figure; subplot(1,2,1); imshow(abs(O_high), []); title(原始振幅); subplot(1,2,2); imshow(abs(O_recon), []); title(重建振幅);這段循環(huán)的關(guān)鍵操作是“振幅替換”在低分辨率網(wǎng)格上把當前估計的振幅換成實測振幅相位保持不變。然后把這個修改后的低分辨率復振幅放回高分辨率頻譜的對應位置。P在這里充當掩膜只更新物鏡通帶內(nèi)的區(qū)域通帶外保持原估計。參數(shù)說明n_iter通常 20 到 50 輪就能收斂再多收益很小。P的構(gòu)造要和正問題一致否則會出現(xiàn)“模型失配”重建質(zhì)量急劇下降。circshift的方向和符號要特別注意正問題里是平移樣品頻譜重建里是反方向提取子孔徑符號搞反了頻譜會越跑越偏。提示如果你發(fā)現(xiàn)重建結(jié)果始終模糊先檢查circshift的平移量是否和正問題互為逆操作。這是 FP 仿真里最高頻的錯誤來源。3. 運行環(huán)境與實操步驟從零跑通第一個重建結(jié)果3.1 MATLAB 版本與依賴工具箱這個仿真項目對 MATLAB 版本要求不高R2016b 及以上都能跑。核心依賴只有 Image Processing Toolbox主要用到imresize、imread、fft2、ifft2、fftshift、ifftshift、circshift。如果你沒有 Image Processing Toolbox可以用interp2替代imresize用隨機矩陣替代imread的測試圖。不需要 GPU不需要 Parallel Computing Toolbox。整個仿真在普通筆記本上跑256×256 高分辨率網(wǎng)格、49 個照明角度、30 輪迭代大約兩三分鐘出結(jié)果。如果你把N_high提到 512時間會漲到十幾分鐘但重建細節(jié)也更豐富。常見做法是把參數(shù)集中寫在一個config.m文件里主腳本開頭run(config.m)這樣改參數(shù)不用翻遍代碼。我一般還會在config.m里加一個use_gpu開關(guān)有 GPU 就gpuArray加速沒有就純 CPU。3.2 分步運行數(shù)據(jù)生成、重建、結(jié)果對比第一步運行數(shù)據(jù)生成腳本generate_data.m。它會生成I_low和O_high兩個變量保存為sim_data.mat。建議把O_high也存下來后面做定量對比用。第二步運行重建腳本reconstruct.m。它加載sim_data.mat執(zhí)行主循環(huán)輸出O_recon。腳本里加一個進度打印每 5 輪輸出一次方便判斷是否卡住。第三步運行對比腳本evaluate.m。計算重建振幅和原始振幅的相關(guān)系數(shù)、峰值信噪比并畫圖對比。下面是一個簡單的評估代碼% 加載數(shù)據(jù) load(sim_data.mat); load(recon_result.mat); % 只取振幅對比 amp_orig abs(O_high); amp_recon abs(O_recon); % 歸一化 amp_orig amp_orig / max(amp_orig(:)); amp_recon amp_recon / max(amp_recon(:)); % 計算相關(guān)系數(shù) corr_coef corr2(amp_orig, amp_recon); fprintf(振幅相關(guān)系數(shù): %.4f\n, corr_coef); % 計算峰值信噪比 mse mean((amp_orig(:) - amp_recon(:)).^2); psnr_val 10 * log10(1 / mse); fprintf(峰值信噪比: %.2f dB\n, psnr_val); % 顯示 figure; subplot(2,2,1); imshow(amp_orig, []); title(原始振幅); subplot(2,2,2); imshow(amp_recon, []); title(重建振幅); subplot(2,2,3); imshow(angle(O_high), []); title(原始相位); subplot(2,2,4); imshow(angle(O_recon), []); title(重建相位);相關(guān)系數(shù)在 0.95 以上說明重建成功0.8 以下就要查參數(shù)。峰值信噪比一般能到 20 dB 以上。相位重建比振幅難如果相位圖看起來像噪聲先確認正問題里有沒有加隨機相位——如果原始樣品相位全零重建相位也會接近零這是正常的。3.3 參數(shù)調(diào)優(yōu)照明角度數(shù)、迭代輪數(shù)與噪聲水平照明角度數(shù)n_angles決定合成孔徑的大小。每維 7 個角度合成 NA 大約是物鏡 NA 加上照明 NA。如果你把NA_illum設得比NA還大頻譜覆蓋會出現(xiàn)空隙重建出現(xiàn)柵格狀偽影。常見做法是讓照明 NA 略小于物鏡 NA保證相鄰子孔徑有重疊。迭代輪數(shù)n_iter不是越多越好。前 10 輪收斂最快20 輪后基本穩(wěn)定。如果你發(fā)現(xiàn) 30 輪后結(jié)果反而變差可能是噪聲被放大了。仿真里可以加高斯噪聲模擬相機噪聲noise_level 0.01; % 噪聲標準差 I_low I_low noise_level * randn(size(I_low)); I_low max(I_low, 0); % 強度非負噪聲水平超過 0.05 后重建質(zhì)量明顯下降。這時候可以加正則化比如在更新頻譜時做一次低通濾波或者用更魯棒的損失函數(shù)。仿真項目的價值就在于你可以精確控制噪聲水平觀察算法在什么條件下失效。注意加噪聲后記得把強度截斷到非負否則sqrt會出現(xiàn)復數(shù)后面全亂。4. 避坑與常見問題排查那些讓重建結(jié)果翻車的細節(jié)4.1 頻譜平移方向搞反重建越跑越糊現(xiàn)象迭代幾輪后重建振幅比第一輪還差頻譜能量往邊緣跑。原因正問題里circshift(O_high, round([ky_shift, kx_shift] * N_high))是平移樣品頻譜重建里提取子孔徑應該用反方向circshift(O_est, -round([ky_shift, kx_shift] * N_high))。如果兩個地方符號一致相當于把頻譜越推越遠。解決在代碼里加注釋明確正問題和重建的平移方向互為逆操作。跑之前先用一個已知的單角度數(shù)據(jù)驗證重建結(jié)果應該和原始低分辨率圖一致。4.2 物鏡孔徑 P 的構(gòu)造與正問題不一致現(xiàn)象重建結(jié)果出現(xiàn)明顯的圓形邊界偽影或者高頻區(qū)域完全沒恢復。原因正問題里P是理想圓形低通重建里如果用了矩形窗或者尺寸不對模型失配會導致更新區(qū)域錯誤。解決把P的構(gòu)造寫成一個獨立函數(shù)make_pupil(N, NA, lambda)正問題和重建都調(diào)用同一個函數(shù)。這樣改參數(shù)時不會漏改。4.3 下采樣與上采樣網(wǎng)格不對齊現(xiàn)象重建圖像出現(xiàn)周期性條紋或重影。原因正問題里field(1:mag:end, 1:mag:end)取的是從第一個像素開始的網(wǎng)格重建里field_new(1:mag:end, 1:mag:end) field_low_new也必須用同樣的網(wǎng)格。如果一邊從 1 開始另一邊從 2 開始就會錯位。解決統(tǒng)一下采樣起始索引建議都用 1。如果要用其他起始點正問題和重建必須一致。4.4 迭代不收斂或誤差震蕩現(xiàn)象誤差曲線上下震蕩不下降。原因?qū)W習率過大或者振幅替換太激進。FP 的交替投影本身是收斂的但如果你在更新時加了額外的約束比如強制相位平滑可能破壞收斂性。解決先去掉所有額外約束只保留最基本的振幅替換確認能收斂后再逐步加正則化。另外檢查P的歸一化如果P的值不是 0 或 1而是有漸變更新公式O_shift .* (1 - P) O_low_new .* P需要相應調(diào)整。4.5 相位重建出現(xiàn) 2π 跳變現(xiàn)象重建相位圖上有明顯的黑白跳變線。原因相位本身是周期性的angle函數(shù)返回的是 ((-π, π]) 區(qū)間跳變是正常的。但如果你用相位做定量分析需要做解包裹。解決MATLAB 里可以用unwrap函數(shù)或者用phase_unwrap工具箱。仿真階段可以先忽略因為振幅重建通常更關(guān)鍵。5. 二次開發(fā)與擴展從仿真到真實數(shù)據(jù)、從 2D 到 3D 的改造思路5.1 接入真實相機數(shù)據(jù)標定與預處理仿真跑通后下一步通常是想用真實數(shù)據(jù)驗證。真實 FP 系統(tǒng)需要標定照明角度、相機響應、背景噪聲。常見做法是先用一張空白樣品拍一組圖估計每個角度的實際頻移量而不是直接用 LED 陣列的幾何位置。因為 LED 到樣品的距離、樣品厚度都會影響等效角度。預處理包括暗場扣除、平場校正、強度歸一化。暗場扣除用相機蓋住鏡頭拍一張平場校正用均勻樣品拍一張。強度歸一化讓每個角度的總強度一致避免亮度波動影響重建。% 真實數(shù)據(jù)預處理示例 I_raw load(real_data.mat); % 假設是 H x W x n_led I_dark mean(I_raw(:, :, 1:5), 3); % 前5張是暗場 I_flat mean(I_raw(:, :, 6:10), 3); % 接下來5張是平場 I_corrected zeros(size(I_raw)); for idx 1:size(I_raw, 3) I_corrected(:, :, idx) (I_raw(:, :, idx) - I_dark) ./ (I_flat - I_dark); end I_corrected max(I_corrected, 0);這段代碼的邏輯是先減去暗場再除以平場消除相機固定模式和照明不均勻。參數(shù)說明暗場和平場的幀數(shù)根據(jù)你的采集協(xié)議調(diào)整一般各 5 到 10 幀取平均。注意平場要選沒有樣品細節(jié)的區(qū)域否則會把樣品信息也除掉。5.2 從 2D 擴展到 3D多層樣品與軸向掃描FP 本身是 2D 技術(shù)但可以通過軸向掃描擴展成 3D。思路是在不同軸向位置采集多組 FP 數(shù)據(jù)每組重建一個 2D 復振幅然后用光學衍射層析ODT算法反推 3D 折射率分布。MATLAB 里可以用fftn做 3D 傅里葉變換把 2D 的交替投影擴展到 3D。改造步驟把O_est從二維矩陣改成三維矩陣P改成三維傳遞函數(shù)circshift改成三維平移。計算量會漲一個數(shù)量級建議用 GPU 加速。仿真階段可以先造一個 3D 樣品比如兩個不同深度的薄片驗證算法能不能分離。5.3 加速技巧GPU 與并行計算MATLAB 的gpuArray可以把fft2、ifft2、circshift自動映射到 GPU。改造方法很簡單在數(shù)據(jù)生成和重建開頭加O_high gpuArray(O_high);結(jié)尾用gather取回 CPU。注意imresize不支持 GPU需要先gather再縮放。并行計算可以用parfor替換角度循環(huán)里的for。但 FP 的循環(huán)有數(shù)據(jù)依賴——每個角度更新的是同一個O_est不能直接并行。常見做法是把角度分組每組獨立更新一個副本最后取平均。這樣會犧牲一點收斂性但速度提升明顯。% 分組并行更新示例 n_group 4; O_est_group cell(n_group, 1); for g 1:n_group O_est_group{g} O_est; end parfor g 1:n_group idx_range g:n_group:n_led; for idx idx_range % ... 同樣的更新邏輯 ... end end % 合并 O_est zeros(size(O_est)); for g 1:n_group O_est O_est O_est_group{g}; end O_est O_est / n_group;這段代碼把角度分成 4 組每組獨立更新最后平均。參數(shù)說明n_group越大并行度越高但收斂越慢。一般取 2 到 4 比較平衡。注意parfor里不能直接修改共享變量所以用cell數(shù)組存每組結(jié)果。5.4 一個實用技巧用誤差曲線判斷是否該停我一般會在每輪迭代后計算重建低分辨率圖與實測圖的誤差畫一條曲線。如果誤差連續(xù) 5 輪下降小于 0.1%就提前停止。這樣既省時間又避免過擬合噪聲。err_history zeros(n_iter, 1); for iter 1:n_iter err_total 0; for idx 1:n_led % ... 重建邏輯 ... err_total err_total mean((sqrt(I_low(:, :, idx)) - abs(field_low)).^2, all); end err_history(iter) err_total / n_led; if iter 5 abs(err_history(iter) - err_history(iter-5)) / err_history(iter-5) 0.001 fprintf(提前停止于第 %d 輪\n, iter); break; end end從那以后我每次跑 FP 重建都強制走一遍誤差曲線不再盲設迭代輪數(shù)。這個習慣幫我省了很多無謂的等待也避免了噪聲被過度放大。希望幫到你。本文還有配套的精品資源點擊獲取