DepthART / src /tool /inspect_nyu_depth_v2.py
sunyijie
first commit
efaa3d7
Raw History Blame Contribute Delete
8.36 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NYU Depth V2 (labeled) 数据集查看与导出工具。
nyu_depth_v2_labeled.mat 是 MATLAB v7.3 (HDF5) 格式,scipy.io.loadmat 无法读取,
这里统一用 h5py 按需读取,避免一次性载入 2.8GB。
数据内容(N = 1449,图像为竖版 640x480,H=640, W=480,RGB 与深度已对齐):
images [N, 3, 640, 480] uint8 RGB
depths [N, 640, 480] float32 已填补深度,单位米
rawDepths [N, 640, 480] float32 原始深度,0 表示缺失
labels [N, 640, 480] uint16 语义标签,0=未标注,1..894 对应 names
instances [N, 640, 480] uint8 实例标签,0=无实例
names [1, 894] cell 类别名(1-based 下标即 label id)
namesToIds containers.Map MATLAB Map,h5py 无法直接解析
scenes [1, N] cell 场景名,如 kitchen_0004
sceneTypes [1, N] cell 场景类型,如 kitchen
rawRgbFilenames [1, N] cell 原始 RGB 文件名
rawDepthFilenames [1, N] cell 原始深度文件名
accelData [4, N] float32 加速度计数据
用法(在 hs/DepthART 目录下执行):
# 打印结构、场景/类别统计和深度数值范围(默认抽样 step=50)
../../venv/bin/python src/tool/inspect_nyu_depth_v2.py --mat ../../datasets/nyu_depth_v2_labeled.mat
# 全量扫描统计(较慢)
... --mat ../../datasets/nyu_depth_v2_labeled.mat --full-stats
# 列出全部 894 个类别名
... --mat ... --list-classes
# 单样本导出:rgb.png / depth.npy / depth_vis.png / raw_depth.npy
... --mat ... --index 0 --out-dir results/nyu_demo --with-labels
# 批量导出,例如某场景全部帧
... --mat ... --export-dir results/nyu_export --scene kitchen_0004 --with-labels
# 批量导出前 50 张
... --mat ... --export-dir results/nyu_export --count 50
"""
import argparse
import collections
from pathlib import Path
import cv2
import h5py
import numpy as np
from tqdm import tqdm
# 深度可视化统一范围(米),超出部分截断
DEPTH_VIS_RANGE = (0.5, 10.0)
def decode_cell(f, ref):
"""把 MATLAB cell 中的 char 数组解码为 str。"""
return ''.join(chr(c) for c in np.asarray(f[ref]).ravel())
def read_cell_list(dset):
return [decode_cell(dset.file, dset[0, i]) for i in range(dset.shape[1])]
def print_info(f, stride, full_stats):
print('== 变量 ==')
for k in f.keys():
if k.startswith('#'):
continue
o = f[k]
line = f'{k:20s} shape={str(o.shape):24s} dtype={o.dtype}'
if o.compression:
line += f' compression={o.compression}'
print(line)
n = f['depths'].shape[0]
h, w = f['depths'].shape[1:]
names = read_cell_list(f['names'])
scenes = read_cell_list(f['scenes'])
scene_types = read_cell_list(f['sceneTypes'])
print('\n== 规模 ==')
print(f'样本数 {n}')
print(f'分辨率 W={w}, H={h}(data layout 为 [N, C, H, W])')
print(f'场景数 {len(set(scenes))}')
print(f'场景类型数 {len(set(scene_types))}')
print(f'语义类别数 {len(names)}(label id 1..{len(names)},0 为未标注)')
print('场景类型分布 ' + ', '.join(f'{k}:{v}' for k, v in
collections.Counter(scene_types).most_common()))
idx = range(0, n) if full_stats else range(0, n, stride)
idx = list(idx)
print(f'\n== 数值统计({"全量" if full_stats else f"抽样 {len(idx)} 张, step={stride}"}) ==')
d_min, d_max = np.inf, -np.inf
raw_missing = 0
raw_total = 0
cls_count = collections.Counter()
inst_max = 0
for i in tqdm(idx, desc='scan', ncols=80):
d = f['depths'][i]
r = f['rawDepths'][i]
d_min = min(d_min, float(d.min()))
d_max = max(d_max, float(d.max()))
raw_missing += int((r == 0).sum())
raw_total += r.size
for c in np.unique(f['labels'][i]):
if c > 0:
cls_count[int(c)] += 1
inst_max = max(inst_max, int(f['instances'][i].max()))
print(f'depths 范围 [{d_min:.4f}, {d_max:.4f}] m')
print(f'rawDepths 缺失 {raw_missing / raw_total * 100:.2f}% (值为 0)')
print(f'出现类别数 {len(cls_count)} / {len(names)}')
print(f'instances 最大 {inst_max}')
print('\n最常见类别 ' + ', '.join(
f'{names[c - 1]}({cnt})' for c, cnt in cls_count.most_common(15)))
def depth_to_vis(depth, raw=False):
lo, hi = DEPTH_VIS_RANGE
norm = np.clip((depth - lo) / (hi - lo), 0.0, 1.0)
vis = cv2.applyColorMap((norm * 255).astype(np.uint8), cv2.COLORMAP_TURBO)
if raw:
vis[depth <= 0] = 0
return vis
def export_sample(f, index, out_dir, with_labels):
entry = out_dir / f'{index:05d}_{decode_cell(f, f["scenes"][0, index])}'
entry.mkdir(parents=True, exist_ok=True)
rgb = np.asarray(f['images'][index]).transpose(1, 2, 0) # CHW -> HWC, RGB
depth = np.asarray(f['depths'][index], dtype=np.float32)
raw = np.asarray(f['rawDepths'][index], dtype=np.float32)
cv2.imwrite(str(entry / 'rgb.png'), cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR))
cv2.imwrite(str(entry / 'depth_vis.png'), depth_to_vis(depth))
cv2.imwrite(str(entry / 'raw_depth_vis.png'), depth_to_vis(raw, raw=True))
np.save(entry / 'depth.npy', depth)
np.save(entry / 'raw_depth.npy', raw)
if with_labels:
labels = np.asarray(f['labels'][index])
np.save(entry / 'labels.npy', labels)
# 语义标签着色:按 label id 的哈希色,0 保持黑色
rng = np.random.default_rng(0)
lut = rng.integers(40, 255, size=(65536, 3), dtype=np.uint8)
lut[0] = 0
cv2.imwrite(str(entry / 'labels_vis.png'), lut[labels])
return entry
def main():
parser = argparse.ArgumentParser(description='查看/导出 NYU Depth V2 labeled 数据集')
parser.add_argument('--mat', required=True, help='nyu_depth_v2_labeled.mat 路径')
parser.add_argument('--list-classes', action='store_true', help='打印全部类别名及 id')
parser.add_argument('--index', type=int, help='导出单个样本的下标')
parser.add_argument('--out-dir', help='--index 模式的输出目录')
parser.add_argument('--export-dir', help='批量导出目录')
parser.add_argument('--count', type=int, help='批量导出数量,默认全部')
parser.add_argument('--step', type=int, default=1, help='批量导出间隔,默认 1')
parser.add_argument('--scene', help='只导出指定场景,如 kitchen_0004')
parser.add_argument('--with-labels', action='store_true', help='同时导出语义标签')
parser.add_argument('--full-stats', action='store_true', help='统计时扫描全部样本')
parser.add_argument('--stats-stride', type=int, default=50, help='抽样统计间隔,默认 50')
args = parser.parse_args()
with h5py.File(args.mat, 'r') as f:
if args.list_classes:
names = read_cell_list(f['names'])
for i, name in enumerate(names, 1):
print(f'{i:4d} {name}')
return
if args.index is not None:
out_dir = Path(args.out_dir or '.')
entry = export_sample(f, args.index, out_dir, args.with_labels)
print(f'已导出 {entry}')
return
if args.export_dir:
n = f['depths'].shape[0]
indices = list(range(0, n, args.step))
if args.scene:
scenes = read_cell_list(f['scenes'])
indices = [i for i in indices if scenes[i] == args.scene]
print(f'场景 {args.scene} 命中 {len(indices)} 张')
if args.count:
indices = indices[:args.count]
out_dir = Path(args.export_dir)
for i in tqdm(indices, desc='export', ncols=80):
export_sample(f, i, out_dir, args.with_labels)
print(f'已导出 {len(indices)} 张到 {out_dir}')
return
print_info(f, args.stats_stride, args.full_stats)
if __name__ == '__main__':
main()