evaluate.py에서 gt값을 불러오는 부분은 다음과 같다.
gt_boxes = load_gt(nusc, self.eval_set, TrackingBox, verbose=verbose)
load_gt()함수를 자세히 살펴보자.
# Read out all sample_tokens in DB.
sample_tokens_all = [s['token'] for s in nusc.sample]
sample token을 sample.json에서 읽어옴.
for sample_token in sample_tokens_all:
scene_token = nusc.get('sample', sample_token)['scene_token']
scene_record = nusc.get('scene', scene_token)
if scene_record['name'] in splits[eval_split]:
sample_tokens.append(sample_token)
sample.json에서 sample_token정보를 읽고 이를 바탕으로 scene.json에서 'name' 값이 splits[eval_split(val로 설정)]안에 있는 경우만 token 저장. 결과적으로 scene-0001부터 scene-1110 중에서 150개의 scene만 저장된다.(validation 150개)
scene.json예시
{
"token": "57146b2ebf10432f84b15a3038fe1755",
"log_token": "f93e8d66ce4b4fbea7062d19b1fe29fb",
"nbr_samples": 40,
"first_sample_token": "be75cf94ec3841cca384e1c9a2499a00",
"last_sample_token": "5abb17189643497f8600a187fb20aa55",
"name": "scene-1110",
"description": "Night, rain, bus, turn right, peds, bus stop"
}
우리가 하려는 task는 detection이 아닌 Tracking이므로 box_cls = TrackingBox 이다.
elif box_cls == TrackingBox:
# Use nuScenes token as tracking id.
tracking_id = sample_annotation['instance_token']
tracking_id_set.add(tracking_id)
sample_annotation.json에서 'instance_token'값을 받아와 tracking_id로 사용
sample_annotation.json 예시
{
"token": "2cd832644d09479389ed0785e5de85c9",
"sample_token": "c36eb85918a84a788e236f5c9eef2b05",
"instance_token": "5e2b6fd1fab74d04a79eefebbec357bb",
"visibility_token": "3",
"attribute_tokens": [],
"translation": [
993.884,
612.441,
0.675
],
"size": [
0.3,
0.291,
0.734
],
"rotation": [
-0.04208490861058176,
0.0,
0.0,
0.9991140377690821
],
"prev": "5cd018cb2448415ab8aff4dc7256999a",
"next": "",
"num_lidar_pts": 2,
"num_radar_pts": 0
}
tracking_name을 간단하게 수정함
tracking_name = category_to_tracking_name(sample_annotation['category_name'])
def category_to_tracking_name(category_name: str) -> Optional[str]:
"""
Default label mapping from nuScenes to nuScenes tracking classes.
:param category_name: Generic nuScenes class.
:return: nuScenes tracking class.
"""
tracking_mapping = {
'vehicle.bicycle': 'bicycle',
'vehicle.bus.bendy': 'bus',
'vehicle.bus.rigid': 'bus',
'vehicle.car': 'car',
'vehicle.motorcycle': 'motorcycle',
'human.pedestrian.adult': 'pedestrian',
'human.pedestrian.child': 'pedestrian',
'human.pedestrian.construction_worker': 'pedestrian',
'human.pedestrian.police_officer': 'pedestrian',
'vehicle.trailer': 'trailer',
'vehicle.truck': 'truck'
}
if category_name in tracking_mapping:
return tracking_mapping[category_name]
else:
return None
결과적으로 gt 데이터 구성은 다음과 같다.
sample_boxes.append(
box_cls(
sample_token=sample_token,
translation=sample_annotation['translation'],
size=sample_annotation['size'],
rotation=sample_annotation['rotation'],
velocity=nusc.box_velocity(sample_annotation['token'])[:2],
num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
tracking_id=tracking_id,
tracking_name=tracking_name,
tracking_score=-1.0 # GT samples do not have a score.
)
)
sample_token : sample.json에서 읽어온 token 값
translation, size, rotation : sample_annotation.json에서 읽어온 bbox 정보
velocity : translation정보를 사용하여 계산함.(box_velocity()함수에서). 이전 frame과 다음 frame 사이 difference.
num_pts : point 개수
tracking_id : sample_annotation.json에서 'instance_token'값을 받아와 tracking_id로 사용
tracking_name : category 이름(car, pedestrian, truck, ...)
tracking_score : 의미 없는 값? (-1.0) gt sample에서는 사용하지 않는다고...
이 sample_boxes 값이 gt_boxes로 return된다.
이 후, ego 차량과 bboxes 정보 사이의 거리를 추가하기 위해 add_center_dist()함수가 호출된다.
# Add center distances.
pred_boxes = add_center_dist(nusc, pred_boxes)
gt_boxes = add_center_dist(nusc, gt_boxes)
이 때, ego 차량 정보는 ego_pose.json 파일에서 불러와 사용함. 두 정보 모두 global coordinate 기준이기 때문에 바로 계산이 가능하다고 한다.(ego_translation = box - ego_pose)
마지막으로 gt, prediction 정보에서 box정보를 필터링하는 과정을 거친다.(nuscenes evaluation code 조건을 만족하기 위함)
Before running the evaluation code the following pre-processing is done on the data:
- All boxes (GT and prediction) are removed if they exceed the class-specific tracking range.
- All bikes and motorcycle boxes (GT and prediction) that fall inside a bike-rack are removed. The reason is that we do not annotate bikes inside bike-racks.
- All boxes (GT) without lidar or radar points in them are removed. The reason is that we can not guarantee that they are actually visible in the frame. We do not filter the predicted boxes based on number of points.
- To avoid excessive track fragmentation from lidar/radar point filtering, we linearly interpolate GT and predicted tracks.
pred_boxes = filter_eval_boxes(nusc, pred_boxes, self.cfg.class_range, verbose=verbose)
gt_boxes = filter_eval_boxes(nusc, gt_boxes, self.cfg.class_range, verbose=verbose)
최종적으로 split(eval_set)에 해당하는 sample만 남기는 과정을 거치면 evaluate을 수행하기 위한 init 과정이 끝난다.
self.tracks_gt = create_tracks(gt_boxes, nusc, self.eval_set, gt=True)
self.tracks_pred = create_tracks(pred_boxes, nusc, self.eval_set, gt=False)