File size: 5,106 Bytes
efaa3d7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DepthART 两路深度输出的相关性/一致性对比。
支持三种输入格式,自动识别:
- 汇总 NPZ(names/depths),如 ONNX 脚本 --dump-npz 生成的文件
- 分块汇总目录(*.npz 中含 names/depths)
- 旧的逐图 NPZ 目录(<图片名>.npz 中含 depth)
指标(逐图计算后汇总 mean/p50/p90/min):
pearson 皮尔逊相关系数
spearman 斯皮尔曼秩相关
cosine 余弦相似度
mae / rmse 原始量纲误差
aligned_* 最小二乘 scale-shift 对齐后的误差
rel_error mae / mean(|ref|)
用法:
../../venv/bin/python src/tool/compare_depth_outputs.py \
--ref results/nyu654/fp32/npz --pred results/nyu654/axmodel \
--out results/nyu654/eval/corr_fp32_axmodel.json
"""
import argparse
import json
from pathlib import Path
import numpy as np
from scipy.stats import spearmanr
from tqdm import tqdm
def load_predictions(path):
path = Path(path)
if path.is_file():
files = [path]
elif path.is_dir():
files = sorted(path.glob('*.npz'))
if not files:
raise ValueError(f'目录中没有 NPZ: {path}')
else:
raise FileNotFoundError(f'路径不存在: {path}')
preds = {}
for fp in files:
with np.load(fp, allow_pickle=False) as data:
if 'depths' in data and 'names' in data:
for name, depth in zip(data['names'], data['depths']):
preds[str(name)] = np.asarray(depth)
elif 'depth' in data:
preds[fp.name[:-len('.npz')]] = np.asarray(data['depth'])
else:
raise ValueError(f'无法识别的 NPZ: {fp}')
return preds
def compare(ref, pred):
a = np.asarray(ref, dtype=np.float64).ravel()
b = np.asarray(pred, dtype=np.float64).ravel()
if a.shape != b.shape:
raise ValueError(f'shape 不一致: {a.shape} vs {b.shape}')
diff = b - a
mae = float(np.mean(np.abs(diff)))
rmse = float(np.sqrt(np.mean(diff ** 2)))
cosine = float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-12))
pearson = float(np.corrcoef(a, b)[0, 1])
spearman = float(spearmanr(a, b).statistic)
slope, offset = np.polyfit(a, b, 1)
aligned = b - (slope * a + offset)
return {
'pearson': pearson,
'spearman': spearman,
'cosine': cosine,
'mae': mae,
'rmse': rmse,
'rel_error': mae / (np.mean(np.abs(a)) + 1e-12),
'scale': float(slope),
'offset': float(offset),
'aligned_mae': float(np.mean(np.abs(aligned))),
'aligned_rmse': float(np.sqrt(np.mean(aligned ** 2))),
}
def summarize(rows):
keys = ('pearson', 'spearman', 'cosine', 'rel_error', 'aligned_rmse')
out = {}
for k in keys:
values = np.array([r[k] for r in rows])
out[k] = {'mean': float(values.mean()), 'p50': float(np.percentile(values, 50)),
'p90': float(np.percentile(values, 90)), 'min': float(values.min())}
return out
def main():
parser = argparse.ArgumentParser(description='两路深度输出相关性对比')
parser.add_argument('--ref', required=True, help='参考输出(基准后端)')
parser.add_argument('--pred', required=True, help='待比较输出')
parser.add_argument('--out', help='结果 JSON 路径')
args = parser.parse_args()
ref = load_predictions(args.ref)
pred = load_predictions(args.pred)
names = sorted(set(ref) & set(pred))
if not names:
raise ValueError('两路输出没有同名样本')
missing = (len(ref) - len(names)) + (len(pred) - len(names))
if missing:
print(f'警告: {missing} 个样本名称不匹配,已忽略')
rows = []
for name in tqdm(names, desc='compare', ncols=80):
rows.append({'name': name, **compare(ref[name], pred[name])})
summary = summarize(rows)
low = [r for r in rows if r['pearson'] < 0.9]
worst = sorted(rows, key=lambda r: r['pearson'])[:5]
report = {
'ref': str(args.ref), 'pred': str(args.pred),
'num_samples': len(rows),
'summary': summary,
'pearson_below_0.9': len(low),
'worst': [{'name': r['name'], 'pearson': r['pearson'],
'mae': r['mae'], 'aligned_rmse': r['aligned_rmse']} for r in worst],
'per_image': rows,
}
print(f'\n样本数 {len(rows)},pearson<0.9 的 {len(low)} 张')
print(f'{"指标":<14}{"mean":>12}{"p50":>12}{"p90":>12}{"min":>12}')
for k, v in summary.items():
print(f'{k:<14}{v["mean"]:>12.4f}{v["p50"]:>12.4f}{v["p90"]:>12.4f}{v["min"]:>12.4f}')
print('\n最差 5 张:')
for r in worst:
print(f' {r["name"]:<32} pearson={r["pearson"]:.4f} mae={r["mae"]:.4f}')
if args.out:
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
with open(args.out, 'w') as fp:
json.dump(report, fp, indent=2)
print(f'\n结果已保存: {args.out}')
if __name__ == '__main__':
main()
|