ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python单目三维重建实战:从SfM原理到点云生成

Python单目三维重建实战:从SfM原理到点云生成 简介基于Python语言实现的单目三维重建项目配有完整源码和文档说明属于答辩评分98分的高分毕业设计。资源面向计算机、通信、人工智能、自动化等相关专业的学生、教师或从业者也适合作为期末课程设计、课程大作业或毕业设计的参考模板。压缩包共12个文件总体约25MB包含可直接运行的Python脚本、README说明文档、相机参数与环境配置文本以及棋盘格标定图、不同视角拍摄图、特征匹配结果图、重建效果图等JPG/PNG图片便于对照各处理阶段的效果。代码经过调试测试覆盖相机标定、特征提取与匹配、三维坐标计算等单目重建关键步骤结构清晰、注释到位既能帮助初学者按流程理解原理也能让有基础者在此基础上调整算法或扩展应用。目前已有180人学习下载对于希望快速入门单目三维重建或准备相关毕设的读者是一份很有参考价值的资源。1. 单目三维重建不是玄学一个Python项目到底能做出什么单目三维重建字面意思是只用一台普通相机、连续采集图像就能在电脑里恢复出场景的三维点云和相机运动轨迹。这在毕设里属于“听起来很高大上但可落地”的方向它不需要深度相机不需要昂贵硬件只需要 Python 和摄像头拍的一组照片就能跑出一个看得见摸得着的三维模型。用 Python 做这个项目优势在于 OpenCV、Open3D、NumPy 这些库把最难的数学封装好了你只需理解原理并调对参数。适合计算机视觉方向的学生、想转行三维视觉的工程师也适合想用“源码文档”体现完整性的毕业设计。下面我从原理、实现、踩坑到进阶把整个方案的落地过程讲清楚。2. 先把原理立住相机模型、三角测量与Python里能直接用的算法2.1 从像素坐标到三维点针孔模型与外参内参的换算做单目重建最先要过的坎是理解相机把三维世界变成二维图像的过程。这里有个常见的直观误区觉得深度是“算不出”的。其实单个像素确实只能对应一条射线没有深度信息但两个不同位置的相机观察同一个三维点时两条射线会交于一点这个交点就是三维坐标这个过程叫三角测量。所以单目重建的核心思路不是“从一张图猜深度”而是“让相机动起来用多帧三角化恢复深度”。要让三角测量能算就得先定下相机模型。最常用的是针孔模型场景中的点 X 从世界坐标系经外参 R,t 变换到相机坐标系再经内参矩阵 K 投影到像素坐标 u。用 Python 写一个默认内参矩阵很简单对于没有标定数据的毕设场景可以先用图像尺寸近似import numpy as np w, h 640, 480 K np.array([[w, 0, w / 2.0], [0, w, h / 2.0], [0, 0, 1.0]], dtypefloat)这里的 K 就是内参矩阵fx 和 fy 是焦距在近似情况下都取图像宽度 wcx 和 cy 是主点取图像中心。注意实际手机相机可能裁切过fx 常常不等于 fy主点也可能偏离中心所以后续一旦效果好建议用棋盘格标定或 OpenCV 的 calibrateCamera 修正。我在毕设里见过不少同学直接用图像高度做焦距结果重建出来的点在 Z 方向严重拉伸这就是内参误差被几何放大的结果。有了 K还需要外参 R,t。单目重建中 R,t 是未知数正是要通过特征匹配和本质矩阵分解来估计。理解这条链路特征匹配给出两张图中对应的像素点对对极几何用点对计算基础矩阵 F再从 F 推导本质矩阵 E最后从 E 分解出旋转 R 和平移 t然后用 R,t 构造两个相机投影矩阵 P1、P2对每个匹配点三角化得到三维坐标。OpenCV 里对应函数是 findFundamentalMat、recoverPose 和 triangulatePoints下面章节会一步步组合起来。2.2 单目重建的三种主流路线SfM、单图深度估计与SLAM怎么选“单目三维重建”也是个大箩筐不同实现路线的输入输出差别很大。毕设选型前先把路线分清否则容易做一半发现方向不对。我按自己接触过的项目把常见路线分成三类路线输入输出计算成本适合毕设程度SfM运动恢复结构一组有序或无序图像稀疏点云、相机位姿中低离线处理最稳妥单图深度估计一张图像深度图可转点云依赖模型需GPU有门槛但结果直观单目SLAM视频流实时轨迹稀疏/半稠密地图高需实时优化偏算法难控制深度如果你的目标是“肉眼可见的三维重建结果”首选 SfM。原因很直接它的每一步都有明确几何意义参数可解释出问题能定位到特征匹配、位姿估计还是三角化写进文档很有说服力。单图深度估计现在多用深度学习模型MiDaS、DPT 等预训练模型能直接出深度图但这类方法是在“猜深度”无法给出严格的尺度而且一旦换场景效果波动很大论文里不好自圆其说。单目 SLAM 则要处理实时性和轨迹漂移工程复杂度高用在毕设里容易陷入调参泥潭。所以给毕设的默认建议是做 SfM 的“两视图三角化”作为核心再扩展一个多帧拼接最后用 COLMAP 做稠密重建来对比结果。这样既有自己写的核心代码又体现了工具链的完整度。2.3 Python生态选型为什么OpenCVOpen3D够用什么时候上COLMAP确定了 SfM 路线后Python 生态里最常用的是 OpenCV、Open3D 和 NumPy。OpenCV 承担特征提取、匹配、基础/本质矩阵估计、位姿恢复和三角化Open3D 负责点云构建、可视化、保存为 PLY 文件NumPy 做矩阵运算和坐标变换。如果不想写太多几何代码甚至可以直接调用 OpenCV 的 SIFT 接口两三百行就能出结果。但也有情况一定要上 COLMAP当你要“稠密点云”而不是几百个稀疏特征点时OpenCV 自带的两视图三角化远远不够。COLMAP 是开源的三维重建工具能自动处理多视图、光束法平差、稠密重建还提供命令行接口。常见做法是先用 OpenCV 做稀疏重建理解原理再用 COLMAP 复现同一个数据生成更密致的模型。安装 COLMAP 在 Windows 上有打包好的 exeLinux 上也可以直接下载预编译版本源码编译依赖很多不推荐新手折腾。另外说下 Open3D它不只是可视化工具还提供体素滤波、统计滤波、法线估计、泊松表面重建这些后处理函数。对毕设来说用 Open3D 对稀疏点云做一次离群点去除就能让结果看起来“干净”很多属于性价比极高的操作。后面第 3 章的代码里我会把 Open3D 的保存和可视化一起带上。3. 用Python把最小SfM流程跑通从特征匹配到点云输出3.1 准备环境与数据一组连续拍摄的图片就够了最简实现只需要两张有足够视差的图像。拍摄时不要原地旋转拍全景而是让相机在物体前方做一段横向或向前的位移保证两张图中同一物体有 40% 以上的重叠区域。我用手机拍一本放在桌面的书平移约 10 厘米就能跑出漂亮结果。如果拍摄目标是房间也可以推着镜头平移但要避免纯白墙壁、玻璃反光和重复纹理区域。环境安装比较简单推荐用 3.10 以上的 Python然后执行pip install opencv-python open3d numpy如果后续想用 SIFT注意 opencv-python 的 4.4 及以上版本已经把 SIFT 放进主库不再需要 opencv-contrib-python。装完后可以用下面命令验证python -c import cv2; print(cv2.__version__); import open3d; print(open3d.__version__)我一般会在项目根目录建 data 和 output 两个文件夹把图片放到 data 下后续点云都写到 output。这样代码、数据、结果分离后期写文档时不容易漏项。3.2 特征提取与匹配SIFT暴力匹配的取舍第一个核心步骤是找两张图中可稳定对应的关键点。SIFT 是目前最平衡的选择尺度不变、旋转不变、对光照变化鲁棒。ORB 速度快但匹配准确率在弱纹理场景差很多SURF 在 opencv-contrib 里也不再默认支持所以直接用 SIFT。下面代码从两张图提取特征并匹配import cv2 import numpy as np img1 cv2.imread(data/001.jpg) img2 cv2.imread(data/002.jpg) gray1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) sift cv2.SIFT_create() kp1, des1 sift.detectAndCompute(gray1, None) kp2, des2 sift.detectAndCompute(gray2, None) bf cv2.BFMatcher(cv2.NORM_L2, crossCheckFalse) raw_matches bf.knnMatch(des1, des2, k2) good_matches [] ratio_thresh 0.75 for m, n in raw_matches: if m.distance ratio_thresh * n.distance: good_matches.append(m) pts1 np.float32([kp1[m.queryIdx].pt for m in good_matches]) pts2 np.float32([kp2[m.trainIdx].pt for m in good_matches])这段代码分了三层逻辑先提取关键点和描述子再执行 KNN 匹配最后用 Lowe 比率测试过滤模糊匹配。ratio_thresh 是 0.75意思是最佳匹配距离如果低于次佳匹配距离的 75%才认为这是可靠匹配。这个值越大保留的匹配越多但误匹配也越多越小则越严格匹配数量变少但更准。我更常用 0.7当特征点太少时再放宽到 0.8写文档时最好把调参过程记录下来。注意这里用的是 BFMatcher对两图规模是够用的。如果图片超过 1000 张才需要考虑 FLANN。但毕设数据量通常不大暴力匹配足够也更容易解释。3.3 恢复相机位姿本质矩阵分解与三角化拿到匹配点后先通过 findFundamentalMat 计算基础矩阵 F并用 RANSAC 剔除误匹配。然后利用内参 K 换算出本质矩阵 E再用 recoverPose 分解出旋转和平移最后三角化。整个过程是 OpenCV 几何模块的标准流水线# 计算基础矩阵RANSAC 阈值 1.0 像素 F, mask cv2.findFundamentalMat(pts1, pts2, methodcv2.FM_RANSAC, ransacReprojThreshold1.0) # 只保留满足对极几何的内点 pts1_in pts1[mask.ravel() 1] pts2_in pts2[mask.ravel() 1] # 近似内参 h, w gray1.shape K np.array([[w, 0, w / 2.0], [0, w, h / 2.0], [0, 0, 1.0]], dtypefloat) # 本质矩阵E K^T F K E K.T.dot(F).dot(K) # 分解 E得到 R 和 t单目 t 没有尺度 _, R, t, mask_pose cv2.recoverPose(E, pts1_in, pts2_in, K) print(R:\n, R) print(t:\n, t.ravel())recoverPose 返回的 mask_pose 会再次滤掉不符合正向深度的点但注意它只给出四个解中的唯一“正确”解前提是输入的点坐标顺序和 K 都正确。单目相机恢复的平移向量 t 并没有真实尺度它和真实世界只差一个比例系数这会在第 4 章专门说。如果你发现打印出来的 t 是[0,0,0]或者 R 接近单位阵往往是两张图视差太小或特征点都在同一平面上。三角化本质上是求解两条射线的最小二乘交点OpenCV 给出了现成函数# 构造两个相机投影矩阵第一帧设在世界坐标系原点 P1 np.hstack((np.eye(3, dtypefloat), np.zeros((3, 1), dtypefloat))) P2 np.hstack((R, t)) # 注意 triangulatePoints 需要点坐标按 (N,2) 转置为 (2,N) pts4D cv2.triangulatePoints(P1, P2, pts1_in.T, pts2_in.T) pts3D pts4D[:3] / (pts4D[3] 1e-8) points pts3D.T # 形状 (N, 3)这里 P1 取了[I|0]相当于把第一帧相机坐标系当成世界坐标系P2 用恢复出的 R,t。triangulatePoints 返回 4×N 的齐次坐标除以最后一维得到非齐次坐标。加 1e-8 是为了防止除零。至此你已经得到了一个小规模三维点云下一步把它保存并可视化。3.4 生成并可视化点云Open3D保存ply并检查结果得到 points 后用 Open3D 构建点云对象可以顺手把对应像素的颜色从原图取出来赋给点这样可视化时能看到“彩色点云”答辩效果立竿见影。代码import open3d as o3d pcd o3d.geometry.PointCloud() pcd.points o3d.utility.Vector3dVector(points) # 从第一张图中取匹配点的颜色注意像素坐标 y 在前 x 在后 colors [] for pt in pts1_in: x, y int(round(pt[0])), int(round(pt[1])) if 0 x img1.shape[1] and 0 y img1.shape[0]: b, g, r img1[y, x] colors.append([r / 255.0, g / 255.0, b / 255.0]) else: colors.append([1, 1, 1]) pcd.colors o3d.utility.Vector3dVector(np.array(colors)) # 保存和可视化 o3d.io.write_point_cloud(output/sparse.ply, pcd) o3d.visualization.draw_geometries([pcd])如果一切正常你会看到一个由几百个彩色点构成的三维轮廓隐约能看出物体形状。如果点云像一片乱云多半是匹配或内参问题可以关掉颜色用纯色显示更容易观察结构。我习惯把可视化后的截图存到 output 目录不需要额外工具直接截图即可。稀疏点云毕竟是稀疏的但作为“单目三维重建”的最小实现它已经足够证明原理。要拿到更完整的表面一般会用 COLMAP 走稠密重建这会在第 6 章补充。现在先厘清几个最容易翻车的坑这些坑几乎每个做单目重建的人都会踩一遍。4. 必踩的坑与排查从特征不够到坐标翻车的实操记录4.1 特征点太少导致重建崩溃现象、原因与解决现象运行完匹配代码good_matches 只有十几对随后 findFundamentalMat 直接返回 None 或者三角化后点云只有寥寥几个点。原因拍摄目标纹理太弱或者两张图片的重叠区域太少加上 SIFT 对比度阈值默认值偏严。我遇到过最典型的是拍白墙和光滑桌面SIFT 在全图找不到稳定角点。解决先换拍摄对象选纹理丰富的物体其次把 SIFT 的对比度阈值调低例如sift cv2.SIFT_create(contrastThreshold0.03)默认是 0.04调低会让更弱的边缘斑点变成关键点。还可以用cv2.resize把图像缩小到宽度 1000 左右减少尺寸过大导致的特征点分散。如果还是不够就把 ratio_thresh 从 0.75 放宽到 0.9但后面需要靠 RANSAC 兜底。4.2 基础矩阵分解出现两个相机位姿怎么选正确的那个现象用cv2.decomposeHomographyMat或者手动分解本质矩阵时会得到多组 R,t不知道选哪个。其实 recoverPose 内部已经做了选择但很多新手不明白背后逻辑。原因本质矩阵 E 存在多解性数学上存在四个解但只有一个解能使所有匹配点都在两个相机前方。recoverPose 正是通过“三角化后检查深度正负”来选择正确解。解决不要自己用 SVD 分解直接用cv2.recoverPose并且注意它要求输入的 pts1、pts2 必须是从第一张图到第二张图的对应关系不能倒转。如果倒转recoverPose 会选错此时点云会翻面或者深度为负。验证方法打印 R 的行列式是否约等于 1以及三角化点的 Z 坐标是否有相当数量大于 0。4.3 三角化的点在相机后方剔除与重投影误差筛选现象点云里有一大堆点集中在相机后方或者模型形状像镜像。原因匹配错误和基础矩阵不精确都会导致三角化射线不交OpenCV 的 triangulatePoints 会用最小二乘给出一个“假交点”它可能落在相机后方。recoverPose 的 mask 只过滤了一部分不代表全部正确。解决在生成 pointcloud 前按两个条件过滤一是点的 Z 坐标必须大于阈值比如 0.1二是计算重投影误差把点重新投影回两张图像素误差大于 2 的直接丢掉。筛选逻辑写进文档里非常加分。代码可以这样写# 重投影到第一张图计算像素误差 p1_proj K.dot((P1.dot(pts4D)).T).T p1_proj p1_proj[:, :2] / p1_proj[:, 2:3] err1 np.linalg.norm(p1_proj - pts1_in, axis1) # 重投影到第二张图 p2_proj K.dot((P2.dot(pts4D)).T).T p2_proj p2_proj[:, :2] / p2_proj[:, 2:3] err2 np.linalg.norm(p2_proj - pts2_in, axis1) valid (points[:, 2] 0.1) (err1 3.0) (err2 3.0) points_filtered points[valid]注意由于我们用的是近似内参阈值可以放宽到 3~5 像素如果用真实标定内参可以压到 1 像素左右。4.4 尺度模糊单目重建没有真实尺度怎么保证视觉质量现象重建出来的物体形状对但尺寸要么飘在天上要么缩成一团甚至不同两帧重建结果大小不一致。原因单目本质矩阵恢复的平移 t 本身就是归一化的结果它的模长是任意比例导致三角化得到的点云尺寸没有物理意义。解决如果只做形状展示这不影响结果你可以在读取点云后做一次归一化让点云坐标除以最大范数或者缩放到固定范围以方便比较不同方法。如果需要真实尺度就需要在场景中放置已知长度的物体比如 A4 纸重建后提取两个点之间的距离按比例系数缩放整个点云。这个操作在文档里写成“尺度恢复”步骤会让项目完整度更高。4.5 图像序列上下颠倒OpenCV坐标系与视觉坐标系的差异现象点云在 Open3D 里看起来是倒的或者绕某个轴转了很久还是不对。原因图像坐标与三维相机坐标的 Y 轴方向不同。OpenCV 的图像坐标原点在左上角Y 轴向下三维相机坐标系中 Y 轴通常向上。recoverPose 返回的 R,t 是建立在“相机坐标系”里的没有严格对应可视化世界的绝对方向。解决最常见做法是在可视化前把点云 Y 轴取反points[:, 1] -points[:, 1]。另外 Open3D 坐标系的默认视角也不一定与 OpenCV 一致可以在 draw_geometries 后手动旋转视角。还有个小坑如果你保存的 PLY 文件在 MeshLab 里显示正常在 Open3D 里却颠倒或者反过来那就要统一坐标变换。总之不要迷信结果先看形状再调方向。5. 让这个项目配得上“高分”源码组织、文档说明与可复现性设计5.1 项目目录结构怎么摆src、docs、data、outputs分隔毕设答辩和代码评审最看重“工程感”。一个把所有代码写在一个 main.py 里的项目即使算法再正确也容易被扣分。我讨巧的布局参考开源仓库的标准monocular_3d_reconstruction/ ├── README.md ├── requirements.txt ├── data/ # 输入图片 │ ├── 001.jpg │ └── 002.jpg ├── output/ # 输出结果 │ └── sparse.ply ├── src/ │ ├── __init__.py │ ├── load_data.py # 读图、转灰度 │ ├── features.py # SIFT特征与匹配 │ ├── pose.py # 本质矩阵、位姿恢复、三角化 │ ├── cloud.py # 点云生成、过滤、保存 │ └── main.py # 串联整条流水线 └── docs/ ├── 理论笔记.md └── 实验报告.md这样做的好处是每个模块只干一件事main.py 只做流程编排复现时打开 README 就能定位问题。如果老师要检查还能把 src 里每个函数按 20 行以内的粒度拆分评审读起来不累。requirements.txt 里固定版本例如opencv-python4.8.1.78避免环境不对跑不起来。实际建目录时直接执行mkdir -p data output src docs touch requirements.txt然后在 src 里拆分文件。不要小看这个动作我见过不少项目源码功能齐全但目录扁平、函数互相调用混乱最后被老师要求重构耽误了很多时间。5.2 写文档说明的四个重点环境、原理、运行、结果所谓“文档说明”不是把代码注释抄一遍而是让读者按文档能完整复现。我写毕设文档固定四节环境配置、方法原理、运行步骤、结果分析。环境配置里写清 Python 版本、依赖安装命令和测试命令方法原理要画一条从图片到点云的数据流同时解释为什么用 SIFT、为什么用 RANSAC运行步骤给出从pip install -r requirements.txt到python src/main.py --img1 data/001.jpg --img2 data/002.jpg的完整命令结果分析要放至少三组不同场景的重建截图和对应点云文件并记录各项指标。一个可用的 README 模板可以这样# 单目三维重建 基于 Python 的最小 SfM 系统输入两张连续图像输出 PLY 点云。 ## 环境 - Python 3.10 - opencv-python, open3d, numpy ## 运行 python src/main.py --img1 data/001.jpg --img2 data/002.jpg ## 结果 输出 output/sparse.ply并弹出 Open3D 窗口。文档里不要写“代码跑通了”要写“为什么能跑通”在原理节写清楚 F 矩阵、E 矩阵和三角化的数学关系哪怕不写公式也要用文字把流程讲顺。如果有图表如特征匹配图、RANSAC 内点分布图就更好这些图用 matplotlib 随手能出。5.3 提升评分的小技巧参数化、日志、可视化与单元测试让项目从“能跑”变成“高分”的技巧通常藏在细节里。第一是参数化把相机内参、RANSAC 阈值、SIFT 对比度、重投影过滤阈值都提到 argparse 或 config.py 里不要写死在函数里。这样你在文档里展示“不同阈值对点云密度的影响”会很方便。例如import argparse parser argparse.ArgumentParser() parser.add_argument(--img1, requiredTrue) parser.add_argument(--img2, requiredTrue) parser.add_argument(--sift-contrast, typefloat, default0.04) parser.add_argument(--ratio, typefloat, default0.75) parser.add_argument(--reproj-thresh, typefloat, default3.0) args parser.parse_args()第二是日志用 Python 的 logging 模块打印每一阶段的特征数量、匹配数量、RANSAC 内点数这既是调试工具也是答辩时的“过程证据”。第三是可视化保存每跑完一个阶段就把中间结果存成图片比如特征匹配图、内点匹配图、三角化前分布图放在 output 里比嘴上解释直观得多。第四是单元测试至少写一个测试函数验证“三角化后重投影误差小于阈值”用 pytest 跑一下。答辩时提起“我对核心函数做了单元测试”印象分会显著提升。5.4 用Git管理源码提交信息与版本回滚的实用习惯毕设源码往往会改很多版最怕的是某天调参把代码改坏了还回不去。建议从第一天就用 Git哪怕只有你一个人开发。进入项目后先执行git init git add . git commit -m feat: 完成两视图SfM初始版本每完成一个稳定阶段就提交一次提交信息写清楚“加了什么功能”或“修了什么 bug”。调参前的状态最好打一个 tag例如git tag -a v1.0 -m 使用SIFT暴力匹配重建房间桌面上物体这样如果新方案翻车随时可以git checkout v1.0回滚到可用版本。我自己吃过亏连续改了三天参数后最佳结果被覆盖又没有提交记录只能靠截图一步步反推白白浪费半天。所以这个习惯不是花架子是真正的后悔药。6. 进阶方向从稀疏点云走向稠密重建与评估6.1 用COLMAP做稠密重建并导出如果你不满足于稀疏点云最简单的进阶是安装 COLMAP直接在命令行里完成从多图到稠密点云的流程colmap feature_extractor --database_path database.db --image_path data colmap exhaustive_matcher --database_path database.db colmap mapper --database_path database.db --image_path data --output_path sparse colmap image_undistorter --image_path data --input_path sparse/0 --output_path dense colmap patch_match_stereo --workspace_path dense --image_path data colmap stereo_fusion --workspace_path dense --output_path dense/fused.ply这条命令链会把 data 下的所有图片跑一遍最后产出 dense/fused.ply。注意 COLMAP 对图片数量要求不高十来张就能出不错效果但图片重叠要够否则补丁匹配会生成大量空洞。6.2 用深度图加相机位姿合成稠密点云COLMAP 也可以先估计每张视图的深度图再反投影合成点云。这个思路和稀疏三角化相比每个有效像素都能变成一个三维点密度高很多。在 Python 里你可以读取 COLMAP 输出的相机位姿和深度图按公式P K^{-1} * depth * [u,v,1]反投影再用 Open3D 拼接多个视图。这个操作能让你更深刻理解深度图与相机内参的关系适合在文档的“扩展实验”里写。6.3 验证重建质量重投影误差、点云密度与肉眼检查无论做稀疏还是稠密都要给结果一个客观评价。最常用的是平均重投影误差将三角化的三维点重新投影到每张图像计算与原始匹配点的欧氏距离通常小于 1 像素为优秀小于 3 像素可接受。点云密度可以用点数、每平方米点数来衡量COLMAP 在 fusion 时会自动输出统计信息。另外别忘肉眼检查点云是否连续、有无大块空洞、有无重复平面。我自己的习惯是每次重建完先转成 PLY 用 Open3D 转一圈看形状轮廓是否清晰如果轮廓干净再用指标支撑结论。单目三维重建这个方向最大的坑不在算法而在“以为一次就能成”。我每次做新数据都要先拍十组照片挑一组纹理好的然后调一次参数、跑一次脚本记下每个阶段出现的问题。把这些记录整理成文档比空谈模型有价值得多。希望这篇笔记能让你少走弯路早日跑出属于自己的三维点云。本文还有配套的精品资源点击获取
返回列表