網(wǎng)絡圖像去噪:殘差學習與PyTorch工程實踐)
簡介資源提供基于深度卷積神經(jīng)網(wǎng)絡DnCNN的圖像去噪完整實現(xiàn)涵蓋網(wǎng)絡結(jié)構(gòu)設計、TensorFlow訓練流程與高斯噪聲去除實驗全過程適合深度學習圖像處理學習者、算法工程師及需要復現(xiàn)經(jīng)典模型的入門研究者。壓縮包共45個文件包含26張JPG訓練與測試樣例圖、12張PNG去噪前后對比圖、4個Python腳本網(wǎng)絡構(gòu)建、配置、算子與主訓練程序以及README說明文檔和許可證整體約1.39MB體積輕量。目錄按訓練集、測試集、訓練結(jié)果和代碼模塊劃分結(jié)構(gòu)清晰便于按步驟對照學習。目前已有6508人學習下載。通過源碼與樣例數(shù)據(jù)可掌握批量歸一化、ReLU激活、噪聲水平自適應輸入等關鍵技術并可直接運行復現(xiàn)DnCNN去噪效果進一步理解如何在TensorFlow中構(gòu)建、訓練和評估圖像復原模型為后續(xù)改進網(wǎng)絡提供扎實基礎。1. 基于深度卷積神經(jīng)網(wǎng)絡圖像去噪為什么“拆噪聲”比“重建圖像”更聰明基于深度卷積神經(jīng)網(wǎng)絡圖像去噪算法說白了就是讓網(wǎng)絡從一張帶噪圖里把“噪聲”單獨拆出來再用原圖減去噪聲剩下的就是干凈圖。手機夜景、老照片掃描、低劑量醫(yī)學影像大量實際場景都卡在噪聲和細節(jié)之間降噪太上強度邊緣糊成一片保住了邊緣噪點又賴著不走。深度卷積網(wǎng)絡用殘差學習解決了這個矛盾在相同噪聲強度下常見結(jié)構(gòu)能比傳統(tǒng)塊匹配類算法高出 1~2 dB 的 PSNR視覺上邊緣和紋理保留得更完整。這篇文章不是給你念論文而是按一條能落地的路徑講先說明網(wǎng)絡為什么這樣設計再講訓練數(shù)據(jù)怎么造、模型怎么復現(xiàn)、參數(shù)怎么調(diào)最后把最常翻車的幾個坑按“現(xiàn)象—原因—解決”列出來。適合正在做圖像降噪、準備把深度學習方案接進項目的開發(fā)者和算法工程師也適合剛接觸圖像復原方向、想找一份可抄作業(yè)流程的研究生。2. 深度卷積網(wǎng)絡去噪的原理殘差學習、感受野與批歸一化怎么配合要搞懂這類算法先給“去噪”一個可計算的數(shù)學定義。加性高斯噪聲模型寫作 x y n其中 x 是觀測到的噪聲圖y 是干凈圖n 是噪聲。網(wǎng)絡的任務就是從 x 估計 y。傳統(tǒng)方法把去噪當成濾波或優(yōu)化問題而深度卷積網(wǎng)絡把它當成監(jiān)督學習問題給網(wǎng)絡喂大量 (x, y) 對讓它自己擬合映射關系。深度卷積神經(jīng)網(wǎng)絡圖像去噪算法的核心不在“用卷積”這件事上而在于三個具體設計網(wǎng)絡內(nèi)部學的是殘差還是完整圖像、卷積層數(shù)決定的感受野有多大、卷積層之間的歸一化怎么加。這三個點直接決定了模型的上限和訓練穩(wěn)定性下面逐一拆開。2.1 殘差學習不是預測干凈圖而是預測噪聲圖早期把殘差學習引入圖像去噪的經(jīng)典思路解決了深度網(wǎng)絡做圖像復原的一個根本矛盾如果讓網(wǎng)絡直接輸出 y輸入是噪聲圖輸出是干凈圖那網(wǎng)絡要學習的是一個“從污染信號重建完整圖像”的映射。這個輸出空間包含大量低頻結(jié)構(gòu)、紋理和邊緣信息網(wǎng)絡需要花很大容量去記住圖像先驗訓練起來既慢又容易欠擬合。換一個目標就不一樣了。定義殘差 r x - y也就是噪聲本身訓練時讓網(wǎng)絡輸出 f(x)用損失 ‖f(x) - r‖2 去約束。因為加性高斯噪聲在空間上近似獨立、均值接近零它的統(tǒng)計結(jié)構(gòu)比自然圖像簡單得多網(wǎng)絡學會“預測噪聲”比學會“重建圖像”容易得多。推理時的輸出直接寫成 ? x - f(x)一張噪聲圖減去網(wǎng)絡預測出的噪聲分量剩下的就是相對干凈的圖像結(jié)構(gòu)。這個設計的另一個隱性好處是網(wǎng)絡初始化不穩(wěn)時輸出接近零意味著 ? 接近 x也就是模型一開始先從“把原圖原樣返回”出發(fā)再逐步修正。相比從空白開始猜一張完整圖像這種做法的訓練曲線要平穩(wěn)得多。實際項目中我一般會用這個殘差結(jié)構(gòu)作為默認選項除非任務特殊否則不輕易改成直接回歸干凈圖。2.2 感受野35×35 的上下文到底意味著什么感受野指輸出特征圖上某個像素能“看到”輸入圖像多大的區(qū)域。對于 3×3 卷積、stride1 的堆疊結(jié)構(gòu)每增加一層感受野邊界向外擴 2 個像素所以 L 層網(wǎng)絡的感受野尺寸是 2L 1。17 層網(wǎng)絡算下來就是 35×35這是這類去噪網(wǎng)絡最常見的深度配置。感受野為什么這么重要因為去噪本質(zhì)上是用周圍像素估計中心像素的“干凈值”。如果感受野只有 5×5網(wǎng)絡能參考的上下文太小遇到大塊邊緣、細紋理時很容易把圖像結(jié)構(gòu)當成噪聲抹掉。35×35 意味著估計每個輸出像素時網(wǎng)絡能綜合周圍 35×35 范圍內(nèi)約 1200 個像素的信息對大多數(shù)自然圖像的局部結(jié)構(gòu)來說已經(jīng)夠用。增加到 20 層以上感受野更大但參數(shù)和顯存開銷漲得比收益快PSNR 往往只提升零點零幾 dB。這個數(shù)值也直接決定了訓練時 patch 的最小尺寸。patch 至少要能完整包住感受野否則裁出來的訓練樣本在邊緣處“信息不全”網(wǎng)絡學到的映射和推理時不一致。我一般會把 patch 設為 40×40 起步正好略大于 35×35兼顧覆蓋范圍和樣本數(shù)量噪聲強度到了 σ50 時建議把 patch 提到 48×48 或 50×50。2.3 批歸一化加速收斂之外的另一層作用BatchNorm 加在每層卷積之后、激活函數(shù)之前先把中間特征圖歸一化到零均值單位方差再用可學習的縮放和平移恢復表達能力。對去噪網(wǎng)絡BN 的價值有兩層第一層很好理解深層網(wǎng)絡有了 BN 之后梯度傳播更穩(wěn)定直接用 1e-3 的學習率也能收斂去掉 BN 的話這個學習率很容易讓 loss 直接發(fā)散第二層是“分布穩(wěn)定化”去噪網(wǎng)絡的輸入是噪聲圖不同 batch 之間的噪聲分布有波動BN 把每層輸入的分布拉回統(tǒng)一尺度相當于給訓練過程加了一個緩沖。但 BN 是有代價的。它依賴 batch 內(nèi)統(tǒng)計量訓練時如果 batch size 壓到 4 以下BN 算出來的均值和方差噪聲很大訓練會明顯抖動推理時如果忘了切換到 eval 模式BN 繼續(xù)用當前輸入 batch 的統(tǒng)計量而推理常常一次只進一張圖統(tǒng)計量嚴重偏移輸出會直接崩掉。這類去噪網(wǎng)絡踩過的坑十有八九和 BN 模式切換有關后面避坑章節(jié)會展開細說。3. 訓練數(shù)據(jù)從哪來噪聲合成、Patch 提取與驗證集劃分深度卷積去噪是監(jiān)督方法需要成對的干凈圖和噪聲圖?,F(xiàn)實里很難拿到“同一場景拍兩次、一次干凈一次帶噪聲”的大量數(shù)據(jù)所以行業(yè)里的通行做法是準備一批高質(zhì)量干凈圖像人為合成噪聲制造訓練對。這一步聽起來簡單但噪聲參數(shù)怎么選、patch 怎么裁、驗證集怎么劃分直接影響模型最終能不能用。3.1 用高斯噪聲作為起點sigma 怎么選加性高斯白噪聲是最常用的合成模型控制噪聲強度的參數(shù)是標準差 σ單位是像素灰度值。σ15 對應輕度噪聲σ25 是學術界的標準基準σ50 是強噪聲肉眼已經(jīng)能看到明顯的顆粒感。實現(xiàn)時用 numpy 的 randn 函數(shù)生成和圖像同尺寸的高斯隨機矩陣乘以 σ 再加到干凈圖上即可。選擇 σ 時要看最終場景。如果任務是手機夜景照片去噪真實噪聲往往比 σ25 更強而且和像素亮度相關不是單純的高斯白噪聲這時訓練集里應該混合多個 σ而不是只做單一等級。我一般會先固定 σ25 把整條數(shù)據(jù)管線跑通確認訓練循環(huán)、驗證指標都正常再擴展到 σ ∈ {15, 25, 50} 混合訓練。不要一上來就追求復雜噪聲模型否則出了問題都說不清是數(shù)據(jù)問題還是網(wǎng)絡問題。3.2 Patch 提取40×40 小圖塊背后的權衡為什么不能拿整張圖直接訓練三個原因。第一是顯存1024×1024 的單張灰度圖直接進 17 層 64 通道網(wǎng)絡一次前向就要占用數(shù) GB 顯存batch 完全跑不起來。第二是樣本量一張圖只能給一次梯度但按 40×40 大小滑窗裁開后一張圖能產(chǎn)生幾百個 patch訓練樣本數(shù)量提升兩個數(shù)量級。第三是感受野匹配patch 只需要略大于網(wǎng)絡感受野就能提供完整上下文再大并不會帶來更多有效信息反而拉慢訓練速度。代碼實現(xiàn)可以直接用 OpenCV 讀取灰度圖滑窗裁剪后逐 patch 加噪聲import cv2 import numpy as np def synthesize_noise_pair(clean, sigma25): 輸入 0-255 的干凈灰度圖返回 (噪聲圖, 干凈圖)取值范圍 0-1。 clean clean.astype(np.float32) / 255.0 noise np.random.randn(*clean.shape).astype(np.float32) * (sigma / 255.0) noisy np.clip(clean noise, 0.0, 1.0) return noisy, clean def extract_patches(clean, patch_size40, stride20): 從單張灰度圖滑窗裁剪 patch返回干凈 patch 列表。 h, w clean.shape[:2] patches [] # 步長小于 patch 尺寸讓相鄰 patch 有重疊數(shù)據(jù)量更大 for i in range(0, h - patch_size 1, stride): for j in range(0, w - patch_size 1, stride): patches.append(clean[i:ipatch_size, j:jpatch_size]) return patches # 讀圖 - 裁 patch - 逐 patch 加噪聲 img cv2.imread(clean_photo.png, cv2.IMREAD_GRAYSCALE) clean_patches extract_patches(img, patch_size40, stride20) train_pairs [synthesize_noise_pair(p, sigma25) for p in clean_patches]這里有兩個關鍵細節(jié)。第一圖像先歸一化到 0-1因為網(wǎng)絡輸出用 MSE 損失時0-1 范圍的梯度更穩(wěn)定σ 也同步除以 255如果保留 0-255 范圍梯度量級會大 255 倍學習率要跟著調(diào)沒有直接歸一化省心。第二stride20 表示 patch 之間有一半重疊一張 256×256 的圖能裁出大約 121 個 patch如果顯存緊張或圖非常多可以調(diào) stride40 減少重疊代價是樣本總量變小、訓練輪數(shù)要相應增加。3.3 按圖劃分訓練/驗證防止數(shù)據(jù)泄漏抬高 PSNR很多初學者把所有 patch 混在一起隨機打亂后按比例切訓練集和驗證集看起來沒問題實際埋了一個大雷同一張干凈圖裁出的不同 patch一個進了訓練集、一個進了驗證集網(wǎng)絡訓練時已經(jīng)見過同一張圖的其他區(qū)域驗證 PSNR 會虛高 0.5~1 dB。拿這個數(shù)字去跟論文對比、去評效果完全沒有參考價值。正確做法是先把干凈圖像按文件列表分組比如前 90% 的圖負責裁剪訓練 patch后 10% 的圖負責裁剪驗證 patch保證驗證圖在訓練階段完全沒出現(xiàn)過。另一個細節(jié)是驗證時固定 σ 列表和隨機種子否則每次驗證因為隨機噪聲不同PSNR 上下波動 0.2 dB實驗之間沒辦法公平對比。我習慣在驗證腳本里寫死 np.random.seed(0)讓每一次驗證結(jié)果可復現(xiàn)。4. 用 PyTorch 復現(xiàn)去噪網(wǎng)絡模型定義、訓練循環(huán)與推理評估原理清楚了數(shù)據(jù)也備好了接下來是核心環(huán)節(jié)把網(wǎng)絡搭出來、跑通訓練、驗證有效。這一章給出的是一份可以直接改著用的 PyTorch 實現(xiàn)結(jié)構(gòu)上以 17 層深度卷積網(wǎng)絡為主這也是深度卷積神經(jīng)網(wǎng)絡圖像去噪算法里最經(jīng)典、最穩(wěn)的配置。4.1 17 層卷積網(wǎng)絡怎么寫第一層和最后一層的特殊處理網(wǎng)絡結(jié)構(gòu)如下注意首層和末層與中間層不同import torch import torch.nn as nn class DenoiseCNN(nn.Module): 17 層深度卷積去噪網(wǎng)絡輸入噪聲圖輸出殘差噪聲圖前向返回去噪結(jié)果。 def __init__(self, depth17, num_features64, in_channels1, out_channels1): super().__init__() layers [] # 第一層Conv ReLU不接 BN保留 bias layers.append(nn.Conv2d(in_channels, num_features, kernel_size3, padding1)) layers.append(nn.ReLU(inplaceTrue)) # 中間 depth-2 層Conv BN ReLUbias 置 False for _ in range(depth - 2): layers.append(nn.Conv2d(num_features, num_features, kernel_size3, padding1, biasFalse)) layers.append(nn.BatchNorm2d(num_features)) layers.append(nn.ReLU(inplaceTrue)) # 最后一層Conv 輸出單通道殘差圖不接 BN 和 ReLU layers.append(nn.Conv2d(num_features, out_channels, kernel_size3, padding1)) self.net nn.Sequential(*layers) def forward(self, x): residual self.net(x) return x - residual三個設計點需要說清楚。第一中間層 conv 的 biasFalse是因為后面緊跟 BatchNormBN 自帶可學習的平移參數(shù)卷積的 bias 是冗余的首層和末層不接 BN必須保留 bias否則卷積輸出的均值漂移無法被補償。第二末層不接 BN 也不接 ReLU因為殘差圖可以是負值——正噪聲和負噪聲都要能表達一旦 ReLU 截斷負值網(wǎng)絡就只能學“加噪”方向沒法學“去噪”方向。第三forward 里的 x - residual 就是殘差學習的落地訓練時網(wǎng)絡學習的目標是殘差噪聲圖推理時原圖減噪聲得到干凈圖。4.2 訓練超參數(shù)學習率、Batch Size 與學習率衰減訓練循環(huán)的框架如下包含優(yōu)化器、學習率衰減、驗證三個部分import math import torch import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # noisy_patches / clean_patches 是形狀為 (N, 1, 40, 40) 的 Tensor dataset TensorDataset(noisy_patches, clean_patches) loader DataLoader(dataset, batch_size32, shuffleTrue, num_workers4) model DenoiseCNN(depth17, num_features64, in_channels1, out_channels1) criterion nn.MSELoss() # 殘差學習使用 MSE對應高斯噪聲的最大似然估計 optimizer optim.Adam(model.parameters(), lr1e-3) scheduler optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.1) def evaluate_psnr(model, loader): model.eval() mse_sum, n_pixels 0.0, 0 with torch.no_grad(): for noisy, clean in loader: out model(noisy) mse_sum ((out - clean) ** 2).sum().item() n_pixels clean.numel() mse mse_sum / n_pixels return 10 * math.log10(1.0 / mse) # 輸入輸出均為 0-1 范圍 for epoch in range(50): model.train() total_loss 0.0 for noisy, clean in loader: optimizer.zero_grad() output model(noisy) loss criterion(output, clean) loss.backward() optimizer.step() total_loss loss.item() * noisy.size(0) scheduler.step() if (epoch 1) % 5 0: val_psnr evaluate_psnr(model, loader) print(fepoch {epoch 1:02d} loss {total_loss / len(dataset):.5f} fpsnr {val_psnr:.2f} dB)訓練超參數(shù)按下面的常用值起步再根據(jù)實際收斂情況微調(diào)參數(shù)常用值調(diào)參方向?qū)W習率 lr1e-3訓練不穩(wěn)時降到 3e-4batch size32顯存不夠時用 8~16但要關注 BN 統(tǒng)計網(wǎng)絡深度 depth17強噪聲可加到 20收益遞減特征通道數(shù)64大圖或強噪聲可加到 96epoch 數(shù)30~50驗證 PSNR 進入平臺期即停關于 evaluate_psnr注意它假設輸入輸出都是 0-1 范圍所以直接用 10log10(1/mse)不需要乘 2552。如果訓練和推理走 0-255 范圍這里要相應改成 10log10(2552/mse)。兩種口徑一致時PSNR 數(shù)值才是對的。4.3 推理階段全卷積推理與 YCbCr 通道處理訓練時用 40×40 的 patch推理時輸入往往是整張 1024×1024 甚至更大的圖。好在網(wǎng)絡是全卷積的可以接受任意尺寸輸入不需要重新裁圖。下面是單張灰度圖的推理函數(shù)import torch import numpy as np def denoise_gray(model, noisy_img_bgr_or_gray, devicecuda): # 輸入 0-255 灰度圖輸出 0-255 去噪圖 model.eval() with torch.no_grad(): x torch.from_numpy(noisy_img).float().div_(255.0) x x.unsqueeze(0).unsqueeze(0).to(device) # (1, 1, H, W) out model(x).squeeze(0).squeeze(0).cpu().numpy() return np.clip(out * 255.0, 0, 255).astype(np.uint8)彩色圖一般不直接在 RGB 三通道上跑。常見做法是把圖像從 RGB 轉(zhuǎn)到 YCbCr 色彩空間只對亮度 Y 通道做深度卷積去噪CbCr 兩個色度通道保持原樣最多做一次很輕的均值濾波。原因是人眼對亮度細節(jié)最敏感色度通道的輕微噪聲感知度低這樣做省掉三分之二的計算量也避免三通道相互干擾導致偏色。如果場景要求三通道都去噪就把模型 in_channels 和 out_channels 改成 3輸入輸出保持 0-1 歸一化同樣能跑但訓練時要保證每個通道的噪聲分布一致。5. 深度圖像去噪避坑指南5 個高頻問題與排查記錄網(wǎng)絡結(jié)構(gòu)能抄數(shù)據(jù)流程能抄但真正消耗時間的永遠是訓練和部署時那些“看著哪都對、跑出來就是不對”的問題。下面幾個坑我基本都踩過按現(xiàn)象、原因、解決的順序?qū)懩阏罩挪楸戎匦伦x論文快得多。5.1 loss 正常下降PSNR 卻只有幾 dB 甚至負數(shù)現(xiàn)象訓練 loss 從 0.1 一路降到 0.01訓練過程看起來完全正常但每次驗證打印出來的 PSNR 只有幾 dB甚至出現(xiàn)負值。原因十有八九是 PSNR 的計算口徑和網(wǎng)絡輸出的歸一化范圍不一致。網(wǎng)絡在 0-1 范圍訓練和輸出驗證時如果直接拿 0-1 范圍的 MSE 套用標準 PSNR 公式 10log10(2552/MSE)就會多算一個 10log10(2552) ≈ 48 dB 的偏置結(jié)果自然低得離譜。另一個常見誘因是網(wǎng)絡輸出沒有做 clip個別像素超出 0-1 范圍導致 MSE 被極端值拉大。解決統(tǒng)一口徑。輸入輸出規(guī)范在 0-1 時用 PSNR 10*log10(1/MSE)如果要在 0-255 域顯示就先把輸出乘 255 再算。順手在 PSNR 之前對輸出做 np.clip保證像素不越界。5.2 驗證時輸出灰蒙蒙BatchNorm 的模式切換現(xiàn)象訓練集上 loss 和肉眼效果都正常一換到驗證集或單張推理輸出圖像發(fā)灰、細節(jié)全無像蒙了一層霧。原因模型沒有切到 eval 模式。BatchNorm 層在訓練時用當前 batch 的均值和方差在 eval 模式才使用訓練階段積累的運行統(tǒng)計量。驗證時如果 batch 只有 1~2 張圖BN 臨時算出來的統(tǒng)計量嚴重偏離訓練分布中間特征被錯誤縮放輸出直接崩掉。解決推理函數(shù)里第一行必須是 model.eval()再配合 torch.no_grad() 關閉梯度計算。這兩句不是優(yōu)化項是 BN 類去噪網(wǎng)絡的硬性要求。如果切了 eval 模式仍然不對回頭檢查訓練時的 batch size 是不是太小小于 8導致 BN 的運行統(tǒng)計量本身就沒學準。5.3 數(shù)據(jù)泄漏讓驗證 PSNR 虛高現(xiàn)象驗證 PSNR 高出同配置論文一大截但點開去噪后的圖片細看視覺上并沒有那么好紋理細節(jié)明顯不夠。原因所有 patch 混在一起隨機切分訓練/驗證同一張干凈圖的不同 patch 同時出現(xiàn)在兩邊。網(wǎng)絡在訓練時見過同一張圖的鄰近區(qū)域驗證時相當于是“開卷考試”PSNR 被抬高。解決按圖像文件劃分而不是按 patch 劃分。先分出驗證圖列表再對驗證圖單獨裁 patch確保驗證圖從不出現(xiàn)在訓練流程里。驗證時固定隨機種子讓每次加噪可復現(xiàn)。5.4 彩色圖去噪后偏色現(xiàn)象灰度圖效果正常換成彩色圖后輸出出現(xiàn)彩色斑點、整體色調(diào)偏移去噪后顏色比帶噪原圖還怪。原因直接在 RGB 三通道上逐通道訓練忽略了通道之間的顏色相關性。相機噪聲在 RGB 通道間并不獨立簡單逐通道處理會破壞顏色關系另一個常見誘因是網(wǎng)絡輸出某個通道越界后沒有 clip導致色平衡被破壞。解決優(yōu)先轉(zhuǎn) YCbCr 只去噪 Y 通道這是最穩(wěn)的做法如果需要 RGB 三通道同時去噪輸入輸出統(tǒng)一歸一化到 0-1推理時用 clip 把三通道都限制在合法范圍內(nèi)。還可以在訓練時對訓練圖做顏色擾動增強讓模型學到更魯棒的通道關系。5.5 真實噪聲場景效果崩盤高斯預訓練模型的邊界現(xiàn)象在合成高斯噪聲上訓練得好好的模型放到真實夜景照片上細節(jié)被磨掉的同時噪聲還剩一大半直觀效果甚至不如傳統(tǒng)塊匹配算法。原因真實相機噪聲不是加性高斯它包含傳感器暗電流、ISP 增益放大、去馬賽克帶來的空間相關噪聲而且強度通常與像素亮度有關。高斯噪聲假設在弱光強噪場景下嚴重失效。解決交付項目前先定邊界。用真實設備采集噪聲-干凈對或者做噪聲標定用少量真實數(shù)據(jù)在預訓練模型上微調(diào)訓練階段把 σ 做成隨機變量讓模型覆蓋不同噪聲強度對弱光圖像可以先做亮度歸一化再進網(wǎng)絡。合成高斯噪聲基線只適合算法驗證和消融對比直接拿去做真實產(chǎn)品一定會翻車。6. 再往前一步盲噪聲模型、test-time augmentation 與邊緣保持驗證固定 σ 訓練的模型碰到未知強度的噪聲時 PSNR 掉得很快因為網(wǎng)絡只在某個噪聲等級附近見過樣本。改進做法是訓練時每個 batch 隨機抽取 σ比如每張 patch 的 σ 從 [5, 50] 均勻采樣再合成噪聲。網(wǎng)絡結(jié)構(gòu)不用改只把數(shù)據(jù)合成里的 σ 從常量變成隨機變量。這樣訓練出的模型在推理時不需要預先估計噪聲強度一張網(wǎng)覆蓋常見強弱噪場景這也是盲去噪的基礎做法。推理階段有個幾乎零成本的增強技巧把輸入圖水平翻轉(zhuǎn)后再送入網(wǎng)絡得到去噪結(jié)果后翻轉(zhuǎn)回來與原圖的結(jié)果取平均。由于網(wǎng)絡對翻轉(zhuǎn)后的輸入會有略微不同的預測誤差兩者平均能抵消部分偏差PSNR 通常能提升 0.1~0.3 dB代價只是多做一次前向。對彩色圖可以先轉(zhuǎn) YCbCr只對 Y 通道做這個操作CbCr 通道不參與計算量可控。評估去噪效果不要只看 PSNR。我習慣在驗證集上同時輸出 SSIM并且對去噪結(jié)果跑一遍 Canny 邊緣檢測對比原圖的邊緣響應如果邊緣變碎、斷裂說明網(wǎng)絡把結(jié)構(gòu)當噪聲抹了再用局部方差圖檢查平坦區(qū)域是否被過度平滑。這些指標比單一 PSNR 更能反映“能不能用”。我自己的教訓是別一開始就追最新的網(wǎng)絡結(jié)構(gòu)先固定 σ25 把數(shù)據(jù)管線、訓練循環(huán)、BN 切換這些基礎環(huán)節(jié)跑穩(wěn)再往上面加盲噪聲、TTA 和真實噪聲微調(diào)否則出了問題根本不知道是網(wǎng)絡的問題還是流程的問題。希望幫到你。本文還有配套的精品資源點擊獲取