☰
D455自定义数据转LINEMOD:DenseFusion 6D位姿训练
2026/11/6 2:28:43 网站建设 项目流程

相机买回来了、模型也建好了,最让人卡住的往往不是 DenseFusion 的网络结构,而是“我拍的这批数据,它到底能不能读进去”。官方仓库给的是一份整理好的 LINEMOD 目录,照着跑能出结果;可一旦换成自己用 D455 采集的图,dataloader 立刻报 KeyError、点云全是 NaN、位姿投回 RGB 图上歪得很远。这篇就讲我自己从头用 D455 采集、标注、整理成 LINEMOD 格式并喂给 DenseFusion 训练的完整链路,包括每一步为什么非得这么干,以及几个我当时反复踩、网上又很少有人说清的坑。适合已经跑通官方 Demo、想换成自己的物体和场景继续做 6D 位姿估计的读者。

1. 先反推 DenseFusion 的 dataloader,再决定采什么

做数据集这件事,我现在的习惯是先把 loader 的代码从头读到尾,把所有meta[...]、cv2.imread(...)、os.path.join(...)都列出来,画一张“一条样本需要哪些文件”的表格,然后才动相机。顺序反了的话,采了几百帧之后发现格式不对,重采的代价非常高。

1.1 一条训练样本实际被消费的字段

DenseFusion 的 LINEMOD 分支在取数据时大致做这几件事:读 RGB 图、读深度图、读 mask 图,从 meta 里拿到相机内参和深度缩放系数,用内参把深度图反投影成相机坐标系下的三维点云,再用 mask 把属于目标物体的点筛出来,最后结合位姿把场景点云变换到物体自身坐标系,和模型侧 FPS 采样出来的 500 个点做配对。也就是说,一帧样本真正被用到的东西是:

文件作用缺失后果
rgb/{id}.png纹理特征输入直接报错
depth/{id}.png反投影出点云,融合的几何分支直接报错
mask/{id}.png筛出属于目标的像素点云混入背景,位姿乱飞
meta/{id}.pkl内参 + 深度缩放 + bbox 范围点云尺度错、投影全偏
gt.yml每帧的旋转矩阵与平移向量无监督信号
models/obj_XX.ply模型点云,用于构造 target报错或点数不匹配
fps/或采样文件固定数量的模型侧参考点点数对不上

把这些串起来看,你会发现整条链路的“地基”其实是内参和深度缩放这两个数。它们错一点点,后面标注再准都没用。我在第一次做的时候就是把对齐后的深度图配了深度流的内参,结果点云整体缩放错了一个比例,ICP 精修怎么调都收敛不到正确位置,查了整整一个下午。

1.2 两个必须一开始就定死的约定

第一个是单位。LINEMOD 原版数据集里,cam_t_m2c用的是毫米,而 DenseFusion 在 loader 里会把它除以 1000 转成米。如果你自己标注时用的是米,又原样写进gt.yml,训练时平移量会小 1000 倍,模型直接学不到东西。我的做法是:内部计算全程用米,写gt.yml时乘 1000,只在最后落盘那一步转换,避免中途混乱。

第二个是坐标系方向。cam_R_m2c和cam_t_m2c的名字已经说明了,是 model→camera,也就是把物体坐标系下的点变到相机坐标系下。用 OpenCV 的solvePnP得到的结果天然就是这个方向,但用 Open3D 做 ICP 时,如果你写反了 source 和 target,拿到的是T_obj_cam,转置一下才等价。这两种错误在训练里都不会报错,只会表现为精度上不去,非常隐蔽。

1.3 目录结构照着摆就行

我最终用的结构是这样,和官方仓库保持一致,省得改代码:

LINEMOD/ obj_01/ data/0001/rgb/0001.png data/0001/depth/0001.png data/0001/mask/0001.png data/0001/meta/0001.pkl data/gt.yml data/info.yml models/obj_01.ply fps/obj_01.xyz

同一物体如果有多段采集(不同光照、不同背景),可以拆成data01、data02,然后写进train_list.txt里。这一点后面第 5 节会细说。先把结构定下来,采集脚本按这个路径直接写文件,后期就只需要专注标注。

2. D455 的采集链路:对齐、同步与文件规范

D455 是一台带 IMU 的双目结构光深度相机,深度量程和视场都比前代宽,室内 0.5~3 米这个区间对桌面级物体的 6D 位姿任务来说很够用。但它出厂时深度流和彩色流的分辨率、视场、畸变模型都不一样,如果不做对齐,你会得到两张对不上的图。

2.1 为什么必须开 align_depth,以及对齐后内参要换

LINEMOD 格式隐含一个强假设:深度图和 RGB 图是逐像素对齐的,用的是同一套内参。因为 loader 是拿 RGB 的像素坐标去索引深度值的。D455 默认输出的深度图和彩色图各自独立,直接存下来,同一像素上 RGB 和深度的物理位置差了几厘米,反投影出来的点云整体错位。

解决办法是用 SDK 的 align 模块,把深度图重投影到彩色相机坐标系下:

import pyrealsense2 as rs align = rs.align(rs.stream.color) frames = pipeline.wait_for_frames() aligned = align.process(frames) depth_frame = aligned.get_depth_frame() color_frame = aligned.get_color_frame()

对齐之后深度图的尺寸变成彩色图的分辨率,这里有个关键细节:内参必须用彩色流的内参,不是深度流的内参。因为重投影已经把深度图“搬到”彩色相机视角了。我见过不止一个人在这里用错,症状是点云看起来形似但整体有规律的偏移,越靠图像边缘越严重。

取内参的代码:

color_profile = profile.get_stream(rs.stream.color).as_video_stream_profile() intr = color_profile.get_intrinsics() fx, fy, cx, cy = intr.fx, intr.fy, intr.ppx, intr.ppy

另外,如果开了彩色流的畸变校正(多数情况下 SDK 已经处理),就不用再做undistort,但如果你把分辨率设成跟出厂标定不一致的值,最好实测一下重投影误差,别默认它是对的。

2.2 采集脚本与我用的参数组合

分辨率我固定在 1280×720、30fps。深度用z16,彩色用bgr8。深度缩放系数通过depth_sensor.get_depth_scale()拿到,D455 出厂一般是 0.001,也就是深度图里数值 1000 代表 1 米,存成 16 位 PNG 完全不会溢出。

W, H, FPS = 1280, 720, 30 cfg = rs.config() cfg.enable_stream(rs.stream.depth, W, H, rs.format.z16, FPS) cfg.enable_stream(rs.stream.color, W, H, rs.format.bgr8, FPS) profile = pipeline.start(cfg) depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()

存盘的两点要求:

  • 深度必须是 PNG,绝对不能存 JPG。JPEG 是有损压缩,相邻像素的深度值会被抹平,本来是平面的桌面会变成波浪,点云质量直接废掉。
  • 彩色存 PNG 或高质量 JPG 都行,但如果要做纹理融合,PNG 更稳妥。我用 PNG,一张 1280×720 大约 1MB,几百帧完全扛得住。

如果你的场景是静止的、物体不动相机绕着走,采 200~400 帧是常见量级;如果相机固定、物体做转台旋转,注意位姿是物体在动,标注时物体坐标系会跟着转,转台的旋转量需要一并算进去——这个坑我在第三部分会展开。

2.3 曝光、白平衡与深度空洞

自动曝光在采集序列里是大忌。相机稍微一转,自动曝光重新调整,同一物体的亮度差异会非常大,训练时模型会把亮度变化当成特征学进去。我的做法是在采集前用一张白纸把画面填满,手动调到一个不过曝的曝光值,然后锁定:

color_sensor = profile.get_device().first_color_sensor() color_sensor.set_option(rs.option.enable_auto_exposure, 0) color_sensor.set_option(rs.option.exposure, 120) color_sensor.set_option(rs.option.enable_auto_white_balance, 0) color_sensor.set_option(rs.option.white_balance, 4600)

深度空洞是另一个绕不开的问题。黑色、高光、透明材质在结构光下基本没有回波,深度图会出现大片 0 值。训练时这些像素反投影出来是无效点,DenseFusion 一般会过滤掉 z 接近 0 的点,但如果空洞正好落在物体中心,剩下的有效点太少,网络会退化成只靠 RGB 猜。所以采集时尽量避开强反光背景,物体表面如果是哑光的会好很多;实在不行就在物体下面垫一层哑光深色布,能显著提升有效点比例。

2.4 命名、质检和“采多少”

文件名我直接用递增的四位数字,0001到0xxx,RGB、深度、mask 用同一个 id,这样脚本里拼路径不会出错。采完一段之后我会立刻做一次质检:随机抽 10 帧,把深度图用伪彩色显示一下,看空洞位置和取值范围;再把彩色图放大看看有没有运动模糊。运动模糊在慢速环绕时特别容易发生,30fps 下如果相机移动太快,边缘会有一两个像素的拖影,标注位姿时会带来几毫米的偏差。

关于数量,我的经验是每个物体不要少于 150 帧有效数据,且视角要覆盖到:俯视、平视、两个侧视、以及物体在不同背景上的组合。纯转台旋转采 300 帧,看起来数量够了,但视角分布退化严重(只有一维变化),泛化会很差。这一点在训练收敛曲线上表现得很明显:训练集 loss 降得很漂亮,换一个背景就崩。

3. 位姿标注:标定板给初值,ICP 做精修

这是整个流程里最耗时间、也最容易做错的一步。6D 位姿标注要么纯手工在三维软件里拖,要么用算法自动算。前者一帧两三分钟,几百帧根本做不完;后者精度可控,但需要一套能跑通的工具链。我采用的是“标定板给初值 + 点云 ICP 精修”的组合。

3.1 为什么不能完全靠手工

手动在点云软件里拖动模型去贴合场景点云,对靠近球形的物体勉强可以,但对薄片状、细长、带凹槽的物体,人眼很难判断深度方向上的对齐。而且手工标注的一致性极差——不同人标、甚至同一人隔一天再标,结果能差出好几度。这种噪声在训练时会被当成真实标签去拟合,最终影响精度上限。

纯算法自动标注(不依赖任何标记物)在学术上是一个独立课题,需要训练一个分割网络再配合渲染比对,工程代价太高。所以我选了一条折中路线:用一个已知尺寸的平面标记物辅助,先拿到一个误差在几毫米量级的初值,再用 ICP 把精度拉到亚毫米到一两毫米。

3.2 从标记物到 solvePnP 的初值链路

我的做法是把一张打印好的方形标记板贴在物体表面(或者固定在一个和物体刚性连接的小支架上),标记板的角点在物体坐标系里的三维坐标事先量好。每次采集时同时拍到物体和标记板,接下来:

  1. 用cv2.aruco.detectMarkers或cv2.aruco.detectBoard检测标记板,拿到角点的像素坐标;
  2. 用cv2.solvePnP结合相机内参求出标记板相对于相机的位姿T_cam_marker;
  3. 再用事先标定好的T_marker_obj(标记板在物体坐标系里的位姿,由 CAD 尺寸决定)左乘,得到T_cam_obj = T_cam_marker @ T_marker_obj。

关键是第 3 步的T_marker_obj必须通过 CAD 模型精确确定,不能凭感觉。我的做法是把物体模型和标记板模型一起摆进 CAD 里,让标记板贴在物体某个平面上,导出两者关系。这个值只需要算一次,之后所有帧复用。

solvePnP的调用要注意角点顺序和模型点顺序严格一一对应,稍微错一个位置,解出来的位姿会翻 90 度,这在可视化时很明显,但如果不看可视化直接进 ICP,就会得到莫名其妙的结果。

ok, rvec, tvec = cv2.solvePnP( obj_pts, img_pts, K, dist_coeffs, flags=cv2.SOLVEPNP_ITERATIVE) R, _ = cv2.Rodrigues(rvec) T_cam_obj = np.eye(4) T_cam_obj[:3, :3] = R T_cam_obj[:3, 3] = tvec.flatten()

这里的tvec单位取决于obj_pts的单位。我统一用米,所以后面 ICP 也在米的空间里做。

3.3 用 Open3D 做点到面 ICP 精修

有了初值,后面的工作是把场景点云和模型点云对齐。场景点云来自深度图加 mask 反投影,模型点云来自 PLY 网格采样。有一个特别容易搞反的地方:ICP 的 source 应该是模型点云,target 是场景点云。因为 ICP 求解的变换满足T @ p_model ≈ p_scene,这正好就是我们要的T_cam_obj。

import open3d as o3d reg = o3d.pipelines.registration.registration_icp( model_pcd, scene_pcd, max_correspondence_distance=0.015, init=T_cam_obj, estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPlane()) T_refined = reg.transformation

几个参数我调过很多次,最后定下来的是:

参数取值说明
max_correspondence_distance0.01~0.02 m初值够准就取小一点,收敛更稳
迭代次数50~100迭代次数太少就停不下来
估计方法点到面平面多的物体收敛更快,需给模型算法线
采样密度模型下采样到 3000~5000 点太密只会拖慢速度

点到面估计需要模型点云带法线,用estimate_normals算一下即可,注意法线方向一致性,必要时用orient_normals_towards_camera_location统一朝向。

精修完一定要检查。我用的指标是 ICP 的fitness(重合点比例)和inlier_rmse(重合点残差)。我自己的经验阈值是fitness > 0.6且inlier_rmse < 0.005,不满足的帧我会挑出来人工看一眼,实在不行就丢掉。宁可少几帧,也不要混进错帧,一个明显错误的位姿会在训练早期把 loss 拉得很高,梯度噪声非常大。

3.4 精修失败的三种典型情况

第一种是初值方向就错了。标记板如果被遮挡了一部分,solvePnP可能解出一个翻转的位姿,ICP 会收敛到一个“看起来贴合但整体转了 180 度”的局部极小。判断方法是看物体是否有明显不对称特征,如果有,转 180 度一定看得出来。

第二种是点云被背景污染。mask 没盖干净,物体的边缘和桌面连在一起,ICP 会把一部分平面点也算进去,导致物体往桌面上“沉”。解决办法是把 mask 向内腐蚀一到两个像素,宁可丢一点边缘点,也不要引入背景噪声。

第三种是尺度不一致。模型是从 CAD 导出的,单位是毫米;场景点云用米。如果不转换,ICP 会尝试用一个巨大的平移去补偿,直接发散。这是我第一次做时耗了最久才找到的问题,因为 Open3D 不会报错,只会给你一个fitness极低的结果。现在我的习惯是脚本开头就打印两边的包围盒对角线长度,对不上就立刻停下来。

4. 从 CAD 到模型点云:模型侧的准备

很多人把注意力全放在图像侧,模型侧随便丢一个 PLY 进去,结果训练时 loss 一直降不下去。模型点云在 DenseFusion 里是 target 的角色,它的尺度、原点、朝向如果和标注位姿不一致,整条监督信号就是错的。

4.1 网格的尺度和原点

拿到物体的三维模型(不管是 CAD 导出还是扫描重建),第一件事是确认三件事:单位、原点、朝向。这三样要和标注时使用的物体坐标系完全一致。

我做过的项目里,有一次模型是从网格软件导出的,单位是厘米,而标注脚本假设毫米,结果gt.yml里的平移量比模型点云小了 10 倍,训练出来的位姿在深度方向上严重偏移。后来我加了一个强制步骤:读入模型后立刻用get_axis_aligned_bounding_box打印长宽高,用卷尺量一下真实物体,数字对得上才继续。

原点也很关键。DenseFusion 的监督是模型点云在自身坐标系下的位置,如果原点不在物体几何中心而在某个角点,网络需要多学一个偏置,收敛会更慢,精度也会略差。建议在导出时就把模型平移,让质心落在原点附近,旋转上也尽量让物体的主轴对齐坐标轴,这样标注时的初值也更容易给。

4.2 均匀采样与 FPS 下采样

DenseFusion 在训练时其实用的是 FPS 采样出来的一批固定点。原因是原始网格点可能有几万个且分布极不均匀(曲面处密、平面处稀),直接作为 target 会让梯度偏向密集区域。

我的处理是两步:先在网格表面按面积均匀采样 3 万到 5 万个点,再在这批点上做 FPS 采到 500 点。均匀采样的目的是摆脱原始网格顶点分布的影响,FPS 的目的是让这 500 个点在空间上尽量分散,覆盖物体的各个面。

FPS 写起来很简单,但要注意第一个点的选取。如果固定从第 0 个点开始,不同物体的分布会有系统性偏差。我一般选离质心最远的点作为起点:

def fps(points, n): centroid = points.mean(axis=0) idx = [np.argmax(np.linalg.norm(points - centroid, axis=1))] dist = np.linalg.norm(points - points[idx[0]], axis=1) for _ in range(n - 1): i = int(np.argmax(dist)) idx.append(i) dist = np.minimum(dist, np.linalg.norm(points - points[i], axis=1)) return points[idx]

4.3 模型点云和场景点云的一致性验证

这一步花不了几分钟,但能省掉后面几小时的排查。方法是在一帧标注好的数据上,把T_cam_obj作用到模型点云上,和场景点云叠在一起看。如果两者几乎完全重合,说明模型尺度、原点、位姿方向全都是对的;如果模型明显偏大或偏小、或者整体偏移一个固定距离,那就是尺度或原点的问题。

我把这个检查做成一个脚本,每批数据标注完之后跑一遍,输出叠合后的可视化图和最近邻距离的中位数。中位数在 2 毫米以内我就认为通过,超过 5 毫米我会回去查。

另外提一句对称物体。如果物体的几何是对称的(比如圆柱、方形盒子),位姿的旋转在对称轴上有无穷多个等价解,标注时怎么转都对,但训练时这些“不同”的标签会让网络困惑。DenseFusion 里针对这种情况有专门的对称性处理,需要你在obj_list.txt或对应配置里把对称物体的 id 标出来,让它用对称友好的度量来计算损失。我在这上面吃过一次亏:一个圆柱体的位姿误差怎么都降不下来,最后发现是没标对称性。

5. 元数据落地:gt.yml、meta pkl 与列表文件

图像和点云准备好了,剩下是把这些信息写成 DenseFusion 认识的样子。这一步没有算法难度,但字段多,随便漏一个就是运行时报错。

5.1 gt.yml 的字段写法

gt.yml的顶层是帧编号,每一帧的值是一个列表(同一帧可能出现多个目标),列表里每一项包含:

"1": - cam_R_m2c: [r11, r12, r13, r21, r22, r23, r31, r32, r33] cam_t_m2c: [tx, ty, tz] obj_bbox: [x_min, y_min, x_max, y_max] obj_id: 1

cam_R_m2c是行优先展开的 9 个浮点数,cam_t_m2c是毫米单位的三维向量。这里是最容易翻车的一处:我内部全程用米,写文件时要乘 1000。为了不搞混,我在导出函数里直接写死t_mm = t_m * 1000.0,并且加了一条断言,如果t_mm的某个分量超过 10000(也就是 10 米)就报错——超出这个范围肯定是单位写错了。

obj_bbox可以从 mask 直接算出来,加几个像素的余量。有些实现自己不读它,但保留着没坏处,部分评估脚本会用到。

5.2 meta pkl 的生成

每一帧一个 pkl,内容是一个字典。核心字段是内参和深度缩放,另外还有一组包围盒范围字段(物体点云在相机坐标系下的最小最大坐标),DenseFusion 的部分分支会用来做裁剪:

import pickle, numpy as np meta = { 'intrinsic_matrix': np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1.0]]), 'cam_fx': float(fx), 'cam_fy': float(fy), 'cam_cx': float(cx), 'cam_cy': float(cy), 'cam_scale': float(1.0 / depth_scale), # 通常是 1000 'min_x': float(xyz[:, 0].min()), 'max_x': float(xyz[:, 0].max()), 'min_y': float(xyz[:, 1].min()), 'max_y': float(xyz[:, 1].max()), 'min_z': float(xyz[:, 2].min()), 'max_z': float(xyz[:, 2].max()), } with open('meta/{}.pkl'.format(frame_id), 'wb') as f: pickle.dump(meta, f)

cam_scale这个字段名容易误解:它其实是“深度值除以多少得到米”,也就是1/depth_scale。D455 的depth_scale是 0.001,所以这里填 1000。填错的话点云尺度会整体差三个数量级,表现是所有点都挤成一个点或者散到无穷远。

min_x那一组字段我习惯用该帧 mask 内的点云真实范围来填,而不是用模型的固定包围盒。虽然两种都能用,但用每帧的真实范围能少掉一些边界情况。

info.yml比较简单,主要写图像宽高和深度缩放,格式是:

"1": cam_K: [fx, 0, cx, 0, fy, cy, 0, 0, 1] depth_scale: 1.0

注意depth_scale在info.yml里写的是 1.0(表示数据已经是真实尺度),而 pkl 里写的是 1000(表示原始深度值需要除以 1000),两者含义不同,别照搬。

5.3 训练/验证划分和列表文件

我会把每个物体采到的数据按 8:2 左右划成训练和验证,划分时按采集段划而不是按帧随机划,同一段里相邻帧的视角几乎一样,随机划分会让验证集和训练集高度相似,验证指标虚高。

train_list.txt和test_list.txt每行是一个相对路径,指到data目录那一级:

obj_01/data obj_02/data

obj_list.txt里是所有物体 id 和名称,对称物体要单独标出来。划分完之后跑一次wc -l核对行数,别出现路径写错导致列表为空的情况——空列表在某些版本的代码里不会报错,只会训练一个永远不动的模型,非常浪费时间。

6. 训练前自检与踩坑排查

数据全部生成完,先别急着开训。我现在的固定流程是花十几分钟做三项自检,这三项能挡掉绝大多数“训练不收敛”的问题。

6.1 把位姿投回 RGB 看

最有效的一招:用内参把模型点云按T_cam_obj投到 RGB 图上,画出投影点和模型的包围框轮廓。正确的位姿下,投影轮廓应该严丝合缝地贴在物体边缘上。

pts_2d, _ = cv2.projectPoints(model_pts, rvec, tvec, K, None) for p in pts_2d.reshape(-1, 2).astype(int): cv2.circle(img, tuple(p), 1, (0, 0, 255), -1)

我现在会抽 20 帧拼成一张网格图快速过一遍。如果整体轮廓偏移一个恒定向量,说明内参不对或者用了未对齐的深度;如果轮廓大小对但整体旋转错,多半是cam_R_m2c转置了;如果只有部分帧错、而且错得离谱,那就是那几帧的标注本身有问题,直接剔除。

6.2 常见崩点速查

下面这张表是我踩过的坑的汇总,基本上对着症状就能定位:

症状最可能的原因排查方法
点云整体偏移,越到边缘越明显用了深度流内参而非彩色流内参对比两组内参的 fx/cx
点云缩放到离谱量级cam_scale填错或单位没统一打印点的 z 分布
训练 loss 一开始就巨大gt.yml里平移用米没转毫米检查 t 的数量级
loss 能降但验证精度极差训练/验证划分泄漏或视角退化看划分方式与视角分布
部分帧点云为空mask 全黑或者深度全空洞统计每帧有效点数
ICP 结果贴到桌面上mask 污染,混入背景点mask 向内腐蚀后再试
模型点云与场景始终差一点模型原点/尺度与真实不符量包围盒尺寸对比

6.3 数据量和多样性对收敛的影响

我做过一组粗略对比:同样一个物体,150 帧集中在转台旋转上的数据和 250 帧带多背景、多俯仰角的数据,后者在测试集上的效果明显更好。原因不难理解,DenseFusion 的融合分支要同时学到纹理和几何的对应关系,如果所有图的背景都一样,RGB 分支很容易记住背景而不是物体本身。

所以我在后期调整采集策略时,会把“背景数量”和“俯仰角范围”当成两个独立维度去覆盖。哪怕每个组合只采二三十帧,也比单维度堆数量强。这个过程有点枯燥,但确实是最直接有效的精度提升手段,比调网络参数见效快得多。

6.4 一个小技巧:先跑通单物体再扩多物体

第一次做的时候我贪心,一口气标了五个物体,结果一个位姿都不对,问题全混在一起。后来我改成先用一个物体、50 帧数据,走完整条链路,确认能正常训练、验证精度也合理,再逐步扩展到多物体、多背景。因为整条链路里可能的出错点有十几个,同时上多个物体等于让十几个变量互相干扰,排查成本会成倍上升。

单物体跑通之后,多物体的扩展其实很轻:每个物体一个子目录,gt.yml里按obj_id区分,obj_list.txt加一行就行。真正麻烦的只有第一遍。

以上这些步骤走下来,从我自己的经验看,一个物体从采集到能训练,大概需要半天采集加一天标注,如果标记板方案调顺了还可以再快一点。真正费时间的永远是那几个隐蔽的单位和坐标系问题,一旦这套流程固定下来,后面加新物体基本就是重复劳动了。我在实际使用中发现,把采集脚本和导出脚本里的关键量(内参、深度缩放、平移单位)全部打印成日志附在数据集旁边,几个月后回头看依然能快速排查,这个习惯值得养成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询