nuScenes数据集标注格式
一、标注格式
官方提供的标注数据一共有15个json文件:
1、category.json
nusc.list_categories()
nusc.category[9]
(1)token: 唯一标识;
(2)name:物体类别名称 ;
(3)description :类别详细描述。
其中物体类别一共有23类,涵盖了行人、汽车、楼房、动物等
2、attribute.json
nusc.list_attributes()
my_instance = nusc.instance[27]
first_token = my_instance[‘first_annotation_token’]
last_token = my_instance[‘last_annotation_token’]
nbr_samples = my_instance[‘nbr_annotations’]
current_token = first_token
i = 0found_change = False
while current_token != last_token:
current_ann = nusc.get(‘sample_annotation’, current_token)
current_attr = nusc.get(‘attribute’, current_ann[‘attribute_tokens’][0])[‘name’]
if i == 0:
pass
elif current_attr != last_attr:
print("Changed from `{}` to `{}` at timestamp {} out of {} annotated timestamps".format(last_attr, current_attr, i, nbr_samples))
found_change = True
next_token = current_ann['next']
current_token = next_token
last_attr = current_attr
i += 1
描述了物体本身的一些状态,比如行驶、停下等等:
(1)token :唯一标识;
(2)name :属性名称 ;
(3)description :属性详细描述。
其中属性一共有8种
3、visibility.json
nusc.visibility
anntoken = ‘a7d0722bce164f88adf03ada491ea0ba’
visibility_token = nusc.get(‘sample_annotation’, anntoken)[‘visibility_token’]
print(“Visibility: {}”.format(nusc.get(‘visibility’, visibility_token)))
nusc.render_annotation(anntoken)
anntoken = ‘9f450bf6b7454551bbbc9a4c6e74ef2e’
visibility_token = nusc.get(‘sample_annotation’, anntoken)[‘visibility_token’]
print(“Visibility: {}”.format(nusc.get(‘visibility’, visibility_token)))
nusc.render_annotation(anntoken)
描述一个物体可视的程度,即被遮挡、截断的程度。在kitti中就是那两个遮挡、截断的数字,nuscences中用一个百分比来表示的
(1)token :唯一标识;
(2)level:可视化级别,是一个百分数,越高则越清晰,即识别越简单 ;
(3)description: 详细描述。一共有4个等级,分别是0到40%,40到60%,60到80%,80到100%。
4、instance.json
以实例为单位,记录某个实例出现的帧数、初始token、结尾token等:
(1)token:唯一表示
(2)category_token:类别标识,可以找到category.json里的对应类别
(3)nbr_annotations:出现的数量,即该实例在此数据集一共出现了多少帧
(4)fist_annotation_token:第一帧的annotation标识,在sample_annonation.json里可以找到对应标注,下同
(5)last_annotation_token:最后一帧的annotation标识
5、sensor.json
nusc.sensor
nusc.sample_data[10]
保存所有传感器的数据表,包含一些简单的传感器类型,内容如下图:
包含3个key,分别是:
(1)token:唯一标识;
(2)channel:位置;
(3)modality:类型(camera、lidar、radar)。
6、calibrated_sensor.json
nusc.calibrated_sensor[0]
一个比较大的数据表,存放了所有场景下相机的标注信息,包括了外参和内参。虽然说相机大部分场景下都是同一个,但是相机外参难免会发生微调,内参也会出现细微的变动,因此对于每一个照片,都有一个对应的相机标注
(1)token:唯一标识;
(2)sensor_token:从sensor.json中对应得到相机类型;
(3)translation:相机外参,偏移矩阵,单位为米;
(4)rotation:相机外参,四元数旋转角;
(5)camera_intrinsic:相机内参(似乎只有camera会有)。
两个相机外参都是相对于ego,也就是相机所在车的坐标系的参数,即一个相对量,这里在ego_pose.json中还会提到。
7、ego_pose.json
nusc.ego_pose[0]
相机所在车的标注信息
(1)token:唯一标识;
(2)timestamp:Unix时间戳,应该是保存数据表时候的一个时间戳,怀疑与图片名的后缀一一对应,没有详细考证;
(3)rotation:车辆外参,四元数旋转角;
(4)translation:车辆外参,偏移矩阵,单位为米。
ego车辆,还有照片中其他车辆(sample_annotation.json)的外参,参考坐标系是世界坐标系,世界坐标系的原点是lidar或radar定义的,没有什么规律,所以要求其他车辆的相机坐标系坐标,就需要在这三个外参(ego、camera、sample)换算一下
8、log.json
print(“Number of logs
in our loaded database: {}”.format(len(nusc.log)))
nusc.log[0]
一些场景、日期的日志信息,大部分情况没有太大作用
(1)token:唯一标识;
(2)logfile:日志文件;
(3)vehicle:车辆名称(咱也不知道是个啥);
(4)data_captured:拍摄日期;
(5)location:拍摄地点(新加坡和波士顿)。
9、scene.json
nusc.list_scenes()
my_scene = nusc.scene[0]
my_scene
(1)token:唯一标识;
(2)log_token:日志token,从log.json索引出对应日志;
(3)nbr_samples:场景中出现的sample的数量,就是该场景下一共出现过多少个标注的物体,同一物体就算一次;
(4)first_sample_token:第一个sample的token,从sample.json中可以索引出唯一sample,下同;
(5)last_sample_token:场景下的最后一个sample;
(6)name:场景名;
(7)description:场景描述。
10、sample.json
first_sample_token = my_scene[‘first_sample_token’]
The rendering command below is commented out because it tends to crash in notebooks# nusc.render_sample(first_sample_token)
my_sample = nusc.get(‘sample’, first_sample_token)my_sample
nusc.list_sample(my_sample[‘token’])
(1)token,唯一标识;
(2)timestamp:时间戳;
(3)prev:上一张照片token;
(4)next:下一张照片的token;
(5)scene_token:场景标识,从scene.json中对应唯一场景。
11、sample_data.json
my_sample[‘data’]
sensor = 'CAM_FRONT’cam_front_data = nusc.get(‘sample_data’, my_sample[‘data’][sensor])
cam_front_data
nusc.render_sample_data(cam_front_data[‘token’])
sample对应的简单信息,不包括标注,可以索引出同一个物体前后帧的信息
(1)token:唯一标识;
(2)sample_token:可以从sample.json中索引出唯一对应的sample;
(3)ego_pose_token:对应的ego车辆的token,可以从ego_pose中索引出来,据我观察,1和3都是相同的;
(4)calibrated_sensor_token:可以从calibrated_sensor.json中索引出对应的相机外参和内参,3和4就对应索引出上文所说的ego和camera的外参,sample的外参并不在这个表里,而是在sample_annotation.json中,见下文;
(5)timestamp:时间戳;
(6)fileformat:文件格式,照片和雷达格式;
(7)is_key_frame:是否是关键帧,Nuscenes中,每秒两帧关键帧,提供标注信息;
(8)heihgt:照片像素高度,似乎只有jpg才会有,都是900;
(9)width:同上,像素宽度,都是1600;
(10)filename:照片名;
(11)prev:上一个sample_data的token,从本数据表中可以索引出对应的数据,是同一个物体的上一个标注,即上一次出现这个物体是在哪里,下同;
(12)next:下一个sample_data的token。
12、sample_annotation.json
my_annotation_token = my_sample[‘anns’][18]
my_annotation_metadata = nusc.get(‘sample_annotation’, my_annotation_token)
my_annotation_metadata
nusc.render_annotation(my_annotation_token)
my_instance = nusc.instance[599]
my_instance
instance_token = my_instance[‘token’]
nusc.render_instance(instance_token)
print(“First annotated sample of this instance:”)
nusc.render_annotation(my_instance[‘first_annotation_token’])
print(“Last annotated sample of this instance”)
nusc.render_annotation(my_instance[‘last_annotation_token’])
保存了物体的标注信息
(1)token:唯一标识;
(2)sample_token:从sample.json中索引出唯一对应的sample;
(3)instance_token:从instance.json中索引出唯一对应的instance;
(4)visibility_token:从visibility.json中索引出唯一对应的visibility;
(5)attribute_token:从attribute.json中索引出唯一对应的attribute;
(6)translation:物体外参,偏移矩阵,单位为米;
(7)size:物体大小,单位为米,顺序为宽、长、高;
(8)rotation:物体外参,四元数旋转矩阵;
(9)prev:同一个物体,上一帧标注的token,在本数据表中索引出唯一对应的标注信息,下同;
(10)next:下一帧的标注token;
(11)num_lidar_pts:bbox中出现的lidar点数量,下同;
(12)num_radar_pts:bbox中出现的radar点数量。
13、map.json
print(“There are {} maps masks in the loaded dataset”.format(len(nusc.map)))
nusc.map[0]
(1)category:地图类别,似乎都是sematic的,因为提供的地图图片都是分割的,Nuscenes本身也包括了道路分割的数据集;
(2)token:唯一标识;
(3)filename:对应的地图文件名;
(4)log_tokens:地图中的日志文件。
14、image_annotations.json
这个表是没有出现在官方的标注格式说明中的,可以看出还是有一点冗余的,但是如果不用官方接口,自己写dataloader,还是很重要的,本表包括了2DBbox等信息
(1)attribute_token:从attribute.json中索引出唯一对应的attribute;
(2)bbox_corners:2DBbox像素坐标,分别是x1,y1,x2,y2;
(3)category_name:类别名称(谢天谢地终于不用索引了);
(4)filename:图片名;
(5)instance_token:从instance.json中索引出唯一对应的instance;
(6)next:下一个物体的信息,这个应该是没有规律的,最多是按照顺序来依次记录每个出现的物体,通过这个文件可以遍历整个数据集中的所有物体;
(7)num_lidar_pts:bbox中出现的lidar点数量,下同;
(8)num_radar_pts:bbox中出现的radar点数量;
(9)prev:上一个物体,同6;
(10)sample_annotation_token:从sample_annotation.json中索引出唯一对应的sample_annotation;
(11)sample_data_token:从sample_data.json中索引出唯一对应的sample_data;
(12)visivility_token:从visibility.json中索引出唯一对应的visibility。
总结
Nuscenes数据集中存在很多token之间的互相引用跳转,要仔细看好token索引的到底是哪一个数据表,不然容易出错。
Nuscenes的数据表,总的来说感觉还是存在一些冗余的,如果要遍历所有数据,给两个思路,一个是通过image_annotations.json来遍历,一个是通过sample_data.json来遍历,似乎后者更好一些,因为官方的方法是通过sample_data。
另外,官方提供了一个pytorch的开发包来写dataloader,同时因为数据都是公开的,也完全可以自己写一个。博主实验之后发现自己写的dataloader效率可能比官方提供的包还要高一些(毕竟省去了一些不必要的初始化)但建议还是用官方的,更加标准同时也更加准确,因为不熟悉数据标注,我自己写的出了很多错误,特别是外参转换上。
nuScenes基础
nusc.category[0]
cat_token = nusc.category[0][‘token’]
cat_token
nusc.get(‘category’, cat_token)
nusc.sample_annotation[0]
nusc.get(‘visibility’, nusc.sample_annotation[0][‘visibility_token’])
one_instance = nusc.get(‘instance’, nusc.sample_annotation[0][‘instance_token’])
one_instance
1、Use nusc.field2token()
ann_tokens = nusc.field2token(‘sample_annotation’, ‘instance_token’, one_instance[‘token’])
ann_tokens_field2token = set(ann_tokens)
ann_tokens_field2token
ann_record = nusc.get(‘sample_annotation’, one_instance[‘first_annotation_token’])
ann_record
ann_tokens_traverse = set()
ann_tokens_traverse.add(ann_record[‘token’])
while not ann_record[‘next’] == “”:
ann_record = nusc.get(‘sample_annotation’, ann_record[‘next’])
ann_tokens_traverse.add(ann_record[‘token’])
print(ann_tokens_traverse == ann_tokens_field2token)