ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linemod_preprocessed 数据底座:从 DenseFusion 到 PVN3D 的 6D 位姿估计复现指南

2026/10/5 7:37:47 拓冰建站 浏览量
Linemod_preprocessed 数据底座:从 DenseFusion 到 PVN3D 的 6D 位姿估计复现指南 简介这份资源面向三维点云与6D位姿估计方向的研究者和学习者提供DenseFusion、PVN3D等经典算法常用的Linemod_preprocessed数据集及配套check_points解决从BOP官网获取原始数据后仍需自行预处理、且Google云盘下载易中断的痛点。资源包共1个文件为docx文档大小约208KB其中整理了Linemod_preprocessed.zip与trained_checkpoints.rar的百度云获取方式并附提取码方便读者直接取得数据集与预训练权重。文档还说明了原Google云盘下载易断线的问题及IDM配合代理的下载思路并提示链接失效可留言更新便于长期维护。目前已有1467人学习下载适合刚接触点云位姿估计、需要快速搭建训练与评测环境的中高级读者可省去繁琐的数据准备环节把精力集中在模型复现与实验对比上。1. Linemod_preprocessed 到底解决了什么从 DenseFusion 到 PVN3D 的数据底座如果你最近在复现 6D 位姿估计的论文大概率会在 DenseFusion 或 PVN3D 的 README 里撞见Linemod_preprocessed这个名字。它不是什么新算法而是把 BOP 官网那套 Linemod 原始数据重新切分、对齐、打包之后的一份“开箱即用”版本——13 个物体、每个物体约 1200 帧 RGB-D 序列外加相机内参、物体 3D 模型PLY、位姿标注和掩码。DenseFusion 用它训练像素级特征融合PVN3D 用它做关键点投票两篇论文的精度数字都建立在这份数据之上。换句话说你复现不出论文结果很多时候不是网络结构写错了而是数据加载这一步就没对齐。这份资源把Linemod_preprocessed.zip和trained_checkpoints.rar一起打包前者是数据本体后者是配套权重适合正在跑 6D 位姿 pipeline、需要一份“能直接喂进 DataLoader”的从业者。下面按“数据长什么样 → 怎么接进训练 → 坑在哪 → 怎么验证”的顺序拆开讲。2. 拆开 Linemod_preprocessed目录结构、标注格式与坐标系约定2.1 目录树与每个文件的实际用途拿到压缩包先别急着解压到训练目录花五分钟看清结构能省掉后面几小时的 debug。解压Linemod_preprocessed.zip后典型布局是这样的Linemod_preprocessed/ ├── data/ │ ├── 01/ # 物体 ID01~13 对应 ape、benchvise 等 │ │ ├── rgb/ # 彩色图命名 0000.png ~ 1199.png │ │ ├── depth/ # 16 位深度图单位毫米 │ │ ├── mask/ # 实例掩码255 为前景 │ │ ├── gt.yml # 每帧的位姿与 bbox 标注 │ │ └── info.yml # 相机内参、深度缩放因子 │ └── ... ├── models/ │ ├── obj_01.ply # 物体 CAD 模型单位毫米 │ └── models_info.yml # 每个物体的直径、对称性标记 └── camera.yml # 全局相机参数gt.yml是核心它按帧号索引每帧是一个列表列表里每个 dict 对应一个物体实例字段包括cam_R_m2c3x3 旋转矩阵行优先展开成 9 个数、cam_t_m2c3x1 平移毫米、obj_bbox2D 框。info.yml里的depth_scale通常是 1.0但有些版本是 0.1这个值直接决定你深度图转点云时会不会整体缩放错。models_info.yml里的diameter在计算 ADD 指标时要用别自己拿 PLY 顶点瞎算。2.2 坐标系与单位为什么你的点云总是飘Linemod 的坐标系约定是相机坐标系 Z 轴朝前X 右 Y 下单位毫米物体模型坐标系原点在 CAD 模型质心附近但不同物体不完全一致。cam_R_m2c和cam_t_m2c描述的是“模型坐标系到相机坐标系”的变换即P_cam R * P_model t。很多人写反成P_model R * P_cam t结果可视化时物体飞到相机背后。验证方法很简单取一帧把 PLY 顶点用这个变换投到相机系再用内参投影到像素应该和 RGB 图里的物体重合。如果重合但深度对不上检查depth_scale如果整体偏移一个固定量检查是否把毫米当米用了。2.3 与 BOP 原始 Linemod 的差异BOP 官网的 Linemod 是“原始版”图像命名、标注格式和划分方式跟这份预处理版不同。预处理版做了三件事统一了图像尺寸到 640x480、把标注转成 YAML 方便 Python 读取、固定了训练/测试划分通常每物体前若干帧训练、后若干帧测试。这意味着你直接拿 BOP 的评测脚本跑这份数据索引会对不上。常见做法是训练用这份预处理版评测时如果要报 BOP 官方指标再单独下载 BOP 版做映射。别混用。3. 把数据接进 DenseFusion/PVN3DDataLoader 写法与参数对齐3.1 最小可运行的 Dataset 类下面这段代码是我在复现时常用的骨架直接继承torch.utils.data.Dataset读gt.yml和info.yml返回 RGB、深度、掩码、位姿和物体 ID。注意路径里的物体 ID 是字符串01而不是整数1。import os import cv2 import yaml import numpy as np import torch from torch.utils.data import Dataset class LinemodDataset(Dataset): def __init__(self, root, obj_ids, splittrain, train_ratio0.8): self.root root self.obj_ids [f{i:02d} for i in obj_ids] self.samples [] # (obj_id, frame_id) for oid in self.obj_ids: gt_path os.path.join(root, data, oid, gt.yml) with open(gt_path, r) as f: gt yaml.safe_load(f) frames sorted(gt.keys()) n_train int(len(frames) * train_ratio) use frames[:n_train] if split train else frames[n_train:] for fid in use: self.samples.append((oid, fid)) # 读相机内参只读一次 with open(os.path.join(root, data, self.obj_ids[0], info.yml), r) as f: info yaml.safe_load(f) self.K np.array(info[0][cam_K]).reshape(3, 3) self.depth_scale info[0].get(depth_scale, 1.0) def __len__(self): return len(self.samples) def __getitem__(self, idx): oid, fid self.samples[idx] base os.path.join(self.root, data, oid) rgb cv2.imread(os.path.join(base, rgb, f{int(fid):04d}.png)) rgb cv2.cvtColor(rgb, cv2.COLOR_BGR2RGB) depth cv2.imread(os.path.join(base, depth, f{int(fid):04d}.png), cv2.IMREAD_UNCHANGED) mask cv2.imread(os.path.join(base, mask, f{int(fid):04d}.png), cv2.IMREAD_GRAYSCALE) with open(os.path.join(base, gt.yml), r) as f: gt yaml.safe_load(f) ann gt[int(fid)][0] # 单物体场景取第一个 R np.array(ann[cam_R_m2c]).reshape(3, 3).astype(np.float32) t np.array(ann[cam_t_m2c]).reshape(3, 1).astype(np.float32) return { rgb: torch.from_numpy(rgb).permute(2, 0, 1).float() / 255.0, depth: torch.from_numpy(depth.astype(np.float32) * self.depth_scale), mask: torch.from_numpy((mask 0).astype(np.float32)), R: torch.from_numpy(R), t: torch.from_numpy(t), K: torch.from_numpy(self.K.astype(np.float32)), obj_id: int(oid), }逻辑说明train_ratio0.8是按帧顺序切分不是随机切分——Linemod 相邻帧高度相关随机切分会导致训练集和测试集泄漏精度虚高。depth_scale从info.yml读不要硬编码。mask 0是因为有些版本掩码值不是 255 而是 1。参数方面obj_ids传[1,2,...,13]root指向解压后的Linemod_preprocessed目录。如果你的显存吃紧把 RGB resize 到 480x640 以下但内参要同步缩放。3.2 点云生成与体素下采样DenseFusion 和 PVN3D 都需要从深度图反投影出点云。核心公式是X (u - cx) * Z / fxY (v - cy) * Z / fyZ是深度值米。注意单位Linemod 深度是毫米转点云时要么先除以 1000要么在平移量上保持一致。我一般统一转成米因为 PyTorch3D 和 Open3D 默认米制。def depth_to_pointcloud(depth, K, maskNone): # depth: HxW 毫米, K: 3x3 fx, fy K[0, 0], K[1, 1] cx, cy K[0, 2], K[1, 2] H, W depth.shape u np.arange(W) v np.arange(H) uu, vv np.meshgrid(u, v) Z depth / 1000.0 # 毫米转米 X (uu - cx) * Z / fx Y (vv - cy) * Z / fy pts np.stack([X, Y, Z], axis-1).reshape(-1, 3) if mask is not None: pts pts[mask.reshape(-1) 0] return pts参数说明mask用来剔除背景DenseFusion 训练时只用前景点。下采样用 Open3D 的voxel_down_sample(voxel_size0.01)1 厘米体素在 Linemod 尺度上比较合适太小点数爆炸太大物体结构丢失。PVN3D 的关键点投票对点数更敏感通常保留 20000 个点以内。3.3 与 trained_checkpoints 的对接trained_checkpoints.rar里通常是 DenseFusion 或 PVN3D 在 Linemod 上训好的权重。加载时注意两点一是权重里的物体 ID 映射可能从 0 开始而数据里是 1 开始差一位二是有些 checkpoint 保存的是module.前缀DataParallel加载前要state_dict里去掉前缀。验证权重是否匹配最直接的方法是拿一帧跑前向看预测位姿和 GT 的 ADD 是否在合理范围Linemod 上 DenseFusion 的 ADD 通常在 0.02~0.05 倍直径。4. 避坑与排查五个让复现翻车的细节4.1 现象训练 loss 不降ADD 始终大于 0.1 倍直径原因最常见的是深度图和 RGB 没对齐或者depth_scale读错。Linemod 的深度图有些版本是 16 位 PNGcv2.imread默认读成 8 位会截断。解决读深度必须加cv2.IMREAD_UNCHANGED然后打印几个前景点的深度值正常应该在 300~1500 毫米之间。如果全是 0 或 255说明读法错了。4.2 现象可视化时物体模型和 RGB 里的物体镜像对称原因cam_R_m2c是行优先还是列优先搞反了。YAML 里存的是 9 个数np.array(...).reshape(3,3)默认行优先但有些版本存的是列优先。解决拿一帧做投影验证如果镜像把 reshape 后转置一下再试。这个坑在 Linemod 上尤其常见因为不同预处理脚本对旋转矩阵的序列化方式不统一。4.3 现象DataLoader 报KeyError: 0或帧号对不上原因gt.yml的键是字符串还是整数取决于 YAML 解析器。yaml.safe_load通常把数字键解析成整数但有些文件里键带前导零变成字符串。解决统一用int(fid)去索引或者在读入后把gt的键全部转成整数。另外帧号在文件名里是 4 位补零gt.yml里可能是0而不是0000索引前先归一化。4.4 现象多物体场景只取到第一个标注原因Linemod 大部分帧是单物体但少数帧有多个实例gt[fid]是个列表。如果你的 Dataset 只取[0]多物体帧就丢了。解决训练时如果做单物体任务可以过滤掉列表长度大于 1 的帧如果做多物体要把列表里每个实例都展开成独立样本并注意掩码要按实例分开。4.5 现象换到 PVN3D 后关键点投票发散原因PVN3D 需要物体关键点而 Linemod 预处理版不一定带关键点标注。常见做法是用 FPS最远点采样从 PLY 顶点里选 8 个关键点但选完后要保证训练和测试用同一套关键点索引。解决把选好的关键点索引存成 npy训练和推理都读同一个文件。另外PVN3D 的投票损失对点云归一化方式敏感建议把点云减去质心再除以直径保持尺度一致。5. 验证与进阶用 ADD 指标闭环把这份数据用透数据接进去只是第一步能不能复现论文数字最终要看评测闭环。Linemod 上最常用的指标是 ADD 和 ADD-S对称物体用 ADD-S。ADD 的定义是把模型顶点用预测位姿变换后与用 GT 位姿变换后的对应顶点求平均欧氏距离小于直径的 10% 算正确。下面这段代码可以直接抄def compute_add(R_pred, t_pred, R_gt, t_gt, model_pts): # model_pts: Nx3 米制 pred (R_pred model_pts.T).T t_pred.reshape(1, 3) gt (R_gt model_pts.T).T t_gt.reshape(1, 3) return np.mean(np.linalg.norm(pred - gt, axis1)) def compute_adds(R_pred, t_pred, R_gt, t_gt, model_pts): pred (R_pred model_pts.T).T t_pred.reshape(1, 3) gt (R_gt model_pts.T).T t_gt.reshape(1, 3) # 对称物体每个预测点找最近的 GT 点 dist np.linalg.norm(pred[:, None, :] - gt[None, :, :], axis2) return np.mean(np.min(dist, axis1))参数说明model_pts从 PLY 读单位要和t一致米。diameter从models_info.yml读别用包围盒对角线代替。评测时按物体分别统计再取平均这样能看出哪个物体拖后腿。我一般会先把trained_checkpoints里的权重跑一遍确认 ADD 在论文报告值附近DenseFusion 在 Linemod 上约 0.03~0.05 倍直径再动自己的训练代码。如果权重跑出来偏差很大先查数据加载别急着改网络。进阶用法上这份数据还可以做域随机化增强随机替换背景、加高斯噪声、模拟深度缺失。Linemod 只有 13 个物体直接训容易过拟合加增强后泛化会好一些。另外PVN3D 之后的 FFB6D 等工作也在这份数据上评测如果你想横向对比保持同一套训练/测试划分就行。最后说个血泪经验每次换机器或重装环境我一定先跑一遍“读一帧 → 投影 → 算 ADD”的最小闭环确认数据管线没坏再开训练。这个习惯帮我省掉了至少三次通宵排查。希望帮到你。本文还有配套的精品资源点击获取