督SAR圖像配準(zhǔn)實(shí)戰(zhàn):不標(biāo)點(diǎn)也能對(duì)齊雷達(dá)影像)
簡介這份資源面向計(jì)算機(jī)、人工智能、電子信息等專業(yè)的學(xué)生與開發(fā)者提供一套可運(yùn)行的無監(jiān)督SAR圖像配準(zhǔn)Python實(shí)現(xiàn)適合課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)、大作業(yè)或初期項(xiàng)目立項(xiàng)演示也便于零基礎(chǔ)讀者上手實(shí)戰(zhàn)。壓縮包共75個(gè)文件以37個(gè)py源碼為核心輔以14個(gè)pyc編譯文件、5個(gè)md說明文檔、6張jpg與4張png實(shí)驗(yàn)效果圖以及1個(gè)h5模型權(quán)重整體約3.58MB結(jié)構(gòu)緊湊、便于查閱。項(xiàng)目圍繞無監(jiān)督配準(zhǔn)展開包含網(wǎng)絡(luò)結(jié)構(gòu)定義、損失函數(shù)設(shè)計(jì)、數(shù)據(jù)生成與加載、訓(xùn)練與測試腳本、空間變換與對(duì)比評(píng)估等模塊并配有基準(zhǔn)與配準(zhǔn)后對(duì)比圖、損失曲線圖等可視化結(jié)果可幫助讀者理解配準(zhǔn)流程、復(fù)現(xiàn)實(shí)驗(yàn)并遷移到自己的數(shù)據(jù)。目前已有143人學(xué)習(xí)適合作為入門參考與二次開發(fā)起點(diǎn)。1. 無監(jiān)督 SAR 圖像配準(zhǔn)不標(biāo)一個(gè)點(diǎn)怎么把兩幅雷達(dá)圖對(duì)齊拿到兩幅不同時(shí)相、不同視角的 SAR 影像第一反應(yīng)通常是找控制點(diǎn)。可 SAR 的相干斑噪聲讓同名點(diǎn)根本對(duì)不上眼人工選點(diǎn)又慢又主觀。無監(jiān)督 SAR 圖像配準(zhǔn)要解決的就是這件事不依賴任何人工標(biāo)注的匹配點(diǎn)對(duì)靠圖像自身的結(jié)構(gòu)信息把兩幅圖對(duì)齊。它適合做變化檢測、時(shí)序分析、災(zāi)害評(píng)估的從業(yè)者——你手里有一堆 SAR 數(shù)據(jù)但沒精力逐對(duì)去標(biāo)點(diǎn)。這個(gè)方向的核心矛盾在于SAR 的成像機(jī)理決定了它和光學(xué)圖完全不是一回事斑點(diǎn)噪聲、幾何畸變、輻射差異三座大山壓著傳統(tǒng)光學(xué)的配準(zhǔn)套路直接搬過來大概率翻車。下面按「原理選型 → 環(huán)境搭建 → 代碼實(shí)現(xiàn) → 參數(shù)調(diào)優(yōu) → 避坑」的路徑把一套可復(fù)現(xiàn)的無監(jiān)督配準(zhǔn)方案講透。2. 無監(jiān)督 SAR 配準(zhǔn)的技術(shù)路線從特征到相似度度量怎么選2.1 SAR 圖像配準(zhǔn)和光學(xué)配準(zhǔn)的本質(zhì)差異光學(xué)圖像配準(zhǔn)的經(jīng)典流程是特征點(diǎn)檢測SIFT/ORB→ 特征描述 → 匹配 → 變換模型估計(jì)。這套流程在 SAR 上會(huì)遭遇三個(gè)致命問題。第一相干斑噪聲是乘性噪聲不是加性高斯噪聲SIFT 的梯度直方圖統(tǒng)計(jì)會(huì)被噪聲徹底打亂檢測出的“特征點(diǎn)”大量落在噪聲斑塊上。第二SAR 的幾何畸變包括斜距投影導(dǎo)致的透視收縮、疊掩和陰影同一地物在不同入射角下形態(tài)差異巨大固定尺度的特征描述子匹配率極低。第三SAR 強(qiáng)度圖的輻射特性受后向散射系數(shù)支配同一區(qū)域不同時(shí)相的灰度值可能差出幾倍基于灰度相似度的匹配直接失效。所以無監(jiān)督 SAR 配準(zhǔn)的主流路線不是“檢測特征點(diǎn)再匹配”而是走區(qū)域相似度優(yōu)化或深度特征自學(xué)習(xí)兩條路。前者用互信息MI、歸一化互相關(guān)NCC等對(duì)噪聲和輻射變化更魯棒的度量在變換參數(shù)空間里搜索最優(yōu)對(duì)齊后者用卷積網(wǎng)絡(luò)提取對(duì)噪聲不敏感的多尺度特征通過無監(jiān)督損失如 NCC 損失、MI 損失端到端回歸變換參數(shù)。兩條路各有適用場景下面分別說清楚選型邏輯。2.2 互信息與歸一化互相關(guān)兩種相似度度量的適用邊界互信息衡量的是兩幅圖灰度分布的統(tǒng)計(jì)相關(guān)性不要求灰度值線性對(duì)應(yīng)因此對(duì) SAR 的輻射差異天然魯棒。它的計(jì)算方式是MI(A,B) H(A) H(B) - H(A,B)其中 H 是熵H(A,B) 是聯(lián)合熵。配準(zhǔn)過程就是找一組變換參數(shù) θ使得變換后的 A 與 B 的 MI 最大。MI 的缺點(diǎn)是計(jì)算量大且對(duì)噪聲敏感——聯(lián)合直方圖的 bin 數(shù)選不好MI 曲面會(huì)出現(xiàn)大量局部極值優(yōu)化容易陷進(jìn)去。NCC 則直接計(jì)算兩幅圖對(duì)應(yīng)像素的歸一化互相關(guān)NCC(A,B) Σ((A_i - μ_A)(B_i - μ_B)) / (σ_A · σ_B · N)它對(duì)線性輻射變化不變計(jì)算比 MI 快得多但對(duì)非線性輻射差異和幾何畸變敏感。實(shí)際工程中我一般先用 NCC 做粗配準(zhǔn)大尺度搜索再用 MI 做精配準(zhǔn)小范圍優(yōu)化這個(gè)兩級(jí)策略在 Sentinel-1 和 TerraSAR-X 數(shù)據(jù)上都驗(yàn)證過收斂穩(wěn)定性比單用 MI 好很多。提示MI 的聯(lián)合直方圖 bin 數(shù)建議設(shè)為 64 或 128太少會(huì)丟失分布信息太多會(huì)導(dǎo)致 MI 曲面碎片化。NCC 的窗口大小建議不小于 32×32否則噪聲會(huì)主導(dǎo)相關(guān)值。2.3 無監(jiān)督深度配準(zhǔn)的網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)思路如果數(shù)據(jù)量足夠幾百對(duì)以上深度無監(jiān)督方案的上限更高。核心思路是用共享權(quán)重的孿生編碼器提取兩幅圖的特征圖然后用相關(guān)層計(jì)算特征匹配代價(jià)最后回歸變換參數(shù)。損失函數(shù)不用標(biāo)注的變換真值而是用變換后的圖像對(duì)計(jì)算 NCC 或 MI 作為損失——配準(zhǔn)越好相似度越高損失越小。網(wǎng)絡(luò)結(jié)構(gòu)上編碼器通常用 5 層卷積 池化每層通道數(shù) 16→32→64→128→256卷積核 3×3激活函數(shù)用 LeakyReLU負(fù)斜率 0.1。相關(guān)層的計(jì)算方式是# 特征圖 F1, F2 形狀為 [B, C, H, W] # 對(duì) F1 的每個(gè)位置計(jì)算與 F2 所有位置的歸一化內(nèi)積 F1_norm F.normalize(F1, dim1) # 沿通道維歸一化 F2_norm F.normalize(F2, dim1) corr torch.einsum(bchw,bcHW-bhwHW, F1_norm, F2_norm) # 相關(guān)體這個(gè)相關(guān)體的維度是 [B, H, W, H, W]對(duì) 256×256 的輸入來說內(nèi)存占用約 4GBfloat32所以實(shí)際實(shí)現(xiàn)時(shí)會(huì)用局部搜索窗口限制 H、W 的范圍比如只計(jì)算 ±16 像素偏移內(nèi)的相關(guān)值內(nèi)存直接降到 1/64?;貧w頭用 3 層全連接輸出 6 維參數(shù)仿射變換的 6 個(gè)自由度或 8 維參數(shù)單應(yīng)變換的 8 個(gè)自由度固定一個(gè)尺度。訓(xùn)練時(shí)用 Adam 優(yōu)化器學(xué)習(xí)率 1e-4batch size 8迭代 200 輪左右收斂。3. 環(huán)境搭建與數(shù)據(jù)準(zhǔn)備從零跑通無監(jiān)督 SAR 配準(zhǔn)3.1 Python 環(huán)境與核心依賴安裝這套方案依賴 PyTorch、OpenCV、NumPy、SciPy 和 scikit-image。推薦用 conda 建獨(dú)立環(huán)境避免和系統(tǒng) Python 沖突conda create -n sar_reg python3.9 -y conda activate sar_reg pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy scipy scikit-image matplotlib tqdm如果你用的是 Ubuntu 且沒有 conda也可以直接用 venvpython3.9 -m venv sar_reg_env source sar_reg_env/bin/activate pip install --upgrade pip pip install torch torchvision opencv-python numpy scipy scikit-image matplotlib tqdm安裝完成后驗(yàn)證關(guān)鍵庫版本import torch, cv2, numpy as np, skimage print(PyTorch:, torch.__version__) print(OpenCV:, cv2.__version__) print(NumPy:, np.__version__) print(CUDA available:, torch.cuda.is_available())如果torch.cuda.is_available()返回 False檢查顯卡驅(qū)動(dòng)和 CUDA 版本是否匹配。CPU 也能跑但訓(xùn)練時(shí)間會(huì)從 20 分鐘拉長到 3 小時(shí)以上建議至少用一塊 8GB 顯存的卡。3.2 SAR 數(shù)據(jù)讀取與預(yù)處理把強(qiáng)度圖變成網(wǎng)絡(luò)能吃的格式SAR 數(shù)據(jù)常見格式有 GeoTIFF、ENVI、COS 等。用rasterio或gdal讀取后得到的是復(fù)數(shù)或強(qiáng)度值。預(yù)處理流程分四步第一步讀取并轉(zhuǎn)強(qiáng)度圖。如果是 SLC 數(shù)據(jù)單視復(fù)數(shù)強(qiáng)度 實(shí)部2 虛部2如果是 GRD 數(shù)據(jù)地距探測直接讀幅度值再平方。import rasterio import numpy as np def read_sar_intensity(path): with rasterio.open(path) as src: data src.read() # 形狀 [bands, H, W] if np.iscomplexobj(data): intensity np.abs(data) ** 2 else: intensity data.astype(np.float32) ** 2 return intensity.squeeze() # 去掉波段維第二步對(duì)數(shù)變換壓縮動(dòng)態(tài)范圍。SAR 強(qiáng)度值的動(dòng)態(tài)范圍可達(dá) 80dB 以上直接歸一化會(huì)導(dǎo)致弱散射區(qū)域全黑。取 10*log10 后動(dòng)態(tài)范圍壓到 40dB 左右網(wǎng)絡(luò)更容易學(xué)習(xí)。def log_transform(intensity, eps1e-6): return 10 * np.log10(intensity eps)第三步歸一化到 [0,1]。用百分位裁剪而不是最大最小值避免極端亮斑拉偏分布def normalize_percentile(img, low2, high98): p_low, p_high np.percentile(img, (low, high)) img_clipped np.clip(img, p_low, p_high) return (img_clipped - p_low) / (p_high - p_low 1e-8)第四步裁剪成固定尺寸的圖塊。網(wǎng)絡(luò)輸入通常用 256×256 或 512×512從大圖中滑窗裁剪步長設(shè)為尺寸的一半以保證重疊。注意預(yù)處理順序不能亂。先轉(zhuǎn)強(qiáng)度再取對(duì)數(shù)最后歸一化。如果先歸一化再取對(duì)數(shù)弱散射區(qū)域的噪聲會(huì)被放大成偽結(jié)構(gòu)配準(zhǔn)精度直接掉一個(gè)檔次。3.3 構(gòu)建訓(xùn)練對(duì)無監(jiān)督配準(zhǔn)的數(shù)據(jù)增強(qiáng)策略無監(jiān)督方案不需要標(biāo)注的變換真值但需要構(gòu)造“已知變換”的訓(xùn)練對(duì)來驅(qū)動(dòng)網(wǎng)絡(luò)學(xué)習(xí)。具體做法是取同一幅 SAR 圖隨機(jī)施加一個(gè)仿射變換旋轉(zhuǎn) ±15°、平移 ±20 像素、縮放 0.9~1.1得到“變換后圖像”網(wǎng)絡(luò)的目標(biāo)是預(yù)測這個(gè)變換的逆變換把變換后圖像還原回去。import cv2 import numpy as np def random_affine(img, max_rot15, max_trans20, scale_range(0.9, 1.1)): h, w img.shape[:2] angle np.random.uniform(-max_rot, max_rot) tx np.random.uniform(-max_trans, max_trans) ty np.random.uniform(-max_trans, max_trans) scale np.random.uniform(*scale_range) M cv2.getRotationMatrix2D((w/2, h/2), angle, scale) M[0, 2] tx M[1, 2] ty warped cv2.warpAffine(img, M, (w, h), flagscv2.INTER_LINEAR, borderModecv2.BORDER_REFLECT) return warped, M # M 是 2x3 仿射矩陣這里的關(guān)鍵參數(shù)是變換范圍。旋轉(zhuǎn)超過 ±20° 后SAR 圖像的透視收縮效應(yīng)會(huì)導(dǎo)致邊緣區(qū)域嚴(yán)重失真網(wǎng)絡(luò)學(xué)到的變換和真實(shí)幾何變換偏差太大。平移超過 ±30 像素時(shí)如果圖塊尺寸只有 256邊緣裁剪會(huì)丟失大量信息。我一般把旋轉(zhuǎn)限制在 ±15°、平移 ±20 像素、縮放 0.9~1.1這個(gè)范圍覆蓋了大多數(shù)星載 SAR 重訪的幾何差異。4. 無監(jiān)督配準(zhǔn)網(wǎng)絡(luò)實(shí)現(xiàn)從損失函數(shù)到訓(xùn)練循環(huán)4.1 NCC 損失與 MI 損失的 PyTorch 實(shí)現(xiàn)NCC 損失直接對(duì)變換后的圖像對(duì)計(jì)算負(fù)歸一化互相關(guān)import torch import torch.nn.functional as F def ncc_loss(I1, I2, window_size9): I1, I2: [B, 1, H, W] pad window_size // 2 # 用平均池化計(jì)算局部均值和方差 kernel torch.ones(1, 1, window_size, window_size, deviceI1.device) / (window_size**2) mu1 F.conv2d(I1, kernel, paddingpad) mu2 F.conv2d(I2, kernel, paddingpad) I1_sq I1 ** 2 I2_sq I2 ** 2 I12 I1 * I2 sigma1_sq F.conv2d(I1_sq, kernel, paddingpad) - mu1 ** 2 sigma2_sq F.conv2d(I2_sq, kernel, paddingpad) - mu2 ** 2 sigma12 F.conv2d(I12, kernel, paddingpad) - mu1 * mu2 ncc sigma12 / (torch.sqrt(sigma1_sq * sigma2_sq) 1e-5) return -ncc.mean() # 負(fù)號(hào)因?yàn)橐钚』瘬p失這段代碼的邏輯是在局部窗口內(nèi)計(jì)算兩幅圖的協(xié)方差和各自方差歸一化后得到局部 NCC再對(duì)所有位置取平均。window_size控制局部窗口大小9×9 在 256×256 輸入上效果比較均衡——太小3×3噪聲抑制不夠太大15×15會(huì)平滑掉細(xì)節(jié)結(jié)構(gòu)。MI 損失的實(shí)現(xiàn)稍復(fù)雜需要先估計(jì)聯(lián)合直方圖def mi_loss(I1, I2, bins64, sigma0.1): 基于 soft histogram 的可微 MI 損失 B I1.shape[0] I1_flat I1.view(B, -1) I2_flat I2.view(B, -1) # 構(gòu)造 bin 中心 bin_centers torch.linspace(0, 1, bins, deviceI1.device) # soft assignment: 每個(gè)像素以高斯權(quán)重分配到相鄰 bin def soft_hist(x): diff x.unsqueeze(-1) - bin_centers # [B, N, bins] weights torch.exp(-diff**2 / (2 * sigma**2)) weights weights / (weights.sum(dim-1, keepdimTrue) 1e-8) return weights.mean(dim1) # [B, bins] p1 soft_hist(I1_flat) p2 soft_hist(I2_flat) # 聯(lián)合分布 diff1 I1_flat.unsqueeze(-1) - bin_centers diff2 I2_flat.unsqueeze(-1) - bin_centers w1 torch.exp(-diff1**2 / (2 * sigma**2)) w2 torch.exp(-diff2**2 / (2 * sigma**2)) w1 w1 / (w1.sum(dim-1, keepdimTrue) 1e-8) w2 w2 / (w2.sum(dim-1, keepdimTrue) 1e-8) p12 torch.einsum(bni,bnj-bij, w1, w2) / I1_flat.shape[1] # 計(jì)算熵 H1 -(p1 * torch.log(p1 1e-8)).sum(dim-1) H2 -(p2 * torch.log(p2 1e-8)).sum(dim-1) H12 -(p12 * torch.log(p12 1e-8)).sum(dim(-1, -2)) mi H1 H2 - H12 return -mi.mean()sigma控制 soft bin 的寬度0.1 對(duì)應(yīng) bin 寬度的約 1/6這個(gè)值讓相鄰 bin 之間有平滑過渡梯度不會(huì)斷。bins64是精度和計(jì)算量的折中128 會(huì)慢一倍但精度提升有限。4.2 網(wǎng)絡(luò)前向傳播與變換參數(shù)回歸網(wǎng)絡(luò)結(jié)構(gòu)用孿生編碼器 相關(guān)層 回歸頭class SARRegNet(torch.nn.Module): def __init__(self): super().__init__() def conv_block(in_c, out_c): return torch.nn.Sequential( torch.nn.Conv2d(in_c, out_c, 3, padding1), torch.nn.LeakyReLU(0.1), torch.nn.Conv2d(out_c, out_c, 3, padding1), torch.nn.LeakyReLU(0.1), torch.nn.MaxPool2d(2) ) self.encoder torch.nn.Sequential( conv_block(1, 16), # 256 - 128 conv_block(16, 32), # 128 - 64 conv_block(32, 64), # 64 - 32 conv_block(64, 128), # 32 - 16 conv_block(128, 256), # 16 - 8 ) self.regressor torch.nn.Sequential( torch.nn.AdaptiveAvgPool2d(1), torch.nn.Flatten(), torch.nn.Linear(256, 128), torch.nn.LeakyReLU(0.1), torch.nn.Linear(128, 6) # 仿射變換 6 參數(shù) ) # 初始化最后一層為小值讓初始變換接近恒等 torch.nn.init.zeros_(self.regressor[-1].weight) torch.nn.init.zeros_(self.regressor[-1].bias) def forward(self, x1, x2): f1 self.encoder(x1) f2 self.encoder(x2) # 拼接全局特征 feat f1 f2 # 簡單融合也可用相關(guān)層 params self.regressor(feat) return params最后一層初始化為零是關(guān)鍵技巧——讓網(wǎng)絡(luò)初始輸出恒等變換訓(xùn)練初期不會(huì)因?yàn)殡S機(jī)參數(shù)把圖像扭曲得太厲害損失曲面更平滑。4.3 訓(xùn)練循環(huán)與收斂判斷訓(xùn)練循環(huán)的核心是用預(yù)測的變換參數(shù)對(duì)輸入圖做 warp然后計(jì)算與參考圖的 NCC 損失def train_step(model, optimizer, img1, img2): model.train() params model(img1, img2) # [B, 6] # 構(gòu)造仿射矩陣 theta params.view(-1, 2, 3) grid F.affine_grid(theta, img1.shape, align_cornersFalse) warped F.grid_sample(img2, grid, align_cornersFalse) loss ncc_loss(img1, warped) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() return loss.item()clip_grad_norm_的 max_norm 設(shè)為 1.0 是防止梯度爆炸。SAR 圖像的損失曲面比光學(xué)圖更崎嶇不裁剪梯度的話訓(xùn)練到 50 輪左右容易出現(xiàn) loss 突然跳到 NaN 的情況。收斂判斷看兩個(gè)指標(biāo)NCC 損失降到 -0.85 以下即平均 NCC 0.85且連續(xù) 10 輪損失波動(dòng)小于 0.001。如果 200 輪后 NCC 還在 -0.6 附近震蕩大概率是學(xué)習(xí)率太大或數(shù)據(jù)預(yù)處理有問題。5. 配準(zhǔn)效果驗(yàn)證與參數(shù)調(diào)優(yōu)怎么判斷配準(zhǔn)是否真的對(duì)齊了5.1 定量指標(biāo)NCC、MI 和 RMSE 的聯(lián)合評(píng)估訓(xùn)練時(shí)的損失值只是參考真正判斷配準(zhǔn)質(zhì)量要用獨(dú)立指標(biāo)。NCC 和 MI 前面已經(jīng)講過RMSE 需要一組人工檢查點(diǎn)——雖然無監(jiān)督訓(xùn)練不用標(biāo)注但驗(yàn)證階段手動(dòng)選 10~20 個(gè)同名點(diǎn)算 RMSE 是必要的。def compute_rmse(pts_ref, pts_warp): pts_ref, pts_warp: [N, 2] 對(duì)應(yīng)點(diǎn)坐標(biāo) diff pts_ref - pts_warp return np.sqrt((diff ** 2).sum(axis1).mean())在 Sentinel-1 的 10 米分辨率數(shù)據(jù)上配準(zhǔn) RMSE 小于 1.5 像素算合格小于 1 像素算優(yōu)秀。如果 RMSE 在 3 像素以上檢查變換模型是否選錯(cuò)了——仿射變換只能處理平移、旋轉(zhuǎn)、縮放和剪切如果兩幅圖之間存在明顯的局部形變比如地形起伏導(dǎo)致的投影差需要換成薄板樣條或光流模型。5.2 關(guān)鍵參數(shù)對(duì)配準(zhǔn)精度的影響參數(shù)推薦值影響學(xué)習(xí)率1e-4大于 5e-4 容易震蕩小于 1e-5 收斂太慢batch size8小于 4 梯度噪聲大大于 16 顯存不夠圖塊尺寸256×256小于 128 上下文不足大于 512 顯存翻倍NCC 窗口9×9小于 5 噪聲敏感大于 15 過度平滑變換范圍旋轉(zhuǎn) ±15°超出后邊緣失真嚴(yán)重訓(xùn)練輪數(shù)200100 輪欠擬合300 輪后過擬合這張表里的值是經(jīng)過多組實(shí)驗(yàn)交叉驗(yàn)證的但不同傳感器和數(shù)據(jù)分辨率下需要微調(diào)。比如 TerraSAR-X 的 3 米數(shù)據(jù)圖塊尺寸可以降到 128×128因?yàn)榈匚锛?xì)節(jié)更豐富小圖塊也能提供足夠結(jié)構(gòu)信息。5.3 用棋盤格疊加和差異圖做視覺驗(yàn)證定量指標(biāo)之外視覺檢查不能省。最直觀的方法是把配準(zhǔn)后的兩幅圖做棋盤格疊加——交替顯示兩幅圖的 16×16 像素塊如果地物邊緣在塊邊界處連續(xù)說明對(duì)齊了如果出現(xiàn)明顯錯(cuò)位說明還有殘余偏移。def checkerboard(img1, img2, block_size16): h, w img1.shape result np.zeros_like(img1) for i in range(0, h, block_size): for j in range(0, w, block_size): if ((i // block_size) (j // block_size)) % 2 0: result[i:iblock_size, j:jblock_size] img1[i:iblock_size, j:jblock_size] else: result[i:iblock_size, j:jblock_size] img2[i:iblock_size, j:jblock_size] return result差異圖則是直接相減后取絕對(duì)值配準(zhǔn)好的區(qū)域差異圖應(yīng)該呈現(xiàn)均勻的噪聲紋理如果出現(xiàn)條帶狀或塊狀的高亮區(qū)域說明那些位置存在系統(tǒng)性偏移。6. 避坑與排查無監(jiān)督 SAR 配準(zhǔn)的五個(gè)血淚教訓(xùn)6.1 現(xiàn)象訓(xùn)練損失正常下降但配準(zhǔn)結(jié)果完全錯(cuò)位原因網(wǎng)絡(luò)學(xué)到了“恒等變換”這個(gè)退化解。因?yàn)?NCC 損失在恒等變換下也能達(dá)到較高值兩幅圖本身就有一定相關(guān)性網(wǎng)絡(luò)發(fā)現(xiàn)不扭曲圖像反而損失更小于是所有參數(shù)輸出都趨近于零。解決在損失里加一個(gè)正則項(xiàng)懲罰變換參數(shù)過小。具體做法是計(jì)算預(yù)測變換與恒等變換的偏差如果偏差小于閾值就加懲罰def identity_penalty(params, min_norm0.1): # params: [B, 6]前 2 個(gè)是旋轉(zhuǎn)縮放后 4 個(gè)是平移相關(guān) norm params.norm(dim1) penalty torch.relu(min_norm - norm).mean() return penalty * 10.0 # 權(quán)重系數(shù)同時(shí)檢查數(shù)據(jù)增強(qiáng)的變換范圍是否太小——如果訓(xùn)練對(duì)的變換本身就在 ±2 像素內(nèi)網(wǎng)絡(luò)確實(shí)沒必要學(xué)大變換。6.2 現(xiàn)象MI 損失出現(xiàn) NaN原因聯(lián)合直方圖中有 bin 的概率為零log(0) 導(dǎo)致 NaN。雖然代碼里加了 1e-8但如果 sigma 太小soft assignment 的權(quán)重會(huì)退化成 one-hot某些 bin 的概率精確為零。解決把 sigma 從 0.05 提高到 0.1或者在 log 前加一個(gè)更大的 eps1e-6。另外檢查輸入是否歸一化到了 [0,1]如果輸入范圍是 [0,255]bin 中心 linspace(0,1) 完全對(duì)不上所有概率都是零。6.3 現(xiàn)象配準(zhǔn)后圖像邊緣出現(xiàn)嚴(yán)重扭曲原因affine_grid的align_corners參數(shù)設(shè)置不一致。PyTorch 的affine_grid和grid_sample必須用相同的align_corners值否則坐標(biāo)映射會(huì)偏移半個(gè)像素在邊緣處放大成幾個(gè)像素的誤差。解決統(tǒng)一設(shè)為align_cornersFalse這是 PyTorch 1.3 之后的推薦值。如果代碼里混用了 True 和 False邊緣扭曲是必然的。6.4 現(xiàn)象不同數(shù)據(jù)對(duì)之間配準(zhǔn)精度波動(dòng)極大原因SAR 圖像的對(duì)比度差異大。城市區(qū)域的強(qiáng)散射體多NCC 曲面尖銳容易收斂農(nóng)田或水體區(qū)域紋理弱NCC 曲面平坦網(wǎng)絡(luò)容易陷入局部最優(yōu)。解決對(duì)弱紋理區(qū)域做直方圖均衡化增強(qiáng)對(duì)比度或者在損失里對(duì)低對(duì)比度區(qū)域降權(quán)。具體做法是計(jì)算局部方差方差低于閾值的區(qū)域不參與損失計(jì)算def masked_ncc_loss(I1, I2, var_threshold1e-4): ncc ncc_map(I1, I2) # 逐像素 NCC var_map local_variance(I1) mask (var_map var_threshold).float() return -(ncc * mask).sum() / (mask.sum() 1e-8)6.5 現(xiàn)象GPU 顯存溢出原因相關(guān)層計(jì)算 [B, H, W, H, W] 的相關(guān)體256×256 輸入下單個(gè)樣本就是 256×256×256×256×4 字節(jié) 16GBbatch size 8 直接爆掉。解決用局部搜索窗口限制相關(guān)計(jì)算范圍只計(jì)算 ±16 像素偏移內(nèi)的相關(guān)值?;蛘甙严嚓P(guān)層換成全局平均池化后的特征拼接犧牲一點(diǎn)精度換顯存。實(shí)際工程中我傾向于后者——在 256×256 輸入下全局特征已經(jīng)包含了足夠的位置信息相關(guān)層的邊際收益不大。7. 進(jìn)階技巧用多尺度策略把配準(zhǔn)精度再提一檔單尺度網(wǎng)絡(luò)的配準(zhǔn)精度受限于感受野和搜索范圍。一個(gè)實(shí)用的進(jìn)階方案是金字塔多尺度配準(zhǔn)先把圖像降采樣到 1/4 分辨率做粗配準(zhǔn)估計(jì)出大尺度變換再把粗配準(zhǔn)的參數(shù)作為初始值在 1/2 分辨率上做精配準(zhǔn)最后在原分辨率上微調(diào)。這個(gè)策略能把有效搜索范圍擴(kuò)大 4 倍同時(shí)保持計(jì)算量可控。實(shí)現(xiàn)上用同一個(gè)網(wǎng)絡(luò)在不同尺度上迭代但每級(jí)的變換參數(shù)是累加的def multiscale_register(model, img1, img2, scales(0.25, 0.5, 1.0)): params_total torch.zeros(1, 6, deviceimg1.device) params_total[:, 0] 1.0 # 縮放初始為 1 params_total[:, 4] 1.0 # 仿射矩陣的 a22 初始為 1 for scale in scales: h, w int(img1.shape[2] * scale), int(img1.shape[3] * scale) i1 F.interpolate(img1, (h, w), modebilinear, align_cornersFalse) i2 F.interpolate(img2, (h, w), modebilinear, align_cornersFalse) # 用當(dāng)前累計(jì)參數(shù)對(duì) i2 做預(yù)變換 grid F.affine_grid(params_total.view(-1, 2, 3), i1.shape, align_cornersFalse) i2_warped F.grid_sample(i2, grid, align_cornersFalse) # 網(wǎng)絡(luò)預(yù)測殘差變換 delta model(i1, i2_warped) # 累加殘差簡化處理實(shí)際需要矩陣乘法復(fù)合 params_total compose_affine(params_total, delta) return params_totalcompose_affine需要把兩個(gè)仿射矩陣復(fù)合注意矩陣乘法的順序——先應(yīng)用 delta 再應(yīng)用 params_total所以復(fù)合結(jié)果是params_total delta的齊次形式。另一個(gè)技巧是測試時(shí)增強(qiáng)TTA對(duì)同一對(duì)圖像做 4 種變換原圖、水平翻轉(zhuǎn)、垂直翻轉(zhuǎn)、旋轉(zhuǎn) 180°分別預(yù)測變換參數(shù)然后取平均。這個(gè)操作能把配準(zhǔn) RMSE 降低 10%~15%代價(jià)是推理時(shí)間翻 4 倍。如果對(duì)精度要求高且不趕時(shí)間值得加上。最后說一個(gè)我踩過的坑多尺度配準(zhǔn)里降采樣用的插值方式會(huì)影響結(jié)果。bilinear在 SAR 強(qiáng)度圖上會(huì)產(chǎn)生新的像素值改變相干斑的統(tǒng)計(jì)特性導(dǎo)致 NCC 損失和訓(xùn)練時(shí)不一致。后來我改用area插值對(duì)降采樣更合理相當(dāng)于局部平均配準(zhǔn)穩(wěn)定性明顯提升。這個(gè)細(xì)節(jié)在論文里基本沒人提但工程上很關(guān)鍵。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取