1. 先搞清楚ScanNet里到底有什么,再决定要不要下载
想下载ScanNet数据集的人,十有八九会在网上搜到一批三四年前的老教程,照着跑一遍,不是Python版本报错就是等待授权邮件等到怀疑人生。我自己当年第一次折腾这套数据时就踩过好几个坑,所以这篇把它整理成一份能直接照着操作的完整记录。
1.1 这组数据解决的是哪类问题
真正在点云、室内场景理解或3D语义分割这条路上摸爬滚打过的人,应该都有类似经历:翻论文、复现PointNet系列,看来看去都会碰上一个绕不开的名字——ScanNet。它是斯坦福大学在2017年CVPR上发布的室内RGB-D数据集,一共包含1513个真实场景的扫描数据,涵盖了酒店房间、书房、厨房、洗手间等真实室内环境。和NYUv2、SUN RGB-D这些早期数据集相比,ScanNet最大优势是不仅有成对的彩色图和深度图,还提供了重建后的完整三维网格模型、表面级语义标注以及逐帧的相机位姿。
也就是说,拿到这组数据,你可以同时做三件之前要被拆成三个数据集才能做的事:一是训练深度估计和位姿估计模型,二是做三维语义分割和实例分割,三是为SLAM系统提供带真值的评测序列。这也是为什么各个3D视觉方向的论文附录里,几乎都能看到"Trained on ScanNet"或者"Evaluated on ScanNet"这样的字样。
1.2 下载前先算清楚磁盘、网络和收益这笔账
不过我要先泼一盆冷水:ScanNet整套数据的体量,绝不是你随手在硬盘上划一块就能放下的。所有场景的--task sensor_data原始文件加起来大概是84GB左右,如果你顺手把语义标注也下全,再加上scannet_posts里按帧解包的color和depth,整体突破200GB很轻松。我个人建议,至少准备一块500GB左右的NVMe固态,并且预留两倍的解压中间空间。别问我为什么强调NVMe——后续对.sens逐帧解包的时候,你在机械硬盘上跑和固态盘上跑,等待时间差距是小时级的。
在动手之前还有个更重要的问题:你要的到底是哪部分数据?ScanNet并不像很多常识理解的"下载一个压缩包就完事",它按任务把数据拆成了好几类,下载时用不同的参数分别拉取。这个细节直接决定你是花半小时下一组测试样例,还是要开着脚本挂一夜。后面第3章我会把参数列表完整展开,但第一步建议先确认自己的场景:要做点云语义分割,核心是sensor_data加scannetv2_labels;要跑2D图像分割,核心是scannet_posts;只做配准评测,单独下scannet_posts里的pose就够了。
2. 许可证申请这一步,建议把两周时间预算进去
2.1 账户注册和学术协议签署的完整流程
ScanNet的下载权限并不是公开开放的,这是它和很多数据集最大的区别。你可能已经在GitHub仓库里看到了download_scannet.py这个脚本,但直接用它是下不到任何东西的,因为脚本会要求验证你在官网上注册的账号和官方下发的密钥。
整个申请流程大概是这样的:第一步,访问ScanNet官网的注册入口,填写姓名和机构邮箱;第二步,进入许可协议页面,完整阅读并勾选"学术用途"相关条款,有些版本还会要求你选填学校和导师信息;第三步,提交之后等待人工审核。审核通过后,你会收到一封邮件,里面包含你的登录账号和一个专属的下载授权凭证,后续脚本运行时要用这个凭证过校验。
这里有三个容易被忽略的细节。第一,注册时最好用学校或研究所的机构邮箱,网页表单里对机构邮箱的识别相对严格,纯个人邮箱被拒的概率明显更高。第二,协议中关于"是否用于商业用途"的选项一定要慎重,ScanNet的学术协议明确限制商业使用,如果你只是想在公司内部做算法预研又没通过商务合作通道,审核会卡得很久。第三,提交一次就好,别反复注册多个账号催进度,后台记录里账号太多反而容易被标注为异常。
2.2 从提交申请到收到下载链接,实际要等多久
不同时期的审核速度差别非常大。我了解到的时间跨度,快的有一两天就通过的,慢的等了将近三周。这里多说一句:ScanNet维护团队是半人工处理方式,如果你正好赶上某顶会截止日前的申请高峰,等待时间普遍会更长。我的建议是,凡是计划做3D视觉方向课题的人,尽量在项目启动前就把ScanNet的申请提交掉,不要等到数据到位了才想起来开始排期。
有些版本的申请流程里还要求额外填一份"使用声明",说明你计划用ScanNet做什么方向的研究。这部分不用写得很复杂,一两句清楚的研究目标就行,关键是让审核人觉得你的用途是真实明确的。如果你只是想跑通教程里的例子,就写"reproducing baseline results on 3D semantic segmentation",完全足够。
等待期间也别闲着,可以在GitHub上把ScanNet的整个仓库clone下来,把下载脚本和工具链先装好,后面拿到密钥就能直接开跑。ScanNet仓库本身不大,几十MB级别,里面除了download_scannet.py,还有用于读取.sens的SensReader,以及一套数据标注工具,都是你迟早会用到的。
3. 官方下载脚本全流程:从clone到数据落盘
3.1 环境准备:Python版本和依赖库
拿到权限之后,正式下载的思路就简单了:找到官方脚本,运行,然后等。但"简单"两个字里藏着一堆环境问题,我先把最常用的一套环境写出来,你照着搭基本不会出错。
git clone https://github.com/ScanNet/ScanNet.git cd ScanNet/Scripts python download_scannet.py --help推荐使用Python 3.6到3.9之间的版本,官方脚本虽然历史久远,但这几个版本下跑是最稳定的,再往上到Python 3.10、3.11,可能会遇到依赖库编译问题。需要确认环境里有numpy和plyfile,这两个库在后面的数据解析阶段是刚需:
pip install numpy plyfile不要一上来就创建最新的虚拟环境并安装一堆最新版本包,这是个常见误区。ScanNet仓库里很多脚本是在2017年到2019年间写的,依赖锁定得比较保守,新的库版本反而容易导致接口不兼容。我的习惯是单独建一个Python虚拟环境给ScanNet用,隔离其他项目,省得莫名其妙被依赖冲突打断下载。
3.2 按需选择任务参数,别让全量下载拖垮磁盘
下载脚本最核心的用法其实就一行,关键是选对任务参数。下面是我平时用得最多的几个命令组合:
# 下载所有场景的原始传感器数据(.sens文件) python download_scannet.py -o /data/scannet --scans --task sensor_data # 只下载指定几个场景,用于前期调试 python download_scannet.py -o /data/scannet -s scan0011_00 scan0021_00 --task sensor_data # 下载语义标注(v2版本) python download_scannet.py -o /data/scannet --scans --task scannetv2_labels # 下载按帧解包好的彩色图/深度图/位姿 python download_scannet.py -o /data/scannet --scans --task scannet_posts这里的--scans参数表示下载全部场景,如果漏了它而你又没指定列表,脚本会跑到最后提示参数不完整,白忙一场。-s参数后面跟场景ID,适合先拉两三个场景做流程测试。--task是真正的核心,不同任务对应不同数据子集,我建议第一次操作时先用-s scan0011_00和--task sensor_data下一组小数据,流程跑通后再上全量。
另外有个非常实用的细节:download_scannet.py会检查本地目标目录里已经存在的文件,如果之前下载过某个场景,重新执行命令时会跳过已经落盘的部分,只补缺的文件。这相当于内置了断点续传能力,别因为中途断了就手动删除所有文件从头开始,直接重跑一遍脚本是最省事的。
3.3 大型下载里没人提醒你的隐藏坑
全量下载时最容易被忽略的是.sens文件的体积差异。扫描场景内容越多,生成的.sens越大,最大的场景文件可以到4GB以上,而小的可能只有几十MB。挂机下载的时候,如果你开着下载文件夹看着进度条,很容易觉得某个文件卡住了,其实只是它在拉一个超大的单体文件。
我还会在下载前先把输出目录的挂载检查一遍:df -h看一下剩余空间,ls -ld /data/scannet确认可写权限。遇到过一位同学把输出目录放在系统盘用户目录下,下到一半才提示磁盘已满,又找不到根目录里占用到底在哪,特别心浮气躁。提前花一分钟排查,能避免后面一晚上的折腾。
4. 下载途中最容易翻车的四个环节
4.1 Python 2与Python 3的兼容性问题
这一步大概是历史遗留问题最多的环节。官方download_scannet.py最初是针对Python 2写的,里面用了urllib2、iteritems这些Python 2专属写法,你在Python 3环境直接跑通常会在导入阶段就报错,提示找不到urllib2模块。网上很多老教程会让你装Python 2.7去跑,但现代系统上搭一个旧版Python环境本身就是新的坑。
我的做法很简单:用Python 3.8,然后对脚本做一次两分钟的小修改。把文件开头的import urllib2换成:
import urllib.request as urllib2再把任何出现的.iteritems()替换成.items(),基本就能跑通了。如果你不想手动改,也可以先试试Python 3环境直接跑,老版脚本有一些分支在同版本下其实兼容得不错,报错了再按上面两个地方排查,大概率立竿见影。
4.2 网络波动导致的任务中断和处理
ScanNet的数据存放位置在海外云存储上,跨区域下载时网络稳定性是最大的变量。我自己实测的经验是,下载单个场景的.sens文件时,如果连接断掉,脚本会报超时然后退出,但不会损坏本地文件。下次重新执行同一命令时,脚本会自动跳过已完成的文件,重新下载那一个没下完的。所以应对网络波动最粗暴也最有效的方法就是:反复重跑同样的命令。
如果重试多次仍然频繁断线,我建议把一次拉取的量拆小,比如用--scans -s先下20个场景,不要一次性全量挂机。或者利用脚本对不同任务分别下发的特征,把sensor_data和labels拆成两个批次的脚本执行,每天下一部分,避免连续长时间占用网络。
挂夜机下载时,我还强烈建议用tmux或screen这类会话工具起任务。它解决的不只是断网问题,还有SSH连接松动导致的进程中断。你不想在第二天早上发现脚本因为终端断开而停在60%的位置,尤其当它还是凌晨三点断掉的时候。
4.3 下载完成后的校验环节
数据下载对完整性要求很高,尤其是深度学习训练场景。ScanNet官网对每个文件提供了对应的文件大小信息,但并不总是提供公开可校验的MD5码。我个人的校验方法是双重的:第一,脚本跑完没有ERROR或WARNING输出;第二,抽查几个场景里所有文件的大小,和官网场景详情页里标注的字节数进行对比。如果有明显不一致,就对那一个文件重新下载。
这里再分享一条经验:不要图省事用find命令把所有文件列出来然后觉得"都有文件了就是完整的"。ScanNet的.sens文件在下载一半中断时同样会生成一个不完整的二进制文件,占着名字但内容已经废了。最可靠的办法还是依赖脚本的日志或者文件大小对比,缺文件的场景会在后续运行你自己写的数据解析代码时原形毕露。
4.4 全量数据的分盘策略
如果机器上磁盘空间实在紧张,可以把不同任务的数据分别放到不同物理硬盘上。比如.sens原始数据放在一块SSD上,语义标注放到一块机械盘上,再通过软链接把目录结构拼回一个统一的/data/scannet路径,这样既能跑通脚本,又不耽误后续工具读文件。命令也很简单:
mkdir -p /data/scannet/scans ln -s /mnt/ssd1/scannet_sensor /data/scannet/scans ln -s /mnt/hdd1/scannet_labels /data/scannet/scans软链接处理得当,大部分后续处理工具都不受影响。我现在的服务器就是两块2TB固态加一块4TB机械盘这么搭配的,ScanNet的全量数据加上其他数据集共存完全没有压力。
5. 按需下载数据子集:语义标注、关键帧和2D图各取所需
5.1 任务与数据子集的对应关系
ScanNet的数据子集划分方式是理解整个数据集的钥匙。简单来说,.sens文件是"母带",里面按帧封装了彩色图、深度图、内参和相机位姿,但你要用某个具体任务时,通常不需要反复去解这个母带,直接下载官方已经解好的子集更高效。下面的表格是我平时对照用的:
| 任务方向 | 优先下载的任务参数 | 对应数据内容 |
|---|---|---|
| 语义/实例分割 | scannetv2_labels | 点级别的语义标签、实例标签 |
| 3D点云分割 | sensor_data + scannetv2_labels | 重建网格、原始帧、标签 |
| 多视图深度/位姿估计 | scannet_posts | 按帧彩色图、深度图、位姿、内参 |
| SLAM/配准评测 | sensor_data 或 scannet_posts | 原始传感器数据和位姿序列 |
这里提醒一下,scannet_posts是已经按帧拆包好的目录,每个场景下面直接是color、depth、pose、intrinsic这些子文件夹,结构直观,加载代码写起来最省事。但如果未来要复现某些老论文里的基础流程,它们更常要求你对.sens自行解包,因为有些作者实现里传递的文件路径格式是写死的。
5.2 用SenseReader读取.sens并提取关键帧
如果你手头只有.sens原始文件,又需要拿到某个任务的输入图,可以写个小脚本提取。ScanNet仓库的SensorData目录下提供了sens_reader.py这个类,本质上就是一个可迭代的传感器数据容器,按帧取出Color、Depth、Pose等信息,然后保存到磁盘。
from sens_reader import SenseReader reader = SenseReader("scan0011_00.sens") for frame_id, frame in enumerate(reader): color = frame.color depth = frame.depth pose = frame.pose # 这里可以按需保存color/depth/pose到本地文件夹实际运行时要注意,解包.sens是非常消耗磁盘I/O的操作,一个4GB的.sens解出来可能占掉原来的三倍空间。所以除非确实需要完整的逐帧数据做训练,我一般只解出关键帧序列。ScanNet官方在sens_reader里提供了s_reader.extract_key_frames()这样的方法,传入一个关键帧的索引列表就能只提取需要的帧,能省下大量空间和时间。
5.3 标注文件的常见命名规则
多次看到有人下载完语义标注之后面对一堆.ply文件不知道怎么用。其实ScanNet v2标注的命名规律非常清晰:*_vh_clean_2.labels.ply是面片级别的语义标注,*_vh_clean_2.segs.json是超体素分割结果,*_vh_clean_2.instances.align.annotated.ply是实例标注。一般点云语义分割模型直接加载.labels.ply文件,里面的class字段就是每个点的类别索引;instance相关文件则配合标注格式用于实例分割。
我还习惯在跑数据处理前先用plyfile读一个标注文件,统计一下类别分布,确认void和有效类别的比例是否正常。这一步看似简单,但能提前发现很多问题:比如标注文件没下载全、版本不一致、或者类别索引偏移,省得等训练跑到一半才发现mIoU完全不对。
6. 申请不下来时的出路:社区镜像、目录结构和校验
6.1 等不到官方许可时的替代方案
总会有一些情况是不得不等的:学校邮箱收不到邮件、审核周期太长、或者你只想要一小部分场景快速跑基线实验。这时候不必死磕官方网站,有两个我实际用过的替代途径可以考虑。
第一个是OpenDataLab这类国内公开数据集平台,上面能找到ScanNet的镜像版本,有的直接提供整包下载链接,不用走学术协议审核流程。第二个是Hugging Face上的社区数据集仓库,有研究机构和个人把ScanNet整理成更现代的目录格式上传,方便用数据集加载库直接读取。不过这些渠道毕竟不是官方同步,版本新旧、标注是否完整、文件是否被重新编码过,都要自己花时间确认。我的建议是:能申请到官方权限还是优先官方,镜像渠道更适合临时跑通代码或做小规模实验。
6.2 拿到数据后如何快速熟悉目录结构
无论从哪个渠道拿到ScanNet,第一步都建议先打印一下目录树,形成对这个数据集的"直觉"。全量下载后,scans目录下是场景ID命名的子目录,每个子目录里至少包含.sens、.txt、重建后的网格模型*_vh_clean.ply以及干净版网格*_vh_clean_2.ply。语义标注则集中在scannetv2_labels目录下,和场景ID一一对应。
打开一个.ply网格文件看一下点数和颜色分布,用一个语义标注文件对比看看PointNet里预处理的输入格式,比自己直接去读一堆论文的DataLoader要直观得多。这一步做好了,后面写数据处理脚本时的效率会高很多。
6.3 关于数据版本和标签类别的最后提醒
ScanNet的标注类别在社区里有一个大家默认的"20类"概念,实际使用时要注意:背景空点位和无法标注的点会被标成void,有效类别加上otherfurniture一共是20个。很多预处理脚本里会把void过滤掉,训练时只对剩余有效点计算损失。如果你直接拿20类的模型去预测别人清理过void的数据,输出的mIoU会有明显差异,这一点在做跨数据集评测时尤其容易踩坑。
最后说个我自己常干的小事:第一次下一份ScanNet子集之后,我会在输出目录里放一个README.txt,把任务参数、下载日期、磁盘占用、版本信息全部记下来。三个月后当你发现某个实验的基线效果不对劲,翻这个文件就能快速定位是数据版本的问题还是自己代码的锅,省下来的排查时间远超当时写这几行字的时间。