简介:面向图像处理与计算机视觉方向的研究人员与技术开发者,这份资料聚焦缺乏原始参考图像时的质量评估难题,给出了一套基于复小波变换的无参考图像质量评价算法设计方案。内容围绕从复小波系数中提取有效质量特征展开,覆盖模糊、噪声、压缩失真等多种退化类型的影响,并系统比较统计特征、结构特征与纹理特征在评价中的有效性和鲁棒性;在此基础上借助支持向量机、神经网络等机器学习与深度学习方法构建评价模型,利用标注重构的数据集完成训练优化与性能验证,理论探索与实验验证并重。资源包为单个 docx 文档,约 11KB,篇幅紧凑、条理清晰,便于通读与二次整理。目前已有 63 人学习,适合希望快速掌握无参考质量评价技术路线、搭建实验框架或支撑论文与项目写作的读者参考。
1. 从压缩失真图到复小波相位:无参考图像质量评价要解决什么
在 ISP 图像处理产线或遥感图像处理的下游,经常遇到这样的局面:手上只有一张已经压缩、去噪、超分或传输丢包后的图,原始参考图根本拿不到,却要判断它到底“坏到什么程度”。无参考图像质量评价要做的就是这件事,而基于复小波变换的算法设计,正好卡在“没有参考”和“要稳定打分”之间。实小波对平移敏感,图像轻微移动一个像素,系数能量就跳变,打分跟着抖;复小波变换把每一层分解成幅度和相位,六个方向子带对边缘、纹理、模糊、块效应的响应更接近人眼。做算法设计时,把复小波系数统计量喂给回归器,就能在 LIVE、TID2013 这类库上训练出可用的 NR-IQA 模型。这套方案适合做图像处理算法、ISP 调优、遥感图像处理质检,以及需要在 OpenCV 图像处理项目里加一道质量闸门的工程师。
2. 复小波变换在图像处理中的分解细节与 NR-IQA 特征构造
2.1 实小波在图像质量评价中的三个硬伤
实小波分解在图像压缩里很成熟,但直接拿去做无参考质量评价,会碰到三个绕不开的问题。第一是平移敏感:输入图像整体移动一个像素,实小波的细节子带能量可能变化百分之几十,而人眼对同一张图轻微平移的感受几乎没变,这导致同一失真程度打出两个分数。第二是方向选择性差:二维可分离实小波只有水平、垂直、对角三个方向,对 15°、45°、75° 这类斜向边缘的响应会混在一起,而压缩块效应和振铃恰恰常出现在斜向纹理附近。第三是缺少相位:实小波只有幅度,无法描述边缘两侧的相位跳变,而相位对结构偏移、模糊扩散和振铃非常敏感。
这三个硬伤在遥感图像处理里尤其明显。遥感图里大量斜向道路、田埂和建筑边缘,实小波会把它们拆到不相关的子带,统计量自然不稳定。常见做法是先做一层实小波做基线,再用复小波补上方向和相位特征,最后用回归器把两类特征一起学。
2.2 双树复小波 DTCWT 的 6 方向分解与近似平移不变性
双树复小波变换(Dual-Tree Complex Wavelet Transform,DTCWT)用两棵并行的实小波滤波器组,一路作为实部,另一路作为虚部,两路之间满足 Hilbert 对关系。对二维图像,每一层会输出一个低通子带和六个方向的高通子带,方向大致覆盖 ±15°、±45°、±75°。每个高通系数是复数,实部和虚部共同决定幅度和相位。
近似平移不变性来自两棵树的互补:输入平移半个像素时,一棵树的响应下降,另一棵树的响应上升,合成复系数的幅度变化远小于实小波。这个性质对 NR-IQA 很关键,因为无参考场景下没有原始图做对齐,任何平移敏感的统计量都会变成噪声。DTCWT 的常用参数包括分解层数nlevels、双树滤波器对biort和qshift。图像尺寸 512×512 时,nlevels=3到4比较常见;再往上,最粗层只剩几十个像素,统计量方差急剧变大,反而拖累回归。
提示:DTCWT 的完美重构需要两棵树滤波器满足特定延迟条件,自己随手拼两套
dbN通常不满足,直接用成熟实现更稳。
2.3 从复系数到可回归特征:幅度、相位、相对相位与广义高斯参数
复数子带不能直接丢给回归器,必须压成固定长度向量。我一般把特征分成四组:低通幅度统计、高通幅度统计、相位统计、相对相位统计。低通反映亮度和对比度整体退化;高通幅度反映边缘能量、噪声和模糊;相位均值与标准差反映结构偏移;相邻方向相对相位反映边缘方向一致性。
| 特征类别 | 典型符号 | 维度估算(3 层) | 对哪些失真敏感 |
|---|---|---|---|
| 低通幅度 | lo_mean、lo_std、lo_p90 | 3 | 亮度偏移、对比度压缩 |
| 高通幅度 | mag_mean、mag_std、mag_e2、mag_p90、mag_p10 | 6 方向 × 3 层 × 5 = 90 | 模糊、噪声、块效应 |
| 绝对相位 | ph_absmean、ph_std | 6 × 3 × 2 = 36 | 振铃、结构偏移 |
| 相对相位 | relph_mean、relph_std | 5 × 3 × 2 = 30 | 斜向边缘、方向混叠 |
| 广义高斯拟合 | gg_shape、gg_scale | 6 × 3 × 2 = 36 | 重尾程度、稀疏性 |
广义高斯分布拟合高通幅度是 NR-IQA 里的经典做法。自然图像的复小波幅度通常呈尖峰重尾,模糊会让分布变平,噪声会让尾部变厚。形状参数和尺度参数各取一个,比直接堆百分位数更紧凑。实际工程里,如果训练样本只有几千张,我会先把维度压到 200 以内,再进回归器;特征维度超过样本量十分之一时,SVR 的泛化会明显变差。
2.4 用 Python 跑通 DTCWT 分解并导出特征
下面这段代码用dtcwt做三层分解,输出一维特征向量。输入统一成uint8单通道,先归一化到[0,1],避免不同位深导致量纲漂移。
import numpy as np import dtcwt def extract_dtcwt_features(gray_u8, nlevels=3): """gray_u8: uint8 单通道图,返回 (特征向量, 特征名列表)""" x = gray_u8.astype(np.float32) / 255.0 trans = dtcwt.Transform2d() pyr = trans.forward(x, nlevels=nlevels) feats, names = [], [] # 最低频低通子带:亮度与对比度退化 lo = np.abs(pyr.lowpass[-1]) feats += [lo.mean(), lo.std(), np.percentile(lo, 90)] names += ['lo_mean', 'lo_std', 'lo_p90'] for lvl, hp in enumerate(pyr.highpasses): for d in range(hp.shape[-1]): c = hp[..., d] # 复数高通系数 mag = np.abs(c) phase = np.angle(c) # 幅度统计:模糊、噪声、块效应都会改变重尾程度 feats += [mag.mean(), mag.std(), np.mean(mag ** 2), np.percentile(mag, 90), np.percentile(mag, 10)] names += [f'l{lvl}_d{d}_mag_mean', f'l{lvl}_d{d}_mag_std', f'l{lvl}_d{d}_mag_e2', f'l{lvl}_d{d}_mag_p90', f'l{lvl}_d{d}_mag_p10'] # 绝对相位:对结构偏移和振铃敏感 feats += [np.mean(np.abs(phase)), np.std(phase)] names += [f'l{lvl}_d{d}_ph_absmean', f'l{lvl}_d{d}_ph_std'] # 相邻方向相对相位:边缘方向一致性 if d > 0: prev = hp[..., d - 1] rel = np.angle(c * np.conj(prev)) feats += [np.mean(np.abs(rel)), np.std(rel)] names += [f'l{lvl}_d{d}_relph_mean', f'l{lvl}_d{d}_relph_std'] return np.asarray(feats, dtype=np.float32), names逻辑说明:dtcwt.Transform2d().forward返回金字塔对象,pyr.highpasses是每层的高通复数数组列表,最后一维是 6 个方向。np.angle(c * np.conj(prev))计算当前方向与前一方向的相位差,能捕捉边缘方向被压缩或模糊打乱的程度。参数nlevels控制层数,层数越多频带越细,但最粗层空间尺寸越小,统计量越不稳。mag_e2是二阶矩,和mag_mean的平方不同,对重尾分布更敏感。若换成pywt的实小波,把pyr.highpasses换成cH/cV/cD即可,但相位特征会全部丢失。
2.5 特征维度爆炸时怎么做初步筛选
三层 DTCWT 加广义高斯拟合,维度很容易到 200 以上。训练集只有一两千张时,先做三步筛选:第一步去掉方差低于1e-6的常量列;第二步按互信息排序,保留前 120 维;第三步用皮尔逊相关系数去掉彼此高于0.95的冗余列。筛选必须在训练折内做,不能在全量数据上做,否则测试集信息会漏进特征选择。筛选后的特征再做StandardScaler,这一步对 SVR 的 RBF 核尤其重要,因为不同子带的幅度量级差几个数量级。
3. 基于复小波特征的无参考图像质量评价算法设计:回归、评估与跨库验证
3.1 分数归一化与失真类型解耦
不同质量库的 MOS 或 DMOS 量纲不一样,LIVE 常用 0 到 100,TID2013 常用 0 到 9,CSIQ 又不同。直接混在一起训练,回归器会先学库的偏置而不是失真。我的做法是每个库内部先做 z-score 或 min-max 归一化到[0,1],跨库实验时再统一映射。更稳的做法是按失真类型分层抽样:JPEG 压缩、JPEG2000、高斯模糊、白噪声、快衰落各占一定比例,避免某一类失真主导损失函数。
复小波特征里,模糊主要压低高通幅度均值,噪声抬高幅度标准差,块效应改变低通与高通的比例。如果训练集里模糊样本占八成,SVR 会倾向把所有图都判成模糊。分层抽样加上每类失真内部归一化,能显著降低这种偏置。遥感图像处理场景还要额外考虑云层和阴影,它们不是传统失真,但会改变低通统计量,最好在训练集里单独标一类。
3.2 回归器选型:SVR、随机森林与浅层 MLP 的取舍
| 回归器 | 适合样本量 | 调参重点 | 跨库稳定性 | 推理耗时 |
|---|---|---|---|---|
| SVR + RBF | 1k 到 20k | C、gamma、epsilon | 较好 | 中 |
| 随机森林 | 5k 以上 | n_estimators、max_depth | 一般 | 低 |
| 浅层 MLP | 20k 以上 | 隐藏层宽度、学习率、早停 | 依赖数据量 | 低 |
| 岭回归 | 任何 | alpha | 差 | 极低 |
我一般先用 SVR 打基线,因为它在千级样本上不容易过拟合,RBF 核也能拟合非线性。C从1到100网格搜,gamma用'scale'起步,epsilon取0.05到0.1。随机森林解释性好,能看特征重要性,但外推能力弱,遇到训练集没覆盖的失真类型容易给出饱和分数。浅层 MLP 在样本上万后可以超过 SVR,但需要早停和权重衰减,否则会把库的偏置背下来。
注意:不要用深度学习直接端到端替代复小波特征,除非样本量过万。小样本下 CNN 很容易学到库的纹理偏置,跨库 SROCC 会掉得很厉害。
3.3 训练/测试划分与 SROCC、PLCC、RMSE 评估脚本
评估 NR-IQA 必须同时看排序相关和绝对误差。SROCC 看单调性,PLCC 看线性相关,RMSE 看绝对偏差。下面用 5 折交叉验证做基线,每折内部先标准化再训练。
import numpy as np from scipy.stats import spearmanr, pearsonr from sklearn.svm import SVR from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline from sklearn.model_selection import KFold def evaluate_nr_iqa(X, y, C=10.0, gamma='scale', epsilon=0.1): kf = KFold(n_splits=5, shuffle=True, random_state=42) srocc, plcc, rmse = [], [], [] for tr, te in kf.split(X): model = make_pipeline( StandardScaler(), SVR(kernel='rbf', C=C, gamma=gamma, epsilon=epsilon) ) model.fit(X[tr], y[tr]) pred = model.predict(X[te]) srocc.append(spearmanr(y[te], pred).correlation) plcc.append(pearsonr(y[te], pred)[0]) rmse.append(np.sqrt(np.mean((y[te] - pred) ** 2))) return float(np.mean(srocc)), float(np.mean(plcc)), float(np.mean(rmse))逻辑说明:make_pipeline把标准化和 SVR 串在一起,保证标准化只在训练折上拟合。KFold的shuffle=True避免数据按失真类型排序造成折间偏差。SROCC用spearmanr的相关系数,PLCC用pearsonr的第一个返回值,RMSE在归一化分数上计算,跨库比较时要注明归一化方式。参数C越大拟合越紧,超过100后训练集误差降但测试集 SROCC 常掉;gamma太大时每个样本影响范围过小,会出现孤岛预测。
3.4 跨库验证与数据增强的注意点
跨库验证是 NR-IQA 最容易被高估的环节。在 LIVE 上训练、在 TID2013 上测试,SROCC 通常会掉 0.1 到 0.2。复小波特征比 CNN 特征掉得少,但也不能忽略失真类型差异。做法是先在源库上训练,再在目标库上取一小部分做线性映射或分位数映射,映射后的 PLCC 更有参考价值。数据增强不要用翻转和旋转来扩样本,因为复小波的六个方向有明确角度含义,旋转会改变方向子带的统计分布。更安全的增强是加不同强度的已知失真,重新生成标签,或者对同一张图做不同质量的 JPEG 压缩,构造连续分数。
4. OpenCV、Matlab 与 FPGA 图像处理链路中的复小波 NR-IQA 落地
4.1 OpenCV 读图到 DTCWT 前处理:灰度、尺寸与边界处理
OpenCV 图像处理项目里最常见的坑是分辨率和通道不统一。DTCWT 特征对尺度敏感,同一张图缩到 256 和 512,高通幅度均值会差很多。我一般把短边统一缩到 512,再中心裁剪 256×256 做推理。滑窗质检时改成重叠切片,每片单独打分再取中位数。
import cv2 import numpy as np from extract_dtcwt import extract_dtcwt_features def iqa_score(model, img_path, crop=256, short_side=512): img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) if img is None: raise FileNotFoundError(img_path) h, w = img.shape scale = short_side / min(h, w) if scale < 1: img = cv2.resize(img, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA) ch, cw = img.shape y0 = max(0, ch // 2 - crop // 2) x0 = max(0, cw // 2 - crop // 2) patch = img[y0:y0 + crop, x0:x0 + crop] feat, _ = extract_dtcwt_features(patch, nlevels=3) return float(model.predict(feat.reshape(1, -1))[0])逻辑说明:cv2.IMREAD_GRAYSCALE直接读单通道,省去cvtColor。INTER_AREA在缩小图时比默认的双线性更少引入伪高频,避免把缩放伪影当成失真。crop和short_side必须和训练时一致,训练用 256 推理用 512 会让分数整体偏移。边界处理用中心裁剪而不是补零,补零会在子带边缘产生强烈伪响应,拉低分数。
4.2 Matlab 复现与数值对齐:先对齐灰度再对齐系数
Matlab 图像处理大作业里常用dwt2做实小波基线。要复现 Python 的复小波特征,先对齐灰度转换和尺寸,再对齐滤波器。Matlab 没有内置 DTCWT,常见做法是调用第三方 DTCWT 函数,或者用dwt2对齐低层幅度特征,相位特征单独用 Python 算。下面这段只做灰度对齐和实小波基线。
img = imread('test.png'); if size(img, 3) == 3 img = rgb2gray(img); end img = im2double(img); img = imresize(img, [512 NaN]); % 短边统一到 512 [cA, cH, cV, cD] = dwt2(img, 'db2'); fprintf('cA mean=%.6f, cH std=%.6f\n', mean(abs(cA(:))), std(cH(:)));逻辑说明:rgb2gray的权重和 OpenCV 不完全一致,跨语言对齐时最好统一用同一套公式或直接存灰度图。im2double把uint8转到[0,1],和 Python 的/255.0对齐。dwt2的'db2'只是基线,复小波要用 DTCWT 滤波器组,不能直接换成'db2'两棵树。imresize的插值默认是双三次,和 OpenCV 的INTER_AREA不同,跨语言对比时要固定插值方式。
4.3 FPGA 图像处理侧的定点化:只保留低层幅度特征
FPGA 图像处理链路做 NR-IQA,通常不追求完整复小波,而是把低层幅度特征定点化后做阈值报警。FPGA 实现 DTCWT 的复数乘法开销大,常见折中是只保留第一层和第二层的幅度均值、标准差,相位特征全部砍掉。定点位宽和资源关系如下表。
| 特征 | 数据位宽 | 累加位宽 | 资源占用 | 说明 |
|---|---|---|---|---|
| 第一层幅度均值 | 12 bit | 24 bit | 低 | 对模糊和噪声敏感 |
| 第一层幅度标准差 | 16 bit | 32 bit | 中 | 需要平方和 |
| 第二层幅度均值 | 12 bit | 24 bit | 低 | 对块效应敏感 |
| 第三层幅度均值 | 12 bit | 24 bit | 低 | 对整体对比度敏感 |
| 相位均值 | 不建议 | — | 高 | 需要 CORDIC,资源翻倍 |
定点化时先统计训练集特征的动态范围,取99.9分位数作为饱和点。幅度均值用 12 bit 小数,标准差用 16 bit,累加器留 2 倍余量。FPGA 输出分数不再做 SVR,而是把定点特征和阈值的线性组合固化,分数分辨率 8 bit 足够报警。
4.4 排错清单:分数抖动、方向混叠与跨库崩掉
| 现象 | 可能原因 | 检查方法 | 处理 |
|---|---|---|---|
| 同一图分数跳变 | 尺寸或灰度不统一 | 固定短边和插值 | 训练推理同参数 |
| 斜向纹理分数异常 | 方向子带混叠 | 看 6 方向特征分布 | 加相对相位特征 |
| 跨库 SROCC 大跌 | 失真类型分布偏移 | 按失真分层统计 | 分层抽样加映射 |
| 推理极慢 | 每片都做 DTCWT | 计时分解与回归 | 特征缓存,滑窗复用 |
| 分数饱和 | 训练集覆盖不足 | 看预测直方图 | 补失真类型或降 C |
排错时优先看特征分布,而不是直接调回归器。把训练集和测试集的同一特征画分位数图,如果某个子带的 90 分位数差两倍以上,问题在预处理而不是模型。
5. 复小波 NR-IQA 的工程化技巧:特征缓存、增量标定与阈值报警
5.1 特征缓存与滑窗推理
产线质检常用滑窗,同一张图重叠切片后 DTCWT 会重复计算。我一般把图像先按256步长128切片,每片特征算完存成float16的.npy,文件名带图像 ID 和坐标。回归阶段只读特征,不重复分解。这样单张 4000×3000 的遥感图,全图分解一次加滑窗聚合,比分片重复分解快三到五倍。缓存目录按日期分桶,避免单目录文件过多。
5.2 增量标定与阈值报警
新相机、新压缩参数上线后,原有回归器会偏。增量标定不需要重新训练 SVR,只需在目标域上取 50 到 100 张人工标定图,计算预测分数和主观分数的线性映射,把斜率和截距写进配置文件。阈值报警用两个门限:低于T_low直接拦截,高于T_high放行,中间区间送人工复核。门限按业务误检率反推,通常先取训练集分数的5%和95%分位数,再按误检率微调。
5.3 与 ISP 反馈闭环的验证方法
把 NR-IQA 分数接回 ISP 调优时,验证要闭环:固定一组场景,分别用旧参数和新参数各拍 200 张,比较复小波特征里高通幅度均值和相对相位标准差的变化,再看 NR-IQA 分数是否同向。常见误区是只看平均分,不看分布,结果整体分数升了但暗部噪声被放大。验证脚本里至少保留三个统计量:分数中位数、分数标准差、低分样本占比。复小波特征缓存可以直接复用,不增加额外采集成本。最后把每次调参前后的特征分布和分数分布落盘,作为下一轮阈值更新的依据。
本文还有配套的精品资源,点击获取