简介:面向偏振图像处理与去噪研究的Python项目,适合计算机视觉、图像分析与机器学习方向的学习者和研究者,用于理解偏振信息增强、噪声抑制及模型评估的完整流程。项目包含38个文件,以15个Python源码和14个字节码文件为主,辅以5张示例图、说明文档、Dockerfile及许可证,压缩包约906KB,规模轻量但结构覆盖数据加载、变换、网络模型、评价指标与可视化等环节。已有401人学习,说明其在小众方向中具备一定参考热度。读者可据此了解偏振图像数据的组织方式,梳理去噪网络从输入变换到指标计算的实现路径,并借助随包数据集与示例图复现实验、对比不同模型表现;其中模型侧涉及多种网络结构,工具与可视化代码也有助于二次开发和结果分析,适合作为课程实践、课题预研或算法复现的基础材料。
1. 偏振图像去噪不是把 RGB 去噪搬过来:先看噪声从哪来
偏振图像去噪在 Python 项目里最容易翻车的地方,不是选哪个滤波器,而是把 RGB 去噪那套直接搬过来。RGB 三通道去噪只看亮度保真,偏振图像还要看 I0/I45/I90/I135 四个方向、Stokes 分量、DoLP 和 AoP;噪声一旦把弱偏振信号抹平,PSNR 可能还好看,偏振度图已经花了。这套资源围绕偏振图像去噪,给出可跑的 Python 代码和配套数据集,适合做偏振成像、材质检测、去雾前处理的从业者。拿到数据集先别急着训练,先核对通道顺序和位深,后面很多“玄学”问题都出在这一步。
2. 数据与物理量对齐:从 Stokes 参数到 DoLP/AoP 的 Python 落地
2.1 偏振数据到底存了什么:I/Q/U 与 DoFP 马赛克
偏振图像常见的存储形式有两类:一类是相机直接输出的分焦平面马赛克,单帧图里每个 2×2 像素块对应四个微偏振片方向,常见排列是 0°、45°、90°、135°;另一类是已经拆好的四通道图或 Stokes 分量图。分焦平面相机的优势是一次曝光拿全偏振信息,代价是空间分辨率被四个方向分摊,去马赛克插值本身会引入伪影。分时相机的数据更干净,但运动场景容易对齐失败。
真正落地时先确认数据里每个文件代表什么。随包数据集通常会把原始马赛克和参考图分开放,也可能直接给 I0/I45/I90/I135 四张图。Stokes 参数的计算关系不复杂:
- S0 = (I0 + I45 + I90 + I135) / 2
- S1 = I0 - I90
- S2 = I45 - I135
- DoLP = sqrt(S1² + S2²) / S0
- AoP = 0.5 × atan2(S2, S1)
这里的 1/2 因子来自理想偏振片的透过率模型,I(θ)=0.5×(S0+S1cos2θ+S2sin2θ)。有些数据集已经乘过 2 或者做过归一化,直接套公式会得到大于 1 的 DoLP,先看数据范围再决定要不要补这个系数。
| 文件形式 | 常见内容 | 读取注意 |
|---|---|---|
| 单帧马赛克 | 2×2 微偏振片排列 | 不能当灰度图直接 resize |
| 四通道图 | I0/I45/I90/I135 | 确认通道顺序和位深 |
| Stokes 图 | S0/S1/S2 | S1/S2 有负值,不能用 uint8 存 |
| 参考图 | 高曝光平均或多帧融合 | 确认和输入是否同一场景对齐 |
提示:通道顺序错一位,后面的去噪、评估、可视化全错。先拿一张均匀白光图看四通道均值是否接近,再旋转偏振片看四个方向是否按正弦变化。
2.2 读取与拆分通道:一段可复现的 Python 预处理
下面这段代码按 2×2 分焦平面排列拆分马赛克,并计算 Stokes 分量。它不做归一化,也不做 resize,目的是先把原始位深和通道顺序固定下来。
import numpy as np import cv2 def load_polar_mosaic(path): # 保持原始位深读取,16 位数据不要在这里转成 8 位 img = cv2.imread(path, cv2.IMREAD_UNCHANGED) if img is None: raise FileNotFoundError(path) if img.ndim == 3: img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) return img.astype(np.float32) def split_dofp_2x2(mosaic): # 常见 2x2 排列:[[0, 45], [90, 135]] # 如果数据集是 [[0, 90], [45, 135]],需要改下面四行索引 h, w = mosaic.shape h2, w2 = h - h % 2, w - w % 2 m = mosaic[:h2, :w2] I0 = m[0::2, 0::2] I45 = m[0::2, 1::2] I90 = m[1::2, 0::2] I135 = m[1::2, 1::2] return I0, I45, I90, I135 def stokes_from_intensity(I0, I45, I90, I135, eps=1e-6): # 理想偏振片模型下,四通道和是 2*S0 S0 = (I0 + I45 + I90 + I135) / 2.0 S1 = I0 - I90 S2 = I45 - I135 return S0, S1, S2 def dolp_aop(S0, S1, S2, eps=1e-6): # DoLP 限制在 [0,1],AoP 返回 [-pi/2, pi/2] dolp = np.sqrt(S1 ** 2 + S2 ** 2) / (S0 + eps) dolp = np.clip(dolp, 0.0, 1.0) aop = 0.5 * np.arctan2(S2, S1 + eps) return dolp, aopload_polar_mosaic用IMREAD_UNCHANGED是为了避免 16 位被压成 8 位;如果随包数据是.npy,把读取那行换成np.load即可。split_dofp_2x2假设微偏振片按 2×2 块排列,奇数行和奇数列的下标含义要跟相机手册对齐。stokes_from_intensity里的eps是防止 S0 在暗区接近 0 时除零;dolp_aop里对 DoLP 做截断,是为了暴露数据问题而不是悄悄把错误值藏起来。
我一般会先跑一次检查脚本:对每个场景抽一帧,输出四个通道的均值、最小值、最大值,以及 DoLP 的直方图。如果 DoLP 均值长期接近 0,要么通道顺序错了,要么数据被过度平滑;如果 DoLP 大量等于 1,多半是 S0 太小或者除法没有保护。这个检查不花时间,但能省掉后面几小时的训练排查。
2.3 噪声模型与评估指标:PSNR 不够,还要看偏振度误差
偏振图像的噪声不是单一的高斯噪声。暗场景下读出噪声占主导,亮场景下散粒噪声更明显,微偏振片之间的串扰和去马赛克插值还会带来空间相关噪声。工程上常用泊松-高斯模型近似:方差随信号强度变化,亮区噪声方差更大。直接假设固定 sigma 的高斯噪声,去噪网络会把暗区过度平滑,弱偏振细节先丢。
噪声水平估计可以用暗帧:盖上镜头拍一组,算每个通道的标准差;也可以用单帧小波估计,在平坦区取高频系数的中位数。仿真训练时按泊松-高斯加噪,比单一高斯更接近真实分布。验证时不要只看 PSNR,至少加三项:DoLP 的 MAE、AoP 的角度误差、低 DoLP 区域的细节保留。
def psnr(x, y, peak=1.0): # peak 按数据范围给:归一化到 [0,1] 用 1.0,16 位数据用 65535 mse = np.mean((x - y) ** 2) return 10.0 * np.log10(peak ** 2 / (mse + 1e-12)) def aop_error(aop_gt, aop_pred): # AoP 有 pi 周期,先映射到单位圆再算角度差,避免 pi 跳变 d = np.angle(np.exp(1j * 2.0 * (aop_pred - aop_gt))) / 2.0 return np.abs(d) def dolp_mae(dolp_gt, dolp_pred): return np.mean(np.abs(dolp_gt - dolp_pred))psnr的peak必须和输入数据范围一致,混用 1.0 和 255 会让指标失真。aop_error用复指数把角度差折叠到 [-π/2, π/2],否则 179° 和 -179° 会被算成 358° 的误差。dolp_mae只统计有效区域,暗背景像素 S0 太小,DoLP 本身不稳定,建议用 S0 做掩码再统计。
评估时把高 DoLP 和低 DoLP 分开看。高 DoLP 区域看结构保真,低 DoLP 区域看噪声是否被误判成偏振信号。很多模型在整体 PSNR 上赢,但在低 DoLP 区域把噪声抹成一片,下游材质分类反而掉点。
3. 去噪算法选型:从 BM3D 到盲噪声估计,哪些适合偏振图像
3.1 传统方法的上限:BM3D/NLM 在偏振通道上的表现
BM3D 在加性高斯噪声上依然是强基线,尤其是数据量小、没有配对训练集的时候。它的思路是相似块匹配加协同滤波,对重复纹理有效。放到偏振图像上,常见做法是对 I0/I45/I90/I135 四个通道分别去噪,再算 Stokes;或者对 S0/S1/S2 分别去噪。两种做法都有边界:分通道去噪会破坏通道间的物理约束,去噪后的四个强度图可能不再对应同一个 DoLP;对 Stokes 去噪则要处理 S1/S2 的负值,BM3D 默认输入范围在 [0,1],直接丢负值进去会被截断。
我一般先把 S1/S2 线性映射到 [0,1] 再去噪,完了再映射回来,虽然多了一步,但比截断负值靠谱。sigma 参数不要拍脑袋,用暗帧或者小波估计给一个初值,再在验证集上小范围搜。sigma 给小了噪声残留,给大了弱偏振纹理被抹平,DoLP 边缘会出现光晕。
from bm3d import bm3d def to_unit(x): lo, hi = float(x.min()), float(x.max()) return (x - lo) / (hi - lo + 1e-8), lo, hi def from_unit(x, lo, hi): return x * (hi - lo + 1e-8) + lo def denoise_stokes_bm3d(S1, S2, sigma=0.02): # S1/S2 有负值,先映射到 [0,1] 再调用 BM3D S1u, lo1, hi1 = to_unit(S1) S2u, lo2, hi2 = to_unit(S2) S1d = from_unit(bm3d(S1u, sigma), lo1, hi1) S2d = from_unit(bm3d(S2u, sigma), lo2, hi2) return S1d, S2dsigma是噪声标准差,按归一化后的尺度给;如果数据没有归一化,先除以位深最大值。to_unit和from_unit保住了负值信息,代价是每个通道的缩放系数不同,后续算 DoLP 时要注意 S0 和 S1/S2 是否在同一尺度。更稳妥的做法是四个强度通道用同一组缩放系数,别让通道间的相对关系被破坏。
NLM 对低纹理区域更平滑,但偏振图像里弱偏振细节往往就是低对比度,NLM 容易把这些细节当噪声。传统方法适合做基线,不适合作为最终方案,尤其是数据里包含金属、玻璃这类高反射材质时,局部相似块匹配会失效。
3.2 学习式方法的门槛:配对数据、噪声建模与通道约束
学习式去噪的核心门槛不是网络结构,是配对数据。真实偏振图像很难同时拿到干净参考和带噪输入,常见做法是仿真:拿高质量参考图,加泊松-高斯噪声,训练网络;再用少量真实配对数据微调。仿真噪声和真实噪声的分布差异是主要误差来源,尤其是微偏振片串扰带来的空间相关噪声,仿真里很难完全复现。
另一个门槛是通道约束。RGB 去噪网络输入三通道,输出三通道,损失只算像素差。偏振图像如果只对 S0 训,S1/S2 的噪声没人管;如果对四个强度通道训,输出可能不满足 Stokes 关系。常见做法是把 S0/S1/S2 作为三通道输入输出,损失里加一项 DoLP 一致性,权重从 0.1 开始试。权重太大,网络为了保 DoLP 牺牲 PSNR;权重太小,物理约束形同虚设。
盲噪声估计是另一个方向:不假设固定 sigma,让网络从输入里估计噪声水平。优点是换场景不用重新调参,缺点是小数据集上估计器本身容易过拟合。我的经验是,数据量少的时候先用固定噪声水平加数据增强,等数据量上来再上盲估计。
3.3 一个可跑的基线:残差网络去噪与偏振一致性损失
下面这个轻量残差网络适合做第一版基线。输入三通道,通道顺序按 S0/S1/S2 排,输出残差,也就是预测噪声再减掉。网络不复杂,目的是先把训练和评估流程跑通,再换更重的结构。
import torch import torch.nn as nn class ResBlock(nn.Module): def __init__(self, ch=32): super().__init__() self.net = nn.Sequential( nn.Conv2d(ch, ch, 3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(ch, ch, 3, padding=1), ) def forward(self, x): return x + self.net(x) class PolarDenoiser(nn.Module): def __init__(self, in_ch=3, ch=32, depth=6): super().__init__() self.head = nn.Conv2d(in_ch, ch, 3, padding=1) self.body = nn.Sequential(*[ResBlock(ch) for _ in range(depth)]) self.tail = nn.Conv2d(ch, in_ch, 3, padding=1) def forward(self, x): # x: [B, 3, H, W],通道为 S0/S1/S2 noise = self.tail(self.body(self.head(x))) return x - noise def polarization_consistency_loss(pred, clean, eps=1e-6): # pred/clean: [B, 3, H, W],通道顺序 S0/S1/S2 s0p, s1p, s2p = pred[:, 0], pred[:, 1], pred[:, 2] s0c, s1c, s2c = clean[:, 0], clean[:, 1], clean[:, 2] dolp_p = torch.sqrt(s1p ** 2 + s2p ** 2 + eps) / (s0p.abs() + eps) dolp_c = torch.sqrt(s1c ** 2 + s2c ** 2 + eps) / (s0c.abs() + eps) return torch.mean(torch.abs(dolp_p - dolp_c))ResBlock里不做下采样,保持分辨率,适合小 patch 训练。PolarDenoiser的depth控制残差块数量,ch控制通道宽度,第一版用 32 和 6 就够跑通。polarization_consistency_loss只约束 DoLP,不约束 AoP;AoP 在低 DoLP 区域不稳定,早期加进去容易让训练震荡。等 DoLP 误差降下来,再把 AoP 误差以更小的权重加进去。
训练时总损失可以写成MSE(pred, clean) + 0.2 * polarization_consistency_loss(pred, clean)。这个 0.2 不是固定值,数据里高 DoLP 区域多就调小一点,低 DoLP 区域多就调大一点。先跑 20 个 epoch 看验证集 DoLP MAE 有没有下降,再决定要不要加网络深度。
4. 训练与验证流程:把数据集切成能复现实验的三份
4.1 数据划分与增强:偏振通道不能做任意翻转
偏振图像的数据划分不能随机打散。同一个场景的相邻帧、同一块材质的多个角度样本如果同时出现在训练集和验证集,指标会虚高。常见做法是按场景划分:训练集覆盖一部分场景,验证集留另一部分场景,测试集再留完全没见过的材质或光照条件。如果数据集里场景标签不清楚,至少按拍摄批次分,别让同一批数据跨集。
增强也要小心。水平翻转会交换 45° 和 135° 通道,垂直翻转会改变微偏振片排列,90° 旋转会改变 AoP 的参考方向。不做同步重排就直接翻转,网络学到的是错误的偏振关系。保守做法是只做随机裁剪和不改变偏振参考系的亮度扰动;要做翻转,就必须同时交换对应通道并重新计算 Stokes。
import numpy as np import torch from torch.utils.data import Dataset class PolarPatchDataset(Dataset): def __init__(self, samples, patch=128, train=True): # samples: [{"input": path, "target": path}, ...] self.samples = samples self.patch = patch self.train = train def __len__(self): return len(self.samples) def __getitem__(self, idx): s = self.samples[idx] noisy = np.load(s["input"]).astype(np.float32) # [3, H, W] clean = np.load(s["target"]).astype(np.float32) # [3, H, W] _, h, w = noisy.shape if self.train: y = np.random.randint(0, h - self.patch) x = np.random.randint(0, w - self.patch) else: y = (h - self.patch) // 2 x = (w - self.patch) // 2 noisy = noisy[:, y:y + self.patch, x:x + self.patch] clean = clean[:, y:y + self.patch, x:x + self.patch] return torch.from_numpy(noisy), torch.from_numpy(clean)patch取 128 是显存和感受野的折中;太小网络看不到偏振纹理,太大 batch 上不去。训练时随机裁,验证时中心裁,保证每次评估位置固定。这里没有写翻转增强,就是为了避免通道交换的坑。如果一定要翻转,先把 45° 和 135° 通道对调,再检查 S2 的符号是否跟着变。
4.2 训练循环与早停:损失函数里加一项偏振一致性
训练循环本身不复杂,关键是监控指标。只盯训练 loss 很容易过拟合,验证集上 PSNR 涨了但 DoLP MAE 变差,说明网络在平滑偏振细节。早停条件建议同时看 PSNR 和 DoLP MAE,两者连续几个 epoch 都不改善再停。
def train_one_epoch(model, loader, optimizer, device, cons_weight=0.2): model.train() total = 0.0 for noisy, clean in loader: noisy, clean = noisy.to(device), clean.to(device) pred = model(noisy) loss = torch.mean((pred - clean) ** 2) loss = loss + cons_weight * polarization_consistency_loss(pred, clean) optimizer.zero_grad() loss.backward() optimizer.step() total += loss.item() * noisy.size(0) return total / len(loader.dataset) def validate(model, loader, device): model.eval() psnr_list, dolp_list = [], [] with torch.no_grad(): for noisy, clean in loader: noisy, clean = noisy.to(device), clean.to(device) pred = model(noisy).clamp(-1e3, 1e3) mse = torch.mean((pred - clean) ** 2).item() psnr_list.append(10.0 * np.log10(1.0 / (mse + 1e-12))) s0p, s1p, s2p = pred[:, 0], pred[:, 1], pred[:, 2] s0c, s1c, s2c = clean[:, 0], clean[:, 1], clean[:, 2] dolp_p = torch.sqrt(s1p ** 2 + s2p ** 2 + 1e-6) / (s0p.abs() + 1e-6) dolp_c = torch.sqrt(s1c ** 2 + s2c ** 2 + 1e-6) / (s0c.abs() + 1e-6) dolp_list.append(torch.mean(torch.abs(dolp_p - dolp_c)).item()) return float(np.mean(psnr_list)), float(np.mean(dolp_list))cons_weight控制偏振一致性损失的权重,第一版从 0.2 起步。validate里的clamp只是防数值爆炸,不要把它当成物理约束;真正的物理投影放在后处理里做。PSNR 按归一化尺度算,如果数据没归一化,这里要换成对应的peak。早停的patience取 10 到 15 个 epoch,按数据量调整。
优化器用常见配置就行:学习率 1e-3,batch size 8 到 16,权重衰减 1e-4。学习率太大时 DoLP 误差会先降后升,看到这种曲线先把学习率减半。
4.3 验证与可视化:看残差图、DoLP 误差图和 AoP 缠绕
验证阶段不要只看一张 PSNR 表。残差图能看出噪声残留是均匀的还是集中在边缘,DoLP 误差图能看出弱偏振区域有没有被抹平,AoP 图能看出角度缠绕有没有处理错。下面这段代码把 S0 的 GT、预测和误差并排画出来,换成 DoLP 和 AoP 同理。
import matplotlib.pyplot as plt def show_error_maps(gt, pred, name="sample"): # gt/pred: [3, H, W],通道 S0/S1/S2 err = np.abs(gt - pred) fig, axes = plt.subplots(1, 3, figsize=(12, 4)) for ax, img, title in zip( axes, [gt[0], pred[0], err[0]], ["GT S0", "Pred S0", "Abs error"] ): ax.imshow(img, cmap="gray") ax.set_title(title) ax.axis("off") fig.tight_layout() fig.savefig(f"{name}_error.png", dpi=150) plt.close(fig)gt和pred的通道顺序要一致,否则误差图没有意义。dpi=150够看细节,批量验证时别每张都存,抽场景存就行。DoLP 误差图建议用热力图,AoP 用色相图并把角度范围固定到 [-π/2, π/2],不然不同样本之间没法比。
| 检查项 | 看什么 | 常见异常 |
|---|---|---|
| 残差图 | 噪声是否均匀 | 边缘亮圈说明过平滑 |
| DoLP 误差图 | 弱偏振区是否被抹平 | 低 DoLP 区域大片红 |
| AoP 图 | 角度是否缠绕 | 黑白跳变说明周期没处理 |
| 分区域统计 | 高/低 DoLP 分开算 | 整体指标掩盖局部退化 |
5. 踩坑与排查:偏振去噪最常见的五类翻车
5.1 通道顺序错位:I/Q/U 反了,指标全绿但图是错的
现象:训练 loss 正常下降,PSNR 也不低,但 DoLP 图看起来像随机噪声,AoP 方向跟实际偏振片旋转对不上。原因:拆分 2×2 马赛克时把 45° 和 135° 的顺序搞反,或者数据集本身是 0/90/45/135 排列,代码按 0/45/90/135 拆。S1 和 S2 的定义跟着错,DoLP 幅值可能还碰巧接近,方向完全反。
解决:拿一张均匀白光加线偏振片的图,旋转偏振片 0°、45°、90°、135°,看四个通道的响应是否按预期正弦变化。再用同一场景跑一遍 S1/S2 的符号检查:S1 在 0° 亮、90° 暗时为正,S2 在 45° 亮、135° 暗时为正。通道顺序定下来之前不要开始训练,否则后面调参全是黑匣子。
5.2 噪声水平估计偏差:高斯假设把泊松噪声抹平
现象:暗区去噪很干净,亮区反而残留噪声;或者亮区干净,暗区细节全没。原因:用固定 sigma 的高斯噪声训练,真实数据是泊松-高斯分布,方差随信号变化。网络在训练分布上表现好,换到真实场景就偏。
解决:先拍暗帧估计读出噪声,再在均匀亮板上估计散粒噪声,拟合一个方差随强度变化的曲线。仿真加噪时按这个曲线采样,别只用固定 sigma。验证集里单独统计暗区和亮区的残差,如果两边差异大,说明噪声模型不对。
5.3 角度缠绕与 DoLP 溢出:AoP 不能直接算 MSE
现象:AoP 的 MSE 大得离谱,但看图方向明明差不多。原因:AoP 有 π 周期,-89° 和 91° 在物理上接近,直接相减会得到 180° 的误差。DoLP 溢出则是 S0 接近 0 时除法放大噪声,出现大于 1 的值。
解决:AoP 误差先映射到单位圆再算,代码里用angle(exp(1j*2*(pred-gt)))/2。DoLP 计算时给 S0 加 eps,评估时用 S0 掩码去掉暗背景。后处理阶段把 DoLP 截断到 [0,1],但截断前先记录溢出比例,比例高说明去噪把 S0 压得太狠或者 S1/S2 被放大。
5.4 数据泄漏与过度调参:验证集不能来自同一场景
现象:验证集 PSNR 很高,换一批数据就崩;或者调参时每次换随机种子指标波动很大。原因:同一场景的相邻帧被随机分到训练和验证集,网络记住了场景纹理而不是去噪规律。小数据集上反复调参也会让验证集失去参考价值。
解决:按场景或拍摄批次划分,训练集、验证集、测试集的材料和光照尽量不重叠。调参阶段固定一个小的验证子集,最终指标在独立测试集上跑一次。数据量少的时候,少调网络深度,多调噪声模型和数据增强。
5.5 保存位深与归一化:8 位截断吃掉弱偏振信号
现象:训练时指标正常,保存成图片后 DoLP 图出现色带,弱偏振区域变成一片平。原因:S1/S2 有负值,存成 uint8 时负值被截断;或者训练时归一化到 [0,1],推理后忘记反归一化,保存时又乘错系数。
解决:中间结果用 float32 的.npy存,可视化再转 8 位。归一化参数按通道记录,推理后原样反算。保存 16 位图时确认写入库没有自动截断,最好读回来抽查最小值和最大值。弱偏振信号本来就在低位,8 位截断一次就找不回来了。
6. 用偏振一致性做后处理:一个不涨 PSNR 但能救下游的检查
去噪网络输出三通道 Stokes 分量后,直接算 DoLP 经常出现大于 1 的像素,尤其是在暗区或者高反射边缘。这个现象不影响 PSNR,但会让下游的材质分类、偏振度阈值分割翻车。我一般加一步物理投影:把 S0 压到非负,再把 S1/S2 按比例缩到 DoLP 不超过 1。它不改变整体结构,只是把不可能存在的偏振状态拉回可行域。
def project_to_physical(S0, S1, S2, eps=1e-6): # 投影到物理可行域:S0 > 0 且 DoLP <= 1 S0 = np.maximum(S0, eps) dolp = np.sqrt(S1 ** 2 + S2 ** 2) / (S0 + eps) scale = np.minimum(1.0, 1.0 / (dolp + eps)) return S0, S1 * scale, S2 * scaleeps防止暗区除零,scale只缩小不放大,避免把噪声抬成偏振信号。投影后再算一次 DoLP 溢出比例,正常应该接近 0;如果还是很高,回去查通道顺序和 S0 的 1/2 因子。这个后处理对 PSNR 通常没有提升,甚至可能略降,但对 DoLP MAE 和下游阈值稳定性有帮助。
| 自检项 | 操作 | 通过标准 |
|---|---|---|
| 通道顺序 | 旋转偏振片看四通道响应 | 按正弦变化,符号正确 |
| 位深 | 读回保存的 npy 看范围 | 负值未丢,最大值未截断 |
| DoLP 溢出 | 投影前后统计大于 1 的比例 | 投影后接近 0 |
| AoP 缠绕 | 用复指数算角度误差 | 误差集中在真实偏差上 |
| 场景泄漏 | 按场景查训练/验证交集 | 无同场景跨集 |
我踩过最亏的一次,是验证集 PSNR 涨了 1.2 dB,结果 DoLP 图在低偏振区域全是噪点,下游分割直接崩。从那以后我每次跑完偏振去噪,都强制把物理投影和 AoP 缠绕检查走一遍,再谈指标。希望帮到你。
本文还有配套的精品资源,点击获取